Skip to content

OpenHands resolver: host driver, one owner issue to one draft PR (stages 1-2, offline-tested) - #489

Merged
seathatflowsinourveins merged 98 commits into
mainfrom
claude/openhands-resolver-20260928
Oct 4, 2026
Merged

seathatflowsinourveins merged 98 commits into
mainfrom
claude/openhands-resolver-20260928

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Scope

This PR adds the host driver for the OpenHands issue-to-PR resolver on top of the merged #425 recipe. One explicitly scoped, owner-authored issue can produce one validated patch, one draft PR, one COMMENT review and one repair round. The model's patch is not executed on the host. A draft PR can execute it in GitHub CI under the resolver-mode amendment decided on 2026-10-04: option 1, with a trusted pre-push gate that checks every agent commit before any push (see "Pre-push gate" below).

  • Base commit: d2fc3803e01178b4687771d0bf91faf453bb6933, merged in db24156b9 without rebasing or force-pushing. The original PR head 501bcc9e50bf3ffa3acc82b6ecf20aa4e23c593b remains in history. Head: 7c1d24cc5600bed8b56435aef37ec8d267f889fe. On top of 0f7b27578 it corrects this round's dates to 2026-10-04, as date -u gives them, in b763cb294 (text only; the commit also resets the registry to main's copy), and re-registers the rows in 7c1d24cc5 (the last commit). On top of 4eb6b4cc9, 0f7b27578 added the merge 4f963c9b2 of main 6af8e55bd (CI least privilege: permissions {} by default, cache-mode none on pull requests, workflow policy tripwire #681, CI least privilege, with Port #435: keep jCodeMunch execute off across all four carriers #672, Codex 0.159.3 -> 0.160.0 with the Python SDK pair: pins, qualification receipt, tests and checkpoint #626 and Preserve PR561 lifecycle fixes and retire source-host evidence #679). It also adds the gate fix that merge needed: the derivation follows code that gate code runs, not code it only reads (GateReads.executed), in 57d0dc065. Main's Preserve PR561 lifecycle fixes and retire source-host evidence #679 made tools/adoption/install_claude_profile.py read three workflow scripts, and following them had protected all of blueprints/. The decision record, RESOLVER.md and evidence part 8 are in 85262b1f8, and the registry rows in 0f7b27578 (the last commit). The main-merge list below predates this merge. On top of 21b24dede, 4eb6b4cc9 added the repair round for the cross-family read of 40f12ba5 (GPT-6.1 Sol, findings P1 and P2): the merge db287ea72 of main 3bdacabd5, the gate-data reader, the instruction fix and their tests in 86688e1e1, the decision record, RESOLVER.md and evidence part 7 in 0092ae213, the merge 4a03dd796 of main ba0e8c48f, the figures on that merge in 1ec9d04c3, a receipt test and the final control runs in e1b4f5c68, and the registry rows in 4eb6b4cc9 (the last commit). Before that, on top of 050bca5d5: the zizmor pin read from .github/requirements-ci.txt in a5194090b (registry 4009a96ec and 40f12ba51), then the CI-blocker round for CodeQL alert 90 and validate-macos (the merge d1bb9cf15 of main f474f6d22, 36440d64a, 9602c2274, 8be0c1d39, registry 21b24dede). On top of 456f8fee6, 050bca5d5 added the decided amendment's trusted pre-push gate: code and tests in 00905292d and 48831bc65, the decision record, RESOLVER.md and the evidence log in 868f02501, and the registry rows in 050bca5d5. Before that, on top of b0c11c324, came wording-only fixes for the independent re-check and the coordinator's follow-up: content in f4e7aa2a2 and 54438ce7f, registry rows in d89ad3b44 and 456f8fee6. This description was written for b0c11c324. Parts were updated for 050bca5d5: the "Pre-push gate" bullet, the SOTA "Pre-push gate" line, the evidence rows and commands, the "Decision record" section and the decision item under "What is not done". For 4eb6b4cc9, this line, the main merges, the owned paths, the "Pre-push gate" bullet and a SOTA "Gate data" line are updated; the evidence table and the commands still describe 050bca5d5, and the later rounds' evidence is in evidence/push-gate-fail-first.txt parts 6 and 7 and the PR comments. The SOTA "Step 6 repair round" line was updated for 456f8fee6.

  • Main merges during custody. Each followed the hot-file protocol: main's manifests/evidence.json, then this PR's owned rows re-registered.

    • 9b0b8d6d2 in 37cb51899;
    • 4ced29230, which carried main's AGENTS.md update, in 85830e815;
    • 59f8a1e36 in d35633fad;
    • d2fc3803e, which contains ecea28654, in db24156b9;
    • f474f6d22, which carried main's macOS CI scope step (e0c329ae9), in d1bb9cf15;
    • 3bdacabd5 in db287ea72;
    • ba0e8c48f, which carried the OSV and SARIF hardening port, in 4a03dd796.

    Main has since advanced to b5b9c9ddb (Port #435: keep jCodeMunch execute off across all four carriers #672), which is not merged. Its 13 paths are jCodeMunch carrier files, documents, one test and evidence; none is a workflow or a gate script. The coordination merge-tree landing check of 4eb6b4cc9 against it reports LANDABLE: a clean three-way merge, 21 merged-vs-main paths and none outside the PR-owned set, no overlap with main's 13 drifted paths, the registry's foreign rows equal and in order, 20 PR-owned rows, and a sorted files[] without duplicates (9823 rows).

  • Lane: lane:foundation.

  • Owned content paths: blueprints/runtime-workers/openhands/RESOLVER.md, resolver.py, resolver/{patch_policy,gh_harness,outgoing_guard,push_gate,gate_reads}.py, skills/resolver/SKILL.md, host.py, dispatch.py, worker.py, receipt.py, e2e/task.py, both evidence/stage2-*fail-first.txt logs and evidence/push-gate-fail-first.txt, docs/decisions/2026-09-28-openhands-resolver-isolation.md, and the three OpenHands test modules (tests/test_runtime_worker_openhands_push_gate.py is new). The registry commits re-register these files over main's manifests/evidence.json and regenerate the four foundation reports through their supported commands. The last full pass is in the merge db24156b9. b0c11c324 refreshes the rows of the three files that the step 6 repair round changed. 050bca5d5 refreshes the six rows the gate change touched and adds three new files' rows. 4eb6b4cc9 registers the 20 PR-owned files over main ba0e8c48f's registry: 7 rows updated and 13 added, resolver/gate_reads.py among them. Both generators' --check passed each time, so no report was rewritten.

  • Existing main defect disclosed. Commits 0c9b7acf6 and 3e3877979 fix the SWE-bench skill contract and instruction. At the merged base, host.py:382 requires verification-before-completion and e2e/task.py:52 tells the worker to invoke it, while the runtime manifest lists that skill under excluded. The fix keeps the excluded skill out of both contracts and preserves the instruction to run and report the reproducing tests before finishing.

  • Custody repair. The alias-refusal fixture now builds all seven entries directly in a scratch Git index. It preserves case and decomposed Unicode names on filesystems that fold them, disables Git's macOS argument precomposition for those insertion commands, and exports the cached patch without restaging the worktree. All existing refusal assertions remain, with an added check that every intended name reached the validator.

  • Review-round repair (2ede7b871, tests only; its registry row in b2d556c95). The outgoing-guard fixtures no longer depend on TMPDIR. Two host_path assertions now use a synthetic absolute host root. The symlink case needs a real temporary root, so it probes that prerequisite. It skips with an explicit message when the root matches a scripts/validate.py PRIVATE_CONTENT pattern, such as a personal home path; the reason was reworded in cf42c6d08. No assertion or reason label changed.

  • Step 6 repair round (cf42c6d08: documentation and one test's comment and skip text; its registry rows in b0c11c324).

    • The decision record's option 2 and RESOLVER.md now state that the resolver pushes to and opens PRs only in this repository. A fork therefore needs a separately reviewed harness change before any run.
    • Both options now address condition 3 and CI egress.
    • The G4 residual gives the actual refusal order.
    • No behaviour changed.
  • Pre-push gate (the amendment's decision of 2026-10-04; content in 00905292d, 48831bc65 and 868f02501, registry in 050bca5d5; the cross-family repair in 86688e1e1, 0092ae213, 1ec9d04c3 and e1b4f5c68, registry in 4eb6b4cc9).

    • Push path. GhHarness.push runs resolver/push_gate.py on the exact agent commit before any push and pushes that commit by name (<sha>:refs/heads/<branch>, never HEAD). GhHarness.run refuses any push of a commit the gate did not pass, and any push without a gate.
    • Refusals. The gate diffs the commit against its base. It refuses, with no push, changes to .github/**, a CODEOWNERS anywhere, the resolver's gate and harness code, the workflow-policy tests (tests/**.py naming .github), and every file that a workflow reachable from the push or the PR executes or reads as a gate. That last list is derived from the workflow files by a text-level reader that reuses patch_policy.names_in_text: the files run: steps name, the import closure of the gate scripts, unittest discovery and local actions. It also refuses a step that interpolates untrusted event text.
    • Gate data (cross-family P1). The gate also refuses changes to the data those gate scripts read (ci_read, resolver/gate_reads.py), such as blueprints/convergence-practice/contract.schema.json, which scripts/validate_convergence.py reads. Each gate script's expressions are evaluated to the repository paths they spell. An exact path protects that file, and a computed one protects the directory or glob it resolves under. Code that gate code runs or reads is followed to a fixpoint. A read the reader cannot resolve refuses every commit (gate_input_unresolved) rather than protecting the whole tree; none is unresolved on the merged tree. The cost is breadth. The merged tree 4a03dd796 has 10,528 tracked files, and the gate protects 7,559 of them (441 before). Outside evidence/, tests/ and .github/ it protects 633 of 3,444 (122 before). Most of it comes from tools/sota-convergence/gap_wave_ledger.py's computed evidence/artifacts and docs/*.md paths. The decision record's residual risks name the two deferred narrowings, and a repository test bounds the breadth.
    • zizmor. The gate runs the zizmor CI pins (1.30.1) with its own flags (no config, no ignores, offline). The excessive-permissions, dangerous-triggers, cache-poisoning, artipacked and template-injection audits refuse, and a missing or wrong zizmor fails closed.
    • Trusted copy. The gate runs only from the checkout it sits in, never from inside an agent tree. Its four files (push_gate.py, patch_policy.py, gate_reads.py, gh_harness.py) must equal the trusted commit's and the base's blobs, the trusted commit must be on main at the base, and the trusted commit is recorded. Each check leaves one record per commit in resolver-outcome.json (push_gate) and a code-and-count projection in the receipt.
    • CLI. run takes --zizmor, and the plan reports push_gate: trusted_copy_checked.
    • Agent instructions (cross-family P2). The resolver skill and the generated instruction name every category the gate refuses. When a fix would need such a change, including a new or changed test (tests/** is protected here), they tell the agent to stop and report instead of editing. A test keeps both in step with the gate's rules and the receipt's.

The resolver's Stage 1 logic covers owner/edit provenance, a patch policy derived from the base's host-executed files, fixed gh templates, guarded outgoing text and a bounded PR loop. Stage 2 adds:

  • the same O1 containment topology;
  • fresh P0-P2 receipts and stage-gates.json;
  • G5 checks before container creation and at dispatch;
  • a pinned-main checkout with no MCP servers, and a fixed skill set;
  • a validated patch whose committed diff must match byte for byte.

Its receipt does not infer successful completion from model-writable data.

Independent review history

  1. Stage 1 received a read-only GPT cross-family review (gpt-6-astra, max). Four findings were repaired, and a Claude closure review confirmed them closed: import shadowing, edited issue provenance, missing required contexts, and a review bound to a different commit.

  2. Stage 2 received three independent Claude reviews. The verifier accepted with low notes; the security and design reviewers requested changes. The retained repair round addressed:

    • patch-content checking before git apply;
    • binding the G4 reviewer argv to a recorded hash;
    • retaining a PR record when GitHub holds the PR;
    • the residuals comment;
    • the receipt after a dispatch failure;
    • the excluded-skill instruction;
    • containment evidence;
    • fourteen smaller items.

    Both fail-first logs are unchanged: their blob SHAs at this head (ed55377f, 3f41c01b) equal those at 501bcc9e.

  3. Stage 2's requested separate read-only GPT-6.1 Astra/max review through the packaged lane (custody contract step 6(a)) was pending at b2d556c95. It ran at db24156b9 (job rev-489-astra) and returned repair with one should-fix finding, which cf42c6d08 repairs (see "Step 6 reviews" and "Step 6 repair round"). The builder's diagnosis and tests do not count as that review.

  4. The headless Opus 5.5 closure review of the whole repaired head (step 6(b)) was also pending at b2d556c95. It returned repair at db24156b9, with two should-fix and seven minor findings; "Step 6 repair round" gives each disposition. Before the PR leaves draft, the coordinator must still:

    • have the reviewers re-read this delta (contract step 6);
    • resolve every review thread;
    • confirm the required contexts on the final head.
  5. A read-only Opus 5.5 custody review of d35633fad returned repair, with two should-fix and six minor findings. Their dispositions are under "Review round" below; reviews 3 and 4 cover the resulting head.

Step 6 reviews (2026-10-03, head db24156)

Items 3 and 4 of "Independent review history" and row 7 of "Review round" call these two reviews pending at b2d556c95; this section records their returned verdicts at db24156b92ac. Findings are condensed to one line each (severity, location, problem) without the reviews' fix proposals. Host paths are replaced by repository-relative paths or <private path>. Line numbers cited for the PR body are those of the body as read at 2026-10-03T19:20Z, before this section was added.

(a) GPT-6 Astra/max cross-family review, run through the packaged lane, job rev-489-astra. Verdict: repair. One finding.

  • should-fix | docs/decisions/2026-09-28-openhands-resolver-isolation.md:228-236; blueprints/runtime-workers/openhands/RESOLVER.md:535-537 | The fork option needs explicit implementation and safety prerequisites. The runbook permits proceeding after either choice, but gh_harness.py:44-47,257-259,704-708 still targets the upstream repository and rejects a fork push URL, so following it after choosing a fork would use the same-repository path. Forking also leaves networked CI and final-message publication unresolved, while the unconditional token/secrets guarantee is explicitly unverified.

(b) Opus closure review. Verdict: repair. Nine findings: two should-fix and seven minor.

  • should-fix | docs/decisions/2026-09-28-openhands-resolver-isolation.md:227-236; blueprints/runtime-workers/openhands/RESOLVER.md:519-521 (also :420-422) | The fork option (option 2) is described incompletely, and RESOLVER.md files it under the wrong kind of change, so the owner cannot see what it costs or leaves open. (i) RESOLVER.md:519-521 lists "pushing to an owner fork" among the "workflow changes outside this PR", but it changes this PR's own harness, which only targets this repository (gh_harness.py:44, :241-254, :257-259, :448, :627-628, :684-689, :704-709; the record's ruleset section, :131-178, binds only this repository's openhands/* refs), so option 2 needs harness changes, a ruleset on the fork and its own review before any run, while option 1 needs no code change. (ii) Option 2 (:230-232) says nothing about condition 3: build_pr_body (resolver.py:547-550, :590-592) publishes up to 6000 characters of the agent's final message wherever the branch lives, so option 2 leaves that deviation open while option 1 explicitly accepts it, and the record's closing sentence (:235-236) implies that accepting either option lets the first live run go ahead. (iii) The fork's owner is not named (the repository is public and owned by a User account; isFork false, forkCount 0), and the record already says GitHub's fork-PR limits were not re-read.
  • should-fix | PR OpenHands resolver: host driver, one owner issue to one draft PR (stages 1-2, offline-tested) #489 body (gh pr view 489 --json body, read 2026-10-03T19:20Z): lines 5, 6, 8, 46, 52, 149 ("CI on this head"), 151 | The body still describes b2d556c95, not the reviewed head db24156b9: line 5 (base and head); line 6 (main ecea2865 "not merged", and the merge list omits db24156b9, which merges main d2fc3803e, already containing ecea28654); line 8 (last full registry pass d35633fad); lines 46 and 151 (d35633fad as the Linux full-suite evidence, "differs from this head only in the repaired test module"); line 52 and the "CI on this head" paragraph, line 149 (validate failed on main's unsorted registry pair). At db24156b9 the registry has 0 unsorted pairs and validate passed: job 111264954786 (head_sha db24156b9, CI merge 69650a33 onto d2fc3803e) shows validate.py {"hashed_files": 9550, "receipts": 193, "status": "passed"}, component_matrix.py --check {"rows": 32, "status": "checked"}, the new-host grand-list check passed, and python3 -m unittest "Ran 10078 tests ... OK (skipped=968)". Contract step 8 requires the merged main SHA, the local commands with exit codes and an evidence table for the head that lands.
  • minor | tests/test_runtime_worker_openhands_resolver.py:1582-1589 | The probe and the assertions are correct; only the skip message is too narrow. The probe string f"{self.tmp}/" (:1584) fires exactly when the guard (outgoing_guard.py:165-170) would refuse as private_content before host_path, and no assertion is weakened (host_path is still asserted at :1565 on the synthetic root, the ValueError cases moved unchanged to :1570-1573 ahead of the skip, and the symlink and realpath assertions at :1590-1592 are unchanged). The skip message (:1588-1589) and the comment (:1582) blame "TMPDIR is under a personal home path", but the probe fires on any scripts/validate.py PRIVATE_CONTENT pattern (validate.py:25-37), such as a session-UUID, task-handle or Windows-user-path TMPDIR, and then the skip names the wrong cause.
  • minor | PR OpenHands resolver: host driver, one owner issue to one draft PR (stages 1-2, offline-tested) #489 body line 175; blueprints/runtime-workers/openhands/RESOLVER.md:514-516; order at blueprints/runtime-workers/openhands/resolver.py:1589-1591 and host.py:1359-1361 | The body says "G4 remains unrecorded, so a real run refuses with stage_gate_g4_not_recorded", but that reason code applies only once the other gates are recorded. Today there is no <state>/stage-gates.json: plan_run calls host.verify_stage_gates first (resolver.py:1589), and read_stage_gates raises stage_gates_not_recorded (host.py:1360-1361) before verify_reviewer_gate (resolver.py:1591) can raise stage_gate_g4_not_recorded. The gates themselves are intact: no bypass flag or environment override exists, _cmd_run requires --reviewer-command (resolver.py:1663-1664), and at dispatch start verify_isolation (dispatch.py:319) re-checks P0-P2 freshness (host.py:1445-1447), the stage gates and G5 (host.py:1463-1464).
  • minor | Required context validate-macos on db24156b9 (run 37144290967, job 111264957003) | Verification gap, not a defect: validate-macos was still pending (queued) at 2026-10-03T19:20:37Z; the other seven required contexts passed on db24156b9. The native-macOS evidence the review read is from the earlier head d35633fad (artifact 11278304065 of run 37129286224, full-suite-macos.log: "Ran 10070 tests ... OK (skipped=1329)", test_case_unicode_and_filesystem_aliases_are_refused ... ok, no FAIL or ERROR in tests.test_runtime_worker_openhands_resolver). That confirms the git-plumbing alias fixture on APFS but predates the TMPDIR repair 2ede7b871; the db24156b9 run is the first native-macOS run of that repair.
  • minor | Contract step 6(a) (<private path>:71); PR OpenHands resolver: host driver, one owner issue to one draft PR (stages 1-2, offline-tested) #489 review state | Verification gap, not a defect: the separate read-only GPT-6.1 Astra/max cross-family review was still pending when this review ran; GraphQL at 19:20Z showed 0 review threads and 0 reviews on the PR.
  • minor | origin/main 1f5a791b02a230aced670c88bab3d3d0ebcf401a versus the merged base d2fc3803e01178b4687771d0bf91faf453bb6933; manifests/evidence.json | Verification gap, not a defect: main moved after the custody merge (Grand dashboard checkpoint 2026-10-03: nine gate rows for work merged through #622 #640, docs: carry the three Claude-owned #613 residual passages #648). Three registry rows changed: docs/harness-defaults.md, observability/grand-dashboard/state.json and docs/token-session-handbook.md. Their hunks do not overlap this PR's evidence.json hunks, but the landing head no longer merges current main.
  • minor | Contract step 5 commands at db24156b9 | Verification gap, not a defect: the review ran no acceptance command (it had no Bash; those are the coordinator's commands). Not re-run at db24156b9: the unit-test pair under a neutral TMPDIR and a home-path TMPDIR, the planted-defect mutations, resolver.py --help and run --help, git diff --check and the pre-push gitleaks scan. The body records them only at b2d556c95. CI on db24156b9 covers the Linux full suite, validate.py, the generator checks and secret-scan.
  • minor | Items 3-4 and the safety boundary, verified at db24156b9 | No defect (verification record). Scope: d2fc3803e..db24156b9 is exactly the 17 allowed paths (README.md, .github/ and blueprints/us-equities/ untouched; owned files equal b2d556c95). Hot-file merge: all 9541 rows of main's evidence.json kept in order, 9 new and 7 updated owned rows added, all 16 sha256/bytes values match HEAD, receipts[] and convergence_records[] equal main's, files[] sorted with no duplicates. Preserved: both fail-first blobs (ed55377f, 3f41c01b), decision-record lines 1-15 (match 501bcc9e and main), the amendment heading at :208, the skill-contract fix (host.py:408-413, e2e/task.py:46-56) and the A7 env-name read in teardown (host.py:1095). At the merged base the 11 pull_request workflows still declare contents: read and use no secrets, and the SARIF upload jobs still skip pull_request. fold(), HFS_IGNORABLE and the alias rules (patch_policy.py:116-129, :893-897, :953-958) decide from git data, and the outgoing guard's check order and 0600 O_EXCL|O_NOFOLLOW body files are intact.

SOTA sources

Evidence-class table

Claim Evidence class Command / receipt
Resolver templates, patch policy, outgoing guard, PR loop, host mode and end-to-end fake scenarios local_integration Required OpenHands test pair at b0c11c324 with a neutral TMPDIR: 248 tests, exit 0. It uses fake Docker, GitHub and agent-server operations and real local Git
The pair no longer depends on TMPDIR local_integration The same pair at b0c11c324 with a throwaway TMPDIR under the host's home path: exit 0, OK (skipped=2). The two symlink subtests are skipped by the probed prerequisite with the reworded reason. At d35633fad the same run exited 1 with failures=2 ('private_content' != 'host_path')
The repaired guard assertions still catch planted defects synthetic, at 2ede7b871 Scratch worktree at 2ede7b871. With the host_path refusal disabled, both tests fail under both TMPDIRs (exit 1). With the realpath form dropped, the symlink subtest fails under a neutral TMPDIR (exit 1) and is skipped under a home-path TMPDIR. Not re-run at b0c11c324: cf42c6d08 changes only that test's comment and skip text, not an assertion
The previous alias fixture fails when its three names collapse synthetic Existing unittest with only the three alias writes remapped: three matching failed assertions, exit 1 before repair; exit 0 after repair
All seven alias paths reach the unchanged validator and retain every refusal check local_integration All 11 PatchValidatorTests inside the pair run at b0c11c324, exit 0 on Linux with real Git 2.43.0; no platform skip
The repaired alias fixture and the TMPDIR repair pass on native macOS source_review of retained CI execution output db24156b9: validate-macos job 111264957003 (run 37144290967), artifact full-suite-macos.log: Ran 10078 tests, OK (skipped=1329), no FAIL or ERROR block. test_case_unicode_and_filesystem_aliases_are_refused, test_host_paths_and_the_user_name_are_refused and test_pr_body_follows_the_template_and_renders_model_text_inert are each ok. This is the first native macOS run of 2ede7b871. Earlier, d35633fad's job 111220987307 passed the alias test. b0c11c324: pending
The old native macOS suite failed at exactly the three alias subtests source_review of retained historical execution output Run 36524134513 (head 501bcc9e), job 109263298833, artifact 11015337319, full-suite-macos.log: 7339 tests, failures=3, skipped=959. Its three FAIL blocks are the subtests docs/A.md, Docs/z.md and docs/café.md at test line 725, each ('case_or_unicode_alias', path) not found. Re-downloaded read-only in the custody review round
The original tests catch planted defects synthetic, historical 9 of 9 repair-round mutations detected; unchanged evidence/stage2-*fail-first.txt logs
Git's macOS argument precomposition needs an explicit fixture override source_review git/git v2.43.0 git.c:449, compat/precompose_utf8.c:67-105, Documentation/config/core.txt:44-51
The resolver as built pushes to and opens PRs only in this repository, so the fork option needs a harness change source_review At b0c11c324: resolver/gh_harness.py:44, :241-254, :257-259, :448, :627-628, :684-689, :704-709; the final message reaches the PR body through resolver.py:547-550, :590-592
A real run refuses at the gates stage before G4 today source_review At b0c11c324: plan_run checks the stage gates, G5 and then G4 (resolver.py:1589-1591). read_stage_gates raises stage_gates_not_recorded when stage-gates.json is absent (host.py:1357-1361)
The original workflow/token bounds source_review, historical scope Stage 2 security review of 20 workflows at 94894f54; this is not current-base CI or fork acceptance
Linux full suite in CI source_review of retained CI execution output db24156b9: validate job 111264954786, CI merge 69650a33 of db24156b9 into d2fc3803e, python3 -m unittest: Ran 10078 tests in 1664.615s, OK (skipped=968). b0c11c324: pending
Linux full suite on the host local_integration, before the step 6 round Registry commit 96b96c681 (only .github/workflows/validate.yml and manifests/evidence.json differ at d35633fad), home-path TMPDIR: exit 1; 10,070 tests, failures=12, errors=1, skipped=850. Its 13 failing IDs are the 2 fixture cases repaired in 2ede7b871 and the 11 compared in the next row. Not re-run at b0c11c324: it takes about 43 minutes on this host, which would overrun a scheduled measurement window. The builder's sandbox run (10,046 tests, 485 failures) is superseded
None of the 11 remaining IDs is specific to this PR local_integration, at b2d556c95 Same command and TMPDIR at b2d556c95, in a fresh detached worktree at origin/main ecea2865, and at the then-merged base 59f8a1e36. With a neutral TMPDIR, the same 1 failure on every tree; with a home-path TMPDIR, the same 9 failures on every tree. Supersedes the builder's 607-method archive comparison and the earlier 4ced2923 clone control
Publication validation local_integration scripts/validate.py with a neutral TMPDIR at b0c11c324: exit 0, "status": "passed" (9,550 hashed files, 193 receipts). CI's validate.py passed on db24156b9 (job 111264954786). The builder's exit 1 (the AGENTS.md mismatch before the 4ced2923 merge) is superseded
Registry rebuild and generated reports local_integration At db24156b9: all 16 owned rows over main d2fc3803e's registry. At b0c11c324: the rows of the record, RESOLVER.md and the resolver test module are refreshed. Both generators exit 0 and change no file, and scripts/evidence_manifest.py --check passes (9,550 files)
Configured Gitleaks scan of history local_integration Gitleaks 8.30.1 over origin/main..HEAD at b0c11c324: 36 commits, exit 0, no leaks found. The pre-commit scans of both step 6 commits found no leaks
Required contexts on the pushed head source_review of CI status db24156b9: all eight passed (validate job 111264954786, validate-macos job 111264957003). A later validate re-run there (job 111278594671) was cancelled at 19:56:39Z, after b0c11c324 was pushed. b0c11c324 at 19:58:50Z: dependency-review, osv-scanner, sota-sources, token-report and verdict-review-gate passed; validate and secret-scan were in progress; validate-macos was queued
The pre-push gate refuses planted protected changes before any push, passes a benign commit, refuses from inside the agent tree and fails closed without zizmor local_integration tests.test_runtime_worker_openhands_push_gate at 050bca5d5: 31 tests, exit 0, OK (skipped=1), the PyYAML cross-check, which this interpreter cannot run; its real-zizmor 1.30.1 test ran
The gate's workflow reader matches PyYAML on all 21 workflows local_integration /usr/bin/python3 (PyYAML 6.0.1) -m unittest ...push_gate.RepositoryWorkflowTests: 3 tests, exit 0
The new and updated tests fail without the gate synthetic, failing-first Base 456f8fee6 with the new tests copied in: gate module exit 1 (errors=7), resolver module exit 1 (failures=1, errors=56), each traced to the missing gate API (evidence/push-gate-fail-first.txt, part 1)
The gate's tests catch planted defects synthetic 17 mutations of push_gate.py and gh_harness.py, each failing its named tests (exit 1); the unmutated copy passes (part 2)
The gate on this repository's own trees, with real zizmor local_integration, rehearsal Local clones of 48831bc65 with one planted commit each, real zizmor 1.30.1, no resolver, container or GitHub call. The benign edit passes. Workflow, CODEOWNERS, gate-script, helper, policy-test, new-test, .gitleaks.toml and gate-code edits are refused with their rules, and a planted template injection is refused by zizmor. About 2 s per check (part 4)
Live resolver containment, model/gateway behavior, GitHub writes not run / not accepted The CI posture is decided (option 1 with the trusted pre-push gate). The first live run waits until the gate lands on main, its negative controls pass there, and G2/P3/G5 and G4 are recorded with fresh P0-P2. No live resolver run occurred

Local commands run

# Head 050bca5d5 (pre-push gate), 2026-10-04 04:00-04:02Z. TMPDIR=/var/tmp/489-gate, nice -n 19, after
# git fetch origin main (origin/main aecfaaaa6, merge base d2fc3803e). The tree was clean at this head.
$ python3 -m unittest tests.test_runtime_worker_openhands_resolver tests.test_runtime_worker_openhands
exit 0; Ran 249 tests in 20.319s; OK
$ python3 -m unittest -v tests.test_runtime_worker_openhands_push_gate
exit 0; Ran 31 tests in 8.958s; OK (skipped=1: the PyYAML cross-check; the real-zizmor test ran)
$ /usr/bin/python3 -m unittest tests.test_runtime_worker_openhands_push_gate.RepositoryWorkflowTests
exit 0; Ran 3 tests; OK (PyYAML 6.0.1 cross-check of the workflow reader)
$ python3 scripts/validate.py
exit 0; {"components": 69, "hashed_files": 9553, "profiles": 4, "receipts": 193, "status": "passed"}
$ python3 scripts/evidence_manifest.py --check
exit 0; {"files": 9553, "status": "passed"}
$ python3 scripts/component_matrix.py --check; python3 scripts/new_host_grand_list.py --check   # before the registry commit
exit 0 {"rows": 32, "status": "checked"}; exit 0 {"status": "passed", "layers": 32, "winners": 66}; no --write needed
$ git diff --check origin/main...HEAD
exit 0
$ python3 blueprints/runtime-workers/openhands/resolver.py --help; ... run --help
exit 0; exit 0 (run --help lists --zizmor)
$ gitleaks git . --config .gitleaks.toml --log-opts=origin/main..HEAD --redact --timeout 600
exit 0; 44 commits scanned; no leaks found (the four commits' pre-commit scans: no leaks)
$ python3 <the coordinator's merge-tree landing check> aecfaaaa6 050bca5d5
exit 0; clean three-way merge; merged-vs-main paths 20, outside PR-owned 0; main drift 304 paths, overlap 0;
registry foreign rows equal, order preserved, PR-owned rows 19; merged files[] sorted, no duplicates (9735 rows); LANDABLE
$ git push origin HEAD:claude/openhands-resolver-20260928
exit 0; pre-push registry tests: Ran 3 tests, OK; 456f8fee6..050bca5d5, no force
Failing-first, planted-defect and rehearsal runs: blueprints/runtime-workers/openhands/evidence/push-gate-fail-first.txt.
Not run: the host full suite (about 43 minutes here); CI runs it on the pushed head.

# Head b0c11c324 (step 6 repair round), 2026-10-03 19:53-19:56Z. TMPDIR is a neutral
# directory outside the home directory and every repository, unless the line says
# home-path TMPDIR. The commands ran on the working tree, which was then committed
# unchanged as cf42c6d08 and b0c11c324.
$ git diff --check
exit 0
$ nice -n 19 python3 -c '<host_receipts.register_file(Path("."), p) for each path>' <the record> <RESOLVER.md> <the resolver test module>
exit 0
$ nice -n 19 python3 scripts/component_matrix.py --write
exit 0; {"flip_rule_violations": 0, "rows": 32, "status": "written"}; no file changed
$ nice -n 19 python3 scripts/new_host_grand_list.py --write
exit 0; {"status": "written", "layers": 32, "winners": 66}; no file changed
$ nice -n 19 python3 scripts/evidence_manifest.py --check
exit 0; {"files": 9550, "status": "passed"}
$ nice -n 19 python3 scripts/validate.py
exit 0; {"components": 69, "hashed_files": 9550, "profiles": 4, "receipts": 193, "status": "passed"}
$ nice -n 19 python3 -m unittest tests.test_runtime_worker_openhands_resolver tests.test_runtime_worker_openhands
exit 0; Ran 248 tests in 22.014s; OK
$ (home-path TMPDIR, a throwaway directory removed afterwards) nice -n 19 python3 -m unittest -v <the same pair>
exit 0; Ran 248 tests in 24.993s; OK (skipped=2); both skips give the reworded reason
$ nice -n 19 python3 blueprints/runtime-workers/openhands/resolver.py --help
exit 0
$ nice -n 19 python3 blueprints/runtime-workers/openhands/resolver.py run --help
exit 0
$ nice -n 19 git diff --check origin/main...HEAD
exit 0 (origin/main 463a57b98, merge base d2fc3803e); git diff --name-only origin/main...HEAD: the 17 contract paths
$ cmp <(git show 501bcc9e:<record> | sed -n 1,15p) <(sed -n 1,15p <record>)    # and the same against origin/main
exit 0; exit 0
$ git rev-parse HEAD:<log> 501bcc9e:<log>    # both evidence/stage2-*fail-first.txt logs
ed55377f232aa64f46f1b7dce004fce1be5cc7a7 and 3f41c01b05feff7bf199c16266ea96c73ffa14c5, equal at both commits
$ nice -n 19 gitleaks git . --config .gitleaks.toml --log-opts=origin/main..HEAD --redact --timeout 600
exit 0; 36 commits scanned; no leaks found (the pre-commit scans of cf42c6d08 and b0c11c324: no leaks)
$ nice -n 19 python3 <the coordinator's merge-tree landing check> 463a57b98 b0c11c324
exit 0; clean three-way merge; merged-vs-main paths 17, outside PR-owned 0; main drift 30 paths, overlap 0;
registry foreign rows equal, order preserved, PR-owned rows 16; merged files[] sorted, no duplicates (9571 rows); LANDABLE
$ git push origin HEAD:claude/openhands-resolver-20260928
exit 0; pre-push registry tests: Ran 3 tests, OK; db24156b9..b0c11c324, no force
Not run in this round: the host full suite (about 43 minutes here, which would overrun a scheduled
measurement window; the CI full suites on db24156b9 are below) and the planted-defect mutations
(recorded at 2ede7b871; no assertion has changed since).

# Head b2d556c95, 2026-10-03 17:08-17:10Z (historical). Same TMPDIR convention.
$ nice -n 19 python3 scripts/validate.py
exit 0; {"components": 69, "hashed_files": 9429, "profiles": 4, "receipts": 187, "status": "passed"}
$ nice -n 19 python3 -m unittest tests.test_runtime_worker_openhands_resolver tests.test_runtime_worker_openhands
exit 0; Ran 248 tests in 19.862s; OK
$ (home-path TMPDIR) nice -n 19 python3 -m unittest tests.test_runtime_worker_openhands_resolver tests.test_runtime_worker_openhands
exit 0; Ran 248 tests in 20.310s; OK (skipped=2)
$ nice -n 19 python3 blueprints/runtime-workers/openhands/resolver.py --help
exit 0
$ nice -n 19 python3 blueprints/runtime-workers/openhands/resolver.py run --help
exit 0
$ nice -n 19 git diff --check origin/main...HEAD
exit 0 (origin/main ecea2865, merge base 59f8a1e36)
$ git diff --name-only origin/main...HEAD
exit 0; exactly the 17 contract paths
$ nice -n 19 gitleaks git . --config .gitleaks.toml --log-opts=origin/main..HEAD --redact --timeout 600
exit 0; 34 commits scanned; no leaks found
$ nice -n 19 python3 scripts/component_matrix.py --write
exit 0; {"flip_rule_violations": 0, "rows": 32, "status": "written"}; no file changed
$ nice -n 19 python3 scripts/new_host_grand_list.py --write
exit 0; {"status": "written", "layers": 32, "winners": 66}; no file changed
$ git push origin HEAD:claude/openhands-resolver-20260928
exit 0; pre-push registry tests: Ran 3 tests, OK; d35633fad..b2d556c95, no force

# Head d35633fad, before the fixture repair, 16:55-16:57Z
$ nice -n 19 python3 scripts/validate.py
exit 0; "status": "passed"
$ nice -n 19 python3 -m unittest tests.test_runtime_worker_openhands_resolver tests.test_runtime_worker_openhands
exit 0; Ran 248 tests in 20.274s; OK
$ (home-path TMPDIR) the same pair
exit 1; Ran 248 tests in 20.300s; FAILED (failures=2)
OutgoingGuardTests.test_host_paths_and_the_user_name_are_refused and
PullRequestLoopTests.test_pr_body_follows_the_template_and_renders_model_text_inert: 'private_content' != 'host_path'

# Clean-main comparison of the 11 non-OpenHands IDs that failed in the host full suite below
$ git worktree add --detach <scratch> origin/main      # ecea28654a835fff2cc3651bab77ca0e46b9bec5, clean
$ git -C <scratch> checkout --detach 59f8a1e36          # the then-merged base, clean
$ nice -n 19 python3 -m unittest <the 11 IDs>          # same command in every tree and TMPDIR
tree                     neutral TMPDIR          home-path TMPDIR
b2d556c95                exit 1; failures=1      exit 1; failures=9
d35633fad                exit 1; failures=1      exit 1; failures=9
origin/main ecea2865     exit 1; failures=1      exit 1; failures=9
merged base 59f8a1e36    exit 1; failures=1      exit 1; failures=9
Within each TMPDIR, the failing IDs are identical on every tree:
- under both: tests.test_windows_terminal_defaults.OverlayTests.test_the_installed_client_knows_no_notification_type_without_a_decision
  (the installed client knows a notification type, auth_storage_failure, that has no decision)
- under the home-path TMPDIR only, in tests.test_token_e2e_grader:
  F19c_Stage3Mutants.test_M17c_manifest_canary_off, F19d_RepairRoundMutants.test_M56c_the_export_backstop_is_off,
  F29_Export (3 tests), F29b_CheckHtml (2 tests), F29c_ArgvSanitizer.test_a_grade_run_that_spells_its_flags_with_equals_records_no_path
- passed on every tree under both:
  tests.test_gpt6_family_tiering_20260926.RunnerTests.test_sigterm_records_the_running_call_as_interrupted_without_counting_it,
  tests.test_order_throughput.CapacityRunTests.test_cli_refuses_live_base_url_from_env_file_without_network
$ git worktree remove <scratch>; git worktree prune
exit 0

# Host full suite, registry commit 96b96c681, home-path TMPDIR (coordinator run before the custody review round)
$ nice -n 19 python3 -m unittest
exit 1; Ran 10070 tests in 2568.426s; FAILED (failures=12, errors=1, skipped=850)
13 failing IDs: the 2 OpenHands fixture cases (repaired in 2ede7b871) and the 11 IDs compared above
earlier control, superseded by the comparison above: the 11 IDs in a clean 4ced2923 clone, FAILED (failures=10, errors=1)

# CI, read-only, 2026-10-03T19:57-19:59Z (gh api jobs and check-runs, gh run view --log, gh run download)
db24156b9 validate job 111264954786: success. CI merge 69650a33 (db24156b9 into d2fc3803e);
validate.py {"components": 69, "hashed_files": 9550, "profiles": 4, "receipts": 193, "status": "passed"};
component matrix {"rows": 32, "status": "checked"}; new-host grand list {"status": "passed", "layers": 32, "winners": 66};
python3 -m unittest: Ran 10078 tests in 1664.615s; OK (skipped=968)
db24156b9 validate-macos job 111264957003: success at 19:29:18Z. full-suite-macos.log: Ran 10078 tests in 1794.871s;
OK (skipped=1329); no FAIL or ERROR block; the alias test and both TMPDIR-repair tests ok
db24156b9 required contexts: all eight success; a later validate re-run (job 111278594671) was cancelled at
19:56:39Z, after b0c11c324 was pushed
b0c11c324 at 19:58:50Z: dependency-review, osv-scanner, sota-sources, token-report and verdict-review-gate success;
validate and secret-scan in progress; validate-macos queued

# CI, read-only, historical
$ gh pr checks 489 --required        # 2026-10-03T17:16:15Z
b2d556c95: dependency-review, osv-scanner, secret-scan, sota-sources, token-report and verdict-review-gate pass;
validate fail, job 111250877246: "files[2392]: files[] must be sorted by path (found
'docs/decisions/2026-10-03-omniroute-sdk-worker-0160.md' after
'docs/decisions/2026-10-03-retire-pr320-loki-denominator-host-receipts.md')", the pair main's job 111240392112 reports;
validate-macos pending
d35633fad: all eight passed: dependency-review, osv-scanner, secret-scan, sota-sources, token-report,
validate (29m39s), validate-macos (36m58s), verdict-review-gate
d35633fad validate job 111220987213: Ran 10070 tests in 1679.211s; OK (skipped=968)
d35633fad validate-macos job 111220987307, full-suite-macos.log: Ran 10070 tests in 1849.584s; OK (skipped=1329)

# Builder's sandbox record, before the 4ced2923 and 59f8a1e36 merges (superseded where marked).
# Its first pair run, with the host Gitleaks wrapper, exited 1 on the wrapper's unavailable lock;
# that failed attempt is retained separately, and PATH then selected native Gitleaks 8.30.1.
$ nice -n 19 python3 -m unittest tests.test_runtime_worker_openhands_resolver.PatchValidatorTests
exit 0; Ran 11 tests in 1.185s; OK
$ nice -n 19 python3 -m unittest        # superseded by the host and CI runs above
exit 1; Ran 10046 tests; FAILED (failures=485, errors=195, skipped=863); errors=168 with native Gitleaks on PATH
$ nice -n 19 gitleaks git . --config .gitleaks.toml --pre-commit --redact --timeout 600
exit 0; actual working diff scanned; no leaks found
$ nice -n 19 python3 scripts/validate.py   # superseded: the 4ced2923 merge brought main's AGENTS.md
exit 1; only the AGENTS.md SHA-256 and byte-count mismatch

These are repository integration checks, not unchanged upstream acceptance suites.

CI on this head. The workflows test GitHub's merge of the head with main.

On the reviewed head db24156b9, all eight required contexts passed:

  • validate job 111264954786 tested merge 69650a33 (db24156b9 into d2fc3803e). validate.py reported "status": "passed" with 9,550 hashed files and 193 receipts. The component matrix and new-host grand list checks passed. python3 -m unittest ran 10,078 tests: OK (skipped=968).
  • validate-macos job 111264957003 passed. Its full-suite-macos.log shows 10,078 tests, OK (skipped=1329) and no FAIL or ERROR block.

The merged registry is sorted. The b2d556c95 validate failure (job 111250877246) on main's unsorted registry pair no longer applies.

The new head b0c11c324 changes only the decision record, RESOLVER.md, one test's comment and skip text, and three registry rows. Its required contexts were still running when this description was written. The head that lands must show all eight SUCCESS, and a macOS full-suite artifact with no failure in tests.test_runtime_worker_openhands_resolver.

Linux full-suite acceptance item.

  • CI's Linux and macOS full suites passed on db24156b9, which differs from b0c11c324 only in the files listed above.
  • On the host, the PR's two test modules pass at b0c11c324 under both TMPDIRs.
  • The host full suite was not re-run in this round. At b2d556c95, its 11 other failing IDs failed identically on clean main and on the then-merged base, environment by environment.

Review round

The custody review of d35633fad (independent Opus 5.5, read-only) returned repair. This is its one repair round. Numbers are the findings' indices in the review record, counted from 0, as in commit 2ede7b871's message.

# Severity Finding Disposition
0 should-fix The description still described the state before the custody merge (4ced2923 unmerged, validate.py exit 1) Fixed. Scope, the SOTA "Repository" bullet, the evidence table and the commands now state the merged state: base 59f8a1e36 (merged in d35633fad), with 4ced2923 merged in 85830e815. scripts/validate.py with a neutral TMPDIR: exit 0, "status": "passed" at d35633fad and b2d556c95. The builder's exit 1 remains only as superseded history
1 should-fix The Linux full-suite state was contradictory, with no recorded clean-main comparison Fixed. "Local commands run" records the comparison: a fresh detached worktree, at origin/main ecea2865 and then at the merged base 59f8a1e36, the 11 IDs, the command and every exit code under two TMPDIRs. The failing sets match this head's. The builder's sandbox numbers are marked superseded, and the "remains an acceptance blocker" paragraph is replaced by the evidence summary
2 minor "Including unittest's trailing spaces" is false for the fail-first logs Fixed. The clause is removed. The blob SHAs ed55377f and 3f41c01b are unchanged from 501bcc9e. The same wording in the custody contract, which lives outside the repository, is reported to the coordinator
3 minor The home-path TMPDIR failure was called an environment artifact, but the fixtures and the check order come from this PR Fixed in 2ede7b871 (tests only), with its registry row in b2d556c95. A synthetic absolute host root serves the two host_path assertions. The symlink case probes its prerequisite, following git's test_lazy_prereq pattern, and skips its two subtests with an explicit message. No assertion or reason label changed. Fail-first at d35633fad: exit 1, failures=2. After the fix: exit 0 under both TMPDIRs. Two planted defects are caught (see the evidence table)
4 minor validate-macos had not run on d35633fad Closed for d35633fad: job 111220987307 passed, and its log shows OK (skipped=1329) with the alias test ok. On b2d556c95, validate-macos is pending and validate failed on main's registry order; see "CI on this head"
5 minor The reviewer did not re-run the local acceptance commands Closed: the final-head runs and their exit codes are recorded above
6 minor The reviewer did not download the historical macOS artifact No change needed. Artifact 11015337319 was re-downloaded read-only in this round, and its three FAIL blocks are cited in the evidence table
7 minor Neither contract review has a recorded verdict Open and listed as pending: 6(a), GPT-6.1 Astra/max, after 19:03Z when the GPT-free slot ends; 6(b), the Opus closure review of b2d556c95. The PR had no review threads at 17:16Z

Step 6 repair round

This is the one repair round for the two step 6 reviews of db24156b9. A is the GPT-6 Astra/max review, and O1-O9 are the Opus closure review's findings in the order listed under "Step 6 reviews". The fixes are in cf42c6d08, with their registry rows in b0c11c324.

# Severity Finding Disposition
A should-fix Fork option prerequisites; the runbook permits a run after either choice; CI egress and final message; the fork guarantee is unverified Fixed. The record's option 2 now states that the harness pushes to and opens PRs only in this repository, and lists what option 2 needs before a first run: a fork remote and push-URL check, --head <owner>:<branch>, the rules lookup and a non_fast_forward ruleset on the fork, and its own review. CI egress and condition 3 are addressed under both options, and the "not re-read" caveat is kept. The RESOLVER.md runbook precondition now also stops for option 2 until that change lands. The amendment heading, record lines 1-15 and the live-run wait are unchanged
O1 should-fix Option 2 incomplete; filed under the wrong kind of change; condition 3; the fork's owner Fixed with A. RESOLVER.md Residuals separate the egress block (a workflow change outside this PR) from the fork option (a change to this PR's harness). The record names the fork's owner: the repository's owning User account, which is also the admin login the resolver acts through. The amendment summary in RESOLVER.md is updated to match
O2 should-fix The PR body describes b2d556c95 Fixed in this description: base and head, the merge list, "CI on this head", the evidence rows, and the local commands at b0c11c324 with exit codes
O3 minor The skip message is too narrow Fixed. The comment and skip reason name any scripts/validate.py PRIVATE_CONTENT pattern, such as a personal home path. No assertion changed, and the reworded reason appears in the home-path TMPDIR run
O4 minor The G4 reason-code order is wrong Fixed in the RESOLVER.md G4 residual and under "What is not done" below
O5 minor validate-macos on db24156b9 Closed for db24156b9: job 111264957003 passed, and its artifact is cited in the evidence table. Open: validate-macos on b0c11c324
O6 minor The 6(a) review is pending Closed: 6(a) returned (A above). Open: the reviewers' re-read of this delta
O7 minor Main moved after the custody merge Open, not merged in this round. The landing check against 463a57b98 reports LANDABLE. Contract step 7's "repeat steps 2 and 7" remains the coordinator's call before landing
O8 minor The step-5 commands were not re-run Closed at b0c11c324, except the host full suite and the planted-defect mutations (see "Local commands run")
O9 minor Verification record No change needed

Decision record

docs/decisions/2026-09-28-openhands-resolver-isolation.md records the resolver-mode narrowing and its amendment, proposed 2026-09-28 and decided 2026-10-04: option 1 with trusted pre-push enforcement.

The decision section records the following:

  • the owner's delegation, in their words: "max quality sota convergenced resolution automation workflow at highest quality";
  • the command center's proposal (option 1 with an in-CI tripwire);
  • the GPT-family job 004 vote: disagree on sufficiency, because a check inside PR CI cannot protect against the commit under test. Each reason is re-checked against the GitHub, OpenSSF Scorecard and zizmor sources;
  • the gate and its trusted-copy invariant;
  • the separate defence-in-depth PR for workflow hardening;
  • the residuals, the evidence, and the overturn to option 2 if the gate cannot be kept immutable to the agent.

Option 2's text stays as the alternative; only its line citations moved with the harness change.

The history below describes the amendment before the decision. After cf42c6d08 and the wording fixes f4e7aa2a2 and 54438ce7f, the amendment's option 2:

  • states that the resolver as built pushes to and opens PRs only in this repository;
  • lists the separately reviewed harness change and fork ruleset it needs before a first run;
  • marks the fork's holder undetermined: an organization the owner creates, or a second account. The choice changes the push identity, the fork's ruleset and the --head <holder>:<branch> value;
  • sources the token and secret guarantee, for pull_request runs from a fork, in GitHub's "Workflows in forked repositories".

Both options now address condition 3 and CI egress. The 2026-10-03 fixture-repair section records native Git sources, the skipped-check alternative, the argument-normalization completeness finding, evidence limits and the native macOS condition that would overturn the repair. Lines 1-15 remain unchanged.

What is not done, and what needs the owner

  • Owner decision: decided 2026-10-04. It chose option 1 with trusted pre-push enforcement; the owner delegated the choice to converged practice. Still open:

    • workflow hardening, in a separate defence-in-depth PR: permissions: {} defaults, persist-credentials: false, cache, runner and timeout policy, a protected zizmor configuration, and a strict tripwire test on main. Until then, code under test runs with network, and that residual is accepted;
    • the first live run, which waits until this gate lands on main, its negative controls pass there and the stage gates are recorded;
    • the protected list, which is broad by design: all of tests/**, and every file a reachable step names. A resolver task that needs those files fails at the gate with no push;
    • option 2, an owner fork, which stays the overturn target and would need its own harness change.

    Resolver-PR check results are still not evidence of model-code safety.

  • G4 remains unrecorded. Because no stage-gates.json exists, a real run today refuses earlier, at the gates stage, with stage_gates_not_recorded. Once the recorded stage gates and G5 pass, it refuses with stage_gate_g4_not_recorded until the coordinator records the isolated reviewer argv hash.

  • G5 and stage-gates.json remain required. Both gateways' provider settings and the confinement design still need their own acceptance.

  • These remain separate required steps:

    • fresh P0-P2 receipts, P3-P5 and G2 image qualification;
    • the reviewers' re-read of the step 6 delta;
    • the required contexts on the final head;
    • the first live draft-PR attempt.

    The native macOS check of the alias repair and the TMPDIR repair passed on db24156b9. A7's environment-name read at teardown stays in place.

Host evidence

Not applicable: no file under evidence/hosts/ changes. The historical macOS artifact is distinguished from a new native run, and no live resolver acceptance is claimed.

Checklist

  • No GitHub Actions or workflow files changed; workflow hardening remains a separate defence-in-depth PR.
  • New Actions permissions or pins are not introduced by this change.
  • No credential value was read or published. Gitleaks scans of the history (36 commits at b0c11c324) and of both step 6 commits report no leaks, and the pre-push registry tests passed.
  • No new paid hosting, subscription or billing surface.
  • Peer-owned files and worktrees preserved.
  • Separate Stage 2 GPT review and whole-head Opus closure verdicts recorded, including residuals.
  • The reviewers' re-read of the step 6 delta recorded.
  • Every review thread resolved and all eight required contexts SUCCESS on the pushed head.

Scout and others added 29 commits September 29, 2026 00:08
Adds the deterministic host driver's first two units as new files only
(host.py, dispatch.py, worker.py and recipe.py are untouched):

- resolver.py: owner-only issue and comment selection with dropped-comment
  counts (EXT main.py:396-408 plus the owner triple of the 2026-09-25
  host-request decision), paginated-array parsing (cli/cli@0cf10924
  api.go:524-533, pagination.go:112-150), and the RFC 2046 delimited
  untrusted-input block after EXT main.py:851-857.
- resolver/patch_policy.py: a fail-closed parser for git's patch grammar
  (diff-generate-patch.txt, apply.c) and a validator that refuses symlinks,
  gitlinks, binary hunks, .git*/.github paths, unowned and aliasing paths,
  and the host-executed set derived at the base commit from
  .claude/settings.json, scripts/git-hooks and their Python import closure.
- tests: integration checks with fixture repositories, a miss-oracle for the
  derivation and a negative control.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…, PR loop and CLI

Units 3-7 of the resolver plan (sections 2 and 3), as new files only; host.py,
dispatch.py, worker.py and recipe.py are unchanged.

- resolver/gh_harness.py: an allowlisted child environment, an empty 0700
  working directory, a denylist and then exact templates before any subprocess,
  guard approval for every write, a gh 2.101.0 and auth-status preflight, and a
  push that checks the origin push URL and uses only gh's credential helper for
  github.com.
- resolver.py: branch naming after EXT _branch_name, SOTA sources resolved in
  the base tree, the template PR body, one draft PR with read-back, a bounded
  checks wait, one COMMENT review, one fast-forward repair, a residuals comment,
  and a CLI (plan, validate-patch, open-pr, review; run is reserved for stage 2).
- resolver/outgoing_guard.py: the session key in memory only, validate.py's
  private-content patterns and file scan, host paths and the user name, gitleaks
  with a distinct leak exit code, and closing-keyword and mention refusal for
  model text.
- skills/resolver/SKILL.md and RESOLVER.md: the agent-side bounds and the
  stage-2 integration points.

Local integration checks with fakes; no model call, container, push or GitHub
write.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
A patch adding tests/test_blind_checkout/__init__.py was accepted, although
the pre-push runner would then import that package instead of the protected
module: CPython's FileFinder takes a package before a same-named module and
extension suffixes before .py (Lib/importlib/_bootstrap_external.py:1593-1641,
1724-1732 at v3.12.3), and a valid __pycache__ entry runs instead of the source.

The derivation now records every import name the host-executed code resolves
(existing and absent module stems, and the package directories on the way),
and the validator refuses import_shadow paths (X/, X with any import suffix,
__pycache__/X.*.pyc, a package's __init__ or sibling module) and any bytecode
(python_bytecode). conftest.py, sitecustomize/usercustomize and *.pth have no
loading path from the pre-push runner or CI; ImportLoadingPathTests checks this
on the local interpreter, with a PYTHONPATH positive control.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Repository writers can edit an owner's issue or comment, and neither REST nor
gh's issue JSON fields report an editor. One fixed read-only GraphQL query now
reads the title, body and comment bodies with their edit history (editor,
lastEditedAt, userContentEdits with deletedBy, RenamedTitleEvent actors). The
model gets that snapshot's text only when every actor is the owner. A deleted
account, a truncated history or an edit without a history refuses the issue as
unknown provenance; such comments are dropped and counted. The harness allows
that one query with its exact variables and denies every other GraphQL argv.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…mpleted

`gh pr checks --required` lists only the required checks that have reported
(aggregate.go:36-41 at cli/cli@0cf10924; cli/cli#6448), so a lone passing
"validate" read as settled while seven of main's eight required contexts had
not reported. The wait now reads main's required contexts through one new
read-only template, `gh api --paginate .../rules/branches/main` (GitHub "Get
rules for a branch": required_status_checks parameters[].context; 30 rules to
a page by default). A GET on 2026-09-28 found no classic branch protection
(404 "Branch not protected"), so no protection read is allowed.

The checks settle only when every required context has a completed result.
An absent or pending one stays pending until the 60-minute bound, which now
ends as "incomplete" with the missing contexts, listed in the residuals
comment. gh reads the PR's latest commit, so the head is read before the first
poll and after each; a moved head stops the loop (pr_head_moved). An empty
required set also stops it. The head reads rely on the agent branch's
non_fast_forward rule, which stage 2's preflight must confirm.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
`gh pr diff` fetches the PR's current diff by number (diff.go:127-137 and
212-236 at cli/cli@0cf10924), and GitHub accepts an explicit older commit_id,
so a push from A to B during the checks wait meant the loop reviewed B's diff
and posted commit_id=A. The reviewed diff now comes from immutable revisions:
`git diff <base>...<head>` in the host clone (local git, neutral config, the
export's flags), with both commits present and the base an ancestor of the
head. The open-pr record carries the base, and ReviewLoop takes the clone.
`gh pr diff` leaves the allowlist.

Immediately before the POST the PR is read back through the existing pr view
template; a head other than the reviewed SHA refuses with pr_head_moved before
any write. EXT github-pr-reviewer worker.py (OpenHands/extensions@bea7a20)
does the same: an exact head (245-256), a re-read before reporting (318-322)
and no review on a moved head (289-299). The regression test moves the head
while the reviewer runs and asserts that no review is posted.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…nd write journal

Adds op_base (anonymous ls-remote of refs/heads/main), op_repository
(GET repos/<repo>) with their parsers, GhHarness.base_sha, repository
and branch_rules, and GhHarness.writes, which journals every GitHub
write (push, pr_create, review, pr_comment) with its exit status.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
build_agent(resolver=True) takes exactly tdd, search-first and resolver,
adds AGENTS.md at the base as the always-on 'agents' skill, uses the
resolver suffix and no MCP server, and filters tools to terminal and
file_editor. start_request sends no hook_config in resolver mode, and
main selects the mode from argv. SWE-bench mode is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…TS.md, skills)

host.run(resolver=...) writes the attempt's identity first, runs the
resolver preflight (no memory, embedding or QMD checks), then the stage
gates and G5 before any clone or container. It clones main anonymously
at the pinned base with its remote removed, writes AGENTS.md at the base
into the input mount, and installs tdd, search-first and the resolver
skill through install_skills.py with a generated project manifest that
pins the resolver skill to the driver checkout's commit. No MCP, QMD or
runtime mount. prepare_native_dispatch renders --request --resolver and
hands the session key to an in-memory sink. dispatch.execute and
finish_result take the resolver context; the receipt admits the new
failure stages. SWE-bench statements are unchanged (git diff -w).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…olver section

finish_result hands a REST-finished resolver attempt's export (neutral
git, full object names) and final message to the in-process driver, and
refuses without it, so 'dispatch.py result' alone makes no GitHub step.
An agent limit opens nothing; another end stays an agent failure. The
receipt drops the grader section in resolver mode and adds a resolver
section projected onto fixed fields: issue, base, lane, outcome and
reason codes, branch, PR, head, writes and the gate receipt hashes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
ResolverAttempt holds the session key in memory (host's sink) and, after
the export, validates the patch at the base in a fresh anonymous clone,
guards the commit message, title and PR body before any write, applies
with 'git apply --index --check' then '--index', commits with EXT's
identity and hooks off, requires the commit's diff to equal the
validated patch, pushes through the harness and opens the draft PR.
run plans read-only first (host preflight, stage gates, G5, gh version
and login, repository, owner issue, base, branch and non_fast_forward);
--dry-run prints that plan. The review loop runs after host.run returns,
with a coordinator-given reviewer command and no repair push. The exit
status comes from host-observed results only.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
… a stopped loop

A second run for the same issue, arm and UTC day refused only inside
host.run, after which run read and rewrote the earlier attempt's
receipt. plan_run now refuses an existing attempt directory before any
GitHub read, and run takes the receipt path from host.run's own output.
Any exception or interrupt in the review loop is recorded as a stop.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…h skill contract

workspace_skills required verification-before-completion, which the
runtime manifest lists under 'excluded' (#429, bdf25d2) and the
resolver plan's 2026-09-28 update keeps out of every container skill
set, so every live 'host.py prepare' stopped at the skills stage. That
also blocks the README's probe sequence and P3, which the resolver's
first live attempt needs. The contract now requires tdd and refuses the
excluded skill.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…es the host's side

RESOLVER.md replaces the stage-2 anchor list with what stage 2 does:
the read-only plan and --dry-run, the attempt on the unchanged O1 path
with early gates, the workspace and resolver skill set, the driver's
steps after the export, the statement that nothing the model wrote runs
on the host, the review loop, the receipt, the exit statuses, the
deviations and residuals, and the coordinator's live runbook. The
resolver skill now tells the agent that .git is read-only, which skills
and context it has, and that the host never runs its code. The test's
conversation id is built at runtime so the file passes --scan-file.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…rupt in the driver

ResolverAttempt.finish records the PR number before its head check, so
a head that moves as the PR opens still names the PR in the receipt. An
interrupt (SIGTERM) inside the driver is recorded with its stage and the
write journal instead of losing both. Drops an unused constant and
checks review statuses against their fixed set.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…pins in the plan

The host's gitleaks is adoption/tools/gitleaks-guarded, which exits 75
while another scan holds its per-user lock (:22-32). The guard read that
as a scanner error, so a concurrent pre-commit scan elsewhere could
refuse the PR body and spend the day's run id. The scanner now retries
only that code, 60 times 5 s apart, and never reads it as clean.

plan_run now runs install_skills.py --dry-run with the resolver manifest
against an empty project (host.check_resolver_skills). That checks the
pinned skills binary and every source tree before the attempt exists.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…ument the lock retry and skills dry run

evidence/stage2-fail-first.txt keeps each stage-2 test's failing run
before its implementing commit, with only private path prefixes
replaced, and the eight negative controls on b29ae17b. RESOLVER.md
documents the gitleaks lock retry, the installer dry run in the plan,
and that the write journal records gh and git exit status.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The pushed commit equals the validated patch byte for byte, yet only the
commit message, PR title and PR body passed the outgoing guard; the patch
itself passed validate_patch, which checks names, modes and binary hunks.
A session key, host path, host user name or token shape written into an
owned file was committed with hooks off and pushed.

patch_policy.added_content walks parse_patch's counted hunks and returns
the paths the patch adds and its added lines (an added line that begins
"++ " is content, not a header). ResolverAttempt.finish now runs
patch_content_refusal right after validate_patch accepts: the guard in
plain mode (session key, PRIVATE_CONTENT, host paths, user name, gitleaks)
over that text, then validate.py's scan_file_for_private_content over the
exact patch bytes. A finding is patch_refused with a content_<reason> code
before SOTA selection, apply or any GitHub write.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…t exists

open_pull_request ran `gh pr create`, then a read-back that could stop
(pr_view_failed, pr_not_draft, a body or label mismatch); finish recorded
the PR number only after both returned. So a PR GitHub already held could
be recorded as pr null with exit 3, which reads as a host failure and
invites a next-day rerun that opens a second draft on openhands/issue-N-2.

create_pull_request and confirm_pull_request split the write from the
read-back. finish records status pr_opened and the number as soon as gh
prints the URL, so a later stop keeps them beside its reason. pr_created
reads a PR from that status or from the journal's pr_create exit 0 (when
the URL could not be parsed); resolver_exit then gives 0 or 5, never 3.
_cmd_run starts no review loop after a stop at open and records the review
as stopped with that reason. RESOLVER.md's exit table and run --help say so.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
… words why they stay open

residuals_body took no findings, and _no_repair's fixed driver sentence was
shown in a fence labelled "Report from the repair attempt (model output)".
So every residuals comment listed no finding and presented host text as
model output, against plan step 12 and RESOLVER.md.

residuals_body now takes the review's findings and shows them fenced as
model text, after the guard. _no_repair reports attempted false with no
report; repair_once returns status not_attempted for it, and the driver
says outside every fence that no repair attempt ran, so every finding
stays unaddressed. The checks table says the checks ran the PR's own,
agent-written code on runners with network access, so their results are
model-controlled, not evidence (review item F2's label).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
When dispatch failed after start (the POST, the 1200 s deadline, a wait or
result GET, the final-response contract), dispatch.execute wrote a minimal
receipt {arm, failure_stage, failure_type, ...} and host.run returned it
unchanged. That receipt had no resolver section: no issue, base SHA, lane,
instruction hash or gate hashes, and _cmd_run could not attach the review
status.

host.run now passes a resolver attempt's dispatch failure receipt to
resolver_failure_receipt, which re-reads the window dispatch last wrote,
sets dispatch's failure stage and exception type name, writes check.json
and rebuilds the receipt with create_receipt. SWE-bench mode is unchanged.
The e2e harness can now script API answers and dispatch's clock.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Plan gate G4 (reviewer isolation) lived only in the runbook: any non-empty
--reviewer-command was shell-split and run on the host with the owner's
HOME and the model-written diff on stdin, so a command without the
qualified flags could load user hooks or tools while reading
attacker-influenced text.

host.verify_reviewer_gate requires a passed g4 record in stage-gates.json,
recorded no later than now, whose reviewer_argv_sha256 equals the SHA-256
of the split argv with each element NUL-terminated (the /proc/<pid>/cmdline
form, proc(5)). plan_run checks it with the other gates, before any clone
or container, after requiring an absolute executable; the argv is kept in
memory and only its hash reaches the identity file and the receipt's
gates. verify_stage_gates now reads the file through read_stage_gates and
still does not require g4, since SWE-bench mode runs no reviewer. The
runbook records g4 and prints the hash of $REVIEWER.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…ls (repair D6)

2d1d074d made the SWE-bench skill contract refuse
verification-before-completion, which the runtime manifest excludes, but
worker_instruction still told the agent to invoke that skill before
finishing. Prepare now passes the skills stage, so the agent would get an
instruction naming a skill that is never installed.

The instruction keeps the tdd skill and asks, without naming a skill, for
the reproducing tests to be run again and reported before finishing. A
test reads the runtime manifest: every skill the instruction names must be
selected there, and none excluded.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
A7 reads a names-only environment listing of the agent container and the
proxy access log from the run's own output, but stage 2 produced neither:
finish_result removes the server container first, so no listing could be
taken afterwards, and nothing triaged proxy.log's denied lines.

teardown_attempt now calls record_env_names on the server before its
logs and removal: one docker inspect whose template emits each Config.Env
entry's name (split at the first "=") and never a value; anything but a
JSON array of names reads as unreadable. The template was rendered with
the local Docker CLI 29.8.1 on literal data. The receipt's resolver
section gains containment: the names with the plan's forbidden ones
(GH_*, GITHUB_*, OMNIROUTE_*, *_TOKEN), and the proxy log's hash and
counts: before dispatch start (the probe's own traffic), per allowlisted
route, other request lines and 403s, host side and other lines. Request
lines stay private; receipt.not_allowlisted_lines lists them for the
coordinator's triage (live runbook step 9). The SWE-bench suite's
teardown sequence expects the new read.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…I; propose the record's amendment

RESOLVER.md said the resolver meets the network-none execution rule "by
executing nothing at all", which holds for the host only. The driver
pushes the agent's commit to this repository and opens a PR, so main's
pull_request workflows run model-written code on GitHub-hosted runners
with audit-only egress. The PR body also publishes the agent's final
message. The decision record's narrowing lets issue text reach a model
with tools only when "their patch is graded with no network" and "a
validated patch is the only output"; resolver mode meets neither as
written.

RESOLVER.md now states what runs where, with the bounds read at
origin/main b0fb65b (contents: read tokens, no reachable secret, no
pull_request_target or workflow_run, no write-scoped job on
pull_request), says check results on openhands/* are model-controlled,
and states the published final message. The decision record gains a
dated resolver-mode amendment, proposed and pending the owner's
decision: accept CI execution within those bounds, or push to an owner
fork. The live runbook's precondition waits for that decision. No
workflow is edited; the CI-side mitigations are recorded as residuals.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…eck and the negative controls

evidence/stage2-repair-fail-first.txt holds, for each review item of the
repair round (F1/D4, D1, D2, D5, F3, D6, D7), the returned test run from
before its implementing commit; the before-and-after documentation check
for the text-only items F2 and D3; and nine mutations on c7edc527, each of
which fails its target test while the unmutated targets pass. Only private
filesystem prefixes are replaced, and the builder refuses to write if a
private value remains. validate.py --scan-file passes on the file. These
are our local integration checks, not upstream or live acceptance.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…he driver's steps

The driver's step list still said the guard covers the commit message, PR
title and PR body only. It now has the patch-content step (aae8449f) with
its plain-mode guard and file scan, says why it is needed (the commit runs
with hooks off, so the repository's pre-commit gitleaks does not), and the
file table and guard section say the guard checks what the pushed patch adds.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…he observations behind the CI facts

RESOLVER.md listed four pull_request workflows. Parsing every workflow
file at origin/main b0fb65b (PyYAML) shows 11 of 20, including
adoption-bootstrap's required validate-macos. The bounds still hold: each
of the 11 sets contents: read at the workflow level, none uses a secret
other than GITHUB_TOKEN, and the only write-scoped jobs among them
(security-scan's two SARIF uploads) skip pull_request. RESOLVER.md and the
decision record's proposed amendment now say so.

The repair evidence file gains an observations section with the returned
output of the CI audit and git-grep checks, and of the Docker CLI 29.8.1
rendering that host.ENV_NAMES_FORMAT relies on, plus the text of both
helper scripts. The F2/D3 documentation check was re-run on this tree.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Hot-file protocol (docs/lanes.md): the rebase applied the builders' commits
cleanly; manifests/evidence.json is refreshed here, in the last commit, with
host_receipts.register_file for all 16 files this branch changes. No content
change.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Sep 29, 2026
Scout and others added 24 commits October 4, 2026 11:32
…fore the final hot-file commit

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… (hot-file protocol: every hot-file edit in the last commit)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…egistry plus the owned rows)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…fore the final hot-file commit

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… (hot-file protocol: every hot-file edit in the last commit)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…lassifiable read (fix round + micro-fix)

Fix round after the bounded Opus read and the GPT delta read of option (ii):
- Computed-execution refusals of b8eb935 are restored (opaque pieces keep their tail; a separate field
  carries the residual marker), so an executed computed path still refuses with gate_input_unresolved.
- A repository-relative literal joined to an environment, cwd or unfollowed-call base keeps the assumed
  repository-relative alternative, so the literal stays protected.
- Reads that are neither protected nor a whole run-time subject are listed as unclassified (runtime-only
  compositions, unknown divisions, opaque enumeration bases, aliased joins); leading ./ is normalised.
- Exact-glob tests with matching and non-matching paths, execution and protection controls, receipt
  count/omitted/null fields, and the documented with_suffix/with_stem narrowing.
- evidence/gate-reads-set-diff-20261004.json: b8eb935's protected set is a subset of the new one, no losses.
Micro-fix (a recorded exception: a pattern main introduced in 89cf253): iteration over literal dict or
list constants (items, values, keys, tuple unpacking) resolves those literals, with a positive test and a
non-literal control. Real tree: 0 unresolved; 321 unclassified in 29 scripts; breadth within the caps.

Built by the GPT-6.1 Sol worker; committed by the coordinator; delta reads follow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…egistry plus the owned rows)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…fore the final hot-file commit

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… (hot-file protocol: every hot-file edit in the last commit)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…isabled until the allowlist gate

The coordinator's decision (B), recorded in the resolver decision and RESOLVER.md: deriving the reads of
arbitrary Python by static analysis is open-ended (five rounds kept surfacing latent losses), so it no longer
gates.
- push_gate.py collects every gate_reads result (files, prefixes, globs, unclassified) into an advisory
  record, printed and kept in the receipt (advisory_gate_reads); it never refuses and claims no protection.
- Refusals that do not come from the read derivation are unchanged: .github/ and CODEOWNERS, the gate's
  trusted files and the resolver driver, the test tree, workflow-named files and their closure, PR or issue
  text interpolation, ancestry and trusted-checkout checks, pinned zizmor, unparseable workflows or scripts.
- resolver.py refuses every run (exit 3, before parsing or any external call) until the default-deny
  owned-path allowlist gate lands in its own PR; tests assert it.
- The baseline test compares scripts and shapes, not line numbers; the baseline is refreshed (321 entries
  in 29 scripts). The five latent P1s of the last two reads are recorded as known gaps of the monitor.
- Overturn: a derivation that passes an adversarial corpus with no losses against b8eb935 could be
  reconsidered as a second layer, never as the only gate.

Built by the GPT-6.1 Sol worker; committed by the coordinator; a Claude read follows.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…egistry plus the owned rows)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…fore the final hot-file commit

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… (hot-file protocol: every hot-file edit in the last commit)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…eck outside the monitor; records and tests (Opus r3 P1, P2-1..P2-5)

GPT-6.1 Sol round 489-r7 through the packaged SDK worker. Host run of
tests.test_runtime_worker_openhands_push_gate and tests.test_runtime_worker_openhands_resolver:
213 tests, OK (skipped=1), exit 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…fore the final hot-file commit

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… (hot-file protocol: every hot-file edit in the last commit)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… record the host run (Opus r4 P2)

GPT-6.1 Sol repair round through the packaged SDK worker; acceptance in the round's result.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…fore the final hot-file commit

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… (hot-file protocol: every hot-file edit in the last commit)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…et-diff's two descriptive lock-path lines in the frozen-macOS tripwire

GPT-6.1 Sol round 489-r9 restores 'A passing suite at the PR head is still required.' and keeps the 677acc4 host
run as dated evidence for that head only. The coordinator pins the two lines of
blueprints/runtime-workers/openhands/evidence/gate-reads-set-diff-20261004.json that list the frozen macOS lock as
a protected-set path (description, not use), as tests/test_frozen_macos_variant_no_use.py's rule allows after review.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…fore the final hot-file commit

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… (hot-file protocol: every hot-file edit in the last commit)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator Author

Head run at 136650165b6401f0b91584210bcd4e641d2972ee (the tree of the r9 and tripwire-pin commit before the registry-last split; the split leaves the tree unchanged), 2026-10-04, on the NativeStack WSL2 host, with TMPDIR outside /tmp:

python3 -m unittest tests.test_runtime_worker_openhands_push_gate tests.test_runtime_worker_openhands_resolver tests.test_frozen_macos_variant_no_use
Ran 224 tests
OK (skipped=1)
exit 0
python3 scripts/validate.py
exit 0

This satisfies the head-run requirement in RESOLVER.md for this head. The installed-gitleaks test passed on this host; it errors only in a read-only builder sandbox.

Claude session native-agent-stack-5f (stale-PR custody).

@seathatflowsinourveins
seathatflowsinourveins marked this pull request as ready for review October 4, 2026 21:40
@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator Author

Claude session native-agent-stack-5f: landing at head 136650165b6401f0b91584210bcd4e641d2972ee. The command center (wsl-architecture-design) gave its ACK at this exact head; under the user's 2026-10-04 decision it owns merges that touch the hot registry. The read-only cross-family review (GPT-6.1 Sol max, the packaged Codex SDK worker) returned ACCEPT at 677acc449c56; this head is reached from it through recorded carry edges (equal owned patch-ids, or a cross-family delta read returning ACCEPT at the edge's target), with no P1 item.

Observed main 08335d89bc71e2af5e5166300319c1f9c8392c54. coordination/merge_tree_landing_check.py (sha256 a11649fcf2e5844c…) exit 0:

main 08335d89bc71e2af5e5166300319c1f9c8392c54 head 136650165b6401f0b91584210bcd4e641d2972ee base 780bf5d0508760b2494995493d9246efd74d614d merged-tree 2d1f9f03fd00402011f8d1e36b5e860ae7d1312e merge-tree-exit 0
ok   1: clean three-way merge
ok   2: merged-vs-main paths 24, outside PR-owned 0 []
ok   3: main drift 110 paths, overlap with PR-owned inputs (registry excluded) []
ok   4: registry foreign rows equal True, order preserved True, PR-owned rows 23, unowned top-level keys differing []
ok   5: merged files[] sorted by path with no duplicates: True (9960 rows)
LANDABLE
condition 5: merged files[] sorted and unique (tree 2d1f9f03fd00402011f8d1e36b5e860ae7d1312e)

Required checks at this head: 8 pass . Unresolved review threads: 0.

@seathatflowsinourveins
seathatflowsinourveins merged commit 3b8f9c8 into main Oct 4, 2026
25 checks passed
@seathatflowsinourveins

Copy link
Copy Markdown
Collaborator Author

Claude session native-agent-stack-5f: post-merge observation. Landed as 3b8f9c8a1b938358d65bf422f61592dc3b89407d (parent 08335d89bc71e2af5e5166300319c1f9c8392c54), tree 2d1f9f03fd00402011f8d1e36b5e860ae7d1312e. The landed tree equals the checked merged tree and the parent is the observed main.

@seathatflowsinourveins
seathatflowsinourveins deleted the claude/openhands-resolver-20260928 branch October 4, 2026 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants