Build Log
Hardening Human Intervention in the Owner Loop
September 6, 2026
Replaced shape-based Owner Loop review triggers with an evidence-backed consequence classifier, calibrated so ordinary owner requests stop over-triggering human review — then found and fixed a real bug that had silently defeated it.
Timeline
What shipped
forgekit-os/scripts/consequence-manifest.mjs — the consequence manifest classifier (5 domains, Gate A / Gate B)
forgekit-os/scripts/request-resolution.mjs — real OpenAI call comparing a domain claim against the owner's raw request text
forgekit-os/scripts/owner-request-provenance.mjs — fetchOwnerRequestText, reading the resolver DB credential correctly (fixes the original broken email provenance)
forgekit-os/scripts/tests/consequence-manifest.test.mjs — new, real, network-free test suite for the Gate A invariants and fail-closed contract
Wired the classifier into forgekit-os/scripts/derive-and-accept-candidate.mjs and forgekit-os/scripts/promote-app.mjs (both real production consumers)
products/alloy/lib/human-intervention.ts — Alloy data access reading pending-acceptance records directly off disk
products/alloy/app/w/[slug]/interventions/page.tsx and .../[appId]/[candidateSha]/page.tsx — the Human Intervention Queue list + detail pages
products/alloy/components/InterventionDecisionForm.tsx + products/alloy/lib/intervention-actions.ts — Approve/Deny, dispatching .github/workflows/owner-loop-manual-accept.yml
products/medinaCHF/components/coordinator/AdminIntentPanel.tsx — 'Ready, waiting for review' requester-facing state
Fixed (adversarial audit): forgekit-os/scripts/promote-actor-state.sh was passing an empty request id, silently defeating the classifier in production
Fixed (adversarial audit): forgekit-os/scripts/promote-app.mjs's deriveRequestIdFromCommit silently first-matched on ambiguous multi-request commits
Fixed (adversarial audit): products/alloy/lib/intervention-actions.ts had no auth check of its own — added requireWorkspaceContextBySlug via the same pattern every other mutating Alloy Server Action uses
“An independently observed unguarded-authority-mutation should be a Gate A invariant rather than depending on request-resolution to return prohibited_regardless... The model should interpret whether owner intent resolves a bounded authority change. It should not be the final safety lock on an already-proven unguarded privilege assignment.”