Ticking "Required on all PCs" now generates a configuration policy that installs the application on every non-server endpoint it matches, rather than only flagging a software-compliance report row. Because that save is a fleet-wide deployment, it requires explicit acknowledgement. The gate is enforced in the API, not only in the form: a confirmation dialog that can be sidestepped by POSTing directly is decoration, not a safeguard. It fires only on the transition INTO the required state, since re-saving an already-required application deploys nothing new. Generated policies carry a new 'autoapp' source. They stay editable, and hand-editing one transfers ownership to 'local' so it survives a later uncheck; 'shipped' remains read-only. Existing required applications are not migrated retroactively - policies are only created on save. Also adds the monitorable fields that the shipped-policy breakup will build on, and repairs a mangled non-ASCII byte in copilot-instructions.md that was failing the style gate.
6.8 KiB
Copilot instructions for shopdb-flask
Follow these when suggesting code. They are enforced by a naming/style hook and
by CI (.github/workflows/ci.yml) - suggestions that break them fail the build.
CONTRIBUTING.md is the full authority; this is the short version.
General Guidelines
- Finish the in-flight task before starting new workstreams.
- When asked to "take notes" on a future idea, capture it in the notes file only - do not implement it, and do not expand on it at length in replies.
- Use a temp file for multi-line git commit messages instead of heredoc syntax:
git commit -F <file>. When committing in PowerShell, never pass multi-line commit messages inline (no here-strings like @'...'@ or multiple -m flags). PowerShell mangles them and git errors with "did not match any file(s) known to git". Instead, write the commit message to a temp file in the WORKSPACE ROOT (e.g.commitmsg.tmp), rungit commit -F commitmsg.tmp, then delete the file. Do NOT place the temp file inside .git/ - git prunes stray files there and the file disappears before commit runs. - Avoid using
powershell.exe -Filefor launching scripts, as it fails if the script file is missing or quarantined. - Long-running commands (pytest suites, npm builds, container rolls) must be redirected to a log file under
devnotes/(e.g.devnotes/_pytest.log) and launched non-blocking; never run as a blocking foreground command. Do not poll or tail the output; the user watches the file and will say when it is done. - Before taking any action that modifies code or files, warn the user first if a test run or build may be in progress, so discussion about the project can continue without altering files underneath a running test.
- Always invoke shell scripts with the explicit Git Bash path:
& "C:\Program Files\Git\bin\bash.exe" scripts/<script>.shto avoid issues with WSL resolving barebashin PowerShell.
Installer Logic
- The endpoint agent installer shipped to endpoints is generated by
_install_script()inplugins/endpoints/api/siteagent.py, NOTagent/installer/install-shopdbagent.ps1. Any installer logic change must be made insiteagent.py, followed by rolling the containers. - The user's workflow: the agent hands over only the built EXEs (
shopdbagent.exe,shopdb-endpoint-tray.exe); the user uploads them via the "Upload New/Legacy Agents" field in the Build Site Agent UI and builds the package with the newest version selected at the top.
Naming (LOCKED - the hook rejects violations)
- DB tables: lowercase, concatenated, plural. No underscores, no dashes.
machines,networkdevices,businessunits- NOTmachine_types,BusinessUnits. - DB columns: lowercase, concatenated, singular. No underscores.
machineid,lastzabbixsync,isactive- NOTmachine_id,last_zabbix_sync. - Foreign keys: referenced table (singular) +
id:locationid,vendorid. - Booleans:
is/hasprefix:isactive,isshopfloor. - Index names:
idx_<table>_<column>(underscores allowed here only).
Python
- A variable, attribute, function, or dict key that holds a DB value MUST match
the column name exactly - do NOT convert to snake_case.
Column
machineid->Machine.machineid,{"machineid": 1}, localmachineid. - Pure code that does NOT mirror a DB field uses normal snake_case (PEP 8):
loop_count,current_user,validate_input(). - Classes: PascalCase, spelled out (
NetworkDevice,AssetType).
JavaScript / Vue
- A JS variable holding an API field value matches the API key exactly - do NOT
camelCase it. API
{"machineid": 1}->response.machineid, nevermachineId. - Components: PascalCase (
AssetDetail.vue). CSS classes: lowercase-with-dashes.
API
- Endpoints: lowercase plural nouns, no underscores/dashes:
/api/networkdevices. - Query params + response keys match column names:
?locationid=5,{"machineid": 1, "lastzabbixsync": "..."}.
Allowed acronyms only
Universal: id url api http https json jwt sql os ip dns csv pdf cors ttl uuid html css orm. Domain: cmm cnc pc usb vnc winrm ssh ssl tls tcp udp smtp ldap vlan sso dnc focas clm mtconnect. Anything else: spell it out.
Banned shorthand
Never use cfg ctx mgr req res env util helper or db as a standalone variable
name. Spell out: config context manager request response environment utilities.
_bp is fine only as a suffix with a meaningful prefix (printers_bp).
Style (ASCII only)
- NO emojis anywhere - code, comments, strings, UI.
- NO em-dashes, en-dashes, Unicode arrows, or smart quotes. Plain ASCII only.
- Comments default to NONE. Add one only when the WHY is non-obvious; keep inline
#///comments terse. Docstrings stay normal English. - Dark theme is the default; keep UI functional and professional.
Architecture (do not violate)
- Plugins are the product. Plugin code lives in
plugins/<name>/{models,api,services,schemas}/with amanifest.json(single source of truth: name, version, dependencies, api_prefix) and aBasePluginsubclass inplugin.py. - Plugins never import core internals. Use the contract surface
shopdb.api(e.g.from shopdb.api import db, Asset, success_response). Adding to that surface is a contract-version bump + adocs/PLUGIN-HOOKSupdate in the same PR. - Migrations, never
db.create_all(). The core Alembic chain is inmigrations/versions/; each plugin owns its own chain underplugins/<name>/migrations/. New schema = a new migration with an idempotent guard and a real downgrade. Migrations must run clean on strict MySQL 8. Any model column/table change ships WITH its migration in the SAME change, and bringing up a new image MUST runflask db upgradethenflask plugin upgrade-allbefore serving traffic. A model that references a column its migration has not applied yet 500s every query on that table (1054 Unknown column), so code and schema must never be deployed out of step. - Asset model is the platform contract. Physical things are an
Assetplus a plugin subtype row linked byassetid(FK,ON DELETE CASCADE). Consumables with quantities are standalone tables, not assets. - Ledger pattern: a cached
quantityonhandmoves in the SAME commit as the signed transaction row it reflects. - Configuration for endpoint agent: The endpoint agent's live collector key and base URL are provisioned in the Windows registry
HKLM\SOFTWARE\GE\ShopDB(valuesCollectorKey,BaseUrl), not committed inappsettings.json;appsettings.jsonholds only non-secret runtime settings.
Before you finish a change
Run the three gates (CI runs the same):
python -m pytest tests/ -q
cd frontend && npx vitest run && npm run build && cd ..
bash scripts/check-naming-and-style.sh
Commits: short present-tense subject, body says WHY. No AI/tool attribution in commit messages, code comments, or docs.