2083029ff282725ac369acb8112f2950d7a898bc
14 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2083029ff2 |
Generate the collector script per site, and bring EventSaver into the repo
A site adopting ShopDB had to be handed two files and told what to edit in them. Both are now the product's, and one of them the server writes for you. GET /api/computers/client-script (admin) returns Report-AssetToShopDB.ps1 with this site's values already in it: site_base_url becomes the -ApiUrl default and the new computers_routableranges setting becomes -AllowedRanges. Only the PARAMETER DEFAULTS are substituted - the copy in plugins/computers/client/ stays runnable, so there is no second version to drift from the first - and everything stamped stays overridable by argument or registry, because a bay may need to differ from its site. Settings > Computers > Asset reporter edits the ranges, downloads the script and shows its SHA-256. The collector key is deliberately not stamped in, and a test fails if it ever is. That file lands on every shop-floor PC, and a token spread across hundreds of bays cannot be rotated quietly; it stays in the registry, provisioned per ADOPTING-AT-ANOTHER-SITE.md. The routable ranges are the last thing that was hardcoded in that script. They are now a setting, so West Jefferson's two CIDRs move out of source code and into that site's own configuration - which is what ADR-015 asks for - and a site that sets nothing still works, because the script falls back to the NIC carrying the default route. EventSaver joins it in plugins/slides/client/, source only: EventSaver.cs and EventSaver.ini, no compiled .scr - a binary is a release asset, like the installer exe. The share path that was compiled into Config.Folder is gone. It used to be the fallback when the ini was missing, which silently pointed a new site at the reference site's file server; it is now empty, and failing visibly beats displaying another site's slides. Verified by compiling the edited source in the Windows VM with the in-box csc.exe: 15,872 bytes, exit 0. Also: the DSC example in the adoption guide gains a CollectorRanges resource and stops passing -ApiUrl to a script that already reads BaseUrl from the registry the same example writes, and the guide points at the generated download instead of hand-editing a URL. The contract test caught the endpoint importing shopdb directly for the version string, which ADR-002 forbids a plugin from doing. The product and contract versions are in app.config now, which a plugin reads through current_app. Adds docs/proposals/printer-assignment.md: assign printers to a PC in ShopDB and let the bay install them, with what the fleet data says about drivers - HP and Xerox cover 41 of 44 printers with universal drivers, there are no Brother printers at all despite 208 files of Brother inkjet drivers in the installer, and printerdrivers holds one row pointing at a per-model folder instead of a universal driver. |
||
|
|
96df19702e |
docs: one manual runbook, and ADR statuses that mean something
DEPLOY-WINDOWS-IIS was a second copy of the manual IIS procedure that had diverged from the first: a different MySQL version (8.0, which reached end of life in April), a different port, a different plugin list, and a profile file that does not exist. Two runbooks for one procedure means a reader follows whichever they found, and one of them was wrong. INSTALL-WINDOWS-IIS covers everything it did plus a preflight step and the subpath method, so the one section it uniquely had - redeploying a hand-built server - is folded in there, with the plugin-chain step it was missing and a note to back up first, and the duplicate is gone. Everything that pointed at it now points at the survivor. Three ADR statuses said something untrue. ADR-013 said PROPOSED while half of it had shipped and ADR-014 had been accepted on top of it. A decision that has been implemented and depended upon is not proposed, and leaving one that way devalues every other status in the index. The catalog half is still unbuilt, which is the ordinary state of an accepted decision: accepted means settled, not delivered. ADR-016 said ACCEPTED for a design where nothing is built - the endpoint and permissions it describes do not exist, so a reader goes looking for them. The status stands, because the decision does; the header now says so plainly and points at where today's credentials actually live. ADR-003 and ADR-004 were ACCEPTED with their own Decision lines still opening "**PROPOSED:**", which reads as though the decision was never taken. And the dashboard proposal carried Status: ACCEPTED, which belongs to a decision record. A proposal is a proposal; the contract it produced is the ADR. |
||
|
|
4995456136 |
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. |
||
|
|
035419fa51 |
ADR-015: stop shipping one site's values, and make the rule a gate
The scanner has been reporting the same count for weeks, which is what a rule that only prints becomes. It now FAILS the build, and it looks where the leaks actually were: PowerShell, the installer, the seeds, generated JSON, the frontend - case-insensitively, across plugins, shopdb, scripts, deploy, tools. A line that is deliberate declares itself with an ADR-015-OK marker and a reason, so the claim is visible in review instead of tolerated in silence. What it found, fixed here: - The shadow client wrote one site's ShopDB URL into HKLM whenever the registry disagreed. At the site it was written for that reads as healing drift; anywhere else it overwrites the site's own address on every enforce cycle, and the site cannot win because the cycle repeats. The bay's value now wins, an explicit -BaseUrl seeds it, and with neither there is nothing honest to write, so it says so and skips. - The kiosk dispatcher fell back to one plant's host when HKLM was unset, so a kiosk elsewhere quietly opened a server it has no business reaching. The fallback is now this site's site_base_url, baked in at seed time, and the dispatcher refuses rather than guessing when neither is set. Its legacy shortcut matcher derives the host from that URL instead of naming one. - The OpenAPI generator hardcoded a production hostname into every spec it generated, which then published to a public wiki. The relative mount is the only server it can honestly name; a site passes its own by environment. - Placeholders and examples in the UI and the client help offered real internal subnets and a real production URL. They now use documentation ranges. Both publication gates - the export scrub and the docs publishability test - carry the site patterns, which neither did. One plant's hostname, FQDN and internal networks are out of the documentation and the generated specs. Comments naming the reference site are reworded rather than deleted: the reasoning is worth keeping, the plant name is not what makes it true. |
||
|
|
8b50e6fe2a |
geenforce: first dashboard card, and the widget contract it proves
Wave one of the dashboard proposal, built as a vertical slice so the contract is proven by something real before the other five cards follow. GET /api/geenforce/dashboard/failures lists entries that FAILED on their PC's most recent enforcement cycle. Per ENTRY, not per report: "three PCs failed" is a number, while "Install OpenText failed with exit 1603 on WJSF1234" is something a person can act on. Only current reports count, so a failure that has since been fixed clears itself instead of needing dismissing. Hostnames resolve to computerids in one query so each row links to the PC, and a PC shopdb does not know still appears - the failure is real even when the inventory is behind, and that is the bay most likely to be misconfigured. The data has been there all along. The only way to see any of it was to open one PC's report modal, one PC at a time. The widget declaration is the contract change. The old shape named a Vue component per widget, which cannot survive a lean build where a plugin's component may never be staged into the bundle - which is exactly why five plugins declare widgets pointing at components nobody ever wrote. This declares data, a generic renderer, a permission and a link template, the way ADR-010 already does for asset panels. A test asserts no 'component' key, so the old shape cannot creep back. empty: hide is part of the contract, not decoration. A card reporting "nothing wrong" daily teaches people to stop reading the page, which is how a fleet log reached 3,234 lines with 17 that mattered. Frontend rendering comes next; the endpoint and declaration stand alone and change nothing that exists. |
||
|
|
02d7619b1a |
Propose a dashboard that shows the fleet, not the row count
The dashboard reports totals that are true every day and actionable on none of them. Meanwhile a bay returned 500 to every collector report for a day and a half, eleven markers' backups overwrote each other for weeks, and GE-Enforce records failures, stale manifests and repeated self-heals that surface nowhere. Every one of those was found by someone going to look. Two things found while writing this shaped it. The plugin mechanism already exists - get_dashboard_widgets, an endpoint that aggregates across enabled plugins and isolates failures, and five plugins declaring widgets. And nothing consumes it: the frontend never calls the endpoint and not one of the declared components was ever written. So this finishes a half-built feature rather than starting one. It also proposes fixing the contract while it is still unused. Naming a component per widget does not survive a lean build, where a plugin's component may not be staged into the bundle at all. ADR-010 already solved this for asset panels - declare data and a render mode, let a generic core component draw it - and the same three renderers cover every card listed. The rest is principles that came out of this week: empty cards shrink, because a card that says "nothing wrong" daily trains people to stop reading, which is how a log reached 3,234 lines with 17 that mattered; every row links to the thing; cards declare a permission so the dashboard cannot become a way around RBAC; thresholds are settings with neutral defaults. Wave one is six cards whose data is already reliable. Wave two is the valuable one: diffing what a manifest says a PC should have against what it reports having, which nothing answers today and which is what GE-Enforce exists to guarantee. |
||
|
|
e005d1846a |
docs: wiki staleness sweep (Fable-orchestrated Opus audit)
Audited all 40 docs/ against the live codebase; fixed factual staleness in 23, 14 were clean. Highlights (all verified against code): - equipment -> machines (ADR-011 rename) in INSTALL/DEPLOY-WINDOWS-IIS, PLUGIN-GUIDE, GE-ENFORCE, ROADMAP. - Versions refreshed: contract 0.10.0 -> 0.13.0, product 0.5.0 -> 0.7.0, plus plugin example core_version pins. - Bundled set corrected to the current 13 (PLUGINS.md 7 -> 13 rows; DEPLOY eleven -> thirteen). - Per-plugin Alembic chain workflow (ADR-008) replacing stale core-chain steps in PLUGIN-QUICKSTART / BACKUP-RESTORE; deploy adds plugin upgrade-all. - Frontend plugin staging (ADR-010) replacing 'no frontend plugin system yet' in PLUGIN-GUIDE; view/route paths repointed to plugins/<name>/frontend/. - Corrected file paths (MapView.vue, manifest_schema.json), CLI (shelf-list), API gating (GET /api/plugins is optional-jwt), WJF 15 -> 16 stages, and retired Collector/PC-Types settings pages (ADR-012). - ge-enforce proposal marked ACCEPTED/built. |
||
|
|
6362cef699 |
printedparts docs: record the open-write kiosk decision; defer the dashboard widget
The kiosk take endpoint is the product's first unauthenticated mutation; spell out the acceptance criteria (decrement-only, badge attributed, bounded, physically rate-limited) so future open-write endpoints meet the same bar. The dashboard-widget milestone is marked optional: get_dashboard_widgets predates the ADR-010 data-only renderers and needs a core component to render. |
||
|
|
d99de002bf |
printedparts plugin: design proposal + hands-on development lab
Design for a 3D-printed-parts storefront: item catalog with images and quantity on hand, a transaction ledger attributing every take/restock/ adjust to a badge-scanned employee, an unauthenticated touch kiosk (scan bin barcode, scan badge, keypad quantity), 1x0.5in CODE128 bin labels, and stock/consumption/by-person reports. The lab guide walks a developer through building it in seven checkpointed milestones, reusing the USB badge contract, the measuringtools migration baseline, the models-image upload trio, and the open kiosk-endpoint precedents. |
||
|
|
6dc31c6149 |
Add GE-Enforce observed-state reporting: receipt + self-heal from PCs
PCs now report enforcement results back to shopdb, closing the desired-vs-observed loop. - POST /api/geenforce/report (geenforce.report service token): each cycle a PC posts the published version it applied, install/skip/fail/filtered counts, and per-entry outcomes. - Two tables: manifestenforcementreports (latest-per-host + history: applied version, enforcer version, counts, derived status ok/selfhealed/failed) and manifestenforcementresults (per entry: action installed/skipped/failed, selfhealed flag, exit code, warning/error message). - RECEIVED: reports carry the applied version; the admin view derives receivedlatest by comparing it to the scope's current published version, so the fleet view shows which PCs picked up an update. - SELF-HEAL: per-entry action captures drift correction (installed when it should already be present) vs skipped (already good) vs failed, with messages. - Admin reads: GET /reports (fleet compliance rollup) and GET /reports/<id> (per-entry detail). New geenforce.report permission. - Tables added to the (undeployed) 0001 baseline; geenforce.post_report is a service-token endpoint so it is exempt from the JWT authz sweep, like the collector blueprint. 8 reporting tests; full suite green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
cf2f9c308e |
Add manifest revision history + draft audit trail to GE-Enforce plan
- Every publish is a permanent immutable revision (manifestpublishedversions), kept indefinitely; optional retention policy (keep last M / prune older than N) deferred, default keep-everything. - Draft edits are not versioned (working copy overwrites), so field-level "who changed what between publishes" rides the existing core audit system - no new table, shows in the Audit Logs UI IT already uses. - Runbook: History tab for published versions + roll back; Audit Logs for draft edit provenance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
c88d673d7b |
Fold Fable execution review into GE-Enforce plan: simpler, IT-manageable
Add an execution plan and simplify the design for average-site-IT operability (the governing constraint from the review): Model simplifications: - One wide manifestentries table with an entrytype discriminator, not SQLAlchemy STI subclasses and not a JSON blob. ~64 total entries fleet-wide make sparse columns free and keep rows readable in plain SQL. - Published snapshots freeze the rendered JSON document in a single manifestjson column; drop the row-mirrored manifestpublishedentries family. Immutability is structural, rollback is a one-flag flip, diff is a text diff. - New manifestpayloads table for inline bytes with a ~1 MB app cap. - regvalue stores the raw JSON literal (DWord typing); applymode/updatewindow flagged inert-in-engine so the UI labels them. Execution plan (section 13): - Phases P0-P6 with gates; parity harness spec (two checks, IT-readable output, ~16-18 machine-profile fixtures); first vertical slice through gea-shopfloor-cmm; ranked fail-fast risks. - Milestone 1 = author + publish in shopdb, export to the share by a button, engine/dispatcher/PCs unchanged. Real pain relief at zero client risk, with a rollback IT already knows (restore the _meta/history backup). - Export-to-share promoted to a first-class feature and permanent break-glass. - Split permission geenforce.manage (edit) vs geenforce.publish (ship). - Move Up/Down instead of drag-and-drop; a "what would this PC get" simulator endpoint + UI; three-increment editor build. - Two-source pctypemap transition window; scope-inventory reconciliation (gea-shopfloor-display has no share dir). - Plain-English IT day-to-day runbook proving the design is manageable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
9dd2aa3cc6 |
Revise GE-Enforce plugin plan after review: parity, integrity, snapshots
Fold six review findings into docs/proposals/ge-enforce-plugin.md: - Parity gate is behavioral equivalence, not byte-identity. Re-serialized JSON differs in key order/whitespace/_comment formatting, so a raw diff never converges; the test is same ordered entry set with identical detection/ targeting/action per entry. - Dedicated payloadsha256 column, independent of detectionmethod. DetectionValue is a SHA256 only for detectionmethod=Hash; MSIs with Registry/FileVersion detection carry no payload hash, so an HTTP/inline fetch would otherwise run unverified bytes. Client verifies fetched bytes against payloadsha256. - Immutable published snapshots (manifestpublishedversions). Editing touches a draft only; publish freezes a snapshot; the client is always served the latest published snapshot, never the live draft; rollback republishes a prior snapshot (the post-cutover safety net once the on-share JSON is retired). - Scope uniqueness is (scopename, phase), not scopename alone; preinstall is one flat scope gated internally by PCTypes, not per-pctype scopes. - Alias graph: engine lib stays the single source of truth, shopdb only mirrors it for validation; do not invert to engine-fetches-from-shopdb. - Desired-vs-observed needs a new collector field (the installedVersions status map), not existing data; flagged as a dependency. Plus TLS trust for the SYSTEM-context client and importer skips .bak variants. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
1672e349e5 |
Collector auto-links measuring tools for metrology PCs; settings rail cleanup
Metrology PCs (CMM, Keyence, Genspect, wax-and-trace imaging pc-types) drive an attached measuring instrument. The PC itself stays a shopfloor PC, but the collector now models the instrument: - New METROLOGY_TOOL_MAP (pctypemap.py) maps those pc-types to a MeasuringToolType (CMM, Vision System, Genspect, Form Tracer). - ComputersPlugin._sync_measuringtool_link creates the MeasuringTool asset once and a directional PC->tool "controls" relationship, tagged collector:measuringtool. Idempotent (re-push reuses, no duplicate asset) and self-archiving (a PC re-imaged to a non-metrology type deactivates the link but keeps the asset and any calibration history). Mirrors the printer-link pattern. The MeasuringToolType is created on demand if not seeded. - 4 tests: create+link, idempotent re-push, non-metrology skip, repurpose archives. Non-metrology PCs never warn about a missing controls type. Settings rail cleanup: - Collapsible groups so the 13-group rail fits without scrolling (1511px -> 488px). The group containing the current page expands; the rest collapse. CSS-drawn caret (ASCII source, no Unicode). Empty groups never render, in both the rail and the landing page. - Measuring Tools group placed with the other asset groups (right after Machines) instead of appended last; empty placeholder positions the plugin-contributed cards. - Operating Systems moved from PCs to General Reference: OS is cross-asset (PCs, machines, measuring tools, network devices all run one). Plus docs/proposals/ge-enforce-plugin.md: a planning doc for refactoring GE-Enforce/DSC into a shopdb plugin (manifest as shopdb data, payloads on SMB/HTTP/inline), grounded in the real manifest schema. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |