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.
WJ classic-ASP import loader (reference implementation)
This is site-specific reference glue, not product code. It maps West
Jefferson's classic-ASP shopdb / cmmc_usb / wjf_employees schema onto the
maintained, schema-agnostic import contract in docs/IMPORT-API.md.
Every adopting site has its own source database. Nobody else runs this loader. Instead, copy the pattern:
- Point the harness at your source DB(s) (edit
harness.Source). - Write per-entity stages that read your tables and POST to the same
docs/IMPORT-API.mdendpoints withAuthorization: Bearer <admin PAT>andX-Import-Mode: true. - Persist legacy-id -> new-id crosswalks (see
harness.IdMap) so later stages resolve foreign keys and a crashed run resumes.
The import API is the stable contract; loaders are per-site. The mapping itself is the bulk of the work: enumerate the source tables against this contract and write one stage per entity (this loader is the worked example).
Running (against a THROWAWAY import database)
DATABASE_URL='mysql+pymysql://root:PW@127.0.0.1:3306/shopdb_flask_import?charset=utf8mb4' \
venv/bin/python -m scripts.site_imports.wjf.run --stages reference,employees
Prereqs: a fresh target DB built with flask db upgrade + flask plugin upgrade-all + flask seed permissions/settings/reference-data, and the three
source dumps loaded into scratch DBs (shopdb_src, cmmc_usb_src,
wjf_employees_src). See scratchpad/IMPORT-PLAN.md for the full mapping,
resolved decisions, and remaining stages.
Status
Complete - all 15 stages built and verified end-to-end against a fresh scratch
target (zero endpoint errors): reference, employees, catalog, assets
(the hub + machineid->assetid crosswalk), locations, printers,
communications, applications (+ installs), warranties, notifications,
knowledgebase, relationships, subnets, usb, verify.
Last full run: 983 assets (computer 663, machine 76, network 58, measuring-tool 136, printer 50), 24 locations, 415 employees, 850 installs, 461 primary IPs, 464 warranties, 261 notifications, 341 KB, 93 relationships, 37 subnets, 18 USB devices + 232 events. UI spot-check passed (computer list + shop-floor map).
Known follow-ups: general asset locationid is null (no source column outside
printers); the ~146 duplicate machinenumbers are first-wins-skipped by decision.