Build Log · September 6, 2026

Hardening Human Intervention in the Owner Loop

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.

  • Alloy
  • MedinaCHF
  • Owner Loop
  • Consequence Classifier
consequence-manifest domains (authority, policy, privacy, destructive, recovery)
5

consequence-manifest domains (authority, policy, privacy, destructive, recovery)

new/edited scripts wiring the classifier into real production consumers
6

new/edited scripts wiring the classifier into real production consumers

live production bug found and fixed by an adversarial audit before deploy
1

live production bug found and fixed by an adversarial audit before deploy

new, network-free regression tests passing against the real classifier
6

new, network-free regression tests passing against the real classifier

Timeline

  1. Start

    Received the Charge via Crucible council review

    Two rounds of GPT council refinement, then a 13-section finalized Charge document

  2. Recalibration 1

    Zeb corrected the calibration target

    None of the 6 real historical MedinaCHF candidates should have required review — the classifier was preserving over-intervention, not fixing it

  3. Recalibration 2

    unguarded-authority-mutation promoted to a Gate A invariant

    Must never depend on a model call as the final safety lock over an already-proven unbounded privilege grant

  4. Build

    Wired classifier into derive-and-accept-candidate.mjs and promote-app.mjs

    Fail-closed contract: any classifier error routes to human intervention, never silent auto-proceed

  5. Build

    Built the Alloy Human Intervention Queue

    List page, detail page, Approve/Deny form, dispatched GitHub Actions workflow

  6. Audit

    Ran the Adversarial Build Audit Gate

    Found a live production bug (empty request id silently defeating the whole classifier), a request-id-derivation ambiguity, and a missing auth check on the new Server Action

  7. Fix + verify

    Fixed all three findings, re-typechecked, re-ran the new test suite

    Wrote real access-control evidence, committed, pushed, deployed via npm run deploy:alloy

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.”
— Zeb, recalibration correction mid-session