feat: implement project verification goals and proof runs (#187) - #331
Draft
Joncallim wants to merge 65 commits into
Draft
feat: implement project verification goals and proof runs (#187)#331Joncallim wants to merge 65 commits into
Joncallim wants to merge 65 commits into
Conversation
…(issue #187) Implements the PR #331 architecture for issue #187 verification goals: - Add migration 0035 with protected policy, run, event, evidence, and schedule tables plus shared-ledger dual execution subjects. - Add TypeScript contracts for policy, run lifecycle, system limits, Git safety profile, and operation execution profiles. - Add trusted executable registry, filesystem authority loader, repository profile computer, and root-anchored command launcher. - Add manual run admission, synchronous runner, and POST/GET API route. - Update privilege reconciliation, CI ACL inventory, and installer checks for the nine new protected tables and protected routines. - Add schema-content tests and update existing schema/ingress tests. This is a partial implementation. Remaining architecture slices: - Redis-delivered worker queue claim/ACK instead of synchronous API run. - TrustedProjectRootLease retention across the full runner lifecycle. - History/aggregate evidence reader and reliability-v2 child ingest. - Bounded scheduler with schedule bindings/heads/slots. - Queue expiry/recovery, capacity enforcement, and policy settings API. - Comprehensive adversarial/Git/root/lease tests and operator docs. Validation: npm run test (2116 passed, 64 skipped), npm run lint, npx tsc --noEmit, npm run build.
…eduler authority (issue #187 Slice C/E) Migration 0036 adds the protected business routines the worker, dispatcher, scheduler, and API depend on: capacity-checked idempotent admission, lease claim/renew with a recovery fence, child-operation finalization with the closed v2 outcome mapping, repository/environment snapshot recorders, queue expiry, the Redis dispatch marker, the prefix-validating atomic terminalizer, and schedule binding/slot authority keyed per (project, goal). It also drops the unreleased 0035 terminalizer that accepted a caller-supplied outcome without prefix validation, and the manual runner now finalizes children and runs exclusively through the protected routines. Fresh-install validation against PostgreSQL 16 (installer-equivalent chain through 0036 plus the privilege reconciler) surfaced and fixed: - 0035: missing temporary REFERENCES grants, a missing composite identity unique index, run events referencing later snapshot surfaces, and the shared-ledger expansion running under the wrong role; - 0036: extract() call grammar, the operation_runs CHECK role boundary, reconcile FOUND capture, verifier-required mapping, admission/claim guards, and capacity-blocked schedule slot retention; - the legacy normalizer/proof contamination counts for the 49-table latest position. Privilege reconciliation now verifies the exact runtime-routine owner/ACL boundary, and migrate-through-0036 joins the managed-migration proofs.
- claim routine accepts queued rows only; expired running rows belong to the recovery fence, never to a competing worker - finalize child operation now carries the claim lease token so proof writes are fenced to the current lease holder - idempotent manual replay resolves before capacity accounting so a retried identical request returns its run even at a capacity limit - begin child appends the child_begun event - terminalizer requires repository and environment evidence for passed - reconcile script tracks the 9-arg finalize and 6-arg slot-claim signatures
- add the immutable launcher contract digest for run environment evidence - re-verify the pinned executable identity (device, inode, content digest) immediately before launch; a replaced executable disables execution instead of launching the new file - classify spawn-level failures (ENOENT, EACCES, STDIO_MAXBUFFER) as spawnError, never as exit 0; killed children classify as timedOut - pin the launch promise as observed at creation so an abort during the handle-close window cannot trip process-level unhandled-rejection policy - buildSafeLaunchEnvironment strips NODE_OPTIONS/NODE_PATH and all GIT_* path redirections even when passed through overrides
…sue #187) - admission now calls forge_admit_verification_goal_run_v1, the single authoritative write path; the direct INSERT (for which the application login has no privilege) is removed - the live policy head revision id and sequence travel as real routine parameters; the JSON resolved policy serializes the sequence as text so a bigint never crosses the JSON boundary - code-owned eligibility is enforced at admission: an operation outside the reviewed allowlist/profile throws before any run row is created - the route resolves the live policy head (or seeds the default) and maps the closed SQLSTATE set: P1871/P1872/P1873/P1876 to 409 and P1874 to 429 - trusted executable resolution anchors to the project root as well as the worker cwd
…h (issue #187) - keep the business lease renewed on the reviewed 10-second cadence; any renewal failure or non-owner response disables further proof writes and a lost lease is never re-claimed by the runner - execute the exact bound operations in canonical ordinal order via the reviewed eligibility binding; verifier/classifier/profile drift against the stored digests terminalizes as operation_contract_changed - record the repository and environment evidence snapshots before a passed terminalization, including the launcher contract digest, trusted executable identities, and the git safety profile digest - finalize child operations with the claim lease token - compute the repository profile through the retained project-root descriptor (/dev/fd) with the safe launch environment and deterministic metadata fingerprint; expose headOid on the profile - map launcher failures to specific terminal codes instead of a generic internal error
- launcher: exit classification, spawnError for ENOENT/STDIO_MAXBUFFER (never exit 0), killed-as-timeout, executable change, root dev/ino mismatch, abort, and safe-environment stripping under overrides - runner: happy path with evidence snapshots and lease-fenced finalize, legacy policy key, spawnError, digest drift, lease loss, launcher error, and dirty repository - admission: protected routine call shape, real bigint params, idempotent replay, and allowlist/capability enforcement before any DB call - migration: text assertions on the hardened routine signatures, claim guard, lease-token fencing, passed-evidence guard, and child_begun event, mirrored in the reconcile script - route: POST/GET happy paths, 401/503/400/404/409 guards, SQLSTATE mapping to 409/429, and generic 500 without secret leakage
Joncallim
force-pushed
the
architecture/issue-187-goal-run-policy-final
branch
from
August 19, 2026 16:56
388e9d7 to
5d1aa85
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Source Issue
Issue: #187
Agent Run
Runtime: manual
Run ID: n/a
Delivery mode: combined
Summary
Architecture evidence:
docs/architecture/issue-187-verification-goal-run-policy.mdplus the checked-in orthogonal review / normative amendment trail through Round 14.Implementation evidence: this is a combined delivery PR. It now ships the complete manual-execution vertical slice for project verification goals, end to end:
forge_admit_verification_goal_run_v1(the application login has no direct INSERT on run tables, so no bypassing write exists), then executes synchronously. The runner claims and renews the business lease, re-verifies the pinned executable identities, records the repository and environment evidence snapshots, runs the exact bound operations in canonical ordinal order through the root-anchored launcher, and terminalizes to a deterministic pass/fail/inconclusive outcome throughforge_terminalize_verification_goal_run_v2, which requires the evidence set for apassedresult.Remaining delivery scope: the ticking scheduler worker that drives the schedule-slot routines; goal history (last known-good / first observed failing) and the #185/#186 outcome/reliability integration; and closure E2E evidence. The PR remains draft while these are incomplete.
Acceptance Criteria Validation
passedoutcome is impossible without the required evidence set.Tests / Verification
verification-goal-launcher.test.ts(14 tests): exit-code classification; spawn-level failures (ENOENT, STDIO_MAXBUFFER) surface asspawnErrorand never as exit 0; killed children classify as timeout; executable replacement before launch fails closed asexecutable_changed; root dev/ino mismatch fails asroot_changed; abort classifies ascancelled; the safe launch environment stripsNODE_OPTIONS/NODE_PATHand everyGIT_*redirection even when passed through overrides. Writing these tests exposed a real defect: an abort landing in the handle-close window briefly left the launch rejection unobserved (a process-levelunhandledRejectionunder strict policy). The launcher now pins a marker handler at promise creation so the rejection is always adopted while callers still receive it normally.verification-goal-runner.test.ts(7 tests): happy path with evidence snapshots and lease-token-fenced child finalize; legacyoperationspolicy key rejected asoperation_contract_changed; spawn error →operation_infrastructure_failed; digest drift →operation_contract_changed; lease loss →lease_lostwith no terminalize attempt; launcher error →root_changed; dirty repository →repository_dirty.verification-goal-admission.test.ts(5 tests): the protected routine call shape with no direct INSERT; the live policy head id and sequence passed as real parameters (bigint never crosses the JSON boundary);canonicalOperationOrdinalsin the resolved policy; idempotent replay; non-allowlisted or capability-mismatched operations throw before any DB call.verification-goal-runtime-migration.test.ts(3 tests): migration 0036's hardened routine signatures (9-arg finalize with lease token, 6-arg slot claim), the queued-only claim guard, the lease-token fence, the passed-evidence guard, and thechild_begunevent, all mirrored in the privilege reconcile script.verification-goal-runs-route.test.ts(20 tests): POST/GET happy paths; 401/503/400/404/409 guards; disabled and non-manual goal rejection; closed SQLSTATE mapping (P1871/P1872/P1873/P1876 → 409, P1874 → 429); generic 500 carries a correlation id and leaks no secret material.tsc --noEmitclean; ESLint clean on all changed files.Risks / Follow-up
This is intentionally a large combined delivery PR because #187's architecture and implementation share one security/evidence contract. Internal commits remain small and reviewable: architecture rounds first, then schema/registry slices, then the runtime slices, then the hardening and test slices.
Architect firstis an ordering/evidence rule, not a phase-specific PR rule.The PR head branch was aligned to the delivery branch tip (
codex/issue-187-goal-definition-v2). No content was lost: the three fix commits unique to the old head are contained in, or strictly superseded by, the delivery branch.V1 proof execution remains read-only and does not enable arbitrary shell/test/build commands, host repository writes, auto-repair, autonomy changes, Sentinel actions, branch/commit/PR/merge automation, or live MCP grant issuance. Stronger same-UID/host confinement remains a separately stated residual boundary and is not silently claimed by this PR.