From 4995456136c433a71c96ccbff500f20445966d3b Mon Sep 17 00:00:00 2001 From: cproudlock Date: Fri, 14 Aug 2026 15:38:27 -0400 Subject: [PATCH] docs: take one site's name, hosts and paths off the public wiki The publishability gate caught internal tooling names and developer paths but nothing site-specific, so roughly sixty leaks reached the wiki: the site name in ten documents, real fleet hostnames in the collector and GE-Enforce examples, an internal database name through the whole import guide, imaging-share paths, and a maintainer's username as the Deciders line of every ADR and inside a generated curl example. None of it is a security matter on an air-gapped fleet. It matters because these pages are read by engineers at other plants, and a document that names one site throughout reads as that site's notes rather than a product's documentation - which is exactly what it then gets treated as. Examples now use neutral hostnames, the site is "the reference site" where the distinction carries meaning, and ADRs are decided by "ShopDB maintainers". The gate carries all of these patterns, so the next one fails a build. Two documents leave docs/ because they were never written for an outside reader. PROJECT-REVIEW.md is an internal health memo pinned to a commit from July, whose headline finding (an untracked playbook) has since been fixed - it is history, and git holds it. PILOT-DEPLOY.md is one site's own cutover runbook, complete with a "re-measure before publishing" placeholder; it moves next to the loader it belongs to, in scripts/site_imports/wjf/. ADR-015 is AMENDED rather than rewritten. Its enforcement section still said report-only and its backlog still listed hardcodes that are now cleared, which left the record contradicting itself. The amendment says what changed and why the report-only period ended; the original text stays, because what the decision looked like when it was taken is the part worth keeping. Also corrects llms.txt's response envelope, which had errors at the top level and pagination at meta.total. Both are nested one deeper, so anything written against that description read undefined on every error it tried to handle. --- docs/API-REFERENCE.md | 2 +- docs/ASSET-COMPOSITION.md | 4 +- docs/COLLECTOR-INTEGRATION.md | 8 +-- docs/DEPLOY-WINDOWS-IIS.md | 2 +- docs/GE-ENFORCE-CLIENT.md | 2 +- docs/GE-ENFORCE-DEPLOY.md | 2 +- docs/GE-ENFORCE-DISPLAY.md | 2 +- docs/IMPORT-ADOPTION.md | 4 +- docs/IMPORT-API.md | 8 +-- docs/INSTALL-WINDOWS.md | 2 +- docs/PROJECT-REVIEW.md | 56 ------------------- docs/UPGRADE.md | 4 +- .../adr/ADR-001-asset-as-platform-contract.md | 2 +- docs/adr/ADR-002-plugin-versioning.md | 2 +- docs/adr/ADR-003-plugin-distribution.md | 2 +- docs/adr/ADR-004-deployment-topology.md | 2 +- .../ADR-005-equipment-vs-measuringtools.md | 2 +- docs/adr/ADR-006-collector-contract.md | 2 +- ...ADR-007-product-versioning-and-releases.md | 2 +- .../adr/ADR-008-plugin-migration-ownership.md | 2 +- docs/adr/ADR-009-frontend-plugin-gating.md | 2 +- docs/adr/ADR-010-frontend-plugin-hooks.md | 2 +- docs/adr/ADR-011-machines-rename.md | 2 +- .../ADR-012-geenforce-manifest-ownership.md | 4 +- .../ADR-013-plugin-catalog-and-lean-builds.md | 2 +- docs/adr/ADR-014-schema-lean-per-site.md | 2 +- .../ADR-015-site-specific-configuration.md | 52 +++++++++++++---- docs/adr/ADR-016-credential-delivery.md | 2 +- docs/api-inventory.json | 2 +- docs/geenforce-api-cutover.md | 8 +-- docs/llms.txt | 15 +++-- docs/openapi.json | 2 +- docs/proposals/dashboard-live-fleet.md | 2 +- docs/proposals/ge-enforce-plugin.md | 4 +- .../site_imports/wjf}/PILOT-DEPLOY.md | 0 tests/test_docs_publishable.py | 9 +++ 36 files changed, 104 insertions(+), 118 deletions(-) delete mode 100644 docs/PROJECT-REVIEW.md rename {docs => scripts/site_imports/wjf}/PILOT-DEPLOY.md (100%) 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'),