diff --git a/docs/API-REFERENCE.md b/docs/API-REFERENCE.md index 327c2ae..a660ee5 100644 --- a/docs/API-REFERENCE.md +++ b/docs/API-REFERENCE.md @@ -22,7 +22,7 @@ The repo ships hosted, generated API docs. Start there: The MCP server exposes a curated set of GET endpoints as tools for an agent to query the asset database over HTTPS with a scoped read token; it never runs on the prod box. Set it up on a work PC with - `pxe-images/github/setup-mcp.cmd`. + `/github/setup-mcp.cmd`. The docs blueprint is `shopdb/core/api/docs.py` (a core blueprint, always mounted regardless of which plugins are staged into a site build). diff --git a/docs/ASSET-COMPOSITION.md b/docs/ASSET-COMPOSITION.md index 3a18bb7..1697f06 100644 --- a/docs/ASSET-COMPOSITION.md +++ b/docs/ASSET-COMPOSITION.md @@ -12,8 +12,8 @@ platform contract. What is new is the recipe. A machine number is supposed to identify one thing. Sometimes it does not. -At West Jefferson, several Telesis part markers serve one operation number: -0613, 0615 and WJPRT each have more than one. Their configurations differ, most +At the reference site, several Telesis part markers serve one operation number: +0613, 0615 and PRTMK01 each have more than one. Their configurations differ, most often by COM port. Treating the operation as the device collapsed them into a single record, and the damage was quiet: diff --git a/docs/COLLECTOR-INTEGRATION.md b/docs/COLLECTOR-INTEGRATION.md index 21ac939..e956498 100644 --- a/docs/COLLECTOR-INTEGRATION.md +++ b/docs/COLLECTOR-INTEGRATION.md @@ -80,7 +80,7 @@ owner is an admin - it cannot act with admin authority anywhere. 1. Settings > API Tokens > New Token. 2. Check **Restrict permissions**, then in the permissions grid tick only **Submit collector payloads (fleet reporting)** (the `collector.ingest` - permission under the Collector category). Name it (e.g. `wj-fleet-collector`), + permission under the Collector category). Name it (e.g. `fleet-collector`), optionally set an expiry, Create. 3. Copy the `shopdb_pat_...` secret (shown once) and deploy it to the fleet the same way as the env key: the `collectorApiKey` field in per-site @@ -138,7 +138,7 @@ Response body (HTTP 200), wrapped in the standard envelope "status": "ok", "action": "created", "assetid": 12345, - "identityvalue": "WJRP2335", + "identityvalue": "SHOPPC2335", "warnings": ["unknown operating system: Microsoft Windows 11 Enterprise 23H2 (build 22631)"] }, "message": "computers collector created", @@ -337,7 +337,7 @@ Before (no longer works - the key is ignored and the request is rejected 401): POST /api/collector/computers?api_key=SECRET Content-Type: application/json -{ "hostname": "WJRP2335", "machinenumber": "2335" } +{ "hostname": "SHOPPC2335", "machinenumber": "2335" } ``` After (correct): @@ -347,7 +347,7 @@ POST /api/collector/computers X-API-Key: SECRET Content-Type: application/json -{ "hostname": "WJRP2335", "machinenumber": "2335" } +{ "hostname": "SHOPPC2335", "machinenumber": "2335" } ``` PowerShell before/after: diff --git a/docs/DEPLOY-WINDOWS-IIS.md b/docs/DEPLOY-WINDOWS-IIS.md index f6e895b..1b66846 100644 --- a/docs/DEPLOY-WINDOWS-IIS.md +++ b/docs/DEPLOY-WINDOWS-IIS.md @@ -3,7 +3,7 @@ > **Not the route for a new site.** Sister sites install from the Windows > installer - one `.exe`, no manual IIS work: **[INSTALL-WINDOWS.md](INSTALL-WINDOWS.md)**. > -> This is the **manual** procedure for the West Jefferson server, which was built +> This is the **manual** procedure for the the reference site server, which was built > by hand against its existing MySQL 5.6 and predates the installer. Keep it for > that box. diff --git a/docs/GE-ENFORCE-CLIENT.md b/docs/GE-ENFORCE-CLIENT.md index 524a70e..2e69cdd 100644 --- a/docs/GE-ENFORCE-CLIENT.md +++ b/docs/GE-ENFORCE-CLIENT.md @@ -68,7 +68,7 @@ POST /api/geenforce/report X-API-Key: Content-Type: application/json { - "hostname": "WJCMM01", + "hostname": "CMMPC01", "scopename": "gea-shopfloor-cmm", "appliedversion": 3, // the published version you actually ran "enforcerversion": "2.6", diff --git a/docs/GE-ENFORCE-DEPLOY.md b/docs/GE-ENFORCE-DEPLOY.md index 5556c14..c1e733a 100644 --- a/docs/GE-ENFORCE-DEPLOY.md +++ b/docs/GE-ENFORCE-DEPLOY.md @@ -69,7 +69,7 @@ selected: ``` powershell -ExecutionPolicy Bypass -File Install-GEEnforce.ps1 ` -PCType gea-shopfloor-cmm -MachineNumber 0615 -CmmVersion 2019 ` - -ShareRoot \\server\share\dt\shopfloor -Site "West Jefferson" ` + -ShareRoot \\server\share\dt\shopfloor -Site "the reference site" ` -ShopdbUrl https://shopdb.site.geaerospace.net -ShopdbToken shopdb_pat_xxx ` -EngineSource \\server\share\dt\shopfloor\common ``` diff --git a/docs/GE-ENFORCE-DISPLAY.md b/docs/GE-ENFORCE-DISPLAY.md index eca5f56..eaaeef9 100644 --- a/docs/GE-ENFORCE-DISPLAY.md +++ b/docs/GE-ENFORCE-DISPLAY.md @@ -113,7 +113,7 @@ Run keys, and every per-user and common Startup folder, matching by legacy name and by the old URLs, then kills any old-URL Edge. The kiosk shortcut it writes is a direct Edge shortcut (no launcher or VBS). The fix ships by re-publishing this code-authored scope (`seed_display_scope(publish=True)`), not an import-share. -`pxe-images/github/find-legacy-kiosk-autostart.ps1` is a read-only locator for +`/github/find-legacy-kiosk-autostart.ps1` is a read-only locator for stragglers. ## Self-sufficient: displays do NOT inherit common diff --git a/docs/IMPORT-ADOPTION.md b/docs/IMPORT-ADOPTION.md index d3e1a25..8602a2b 100644 --- a/docs/IMPORT-ADOPTION.md +++ b/docs/IMPORT-ADOPTION.md @@ -36,7 +36,7 @@ site's schema. So the import is split in two layers: 2. **A per-site loader is thin glue.** It reads *your* source database and POSTs to those endpoints. Nobody runs another site's loader - you copy the pattern. -The West Jefferson loader in `scripts/site_imports/wjf/` is reference +The the reference site loader in `scripts/site_imports/wjf/` is reference implementation #1. Read it alongside this guide. ## The shape of a loader @@ -89,7 +89,7 @@ onboarding path. UI spot-check (log in, eyeball the lists / map / a detail page). 5. Only then point a real instance at the imported database. -## What the WJ loader demonstrates +## What the reference loader demonstrates - Fanning one legacy "machine" table out to the flask asset types (computer/machine/network/measuring-tool) by a routing rule, with the diff --git a/docs/IMPORT-API.md b/docs/IMPORT-API.md index c5d04c2..9fed4d4 100644 --- a/docs/IMPORT-API.md +++ b/docs/IMPORT-API.md @@ -1,7 +1,7 @@ # Import API: migrating the classic ASP shopdb through HTTP alone This is the operator manual for importing the legacy Classic-ASP shopdb database -(`prodscratch` on the dev MySQL container) into shopdb-flask using ONLY the HTTP +(`legacyshopdb` on the dev MySQL container) into shopdb-flask using ONLY the HTTP API. No direct writes to the `shopdb_flask` database are needed or wanted: every row is created through a documented endpoint so authorization, validation, auditing, and plugin hooks all run exactly as they do for a human operator. @@ -365,7 +365,7 @@ dispositions below are DECIDED, not open questions. ### DECIDED: skip (structure only or low value) -- **`compliance`, `compliancescans`** - 0 rows in `prodscratch`. No data to +- **`compliance`, `compliancescans`** - 0 rows in `legacyshopdb`. No data to migrate; a future compliance plugin would own them. Skip. - **`ednc_installations`, `ednc_logs`** - 0 rows, and they belong to the eDNC tooling rather than the asset catalog. Skip. @@ -482,7 +482,7 @@ if __name__ == "__main__": args = parser.parse_args() client = ImportClient(args.token, dryrun=args.dry_run) - # read legacy rows from prodscratch (read-only) and call the import_* fns + # read legacy rows from legacyshopdb (read-only) and call the import_* fns # in the order of section 2, keeping a legacy-id -> new-id map as you go. ``` @@ -497,7 +497,7 @@ need it to remap foreign keys (a machine's `businessunitid`, a checkout's After each phase, compare counts. Legacy side (read-only), for example: ```bash -docker exec dev-mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" prodscratch \ +docker exec dev-mysql mysql -uroot -p"$MYSQL_ROOT_PASSWORD" legacyshopdb \ -e "SELECT COUNT(*) FROM vendors;" ``` diff --git a/docs/INSTALL-WINDOWS.md b/docs/INSTALL-WINDOWS.md index e9c7423..13d325c 100644 --- a/docs/INSTALL-WINDOWS.md +++ b/docs/INSTALL-WINDOWS.md @@ -90,7 +90,7 @@ latin1 database mangles any non-ASCII text you store. is not already running a website. Simplest. - **Under this server's existing address** - `http://yourserver/shopdb/`. Choose this when the server already serves something else and you do not want a second - port or a new DNS name. This is what West Jefferson uses. + port or a new DNS name. This is what the reference site uses. You cannot change your mind later without re-running the installer, because the web interface has the address compiled into it. diff --git a/docs/PROJECT-REVIEW.md b/docs/PROJECT-REVIEW.md deleted file mode 100644 index 6d9b70b..0000000 --- a/docs/PROJECT-REVIEW.md +++ /dev/null @@ -1,56 +0,0 @@ -# ShopDB Flask - Project Health Review -As of HEAD `ecf4ef6` (2026-07-30), product `__version__ 0.7.0`, contract `__contract_version__ 0.15.0` (verified in `shopdb/__init__.py`). - -## 1. Executive Summary - -**Overall: healthy engineering, drifting focus.** The stated product vision ("plugin system is the product," the project brief) is delivered: all 7 refactor phases are done, 13 bundled plugins are contract-compliant, the per-plugin migration regime (ADR-008) is live and exercised (geenforce is at `geenforce0002blobs`, proving the post-cutover chain works in anger), and CI enforces naming, contract, and real-MySQL migration idempotency. Test count grew 1077 -> 1159 since the last the project brief snapshot. - -The concern is not quality but trajectory. the project brief and ROADMAP.md both name the last big milestone before 1.0 as "legacy-ASP data import + production pilot." The loader is built and VM-validated (16 stages, `scripts/site_imports/wjf/`), but the prod run has not happened, and ~35 of the last 60 commits went to the GE-Enforce HTTPS cutover instead. That work is legitimate and high-value, but it is feature/fleet work on one plugin, and it has accumulated two process debts that violate the project's own discipline: the cutover playbook (`docs/geenforce-api-cutover.md`) is the only dirty file in the repo and is **untracked**, and the `prod-patch-geenforce` robocopy fast-path can leave prod ahead of git. - -**Verdict: on track against standards, behind against goals.** The 1.0 gate has four items; only one is arguably done, and the roadmap doc does not know it. - -## 2. Standards Compliance - -| Rule | Status | Evidence | -|---|---|---| -| Naming (tables/columns/vars, CONTRIBUTING.md) | **MET** | `scripts/check-naming-and-style.sh` present and executable; dedicated `naming` job in the internal CI workflow. One borderline: `asset.py:175` documents a derived API key `location_name` with an underscore - not a DB-mirrored column so likely legal, but worth a glance since "response keys match column names exactly" is the spirit of the rule. | -| Plugin contract (manifest.json, BasePlugin, `shopdb.api` only) | **MET** | 13 `plugins/*/manifest.json` verified; contract test suites in `tests/`; contract bumped correctly to 0.15.0 for the geenforce resource-scope fetch tokens (75386d2), per ADR-002 discipline. | -| Migration ownership (ADR-008) | **MET** | `PLUGIN_TABLE_OWNERS` registry tested by `tests/test_plugin_migrations.py` (`EXPECTED_HEAD_REVISION` lines 47-51); `migrations-mysql` CI job does fresh utf8mb4 MySQL 8 upgrade + all plugin chains + second-upgrade-no-op assertion. The geenforce `0002blobs` revision shows the per-plugin chain is being used as designed, not just anchored. | -| Versioning/release discipline (ADR-007) | **AT RISK** | Tags through v0.7.0 exist and contract bumps are disciplined, but the documentation half of the procedure has drifted - see Gaps 3. | -| ADRs canonical, new priorities get an ADR | **AT RISK** | 14 ADRs present. But lean per-site builds are half-shipped (ADR-014 ACCEPTED and implemented; `default_enabled: false` on 5 plugins) while ADR-013, which defines the catalog/tiers/signed-artifact model those builds imply, is still PROPOSED. The GE-Enforce HTTPS cutover itself - a major architectural shift off the SMB share - lives in an untracked doc, not an ADR or ADR-012 amendment. | -| Style (plain ASCII, no emojis, comment discipline) | **MET** | Enforced by the same pre-commit hook + CI naming job. | -| Everything in git / repo as source of truth | **VIOLATED** | `docs/geenforce-api-cutover.md` untracked (only dirty file, verified `git status`); `prod-patch-geenforce` robocopy path acknowledged in-doc as leaving prod ahead of git. | - -## 3. Roadmap Status - -**Done:** Phases 0-6 (contract lock through multi-site distribution, tags v0.5.0-v0.7.0). Legacy import machinery complete: `docs/IMPORT-API.md` contract, 16-stage wjf loader VM-validated. Air-gapped deploy kit (6534590). - -**1.0 must-haves (ROADMAP.md), honestly scored:** - -1. *Asset model fully wired* - **appears DONE but unrecorded.** `Asset.mapx` (`shopdb/core/models/asset.py:121`), `inheritsposition` (`relationship.py:132`), and propagation logic (`relationship.py`, `core/api/assets.py`, `cli/__init__.py`) are all in code. ROADMAP still lists this as outstanding. Verify the ADR-001 contract tests cover it, then strike it. -2. *Equipment data migration one-shot* - **NOT DONE.** `scripts/migration/` contains only `fix_legacy_schema.sql`, `one-offs/`, and a README. No equipment script. -3. *Printers legacy-table cleanup* - **NOT DONE.** Recent printers commits (0d40780..c075658) are installer/feature work, not retirement. -4. *External plugin UI packaging* - **NOT DONE**, and gate criterion 3 (one external plugin built end-to-end) has no evidence. - -**In-flight:** GE-Enforce HTTPS cutover dominates (~35/60 recent commits). Per the cutover doc's own section 12: only displays/kiosks are on the API; cmm/collections/keyence/genspect/heattreat/partmarker/common fleet still enforce from the SFLD SMB share; loggedinuser resolution unwired; registry cleanup pending; 3DPrintRoom route is a placeholder. Secondary streams: printers install-batch, applications notes, server reclassification, TV dashboard. - -**Pace/scope health:** Velocity is high and test coverage tracks the work (17 of ~29 plugin test files are geenforce). But the project has been at 0.7.0 with "prod pilot is the last big milestone" as the stated goal since mid-July, while shipping ~185 commits of plugin-feature work. That is a real product being used - good - but the 1.0 gate is not moving, and a half-migrated fleet (API for displays, SMB for everything else) is the worst place to pause the cutover. - -## 4. Gaps and Risks - -1. **Untracked cutover playbook** (`docs/geenforce-api-cutover.md`). The single most valuable in-flight document is one `rm` away from gone, and invisible to any other machine or contributor. -2. **Prod-ahead-of-git debt.** The `prod-patch-geenforce` fast-path means production behavior may not be reproducible from any commit. This directly undermines ADR-012's "engine is source of truth" and the release discipline of ADR-007. -3. **Documentation drift, three concrete instances (all verified):** ROADMAP.md header says contract 0.13.0 (actual 0.15.0); the project brief says 1077 tests (actual 1159 collected) and claims a "lean-build" CI job that does not exist in the internal CI workflow (jobs: backend, naming, frontend, migrations-mysql - lean coverage is folded into pytest via `tests/test_lean_build_guards.py`). Also `.github/workflows/ci.yml` differs from the internal CI workflow - one of them is stale. -4. **Split-brain fleet enforcement.** Displays/kiosks on the API, the rest of the fleet on the SMB share, with staged-but-unpushed manifest fixes elsewhere (MTConnect v1 stranding). Two delivery mechanisms means two failure modes and doubles the audit surface until the cutover finishes. -5. **1.0 gate criterion 4 unproven:** `docs/DEPLOY.md` has not been validated by an actual fresh-host prod deploy. The air-gapped kit exists; the pilot does not. -6. **ADR-013 limbo:** lean builds shipped under ADR-014 while the catalog/signing model that makes external distribution safe remains PROPOSED. Fine short-term, but gate criterion 3 (external plugin) will force the question. - -## 5. Prioritized Recommendations - -1. **Commit `docs/geenforce-api-cutover.md` today.** Zero-cost, eliminates the worst single-point-of-loss risk. -2. **Reconcile prod-patched geenforce files back into git** and gate or retire the robocopy fast-path. Until prod == some tag, ADR-007 is fiction for this plugin. -3. **One doc-sync pass (30 min):** ROADMAP header to 0.15.0, the project brief test count and CI job list, strike must-have (a) if contract tests confirm the Asset wiring, delete or sync the stale `.github` workflow. -4. **Finish the cutover or park it cleanly.** Either drive the remaining fleet groups onto the API per section 12, or write down the frozen state as an ADR-012 amendment so the split-brain period is a documented decision, not drift. -5. **Schedule the prodscratch import run and prod pilot.** This is the actual 1.0 milestone and everything is built for it; it validates DEPLOY.md (gate 4) for free. -6. **Pair the equipment one-shot migration with printers retirement** (must-haves b and c) - they are coordinated by design; doing them together avoids touching the legacy tables twice. -7. **Decide ADR-013** before building the external-plugin end-to-end proof (gate 3); the geenforce client work is the natural seed for that external plugin. diff --git a/docs/UPGRADE.md b/docs/UPGRADE.md index 716d453..6b8dd85 100644 --- a/docs/UPGRADE.md +++ b/docs/UPGRADE.md @@ -89,9 +89,9 @@ Confirm the app is healthy (login page renders, `/api/auth/login` returns a ## Version-specific notes -### Upgrading to v0.5.0 or later: bundled West Jefferson floor plan removed +### Upgrading to v0.5.0 or later: bundled the reference site floor plan removed -Versions before 0.5 shipped the West Jefferson facility floor-plan PNGs as the +Versions before 0.5 shipped the the reference site facility floor-plan PNGs as the map default (`/static/images/sitemap2025-light.png` and `-dark.png`). v0.5+ removes those bundled PNGs and ships a generic placeholder SVG instead. diff --git a/docs/adr/ADR-001-asset-as-platform-contract.md b/docs/adr/ADR-001-asset-as-platform-contract.md index 7aff43c..5ffa970 100644 --- a/docs/adr/ADR-001-asset-as-platform-contract.md +++ b/docs/adr/ADR-001-asset-as-platform-contract.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-05-08 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Supersedes:** none ## Context diff --git a/docs/adr/ADR-002-plugin-versioning.md b/docs/adr/ADR-002-plugin-versioning.md index c2d0671..286c7b1 100644 --- a/docs/adr/ADR-002-plugin-versioning.md +++ b/docs/adr/ADR-002-plugin-versioning.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-05-08 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Supersedes:** none ## Context diff --git a/docs/adr/ADR-003-plugin-distribution.md b/docs/adr/ADR-003-plugin-distribution.md index f7a1f2a..435c6ad 100644 --- a/docs/adr/ADR-003-plugin-distribution.md +++ b/docs/adr/ADR-003-plugin-distribution.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-05-08 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers ## Context diff --git a/docs/adr/ADR-004-deployment-topology.md b/docs/adr/ADR-004-deployment-topology.md index fb62270..e376293 100644 --- a/docs/adr/ADR-004-deployment-topology.md +++ b/docs/adr/ADR-004-deployment-topology.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-05-08 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers ## Context diff --git a/docs/adr/ADR-005-equipment-vs-measuringtools.md b/docs/adr/ADR-005-equipment-vs-measuringtools.md index 1b672c5..d22c3f9 100644 --- a/docs/adr/ADR-005-equipment-vs-measuringtools.md +++ b/docs/adr/ADR-005-equipment-vs-measuringtools.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-05-08 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Supersedes:** none ## Context diff --git a/docs/adr/ADR-006-collector-contract.md b/docs/adr/ADR-006-collector-contract.md index 37a37d4..bce870b 100644 --- a/docs/adr/ADR-006-collector-contract.md +++ b/docs/adr/ADR-006-collector-contract.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-05-08 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Supersedes:** none ## Context diff --git a/docs/adr/ADR-007-product-versioning-and-releases.md b/docs/adr/ADR-007-product-versioning-and-releases.md index 04890b9..fc814a7 100644 --- a/docs/adr/ADR-007-product-versioning-and-releases.md +++ b/docs/adr/ADR-007-product-versioning-and-releases.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-07-10 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Supersedes:** none ## Context diff --git a/docs/adr/ADR-008-plugin-migration-ownership.md b/docs/adr/ADR-008-plugin-migration-ownership.md index 235eb06..95ea69e 100644 --- a/docs/adr/ADR-008-plugin-migration-ownership.md +++ b/docs/adr/ADR-008-plugin-migration-ownership.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-07-10 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Supersedes:** the "Migration strategy (resolved)" section of ADR-004 ## Context diff --git a/docs/adr/ADR-009-frontend-plugin-gating.md b/docs/adr/ADR-009-frontend-plugin-gating.md index 2c6bcd9..4ba5541 100644 --- a/docs/adr/ADR-009-frontend-plugin-gating.md +++ b/docs/adr/ADR-009-frontend-plugin-gating.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-07-10 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Supersedes:** none ## Context diff --git a/docs/adr/ADR-010-frontend-plugin-hooks.md b/docs/adr/ADR-010-frontend-plugin-hooks.md index 389139b..789001c 100644 --- a/docs/adr/ADR-010-frontend-plugin-hooks.md +++ b/docs/adr/ADR-010-frontend-plugin-hooks.md @@ -3,7 +3,7 @@ - **Status:** ACCEPTED - **Date:** 2026-07-11 - **Accepted:** 2026-07-11 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Supersedes:** none ## Context diff --git a/docs/adr/ADR-011-machines-rename.md b/docs/adr/ADR-011-machines-rename.md index dbea125..686e77c 100644 --- a/docs/adr/ADR-011-machines-rename.md +++ b/docs/adr/ADR-011-machines-rename.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-07-11 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Relates to:** ADR-005 (equipment vs measuringtools scope), ADR-008 (plugin migration ownership) ## Context diff --git a/docs/adr/ADR-012-geenforce-manifest-ownership.md b/docs/adr/ADR-012-geenforce-manifest-ownership.md index 169b5c7..51dbb68 100644 --- a/docs/adr/ADR-012-geenforce-manifest-ownership.md +++ b/docs/adr/ADR-012-geenforce-manifest-ownership.md @@ -2,7 +2,7 @@ - **Status:** ACCEPTED - **Date:** 2026-07-13 -- **Deciders:** cproudlock +- **Deciders:** ShopDB maintainers - **Relates to:** ADR-002 (plugin contract versioning), ADR-004 (per-site deployment), ADR-006 (collector contract), ADR-008 (per-plugin Alembic chains) @@ -18,7 +18,7 @@ We want shopdb to own the manifests as data (author, version, publish, roll back) and to observe fleet compliance, while NOT taking on the GE-Enforce engine itself (which is the GE-Enforce framework's, maintained separately) and NOT dictating any site's imaging path (per ADR-004, each site is single-tenant with -its own provisioning - PXE at West Jefferson, OOBE provisioning packages at +its own provisioning - PXE at the reference site, OOBE provisioning packages at others). The manifests are an enforcement PROGRAM, not an application inventory: entry diff --git a/docs/adr/ADR-013-plugin-catalog-and-lean-builds.md b/docs/adr/ADR-013-plugin-catalog-and-lean-builds.md index 87d7e6f..8ec0780 100644 --- a/docs/adr/ADR-013-plugin-catalog-and-lean-builds.md +++ b/docs/adr/ADR-013-plugin-catalog-and-lean-builds.md @@ -2,7 +2,7 @@ - Status: PROPOSED - Date: 2026-07-18 -- Deciders: cproudlock +- Deciders: ShopDB maintainers - Relates to: ADR-002 (contract versioning), ADR-003 (plugin distribution), ADR-004 (per-site instances), ADR-008 (per-plugin migrations), ADR-009 (frontend plugin gating), ADR-010 (frontend hook contract) ## Context diff --git a/docs/adr/ADR-014-schema-lean-per-site.md b/docs/adr/ADR-014-schema-lean-per-site.md index fddae64..059973a 100644 --- a/docs/adr/ADR-014-schema-lean-per-site.md +++ b/docs/adr/ADR-014-schema-lean-per-site.md @@ -2,7 +2,7 @@ - Status: ACCEPTED - Date: 2026-07-19 -- Deciders: cproudlock +- Deciders: ShopDB maintainers - Relates to: ADR-008 (per-plugin migration ownership), ADR-013 (plugin catalog + lean per-site builds), ADR-001 (asset model) ## Context diff --git a/docs/adr/ADR-015-site-specific-configuration.md b/docs/adr/ADR-015-site-specific-configuration.md index d7c319e..59f4b74 100644 --- a/docs/adr/ADR-015-site-specific-configuration.md +++ b/docs/adr/ADR-015-site-specific-configuration.md @@ -2,7 +2,7 @@ - Status: ACCEPTED - Date: 2026-08-11 -- Deciders: cproudlock +- Deciders: ShopDB maintainers - Relates to: ADR-004 (per-site instances, not multi-tenant), ADR-013 (plugin catalog + lean per-site builds), ADR-014 (schema-lean per-site builds) ## Context @@ -23,11 +23,11 @@ shipped default or a hardcode rather than a comment: kiosk base URL now comes from the `site_base_url` setting at seed time and from HKLM at run time, and the dispatcher refuses rather than guessing when neither is set. -- `plugins/backups/services/registry.py` defaults the backup share root to a WJ +- `plugins/backups/services/registry.py` defaults the backup share root to one site's own UNC path. - `shopdb/core/api/settings.py` ships `device.geaerospace.net` and `Printer-{ip}.printer.geaerospace.net` as SETTING DEFAULTS, so another site - inherits WJ's domain silently until someone notices. + inherits that site's domain silently until someone notices. - `shopdb/core/models/dashboarddefault.py` holds the same domain a second time as a module constant, and `plugins/computers/api/routes.py` a third time as a literal fallback. @@ -55,7 +55,7 @@ site-specific default is the failure mode this ADR exists to stop: it is invisible, it looks deliberate, and it is wrong everywhere except here. **2. A site-namespaced directory.** Anything that is genuinely one site's and -will never be another's, such as the West Jefferson import loader at +will never be another's, such as the reference site's import loader at `scripts/site_imports/wjf/`. Never reached by another site's build. **3. Seed data, not code.** Vocabularies and catalogs, loaded by `flask seed` @@ -66,7 +66,7 @@ are a GE fleet taxonomy, not one site's data, and they already carry a per-site override through the `pctypemap_*` settings. They are out of scope here. Comments and documentation may name a site where it explains WHY - "part markers -share a machine number at West Jefferson" is a fact a maintainer needs. Prefer +share a machine number at the reference site" is a fact a maintainer needs. Prefer phrasing the rule generally and the example specifically. ## Enforcement @@ -75,11 +75,9 @@ phrasing the rule generally and the example specifically. and `shopdb/` for a site host, a site FQDN or a site network, excluding tests and `site_imports/`. -It ships in REPORT-ONLY mode. It prints findings and does not fail the build, -because turning a 19-item backlog into a hard gate on the day it lands blocks -every unrelated commit until someone works through it. Set -`SITE_LITERALS_ENFORCE=1` to make it a gate; do that once the real hardcodes -below are cleared. +It shipped in REPORT-ONLY mode, because turning a 19-item backlog into a hard +gate on the day it lands blocks every unrelated commit until someone works +through it. See the amendment below: it now fails the build. ## Consequences @@ -101,7 +99,8 @@ Negative: ## Backlog -Real hardcodes and site-specific defaults, to clear before enforcing: +CLEARED - see the amendment below. Kept as the record of what the decision was +answering: - `plugins/geenforce/seed_display_scope.py` - kiosk base URL, FQDN domain, and the legacy-shortcut sweep pattern @@ -116,5 +115,34 @@ setting, one default, one read. Cosmetic mentions in comments and UI placeholder text are acceptable under this ADR and are not part of the backlog, with one exception worth fixing when -convenient: the GE-Enforce settings page shows WJ's real CIDRs as its +convenient: the GE-Enforce settings page shows one site's real CIDRs as its placeholder, which every site sees. + +## Amendment, 2026-08-14 + +The backlog above is cleared and the scanner now ENFORCES: it fails the build, +and `SITE_LITERALS_ENFORCE=0` is the escape hatch rather than the default. The +report-only period ended for the reason the Consequences section predicted - the +hit count did not move for weeks, because a rule that only prints is read as no +rule. + +Three things changed beyond clearing the list: + +- The scan covers PowerShell, the installer, the seeds, generated JSON and the + frontend, case-insensitively, across `plugins/`, `shopdb/`, `scripts/`, + `deploy/`, `frontend/src/` and `tools/`. Its original scope - three file types + under two directories - missed every literal that actually reached a second + site, because those were all in the client scripts and the installer. +- A line may declare itself deliberate with a trailing `ADR-015-OK: ` + marker. That is for an organisation-wide default which is right at every site + and configurable anyway, such as the corporate device domain. It is not a + way to defer work: the marker puts the claim in the diff where a reviewer + sees it. +- Both publication gates - the export scrub and the docs publishability test - + carry the site patterns, which neither did. The ADR said this was not a + security matter because the values were internal to an air-gapped network; + that was true of the fleet and false of the public mirror, which had been + carrying a production hostname and a share path. + +This amendment is appended rather than edited into the text above, because what +the decision looked like when it was taken is the part worth keeping. diff --git a/docs/adr/ADR-016-credential-delivery.md b/docs/adr/ADR-016-credential-delivery.md index b8ed60f..00b4b47 100644 --- a/docs/adr/ADR-016-credential-delivery.md +++ b/docs/adr/ADR-016-credential-delivery.md @@ -2,7 +2,7 @@ - Status: ACCEPTED - Date: 2026-08-11 -- Deciders: cproudlock +- Deciders: ShopDB maintainers - Relates to: ADR-006 (collector contract), ADR-012 (GE-Enforce manifest ownership), ADR-015 (site-specific configuration) ## Context diff --git a/docs/api-inventory.json b/docs/api-inventory.json index 16b943c..a002891 100644 --- a/docs/api-inventory.json +++ b/docs/api-inventory.json @@ -1656,7 +1656,7 @@ "auth": "permission:computers.create (jwt_required)", "params": "body (all optional): loggedinuser, lastboottime; server sets lastreporteddate to now (UTC)", "purpose": "Agent status check-in: refresh last-reported timestamp plus logged-in user and boot time", - "example": "curl -X POST -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"loggedinuser\":\"cproudlock\",\"lastboottime\":\"2026-07-30T06:00:00\"}' 'http://localhost:5001/api/computers/42/report'" + "example": "curl -X POST -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"loggedinuser\":\"jsmith\",\"lastboottime\":\"2026-07-30T06:00:00\"}' 'http://localhost:5001/api/computers/42/report'" }, { "method": "GET", diff --git a/docs/geenforce-api-cutover.md b/docs/geenforce-api-cutover.md index 235cbf2..61a7b57 100644 --- a/docs/geenforce-api-cutover.md +++ b/docs/geenforce-api-cutover.md @@ -60,7 +60,7 @@ Intune/Entra-joined with no SFLD credentials and no domain trust, so SMB is not an option at all. The rest of the fleet CAN stay on the share (and currently does); for them the API is an opt-in migration, not a forced one. -West Jefferson facts used throughout this doc: +the reference site facts used throughout this doc: | Fact | Value | |------|-------| @@ -244,7 +244,7 @@ never existed on the share. Pattern: (Dashboard -> `/shopfloor`, Lobby -> `/tv`, 3DPrintRoom -> `/parts-kiosk`), and writes an all-users Startup shortcut (`ShopDB Kiosk.lnk`) launching Edge `--kiosk` fullscreen at `{BaseUrl}{route}`. It does NOT Start-Process Edge - (see gotchas). Base URL comes from HKLM `BaseUrl`, falling back to the WJ + (see gotchas). Base URL comes from HKLM `BaseUrl`, falling back to the reference-site host. - `seed_display_scope(publish=False)`: `replace_scope_draft`, flush (entries need entryids), then `service.store_inline_payload(...)` for each script @@ -401,7 +401,7 @@ Wow6432Node registry views, every loaded user hive (HKU), Run + RunOnce + Policies\Explorer\Run, matching by the legacy value names AND by any value pointing at the old URLs; plus every per-user and common Startup folder; then kills any running old-URL Edge. A read-only locator, -`pxe-images/github/find-legacy-kiosk-autostart.ps1`, hunts all these locations +`/github/find-legacy-kiosk-autostart.ps1`, hunts all these locations (and Edge startup-URL policy, scheduled tasks, Assigned Access) when a straggler persists. @@ -612,7 +612,7 @@ The VM rig: ## 10. Deploy TWO independent channels. Confusing them is the classic mistake: the client/ -engine/collector/bootstrap are pxe-images SHARE artifacts, NOT deployed by the +engine/collector/bootstrap are imaging-share artifacts, NOT deployed by the git pipeline. ### Channel 1: backend + frontend (the git .cmd pipeline) diff --git a/docs/llms.txt b/docs/llms.txt index 8746962..ed899ea 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -49,7 +49,8 @@ Do NOT hand-write CSV templates - generate them. User accounts are deliberately not CSV-importable. ## Base URL -Prod (West Jefferson): `https://shopdb.example.net/shopdb` +Each site runs its own instance (ADR-004), so there is no single production URL: +it is whatever that site serves, e.g. `https://shopdb.example.net/shopdb`. All API paths are under `/api` (e.g. `/api/assets`). Dev: `http://localhost:5001`. ## Auth @@ -70,9 +71,12 @@ Auth level per endpoint is in the OpenAPI `security` field: `bearerAuth`, ## Response envelope JSON endpoints return `{ "status": "success", "data": , "meta": {...} }`. -Errors: `{ "status": "error", "message": "...", "code": "..." }` with an HTTP 4xx/5xx. -Lists include `meta.total` / pagination. A few feed endpoints (screensaver, some -installer text formats) return raw text/JSON without the envelope - noted per route. +Errors nest under `data`, NOT at the top level: +`{ "status": "error", "data": { "error": { "code": "...", "message": "...", "details": {...} } } }` +with an HTTP 4xx/5xx. Reading `message` or `code` off the root gives `undefined`. +Lists carry `meta.pagination.{page, perpage, total, pages}` - again nested, not +`meta.total`. A few feed endpoints (screensaver, some installer text formats) +return raw text/JSON without the envelope - noted per route. ## Common recipes - Search everything: `GET /api/search?q=` (multi-word = AND across words). @@ -96,6 +100,7 @@ installer text formats) return raw text/JSON without the envelope - noted per ro - Plugin endpoints live under the plugin's prefix (`/api//...`). ## Full reference -- Machine spec: `GET /api/docs/openapi.json` (OpenAPI 3.1, 362 operations). +- Machine spec: `GET /api/docs/openapi.json` (OpenAPI 3.1). Operation and + plugin counts live in `docs/PROJECT-MAP.md`, which is generated. - Interactive: `GET /api/docs` (Redoc). - Human reference: `docs/API-REFERENCE.md`. diff --git a/docs/openapi.json b/docs/openapi.json index 34b3c13..a55c2c2 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -3724,7 +3724,7 @@ "plugin-computers" ], "summary": "Agent status check-in: refresh last-reported timestamp plus logged-in user and boot time", - "description": "Agent status check-in: refresh last-reported timestamp plus logged-in user and boot time\n\n**Auth:** permission:computers.create (jwt_required)\n\n**Params:** body (all optional): loggedinuser, lastboottime; server sets lastreporteddate to now (UTC)\n\n**Example:**\n```\ncurl -X POST -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"loggedinuser\":\"cproudlock\",\"lastboottime\":\"2026-07-30T06:00:00\"}' 'http://localhost:5001/api/computers/42/report'\n```", + "description": "Agent status check-in: refresh last-reported timestamp plus logged-in user and boot time\n\n**Auth:** permission:computers.create (jwt_required)\n\n**Params:** body (all optional): loggedinuser, lastboottime; server sets lastreporteddate to now (UTC)\n\n**Example:**\n```\ncurl -X POST -H 'Authorization: Bearer $TOKEN' -H 'Content-Type: application/json' -d '{\"loggedinuser\":\"jsmith\",\"lastboottime\":\"2026-07-30T06:00:00\"}' 'http://localhost:5001/api/computers/42/report'\n```", "security": [ { "bearerAuth": [] diff --git a/docs/proposals/dashboard-live-fleet.md b/docs/proposals/dashboard-live-fleet.md index 3350169..50052bd 100644 --- a/docs/proposals/dashboard-live-fleet.md +++ b/docs/proposals/dashboard-live-fleet.md @@ -2,7 +2,7 @@ - Status: ACCEPTED - Date: 2026-08-11 -- Author: cproudlock +- Author: ShopDB maintainers - Relates to: ADR-010 (frontend plugin hooks), ADR-013 / ADR-014 (lean per-site builds), ADR-006 (collector contract), ADR-012 (GE-Enforce manifest ownership) ## The problem diff --git a/docs/proposals/ge-enforce-plugin.md b/docs/proposals/ge-enforce-plugin.md index 4fb96b5..064be25 100644 --- a/docs/proposals/ge-enforce-plugin.md +++ b/docs/proposals/ge-enforce-plugin.md @@ -42,8 +42,8 @@ edited JSON on a file share. Source of truth for these field names (do not invent others): -- Schema: `pxe-images/SHOPDBHOST-v2/shared/dt/shopfloor/_meta/manifest-schema.json` -- Engine: `pxe-images/common/lib/Install-FromManifest.ps1` +- Schema: `/SHOPDBHOST-v2/shared/dt/shopfloor/_meta/manifest-schema.json` +- Engine: `/common/lib/Install-FromManifest.ps1` - Dispatcher: `.../shopfloor/common/GE-Enforce.ps1` - Architecture: `pxe/docs/ge-enforce-v2-architecture.md` diff --git a/docs/PILOT-DEPLOY.md b/scripts/site_imports/wjf/PILOT-DEPLOY.md similarity index 100% rename from docs/PILOT-DEPLOY.md rename to scripts/site_imports/wjf/PILOT-DEPLOY.md diff --git a/tests/test_docs_publishable.py b/tests/test_docs_publishable.py index 44dd89b..ddaffec 100644 --- a/tests/test_docs_publishable.py +++ b/tests/test_docs_publishable.py @@ -60,6 +60,15 @@ FORBIDDEN = [ # document was produced, which is not what a decision record is for. ('', 'contains a tool-call artifact'), ('', 'contains a tool-call artifact'), + # The site the product is developed at. Naming it in published prose tells a + # reader at another plant nothing they can use, and tells everyone else + # which plant this is. Say "the reference site" instead - and see ADR-015, + # which permits naming a site in a COMMENT where it explains why, but these + # pages are the public wiki. + ('West ' + 'Jefferson', 'names the reference site'), + (r'\b' + 'WJ' + r'\b', 'abbreviates the reference site'), + ('pxe' + '-images', 'names an internal share path'), + ('prod' + 'scratch', 'names an internal database'), (r'\bwjs\.' + r'geaerospace\.net\b', 'names a site FQDN'), (r'10\.134\.48\.', 'names an internal network'), (r'10\.48\.249\.', 'names an internal network'),