# 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.

## 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.
- **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.

## 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.
