Repository navigation
OpenHands resolver: host driver, one owner issue to one draft PR (stages 1-2, offline-tested) - #489
Conversation
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]>
…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]>
…mmit) 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]>
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]>
|
Head run at 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). |
|
Claude session native-agent-stack-5f: landing at head Observed main Required checks at this head: 8 pass . Unresolved review threads: 0. |
|
Claude session native-agent-stack-5f: post-merge observation. Landed as |
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 indb24156b9without rebasing or force-pushing. The original PR head501bcc9e50bf3ffa3acc82b6ecf20aa4e23c593bremains in history. Head:7c1d24cc5600bed8b56435aef37ec8d267f889fe. On top of0f7b27578it corrects this round's dates to 2026-10-04, asdate -ugives them, inb763cb294(text only; the commit also resets the registry to main's copy), and re-registers the rows in7c1d24cc5(the last commit). On top of4eb6b4cc9,0f7b27578added the merge4f963c9b2of main6af8e55bd(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), in57d0dc065. Main's Preserve PR561 lifecycle fixes and retire source-host evidence #679 madetools/adoption/install_claude_profile.pyread three workflow scripts, and following them had protected all ofblueprints/. The decision record,RESOLVER.mdand evidence part 8 are in85262b1f8, and the registry rows in0f7b27578(the last commit). The main-merge list below predates this merge. On top of21b24dede,4eb6b4cc9added the repair round for the cross-family read of40f12ba5(GPT-6.1 Sol, findings P1 and P2): the mergedb287ea72of main3bdacabd5, the gate-data reader, the instruction fix and their tests in86688e1e1, the decision record,RESOLVER.mdand evidence part 7 in0092ae213, the merge4a03dd796of mainba0e8c48f, the figures on that merge in1ec9d04c3, a receipt test and the final control runs ine1b4f5c68, and the registry rows in4eb6b4cc9(the last commit). Before that, on top of050bca5d5: the zizmor pin read from.github/requirements-ci.txtina5194090b(registry4009a96ecand40f12ba51), then the CI-blocker round for CodeQL alert 90 and validate-macos (the merged1bb9cf15of mainf474f6d22,36440d64a,9602c2274,8be0c1d39, registry21b24dede). On top of456f8fee6,050bca5d5added the decided amendment's trusted pre-push gate: code and tests in00905292dand48831bc65, the decision record,RESOLVER.mdand the evidence log in868f02501, and the registry rows in050bca5d5. Before that, on top ofb0c11c324, came wording-only fixes for the independent re-check and the coordinator's follow-up: content inf4e7aa2a2and54438ce7f, registry rows ind89ad3b44and456f8fee6. This description was written forb0c11c324. Parts were updated for050bca5d5: 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". For4eb6b4cc9, 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 describe050bca5d5, and the later rounds' evidence is inevidence/push-gate-fail-first.txtparts 6 and 7 and the PR comments. The SOTA "Step 6 repair round" line was updated for456f8fee6.Main merges during custody. Each followed the hot-file protocol: main's
manifests/evidence.json, then this PR's owned rows re-registered.9b0b8d6d2in37cb51899;4ced29230, which carried main'sAGENTS.mdupdate, in85830e815;59f8a1e36ind35633fad;d2fc3803e, which containsecea28654, indb24156b9;f474f6d22, which carried main's macOS CI scope step (e0c329ae9), ind1bb9cf15;3bdacabd5indb287ea72;ba0e8c48f, which carried the OSV and SARIF hardening port, in4a03dd796.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 of4eb6b4cc9against 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 sortedfiles[]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, bothevidence/stage2-*fail-first.txtlogs andevidence/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.pyis new). The registry commits re-register these files over main'smanifests/evidence.jsonand regenerate the four foundation reports through their supported commands. The last full pass is in the mergedb24156b9.b0c11c324refreshes the rows of the three files that the step 6 repair round changed.050bca5d5refreshes the six rows the gate change touched and adds three new files' rows.4eb6b4cc9registers the 20 PR-owned files over mainba0e8c48f's registry: 7 rows updated and 13 added,resolver/gate_reads.pyamong them. Both generators'--checkpassed each time, so no report was rewritten.Existing main defect disclosed. Commits
0c9b7acf6and3e3877979fix the SWE-bench skill contract and instruction. At the merged base,host.py:382requiresverification-before-completionande2e/task.py:52tells the worker to invoke it, while the runtime manifest lists that skill underexcluded. 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 inb2d556c95). The outgoing-guard fixtures no longer depend onTMPDIR. Twohost_pathassertions 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 ascripts/validate.pyPRIVATE_CONTENTpattern, such as a personal home path; the reason was reworded incf42c6d08. No assertion or reason label changed.Step 6 repair round (
cf42c6d08: documentation and one test's comment and skip text; its registry rows inb0c11c324).RESOLVER.mdnow 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.Pre-push gate (the amendment's decision of 2026-10-04; content in
00905292d,48831bc65and868f02501, registry in050bca5d5; the cross-family repair in86688e1e1,0092ae213,1ec9d04c3ande1b4f5c68, registry in4eb6b4cc9).GhHarness.pushrunsresolver/push_gate.pyon the exact agent commit before any push and pushes that commit by name (<sha>:refs/heads/<branch>, neverHEAD).GhHarness.runrefuses any push of a commit the gate did not pass, and any push without a gate..github/**, aCODEOWNERSanywhere, the resolver's gate and harness code, the workflow-policy tests (tests/**.pynaming.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 reusespatch_policy.names_in_text: the filesrun:steps name, the import closure of the gate scripts, unittest discovery and local actions. It also refuses a step that interpolates untrusted event text.ci_read,resolver/gate_reads.py), such asblueprints/convergence-practice/contract.schema.json, whichscripts/validate_convergence.pyreads. 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 tree4a03dd796has 10,528 tracked files, and the gate protects 7,559 of them (441 before). Outsideevidence/,tests/and.github/it protects 633 of 3,444 (122 before). Most of it comes fromtools/sota-convergence/gap_wave_ledger.py's computedevidence/artifactsanddocs/*.mdpaths. The decision record's residual risks name the two deferred narrowings, and a repository test bounds the breadth.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 inresolver-outcome.json(push_gate) and a code-and-count projection in the receipt.runtakes--zizmor, and the plan reportspush_gate: trusted_copy_checked.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
ghtemplates, guarded outgoing text and a bounded PR loop. Stage 2 adds:stage-gates.json;Its receipt does not infer successful completion from model-writable data.
Independent review history
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.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:
git apply;Both fail-first logs are unchanged: their blob SHAs at this head (
ed55377f,3f41c01b) equal those at501bcc9e.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 atdb24156b9(jobrev-489-astra) and returned repair with one should-fix finding, whichcf42c6d08repairs (see "Step 6 reviews" and "Step 6 repair round"). The builder's diagnosis and tests do not count as that review.The headless Opus 5.5 closure review of the whole repaired head (step 6(b)) was also pending at
b2d556c95. It returned repair atdb24156b9, with two should-fix and seven minor findings; "Step 6 repair round" gives each disposition. Before the PR leaves draft, the coordinator must still:A read-only Opus 5.5 custody review of
d35633fadreturned 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 atdb24156b92ac. 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.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, butgh_harness.py:44-47,257-259,704-708still 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.
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, andRESOLVER.mdfiles it under the wrong kind of change, so the owner cannot see what it costs or leaves open. (i)RESOLVER.md:519-521lists "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'sopenhands/*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.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 describesb2d556c95, not the reviewed headdb24156b9: line 5 (base and head); line 6 (mainecea2865"not merged", and the merge list omitsdb24156b9, which merges maind2fc3803e, already containingecea28654); line 8 (last full registry passd35633fad); lines 46 and 151 (d35633fadas 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 (validatefailed on main's unsorted registry pair). Atdb24156b9the registry has 0 unsorted pairs andvalidatepassed: job111264954786(head_shadb24156b9, CI merge69650a33ontod2fc3803e) showsvalidate.py{"hashed_files": 9550, "receipts": 193, "status": "passed"},component_matrix.py --check{"rows": 32, "status": "checked"}, the new-host grand-list check passed, andpython3 -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.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 stringf"{self.tmp}/"(:1584) fires exactly when the guard (outgoing_guard.py:165-170) would refuse asprivate_contentbeforehost_path, and no assertion is weakened (host_pathis still asserted at:1565on the synthetic root, the ValueError cases moved unchanged to:1570-1573ahead of the skip, and the symlink and realpath assertions at:1590-1592are unchanged). The skip message (:1588-1589) and the comment (:1582) blame "TMPDIR is under a personal home path", but the probe fires on anyscripts/validate.pyPRIVATE_CONTENTpattern (validate.py:25-37), such as a session-UUID, task-handle or Windows-user-pathTMPDIR, and then the skip names the wrong cause.blueprints/runtime-workers/openhands/RESOLVER.md:514-516; order atblueprints/runtime-workers/openhands/resolver.py:1589-1591andhost.py:1359-1361| The body says "G4 remains unrecorded, so a real run refuses withstage_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_runcallshost.verify_stage_gatesfirst (resolver.py:1589), andread_stage_gatesraisesstage_gates_not_recorded(host.py:1360-1361) beforeverify_reviewer_gate(resolver.py:1591) can raisestage_gate_g4_not_recorded. The gates themselves are intact: no bypass flag or environment override exists,_cmd_runrequires--reviewer-command(resolver.py:1663-1664), and at dispatch startverify_isolation(dispatch.py:319) re-checks P0-P2 freshness (host.py:1445-1447), the stage gates and G5 (host.py:1463-1464).validate-macosondb24156b9(run37144290967, job111264957003) | Verification gap, not a defect:validate-macoswas still pending (queued) at 2026-10-03T19:20:37Z; the other seven required contexts passed ondb24156b9. The native-macOS evidence the review read is from the earlier headd35633fad(artifact11278304065of run37129286224,full-suite-macos.log: "Ran 10070 tests ... OK (skipped=1329)",test_case_unicode_and_filesystem_aliases_are_refused ... ok, no FAIL or ERROR intests.test_runtime_worker_openhands_resolver). That confirms the git-plumbing alias fixture on APFS but predates theTMPDIRrepair2ede7b871; thedb24156b9run is the first native-macOS run of that repair.<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.origin/main1f5a791b02a230aced670c88bab3d3d0ebcf401aversus the merged based2fc3803e01178b4687771d0bf91faf453bb6933;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.jsonanddocs/token-session-handbook.md. Their hunks do not overlap this PR'sevidence.jsonhunks, but the landing head no longer merges current main.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 atdb24156b9: the unit-test pair under a neutralTMPDIRand a home-pathTMPDIR, the planted-defect mutations,resolver.py --helpandrun --help,git diff --checkand the pre-push gitleaks scan. The body records them only atb2d556c95. CI ondb24156b9covers the Linux full suite,validate.py, the generator checks and secret-scan.db24156b9| No defect (verification record). Scope:d2fc3803e..db24156b9is exactly the 17 allowed paths (README.md,.github/andblueprints/us-equities/untouched; owned files equalb2d556c95). Hot-file merge: all 9541 rows of main'sevidence.jsonkept in order, 9 new and 7 updated owned rows added, all 16 sha256/bytes values match HEAD,receipts[]andconvergence_records[]equal main's,files[]sorted with no duplicates. Preserved: both fail-first blobs (ed55377f,3f41c01b), decision-record lines 1-15 (match501bcc9eand 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 11pull_requestworkflows still declarecontents: readand use no secrets, and the SARIF upload jobs still skippull_request.fold(),HFS_IGNORABLEand the alias rules (patch_policy.py:116-129,:893-897,:953-958) decide from git data, and the outgoing guard's check order and 0600O_EXCL|O_NOFOLLOWbody files are intact.SOTA sources
v2.43.0, matching installed Git 2.43.0: the native index-fixture reference, git-hash-object(1), git-update-index(1), and index insertion implementation. The additional macOS requirement follows git.c's argument conversion, precompose_utf8.c, and core.precomposeUnicode. Installed help, versioned release notes and these primary sources were read on 2026-10-03. Earlier resolver patch handling also followsgit-apply(1),git-diff(1)andgit-merge-base(1)at this revision.v8.30.1: release notes, native Git-range and directory commands, and installed CLI help. The sandbox tests select the same installed upstream binary directly because the host wrapper's lock directory is outside the writable sandbox.bea7a20,skills/github-issue-to-pr/scripts/main.py(branch naming and loop) andskills/github-pr-reviewer/scripts/worker.py; OpenHands/software-agent-sdkfcc102a, v1.49.6; OpenHands/OpenHands7bc33009, the retired resolver comparison. These are the original implementation's historical reviewed pins; the custody change adds no runtime or orchestration alternative.v2.101.0(0cf10924), includingpkg/cmd/pr/checks/aggregate.go, credential-helper behavior and fixedapi/PR operations; GitHub create-review documentation (commit_id) and GraphQL issue/edit provenance, read in the original 2026-09-28/29 implementation review; CPythonv3.12.3,importlib._bootstrap_external.FileFinder, for package-before-module resolution.docs/decisions/2026-09-28-openhands-resolver-isolation.md,docs/lanes.md,.github/pull_request_template.md, the merged Runtime worker: OpenHands on GPT-6 via OmniRoute (recipe, O1 isolation, reviewed repair round) #425 recipe, Runtime-worker skills program: project-scoped installs and a 137-skill manifest #429's runtime skill exclusion and Agent branch ruleset: no history rewrite on openhands/* (applied as 24132241) #465's agent-branch ruleset. The registry follows the hot-file protocol: main's copy at each merge, thenscripts/host_receipts.py:register_filefor the owned rows and the supported report generators.v2.43.0t/test-lib-functions.sh(test_lazy_prereq, a probed prerequisite; filesystem examples such asSYMLINKSandCASE_INSENSITIVE_FSint/test-lib.sh), and CPythonv3.13.15Lib/unittest/case.py(_Outcome.testPartExecutorrecords aSkipTestraised insidesubTestas that subtest's skip;subTest,:538-565, resumes after the block and stops the method under failfast). Read on 2026-10-03; the installed interpreter'scase.pyis byte-identical to that tag.b0c11c324, coveringresolver/gh_harness.py(REPO,op_push,push,op_pr_createand its allowlist entry,check_repositoryandbranch_rules),resolver.pybuild_pr_bodyandplan_run, andhost.pyread_stage_gates. The repository's owner type, visibility and fork count were read withgh apion 2026-10-03. GitHub's fork-PR token and secret limits come from Events that trigger workflows, "Workflows in forked repositories", forpull_requestruns from a fork. Forks, "Which repositories can be forked?" says nothing on forking one's own repository, so the decision record marks the fork's holder undetermined. Both pages were read 2026-10-03 and re-read 2026-10-04T00:23Z.cache-mode,steps[*].run,working-directoryand localuses: ./;push"includes workflows that are not merged into the default branch", and the fork limits;The gate's mechanisms add three more: GitHub Script injections (the untrusted-context endings), Python unittest, "Test Discovery" with the installed CPython 3.13.15
unittest/loader.py, and the installed zizmor 1.30.1--help. In-repository references:patch_policy.py's reviewed derivation,tests/test_workflow_hardening.py's text-level workflow reading, and.github/requirements-ci.txtandvalidate.ymlfor CI's zizmor pin and flags.Path.rglob), fnmatch ("the filename separator ('/' on Unix) is not special to this module"), tomllib and csv (read through a file object), and the comprehension scopes of the Language Reference 6.2.4 and PEP 572. In-repository:scripts/validate_convergence.py(P1's example) andtools/sota-convergence/gap_wave_ledger.py(the main source of breadth).Evidence-class table
local_integrationb0c11c324with a neutralTMPDIR: 248 tests, exit 0. It uses fake Docker, GitHub and agent-server operations and real local GitTMPDIRlocal_integrationb0c11c324with a throwawayTMPDIRunder the host's home path: exit 0,OK (skipped=2). The two symlink subtests are skipped by the probed prerequisite with the reworded reason. Atd35633fadthe same run exited 1 with failures=2 ('private_content' != 'host_path')synthetic, at2ede7b8712ede7b871. With thehost_pathrefusal disabled, both tests fail under bothTMPDIRs (exit 1). With the realpath form dropped, the symlink subtest fails under a neutralTMPDIR(exit 1) and is skipped under a home-pathTMPDIR. Not re-run atb0c11c324:cf42c6d08changes only that test's comment and skip text, not an assertionsyntheticlocal_integrationPatchValidatorTestsinside the pair run atb0c11c324, exit 0 on Linux with real Git 2.43.0; no platform skipTMPDIRrepair pass on native macOSsource_reviewof retained CI execution outputdb24156b9:validate-macosjob111264957003(run37144290967), artifactfull-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_refusedandtest_pr_body_follows_the_template_and_renders_model_text_inertare eachok. This is the first native macOS run of2ede7b871. Earlier,d35633fad's job111220987307passed the alias test.b0c11c324: pendingsource_reviewof retained historical execution output36524134513(head501bcc9e), job109263298833, artifact11015337319,full-suite-macos.log: 7339 tests, failures=3, skipped=959. Its three FAIL blocks are the subtestsdocs/A.md,Docs/z.mdanddocs/café.mdat test line 725, each('case_or_unicode_alias', path) not found. Re-downloaded read-only in the custody review roundsynthetic, historicalevidence/stage2-*fail-first.txtlogssource_reviewv2.43.0git.c:449,compat/precompose_utf8.c:67-105,Documentation/config/core.txt:44-51source_reviewb0c11c324:resolver/gh_harness.py:44,:241-254,:257-259,:448,:627-628,:684-689,:704-709; the final message reaches the PR body throughresolver.py:547-550,:590-592source_reviewb0c11c324:plan_runchecks the stage gates, G5 and then G4 (resolver.py:1589-1591).read_stage_gatesraisesstage_gates_not_recordedwhenstage-gates.jsonis absent (host.py:1357-1361)source_review, historical scope94894f54; this is not current-base CI or fork acceptancesource_reviewof retained CI execution outputdb24156b9:validatejob111264954786, CI merge69650a33ofdb24156b9intod2fc3803e,python3 -m unittest:Ran 10078 tests in 1664.615s,OK (skipped=968).b0c11c324: pendinglocal_integration, before the step 6 round96b96c681(only.github/workflows/validate.ymlandmanifests/evidence.jsondiffer atd35633fad), home-pathTMPDIR: exit 1; 10,070 tests, failures=12, errors=1, skipped=850. Its 13 failing IDs are the 2 fixture cases repaired in2ede7b871and the 11 compared in the next row. Not re-run atb0c11c324: 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 supersededlocal_integration, atb2d556c95TMPDIRatb2d556c95, in a fresh detached worktree at origin/mainecea2865, and at the then-merged base59f8a1e36. With a neutralTMPDIR, the same 1 failure on every tree; with a home-pathTMPDIR, the same 9 failures on every tree. Supersedes the builder's 607-method archive comparison and the earlier4ced2923clone controllocal_integrationscripts/validate.pywith a neutralTMPDIRatb0c11c324: exit 0,"status": "passed"(9,550 hashed files, 193 receipts). CI'svalidate.pypassed ondb24156b9(job111264954786). The builder's exit 1 (theAGENTS.mdmismatch before the4ced2923merge) is supersededlocal_integrationdb24156b9: all 16 owned rows over maind2fc3803e's registry. Atb0c11c324: the rows of the record,RESOLVER.mdand the resolver test module are refreshed. Both generators exit 0 and change no file, andscripts/evidence_manifest.py --checkpasses (9,550 files)local_integrationorigin/main..HEADatb0c11c324: 36 commits, exit 0, no leaks found. The pre-commit scans of both step 6 commits found no leakssource_reviewof CI statusdb24156b9: all eight passed (validatejob111264954786,validate-macosjob111264957003). A latervalidatere-run there (job111278594671) was cancelled at 19:56:39Z, afterb0c11c324was pushed.b0c11c324at 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 queuedlocal_integrationtests.test_runtime_worker_openhands_push_gateat050bca5d5: 31 tests, exit 0,OK (skipped=1), the PyYAML cross-check, which this interpreter cannot run; its real-zizmor 1.30.1 test ranlocal_integration/usr/bin/python3(PyYAML 6.0.1)-m unittest ...push_gate.RepositoryWorkflowTests: 3 tests, exit 0synthetic, failing-first456f8fee6with 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)syntheticpush_gate.pyandgh_harness.py, each failing its named tests (exit 1); the unmutated copy passes (part 2)local_integration, rehearsal48831bc65with 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.tomland gate-code edits are refused with their rules, and a planted template injection is refused by zizmor. About 2 s per check (part 4)Local commands run
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:validatejob111264954786tested merge69650a33(db24156b9intod2fc3803e).validate.pyreported"status": "passed"with 9,550 hashed files and 193 receipts. The component matrix and new-host grand list checks passed.python3 -m unittestran 10,078 tests:OK (skipped=968).validate-macosjob111264957003passed. Itsfull-suite-macos.logshows 10,078 tests,OK (skipped=1329)and no FAIL or ERROR block.The merged registry is sorted. The
b2d556c95validatefailure (job111250877246) on main's unsorted registry pair no longer applies.The new head
b0c11c324changes 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 intests.test_runtime_worker_openhands_resolver.Linux full-suite acceptance item.
db24156b9, which differs fromb0c11c324only in the files listed above.b0c11c324under bothTMPDIRs.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 commit2ede7b871's message.4ced2923unmerged,validate.pyexit 1)59f8a1e36(merged ind35633fad), with4ced2923merged in85830e815.scripts/validate.pywith a neutralTMPDIR: exit 0,"status": "passed"atd35633fadandb2d556c95. The builder's exit 1 remains only as superseded historyecea2865and then at the merged base59f8a1e36, the 11 IDs, the command and every exit code under twoTMPDIRs. 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 summaryed55377fand3f41c01bare unchanged from501bcc9e. The same wording in the custody contract, which lives outside the repository, is reported to the coordinatorTMPDIRfailure was called an environment artifact, but the fixtures and the check order come from this PR2ede7b871(tests only), with its registry row inb2d556c95. A synthetic absolute host root serves the twohost_pathassertions. The symlink case probes its prerequisite, following git'stest_lazy_prereqpattern, and skips its two subtests with an explicit message. No assertion or reason label changed. Fail-first atd35633fad: exit 1, failures=2. After the fix: exit 0 under bothTMPDIRs. Two planted defects are caught (see the evidence table)validate-macoshad not run ond35633fadd35633fad: job111220987307passed, and its log showsOK (skipped=1329)with the alias testok. Onb2d556c95,validate-macosis pending andvalidatefailed on main's registry order; see "CI on this head"11015337319was re-downloaded read-only in this round, and its three FAIL blocks are cited in the evidence tableb2d556c95. The PR had no review threads at 17:16ZStep 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 incf42c6d08, with their registry rows inb0c11c324.--head <owner>:<branch>, the rules lookup and anon_fast_forwardruleset 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. TheRESOLVER.mdrunbook precondition now also stops for option 2 until that change lands. The amendment heading, record lines 1-15 and the live-run wait are unchangedRESOLVER.mdResiduals 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 inRESOLVER.mdis updated to matchb2d556c95b0c11c324with exit codesscripts/validate.pyPRIVATE_CONTENTpattern, such as a personal home path. No assertion changed, and the reworded reason appears in the home-pathTMPDIRrunRESOLVER.mdG4 residual and under "What is not done" belowvalidate-macosondb24156b9db24156b9: job111264957003passed, and its artifact is cited in the evidence table. Open:validate-macosonb0c11c324463a57b98reports LANDABLE. Contract step 7's "repeat steps 2 and 7" remains the coordinator's call before landingb0c11c324, except the host full suite and the planted-defect mutations (see "Local commands run")Decision record
docs/decisions/2026-09-28-openhands-resolver-isolation.mdrecords 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:
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
cf42c6d08and the wording fixesf4e7aa2a2and54438ce7f, the amendment's option 2:--head <holder>:<branch>value;pull_requestruns 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:
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;tests/**, and every file a reachable step names. A resolver task that needs those files fails at the gate with no push;Resolver-PR check results are still not evidence of model-code safety.
G4 remains unrecorded. Because no
stage-gates.jsonexists, a real run today refuses earlier, at the gates stage, withstage_gates_not_recorded. Once the recorded stage gates and G5 pass, it refuses withstage_gate_g4_not_recordeduntil the coordinator records the isolated reviewer argv hash.G5 and
stage-gates.jsonremain required. Both gateways' provider settings and the confinement design still need their own acceptance.These remain separate required steps:
The native macOS check of the alias repair and the
TMPDIRrepair passed ondb24156b9. 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
b0c11c324) and of both step 6 commits report no leaks, and the pre-push registry tests passed.