A command reaching 'succeeded' only proves the agent ran the action, not that it posted a new telemetry snapshot. Reloading panels on command success alone re-rendered stale service/process rows, so a stopped process could still show running right after Refresh Status. Add a lightweight telemetrymeta probe exposing lastreportedat and gate both the header Refresh Status action and inline row Start/Stop on that stamp advancing past a pre-action baseline. Polling is bounded so an offline or idle agent never hangs the button, and messaging distinguishes a confirmed refresh from a queued one. Bring the .NET endpoint agent source (service, tray, installer) into the repo so Cameron can merge and test, with an agent/.gitignore keeping bin/obj/publish out. Includes the DncMain launch-path fix, tray single-file host env scrub, and best-effort file logger.
87 lines
3.9 KiB
Markdown
87 lines
3.9 KiB
Markdown
# Agent workflow rules
|
|
|
|
Companion to `copilot-instructions.md` (which owns naming/style/architecture).
|
|
These cut wasted turns. All are lessons from real sessions.
|
|
|
|
## Environment - know these, do not rediscover
|
|
|
|
- Shell is **PowerShell**: use `;` not `&&`, `2>$null` not `2>/dev/null`.
|
|
- API container `shopdb-flask-wilm-api-1` (Flask, port `5001`).
|
|
- DB container `shopdb-flask-wilm-db-1` (MySQL 8, root pw `root_password_123`).
|
|
- SPA is **baked into the image** (not bind-mounted). Plugin code is
|
|
**bind-mounted read-only**.
|
|
- `docs/` is stripped to a public wiki mirror - keep working docs out of it.
|
|
- `requirements.txt` is hash-checked - never hand-edit.
|
|
|
|
## Verify, don't react
|
|
|
|
- The terminal "Command failed" banner fires on any non-zero exit or stderr
|
|
output and is **usually a false negative** (`curl --fail` on a 400,
|
|
pytest/npm/git writing to stderr). Judge success from real output.
|
|
- Read container health via
|
|
`docker inspect --format '{{.State.Health.Status}}'` plus a probe **without**
|
|
`--fail` - never eyeball the exit code.
|
|
|
|
## Container runs the image, not your edits
|
|
|
|
Before running anything in the container that must reflect a change:
|
|
|
|
1. Frontend: `npm run build`, then
|
|
`docker cp frontend/dist/. shopdb-flask-wilm-api-1:/app/frontend/dist`.
|
|
2. Backend/scripts/templates: `docker cp` the files (or rebuild) first.
|
|
|
|
If a result contradicts a change you just made, suspect stale container code
|
|
before re-deriving the fix.
|
|
|
|
## Testing
|
|
|
|
- Confirm the working directory once, up front.
|
|
- Run the smallest test that proves the point; find the minimal polluter for
|
|
isolation bugs. Run the full suite only at the end.
|
|
- Clear stale local state before plugin/migration tests:
|
|
`if (Test-Path instance/plugins.json) { Remove-Item instance/plugins.json -Force }`.
|
|
- Naming-gate hits inside local venvs (`.ci-venv/`, `env/`) are not repo code -
|
|
do not "fix" vendored libraries.
|
|
- Final gates, exactly as CI: `python -m pytest tests/ -q`;
|
|
`cd frontend; npx vitest run; npm run build; cd ..`;
|
|
`bash scripts/check-naming-and-style.sh`.
|
|
|
|
## Migrations and imports
|
|
|
|
- Every migration: idempotent guard + real downgrade; must run clean on strict
|
|
MySQL 8. If you rename indexes in `upgrade()`, update `downgrade()` to match.
|
|
- Avoid duplicate indexes: `index=True` plus an explicit `db.Index` on the same
|
|
column creates two.
|
|
- Importers/seeders match by natural key and must be idempotent - prove it by
|
|
running twice (expect `updated`, not `created`). Watch all-or-nothing writes.
|
|
|
|
## Don't offer rollbacks you don't need
|
|
|
|
State current state plainly; surface a rollback path only for a real regression.
|
|
|
|
## Wilmington (wilm) local-run known failures - do not re-investigate
|
|
|
|
Running the full `python -m pytest tests/ -q` on a Windows dev box (not CI)
|
|
produces a stable set of environmental failures that are NOT code regressions.
|
|
Treat these as expected locally; judge your change against CI/Linux instead:
|
|
|
|
- `test_core/test_security_headers.py::test_a_served_upload_is_sandboxed` -
|
|
`PermissionError [WinError 32]` (served upload file still locked by another
|
|
process). Windows file-lock timing, not a headers bug.
|
|
- `test_plugin_contract.py::test_import_scan_covers_symlinked_plugins` -
|
|
`OSError [WinError 1314]` (symlink privilege not held). Needs admin/Developer
|
|
Mode to create symlinks; irrelevant to plugin logic.
|
|
- `test_plugin_importguard.py::test_verified_source_signed_ok_tampered_raises` -
|
|
CRLF vs LF (`b'# plugin\r\n' == b'# plugin\n'`). Git autocrlf checkout, not a
|
|
signing bug.
|
|
- `test_docs_publishable.py` and `test_docs_versions.py` - the local `docs/`
|
|
tree is not the stripped public wiki mirror, so doc-count / PROJECT-MAP
|
|
assertions fail. Expected off-CI.
|
|
- `test_core/test_public_read_surface.py::test_every_get_route_is_gated_or_named_public`
|
|
- pre-existing ungated-GET inventory drift; only act on it if YOUR change
|
|
added a new GET route.
|
|
|
|
If a run shows ONLY these, the suite is green for our purposes. Anything outside
|
|
this list is real - investigate it.
|
|
|