ForgeKitFORGEKIT

Build Log

Foreman owns recovery; Zeb handles exceptions

October 2, 2026

Foreman now budgets recovery by actions it actually ran, routes every stuck reason by Zeb's named split, writes every handoff onto the request itself, and deploys verified bytes from one isolated checkout - proven live on canvas-2 and on a real canvas-1 owner deploy.

ForemanOwner LoopEmpty CanvasDeploy provenance
11
commits shipped
1 of 2
live acceptance runs used (passed)
4
production deploys with host confirmed
1 blocker + 7
audit findings fixed before push

What shipped

forgekit-os/scripts/lib/promotion-blocked-reasons.mjs - the one classification of stuck-promotion reasons by Zeb's split (auto / agent / zeb / unknown), data-driven, byte-identical in all four canvas apps and drift-tested

forgekit-os/scripts/foreman/recovery-ledger.mjs + foreman_recovery_attempts (Foreman schema v2, provisioned and verified live on canvas-1..4) - budgets count EXECUTED actions, per (request, execution, recipe) plus a per-request rebuild cap; due-first fair follow-up

forgekit-os/scripts/foreman/recovery-facts.mjs - three-valued facts (candidate on master via GitHub compare API; retry columns; active pause)

forgekit-os/scripts/foreman/recovery-adapters.mjs - fixed recipes with preconditions actually evaluated; thenRun deleted; only open (needs_review) requests are ever recovered

forgekit-os/scripts/foreman/recovery.mjs - compare-and-set on every request write, handOffToPerson written on the request at decision time, freshDispatchRequest rebuild, exhaustion preserving the original reason

forgekit-os/scripts/foreman/authority.mjs - ACTION_AUTHORIZED_APPS plus RECOVERY_LIVE_APPS staged-rollout gate (canvas-2 first, then all four)

forgekit-os/scripts/dispatch-owner-loop-requests.mjs - fetch_failed joins the bounded promotion retry

forgekit-os/scripts/unblock-after-manual-promotion.mjs, fix-promotion-blocked-desync.mjs - writes now compare-and-set on the inspected state

forgekit-os/scripts/record-promotion-outcome.mjs - no immediate email for reasons Foreman owns where recovery is live

forgekit-os/scripts/foreman/rules/promotion-blocked-unrecovered.mjs - recheckResource id bug fixed; routing and severity from the shared classification

forgekit-os/scripts/foreman/notify.mjs - escalations carry the decision needed; human_notified_at stamped only on confirmed delivery

experiments/canvas-1..4/lib/promotion-blocked-class.ts + request-display.ts (+ canvas-1 RequestPanel.tsx, canvas-1/2 owner-loop-stage.ts) - owners see only on it / alerted / needs someone; none asks the owner to act

forgekit-os/scripts/lib/isolated-checkout.mjs - one detached worktree of the approved revision for verification AND packaging, link-aware dirtiness

forgekit-os/scripts/promote-app.mjs - Actor deploy path verifies and packages from the isolated checkout; upload digest; production host must positively serve the new deployment

forgekit-os/scripts/deploy-with-bundled-deps.mjs - manual deploy path rebuilt the same way; stamps uploadDigest and sourceTreeId

forgekit-os/scripts/provision-foreman-tables.mjs - ledger DDL and grants; skips index creation on tables it does not own

forgekit-os/scripts/foreman/acceptance/{probe-stuck-requests,find-acceptance-subject}.mjs + acceptance-probe expect-interrupted-after-publication / authenticity check; acceptance workflow gains after_publication mode and the GH token it was missing

Tests: promotion-blocked-reasons (11), recovery-bounded-retry (26), recovery-adapters (29), recovery-acceptance (10), promote-isolated-checkout (10), deploy-upload-binding (6), rules-fixtures (25); test:os exit 0 on frozen 692ed0b6

“Notification timing must not determine who supposedly owns the work.”

— Zeb's pasted review, before rollout