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>
- 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>
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>
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>
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>