Files
shopdb-flask/.github/copilot-instructions.md
benjamin.baker aa9dafc5ca Add application-driven policy generation with an acknowledgement gate
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.
2026-09-07 13:27:59 -04:00

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), run git 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 -File for 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>.sh to avoid issues with WSL resolving bare bash in PowerShell.

Installer Logic

  • The endpoint agent installer shipped to endpoints is generated by _install_script() in plugins/endpoints/api/siteagent.py, NOT agent/installer/install-shopdbagent.ps1. Any installer logic change must be made in siteagent.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 - NOT machine_types, BusinessUnits.
  • DB columns: lowercase, concatenated, singular. No underscores. machineid, lastzabbixsync, isactive - NOT machine_id, last_zabbix_sync.
  • Foreign keys: referenced table (singular) + id: locationid, vendorid.
  • Booleans: is/has prefix: 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}, local machineid.
  • 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, never machineId.
  • 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 a manifest.json (single source of truth: name, version, dependencies, api_prefix) and a BasePlugin subclass in plugin.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 + a docs/PLUGIN-HOOKS update in the same PR.
  • Migrations, never db.create_all(). The core Alembic chain is in migrations/versions/; each plugin owns its own chain under plugins/<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 run flask db upgrade then flask plugin upgrade-all before 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 Asset plus a plugin subtype row linked by assetid (FK, ON DELETE CASCADE). Consumables with quantities are standalone tables, not assets.
  • Ledger pattern: a cached quantityonhand moves 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 (values CollectorKey, BaseUrl), not committed in appsettings.json; appsettings.json holds 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.