* fix(ci): isolate all native Testbox prepare gates * fix(ci): preserve captured base in remote prepare gates * chore(ci): integrate main fixes for remote gate proof Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
50 KiB
Scripts Guide
This directory owns local tooling, script wrappers, and generated-artifact helper rules.
Wrapper Rules
-
macOS-only Bash scripts use
#!/bin/bashand Bash 3.2-compatible syntax; invoke them directly or with/bin/bash, including fixtures. -
Portable Bash entrypoints using heredocs/here-strings (including sourced helpers) carry the inline Darwin Bash 5.3+ re-exec guard before those operations to prevent heredoc pipe deadlocks; stdin/sourced installers must request
/bin/bashwhen replay is impossible. -
Prefer existing wrappers over raw tool entrypoints when the repo already has a curated seam.
-
For tests, prefer
scripts/run-vitest.mjsor the rootpnpm test ...entrypoints over rawvitest runcalls. -
Never use bare
vitest ...in automation; it starts local watch mode unlessrunor--runis explicit. -
For lint/typecheck flows, prefer
scripts/run-oxlint.mjsandscripts/run-tsgo.mjswhen adding or editing package scripts or CI steps that should honor repo-local runtime behavior. -
For changed-file verification, prefer
scripts/check-changed.mjsand keep lane classification inscripts/changed-lanes.mjs. Usenode scripts/check-changed.mjs --dry-run [--staged|-- <files...>]to inspect the plan before running anything expensive. Do not copy path-scope rules into new hooks or ad hoc CI snippets. -
For one/few lint files, prefer direct
node scripts/run-oxlint.mjs --tsconfig <matching config> <files...>over shardedpnpm lint;check-changed.mjsowns this targeting for core, extension, and script diffs.
Testbox Command Checkout
.github/actions/prepare-testbox-shellowns the Testbox shell/working-directory contract. Noninteractive login shells preserve the caller-selected directory; hydration adapts only the known Blacksmith interactive-SSH auto-cdand probes exact physical cwd before ready registration. Do not force nested shells intoGITHUB_WORKSPACEor the transport checkout.- Raw Crabbox payloads start in the synchronized transport checkout.
crabbox-wrapper.mjsinstead verifies/applies its source capsule and reconciles dependencies in the prepared execution workspace before running the payload. Preserve that intentional receiver handoff; its pathname alone does not identify stale source. Verify selected source bytes/patch as well as directory, and stop/re-warm leases when preparation changes.
TypeScript Syntax
- Keep TypeScript implementation files under
scripts/**erasable by Node without transformation. Do not use parameter properties, runtime enums or namespaces, import-equals, export-assignment, or other transform-required TypeScript syntax. - This syntax rule does not make every script a plain-Node entrypoint. Keep
tsxfor closures that intentionally depend on source runtime trees, frozen checkouts, package aliases, or tsconfig/path resolution. - Native Node execution is opt-in per entrypoint and import closure. Use it only when runtime imports remain Node-resolvable and do not pull broader source trees into this syntax policy.
PR Prepare Gates
- The default agent handoff uses
OPENCLAW_PR_GATES_REMOTE=githubandmerge-run --auto-merge. Preparation recordsgithub_pendingbound to the published head without successful-proof stamps. Merge requires completed review, the exact prepared head, and the enforcedopenclaw/ci-gate; it rejects known failed required checks, accepts pending checks, and skips separate hosted workflow verification and its synchronous CI watcher. GitHub enforces required CI/security checks and reviews; the agent retains responsibility for follow-through. This mode replaces separate scheduled Testbox evidence with the PR's enforced gate; existing completed-evidence modes remain available. Accepted requests return pending, not completion. Follow the maintainer skill's polling cadence, investigate failures and conflicts, and reconcile through native recovery until merge and cleanup are verified. Preserve accepted or uncertain outcome records; never blindly re-arm a request. No implicit admin or REST fallback applies to pending-gate admission; explicitly authorized prior-CI admin admission is a separate mode below. - Gate-mode validation for
prepare-run,prepare-gates, andprepare-pushhappens before PR reads, lock acquisition, or preparation evidence retirement. Select one mode: the GitHub-pending invocation clearsOPENCLAW_TESTBOX; completed hosted proof clearsOPENCLAW_PR_GATES_REMOTE. Unknown or contradictory selectors must fail without replacing saved evidence. This validation does not block merge-outcome reconciliation, whose retained intent owns recovery. - Normal
prepare-initrequires incoming-head READY. To resolve a validated incoming NEEDS WORK review with BLOCKER/IMPORTANT findings, explicitly usescripts/pr prepare-correction-init <PR>. This initializes correction preparation only, preserving the incoming review and contributor ancestry. Commit the fixes, then useprepare-correction-review-initto create a separate exact-candidate JSON review template. JSON alone is authoritative; validation renders its summary. Independently review the full corrected candidate and explain resolution of every required incoming finding. Gates, push, sync and merge require that candidate's READY review; changing the candidate or incoming review invalidates it. Discussion/rejection verdicts cannot use this route. This does not change canonical-wrapper trust or permit use of an unlanded wrapper on another PR. Correction publication does not acceptgithub_pending; use a completed exact-candidate gate mode or the separately authorized protected Crabbox pending route. - PR source acquisition binds the full head SHA authenticated by live PR metadata. An existing direct task-owned destination ref at that exact commit can be reused with Git's checked-out/rebasing branch checks; absent, moved, symbolic, or hidden refs retain canonical-origin fetching and its filter/error behavior. Both paths verify the exact commit and recheck that head SHA, branch, and repository identity stayed unchanged during acquisition. GitHub's asynchronous
refs/pull/<PR>/headprojection is not source authority. All review, prepare, publication, and merge fetches use this owner without changing the private main checkpoint or shared tracking refs. - Supervised PR operations disable automatic Git maintenance through inherited process configuration, preserving repository settings. Explicit maintenance must still join before completion. PR source fetches also disable automatic maintenance: fetching one PR does not authorize repository-wide pruning of unrelated worktree metadata.
- Main freshness belongs to one
scripts/properation: nested entry/review guards share a captured main SHA; gate selection, publication after gates, and merge verification after CI each refresh it. Capture the canonical origin's main from the PR worktree's privateFETCH_HEAD, never a later shared-ref read. Main refreshes never write sharedorigin/main. Cold provisioning fetches into its existing per-PRtemp/pr-<PR>branch without writing canonicalFETCH_HEAD, then fully initializes from that seed before the private checkpoint; the seed is not checkpoint authority. Checkout budgets scale with the measured tree allocation (at least 4 KiB per file), with a four-hour ceiling; the same measurement grants Git at least 30 seconds and at most 30 minutes for signal cleanup before force-kill. Provisioning advertises its measured cleanup allowance over the existing lock-notification pipe so the outer supervisor also joins cleanup after an interrupt; the second-signal force escape is unchanged. Allocation leases, template records, and configuration observation use canonical-repo.local/pr-state, independently of the operator's state-directory environment. Failed native provisioning may remove only its exclusively reserved, unchanged directory after the Git runner settles and the shared cleanup owner proves no admin entry belongs to that path; ordinary rejection removes only an empty reservation, while interruption permits partial checkout removal; registered hook state, preexisting damage, uncertain process cleanup, and changed authority remain preserved. Directory cleanup never releases the failed operation lock. Every standalone command and newly provisioned worktree starts fresh; coalescing must not skip containment, transition recovery, exact-head checks, or the separate Crabbox authority windows. - Native gate planning derives one candidate/main fork base from that captured snapshot and passes it through
pnpm check --baseto all ratchets. Incoming review identity and prior publication receipts do not select the check base; keep those bindings intact when a prepared candidate incorporates main. - Local full-test gates scope
OPENCLAW_BUILD_PRIVATE_QA=1to their initialpnpm buildchild so ordinary test preparation can reuse complete artifacts. Keep the full build profile, freshness checks, and managed-Gateway fences; do not export the flag across check/test workers. Docs-only builds and remote/hosted gate modes retain their existing inputs. scripts/prserializes review, prepare, and merge operations per PR across linked worktrees;scripts/pr gcskips active or indeterminate locks. Its subcommand classification table is the canonical wrapper trust boundary: a mismatched local wrapper may run only a classifiedadvisorysubcommand with--dev-wrapperorOPENCLAW_PR_DEV_WRAPPER=1; classifiedlandingsubcommands always require canonical/origin-main wrapper code. A worktree whose wrapper differs from origin/main (stale base or wrapper-editing branch) loudly substitutes the canonical checkout's wrapper when that checkout is clean and byte-identical to fetchedrefs/remotes/origin/main; it refuses only when no anchor-matching wrapper is available. A successful command return is the trusted synchronous-completion contract: every PR-state-mutating child must be joined before returning, and such work must never daemonize or explicitly escape both the operation group and lock-notification FD. Release on clean exit requires the leader's completion marker; an escaped descendant that merely holds the notify pipe then produces a loud warned release instead of retention (#124583), while all failure shapes still retain. A failed command auto-releases only while its explicit pre-side-effect validation marker remains active; failures after mutation/tool launch, interruptions, and controller loss stay locked because detached children cannot be disproved. After verifying no child tools remain, use the reported exact-OIDscripts/pr lock-recovercommand. Never bypass or delete these refs manually.- Materialized wrappers pin selective third-party dependencies to their installed real package directories before handoff, not mutable top-level
node_modulesaliases. The anchoredpr-lib/materialize-dependencies.mjsowns that set; the child completes it on handoff too, because an older parent may pin fewer packages. Reentry preserves already pinned installations. For anchors predating that helper, the parent performs the same setup against the extracted manifest; pre-#149585 anchors without a manifest retain their original four tooling dependencies. Missing package directories or versions that differ from the anchored package manifest fail before lock acquisition; restore frozen dependencies in a clean trusted-main checkout and retry. Do not link the whole workspace dependency tree or install into the canonical checkout during materialization; only the explicitly selected separate tooling root below may refresh. Keep the resolved package installation intact until the supervised command finishes; pinning paths does not copy or freeze package contents.test/scripts/eager-import-closure.test.tschecks archived eager imports against copied sources and pinned package exports; keeppr-lib/wrapper-components.txtcurrent when that source closure changes. Runpnpm test test/scripts/eager-import-closure.test.ts -t "PR wrapper inventory"for the source-side inventory check before exercising extraction. It names missing eager runtime imports and stale deleted paths; existing shell-launched helpers, workers, and data assets remain explicit inventory roots. OPENCLAW_PR_GATES_REMOTE=testboxruns orderedpnpm build,pnpm check --base <captured-fork-base>, and full-suitepnpm testgates in one isolated Blacksmith Testbox source capsule throughscripts/crabbox-wrapper.mjs(same delegation ascheck:changed). The capsule owns dependency installation; no package gate runs in the local prep checkout. Docs-only changes still skippnpm test. Thetbx_lease id and Actions run URL land in.local/gates.env(REMOTE_GATES_*) and.local/prep.md. Use it for reviewed trusted code when a loaded host makes the local 88-shard run stall-kill; contributor/fork code stays on secretless CI or sanitized AWS unless a maintainer explicitly approves credentialed execution. ExplicitOPENCLAW_TEST_PROJECTS_PARALLELandOPENCLAW_VITEST_MAX_WORKERSvalues are validated as positive integers and forwarded to the remote test command. Empty values preserve the remote scheduler defaults; other caller environment variables are not forwarded by this adapter.OPENCLAW_PR_GATES_REMOTE=crabbox-awsis an explicit active-org-admin fallback, never the default.prepare-gatesrecords a pending handle; afterprepare-pushproves the exact remote head,scripts/pr-lib/ci-dispatch.mjs --backend crabboxsynchronously dispatches the protected-main publisher and waits for its exact-head check. That trusted workflow checksum-installs released Crabbox v0.46, resolves its/v1/whoamiservice principal, and creates sanitized direct AWS proof under the same token withumask 022, trustedscripts/crabbox-untrusted-bootstrap.sh,pnpm build,pnpm check, and the fail-closed PR-derived test plan from the repository's changed-test owner. Every executable changed path must independently resolve to concrete matched test files; broad fallback, partial plans, deleted executable paths, and unmatched/config targets are refused. Only explicit docs andAGENTS.md/CLAUDE.mdinstruction surfaces may produce zero tests. The canonical broker command binds the exact PR base, head, bootstrap hash, and plan digest. The publisher requires the PR base to be the merge base of its immutable workflow SHA and proves that each protected-main snapshot is identical to or descended from that workflow SHA, with an unchanged reread around each comparison. Main may advance during the long remote run, but not inside either validation window. It validates its newly created immutable broker run, ordered complete events, exact broker-resolved owner/org correlation between/v1/whoamiand the run, canonical bootstrap hash, exact command/base/head/plan, active admin actor, and open same-repository PR target before GitHub Actions adds the workflow SHA to the strict summary and publishes the distinctopenclaw/crabbox-gate; draft rejection remains a merge-time rule. Only after that success does.local/gates.envrecord provider/run/lease/URL recovery metadata from the trusted check. Retained logs are checked when present but are optional because released v0.46 can retain zero log bytes for a successful run. Normalopenclaw/ci-gatesemantics stay unchanged. Native merge may add--adminonly when the exact Crabbox check is successful from GitHub Actions, its immutable workflow SHA is an ancestor of a stable final protected-main snapshot, the actor is still an active organization admin, and the sole unsatisfied required check is a normal CI gate with GitHub-owned workflowstartup_failureor a recognized hosted, unacquired, zero-stepfailure/timed_outjob; cancellation, action-required, stale, an assigned runner, job log text, and any failed or executed workflow step never authorize bypass. The flow repeats this verification immediately before the pinned-head merge request; GitHub has no expected-base-OID merge precondition, so the Crabbox path compares the landed squash parent with that final main snapshot in.local/merge-crabbox-parent-audit.jsonand reports a match or intervening main movement after the completed merge. Normal merge paths do not perform this audit.
An interrupted protected Crabbox preparation resumes with
scripts/pr prepare-push <PR> --resume-crabbox-run <Actions run ID> after the
existing exact-token lock recovery, when required. This observes one pinned
publisher attempt; it never refreshes, pushes, or dispatches another proof.
Pending dispatch intent and selected run/controller/attempt stay in gates.env
until the trusted exact-head check succeeds. An accepted dispatch without a
recorded run requires explicit run selection; ordinary prepare-push refuses to
redispatch. Legacy pending preparations use the retained review base and fail
if the publisher proof does not match; current main is never substituted.
Pre-change completed stamps additionally require their retained broker run and
lease to match the trusted check. Their missing controller/attempt is pinned from
the selected Actions run, not treated as historical receipt evidence. A retained
base mismatch requires investigation of the original dispatch; resume refuses
without changing the receipt or substituting today's main or the check's base.
OPENCLAW_PR_TOOLING_ROOT selects a full checkout of the same repository for
materialized wrappers' third-party dependencies; otherwise openclaw.pr.toolingRoot in the
canonical checkout's Git config applies, then the canonical checkout itself.
The standalone CI watcher and Crabbox entrypoint resolve missing third-party
packages from the same tooling root when their checkout has no node_modules,
with the same explicit-root identity checks and exact package versions. They
never link an installation or resolve workspace packages from another checkout.
The wrapper still selects and verifies code against the existing trust anchor.
Installed package versions must exactly match the anchor manifest. On mismatch,
an explicitly selected, separate, clean main checkout is fetched, fast-forwarded,
and installed with pnpm install --frozen-lockfile once before rechecking. A stale
dirty or non-main root cannot refresh; sparse and unrelated roots are always
refused. The canonical checkout is never refreshed. Keep the selected installation
intact until the command finishes. This setting applies at both dependency
materialization handoffs; in-place wrappers keep their checkout's dependency
context, and wrapper selection stays unchanged.
OPENCLAW_PR_GIT selects the Git executable. Startup checks that binary with a
10-second deadline before choosing wrapper code; Darwin process-identity Python
calls use the same deadline. PR metadata, repository authority, writer identity,
comments, contributor authors, and author permissions prefer REST, with GraphQL
fallback on confirmed primary core quota exhaustion. Review snapshots omit unused
check rollups, and preparation reads only the live head fields it consumes.
Repository discovery uses the CLI's local default and host resolution, without a
quota-dependent HEAD request. Host-qualified locators go directly to the API;
review metadata reuses the resolved URL.
Each PR-head observation and merge snapshot explicitly requests
Cache-Control: max-age=0: the relay revalidates that read and may publish its
result, while separate before/after observations must never reuse one cached fact.
The landing-snapshot GraphQL query must exactly match the shipped Octopool shim's allowlist; branch identity comes from the existing REST source-acquisition and cleanup reads.
Writer identity uses the protected CLI with included headers on both transports
to retain the native writer route. Reviewer assignment requires REST and verifies
the retained assignee. The CI watcher polls GraphQL summaries, expanding details
for failures or pending checks after CI succeeds; primary quota exhaustion selects
REST with complete check/status and workflow evidence.
Successful authentication is reused within one shell operation and its nested
worktree entries; new processes and changed credential selection require a new
probe. This private process state is never inherited or written to artifacts.
merge_verify takes one options record with replacementHead,
autoMergeRequested, and observation; a null observation requests a fresh read.
Repository identity comes from the same PR observation as its head facts. Callers
carry that observation through source acquisition and hosted gates; acquisition
still independently rereads complete source identity after the immutable fetch.
Publication revalidates immediately before each Git/GraphQL write, after transport
preparation, and compares the successful publication observation before acquisition.
Ordinary immediate squash prefers REST. Admission reads switch transports only for confirmed primary quota exhaustion or unsupported REST policy or mergeability projections, including UNKNOWN; secondary throttles and access failures never authorize a switch. REST requires proven absence of classic protection and merge queues, supported effective rules, exact-head publisher-bound checks, and the retained-outcome lifecycle. It preserves configured message content from pinned published commits or the PR body and leaves the title to GitHub. Choose the transport before dispatch; never replay an uncertain mutation through another API. Completion comments use the receipt observation's transport and keep their one-attempt marker. Auto-merge, queues, admin admission, and non-squash merges require GraphQL; GitHub has no REST auto-merge endpoint. Legacy hosted workflow proof requires REST. Fallback never waives a required gate. API failures preserve safe quota and retry metadata from the original response. When that response has no usable HTTP framing, a separate GraphQL/core quota probe is labeled supplemental and does not establish the failed request's reset. Diagnostics never add automatic retries.
Octopool string rewrite protection
Keep gh on the Octopool shim; never disable string rewrite protection or select
the raw GitHub CLI to get a landing through. review-init resolves the repository
with the local gh browse command and a child-only URL-printing launcher. Older
Octopool versions reject this singleton command before their guarded best-effort
path. Upgrade to Octopool 0.7.1 or later; setting an explicit
host-qualified GH_REPO=github.com/openclaw/openclaw also avoids discovery while
preserving the subsequent authoritative API checks.
Immediate REST squash uses gh api --method PUT repos/OWNER/REPO/pulls/NUMBER/merge --input <absolute-file> with JSON containing the full prepared 40-hex sha,
merge_method: "squash", and the inspected commit_message; an optional
commit_title is accepted. The shared GitHub subprocess owner stages internal
--input - payload bytes in a private temporary file, keeps child stdin empty,
and removes the file after synchronous completion. Keep the explicit SHA even
when newer Octopool can resolve a missing one. Auto-merge needs Octopool's protected auto-merge support
(openclaw/octopool#179), a numeric PR, --squash --auto --match-head-commit SHA,
an explicit --subject, and --body-file. The wrapper supplies GitHub's
current-head viewerMergeHeadlineText preview so repository title defaults stay
intact. Octopool 0.6.10 and 641ce3c do not support that auto shape.
Prepare's reviewer assignment uses the exact issue-assignee POST with raw
assignees[] fields. Fork commit publication declares its GraphQL JSON with
--input, so the guard can inspect it; Octopool's aggregate input bound still
applies. Native CLI admin, non-squash, queue, and auto-cancellation variants are not
covered by the accepted shapes above. Do not replace them with an immediate REST
merge, which changes admission semantics. A blocked dispatch still follows the
retained-outcome recovery rules below; the generic guard error is not authority
to clear or retry an intent.
The explicit merge-run --admin-evidence <file> --confirmed-operator-admin mode
is a separate immediate-squash admission, not a fallback from auto/queue or a
failed request. It verifies a prior successful CI attempt and its PR/head
provenance, binds the reviewed prior-to-prepared delta and scoped-check
attestations, and revalidates active organization/repository-admin authority and
effective review rules. The original conflict-resolution route permits only
pending/skipped openclaw/ci-gate. An explicitly approved pre-existing-failure
attribution instead binds the current failed attempt, effective gate check-run,
tested merge/base, unchanged failure inputs, and inspected qualification artifacts.
Every failed job and fail-fast cancellation must be accounted for; cancelled
coverage stays unrun. Current openclaw/openclaw PR reruns let every Node matrix
leg finish; only PRs in other workflow repositories use native matrix fail-fast.
Historical runs retain their tested workflow's cancellation policy, so the matrix
attribution route still verifies that exact expression and run context.
An independently attributed cancelled Node test,
check-prod-types, or real-Gateway UI root can use
failures[].failedStep: { number, workflowJob }, with
checks-node-core-test-nondist-shard, check-shard, or
checks-ui-e2e-real-gateway, respectively.
Admission binds its live check-run, complete steps, single failed execution step,
timestamps, and unchanged audited workflow. The production-type and UI routes also
match every declared source step and its ordered timeline. The UI route permits
only its explicit optional runner setup/cleanup pair and requires the successful
private-QA build before its audited test entrypoint. Other steps must
succeed or be skipped through successful cleanup.
Retain the cancelled conclusion in the root proof; this is not passing coverage.
A collateral cancelled job's failed step remains blocking except for
the explicitly qualified historical skipped-producer/missing-artifact case in
the landing workflow. Its secondary evidence stays under cancellation, never
in the causal root list; test, cleanup, and upload transport failures remain blocked.
The review retains tests.result: "fail" with exact
tests.preExistingCi head/run/attempt attribution. Ordinary merge admission refuses
that review; the confirmed admin route must verify the same failed attempt.
Without a failed-step binding, an attributed cancelled Node root requires its
matching live GitHub Actions check-run and complete deadline/cancellation annotations,
consistent head/suite/timestamps, elapsed deadline, one cancelled test step, no
additional failed steps, and unchanged workflow. Retain that cancelled status in
the root proof; it is not fail-fast collateral or passing coverage. Manual or
unverified cancellation remains refused.
Branch-caused or unattributed failures, other required checks, security, and
required reviews remain blocking.
Exact-head github_pending preparation remains pending. GraphQL owns
observations and reconciliation; the protected REST PUT above owns the SHA-pinned
dispatch. The existing retained outcome owns priorCiAdmin evidence and retains
its baseline/prior head and tested merge objects. Exact PR/policy facts and the
landing-parent audit still apply; active prior-CI admission may accept verified
forward-main movement as described below. See the landing workflow
for the evidence fields and supported policy limits.
Generated Outputs
- If a script writes generated artifacts, keep the source-of-truth generator, the package script, and the matching verification/check command aligned.
- Prefer additive generator/check pairs like
*:genand*:checkover one-off undocumented scripts.
Scope
- Keep script-runner behavior, wrapper expectations, and generated-artifact guidance here.
- Leave repo-global verification policy in the root
AGENTS.md.
Native Merge Outcome Recovery
- Ordinary non-admin admission permits main to advance while retaining its pinned tree-proof/intent base and rechecking every PR/head/lifecycle/policy fact; GitHub owns applying the pinned head to current main. Active prior-CI admin admission may also accept forward main-only movement after proving ancestry from both its observed and CI-verification main anchors and checking the new merge is conflict-free and nonempty. For already-selected REST, the last complete observation and main materialization precede one final live authority verification. GraphQL retains its post-authority local-only reread (no lazy or explicit fetch), refusing a newly unavailable main before intent; late REST fallback retains that boundary and rechecks authority. No materialization follows final authority verification. Crabbox admin and OPEN/CLOSED/pending/uncertain reconciliation retain exact full PR/main snapshot stability. Only a validated MERGED receipt may accept forward main advancement between its two observations: every PR fact must remain equal, acquire the reread's exact main commit through the same canonical trusted URL when absent locally, and prove the original observed main is its ancestor. Equal snapshots keep the fast path. Both snapshots stay pinned; never add a third reread loop. This completes an already-proven merge, never grants authority for a future merge; historical tree/source-base checks and receipt/comment/cleanup ownership remain unchanged.
- Local object-availability probes and strict retained-record reads use command-scoped
GIT_NO_LAZY_FETCH=1. Upstream Git 2.45 first supports this environment variable; older Git may still hydrate objects implicitly and cannot promise local-only probes. The immutable-head reuse probe and final GraphQL prior-CI local-only check also use Git's explicit--no-lazy-fetchswitch: unsupported Git falls back to canonical head acquisition or refuses that final main-advance optimization. This is not a new all-command minimum or an offline workflow: explicit canonical fetches and actual tree/diff/archive/push/checkout operations remain available. Completed-receipt correctness depends on pinned facts and ancestry/tree proof, not this download avoidance. - Initial admission alone waits for UNKNOWN mergeability projections for at most three observations, sleeping one then two seconds, with PR and policy facts and each already-known projection pinned; full final rereads and retained-outcome reconciliation never poll. Before intent on PRs without a merge queue, ordinary merges reject gh's BLOCKED/BEHIND/DIRTY refusals and admin merges reject DIRTY; queue and auto retain their distinct admission contracts.
- Explicit prior-CI admission may select the complete REST observation owner after a valid GraphQL snapshot has an UNKNOWN projection. The next observation must preserve every known PR/projection fact; REST rereads retain repository identity, exact head, main, rules, and complete required-check evidence without converting BLOCKED to CLEAN. That reader remains selected through the attempt. During active prior-CI admission, both main endpoints of a complete REST read pass through the same forward-ancestry and merge-composition owner before normalization; branch policy is rechecked, and transient read bounds never enter retained outcomes. The latest validated main is tracked separately from the intent anchor. Only a new proven forward advance may open a three-observation recalculation window (waiting 1 then 2 seconds); exact PR/policy/check facts and original known projection values stay pinned, each subsequent main must advance from the latest read, and UNKNOWN never authorizes dispatch. The final already-selected REST window materializes both endpoints and the verified anchor before authority verification, including recalculation reads. GraphQL and late REST fallback retain their post-authority local-only window. If that GraphQL window observes a new main whose raw object is conclusively absent, it discards the active authority proof and returns to pre-authority materialization of that exact tip. Both earlier pins must remain readable; failed queries, stderr, corruption, permission errors, and unsupported Git never imply absence. Each new round preserves the observed tip as an ancestry lower bound and repeats complete live CI, head, review, evidence, security, and admin verification against the original proof fingerprint. At most three authority rounds may run before a refusal without intent or dispatch; no fetch occurs inside a qualified final authority window. Outside active admission, OPEN/CLOSED snapshot stability remains strict. Both observation and new intent record REST transport, while the existing prior-CI verifier still owns all CI exceptions, enforced reviews, and security clearance. Already-selected REST runs it once after the final complete reread, before unchanged evidence/artifact checks and intent CAS; it does not reopen a redundant observation and verification window. Unknown or malformed REST projections, conflicts, unsupported policy, and changed authority refuse admission. Ordinary REST remains passing-check/CLEAN-only; Crabbox and retained-outcome reconciliation gain no new dispatch authority.
- When GraphQL is selected, ordinary immediate squash dispatch uses the selected writer's exact-head mutation directly after one final explicit pre-merge revalidation. It preserves the captured body, omits the headline so GitHub retains its existing defaults, and cannot enqueue or arm auto-merge. Its initial and final public snapshots bind the head; existing REST source acquisition binds the branch, and both independent post-dispatch receipt observations remain fresh. Queue, auto, admin, merge, and rebase retain native
gh pr mergedispatch and their additional authority windows. merge-runowns remote dispatch separately from the process lock. Before any merge/auto/queue request it records the exact repository identity, PR, main target, prepared head, observed main, method, route, and attempt inrefs/openclaw/pr-merge-outcomes/<PR>. Private Git commits retain the required objects across worktree removal and GC. Do not delete or push these refs; process-lock recovery never clears them.- A failed request can already have merged. After the reported exact process-lock recovery, repeat
scripts/pr merge-run <PR>only for reconciliation.OPEN, a different head, reverted/partially applied content, elapsed time, or an absent process never proves non-execution. There is no automatic clear/retry override. Inspect the PR timeline, authoritative main history, andgit show refs/openclaw/pr-merge-outcomes/<PR>:outcome.json; unresolved uncertainty requires operator action outside this automatic path. Keep the record for that investigation. If an older wrapper left.local/merge-output.logwithout an outcome record, even an empty capture blocks a fresh dispatch; preserve it and reconcile the earlier request manually. - An explicit land/merge/ship request supplies standing operator authorization for investigated recovery on the same prepared head and merge method, including a newly reviewed/prepared replacement head that repairs the same authorized scope. Do not ask for renewed chat approval solely because main moved or a request failed. Investigate each retained outcome before deciding on a bounded recovery attempt; standing authorization does not establish non-execution or permit blind resubmission of accepted, pending, or unresolved requests.
- To repair an accepted or uncertain non-queue auto request, use
merge-recover <PR> <OUTCOME_OID> --confirmed-operator-recovery --cancel-autobefore replacing the remote head. The exact retained intent and matching open PR/head/base are required; a present request must also match the retained method. Cancellation is recorded before dispatch, preserves the original acknowledgment state, captures and ancestry, and reconciles a concurrent merge. Its two stability rereads may change onlymergeableandmergeStateStatus, which do not authorize cancellation; every identity, head, main, queue, request, and policy fact stays pinned. Ordinary merge admission and other recovery comparisons remain unchanged. An already absent request is recorded as retired without a cancellation mutation; absence does not prove that the earlier submission never executed. This investigated operator decision uses standing land authority, not renewed approval or a replacement PR. An uncertain cancellation is observation-only on retry. Only confirmed retirement admits ordinary recovery on the retained or explicitly reviewed/prepared replacement head, with completed gates. Queue/admin cancellation is unsupported. Never clear outcome refs or edit receipts by hand. - After that investigation, ordinary recovery uses
scripts/pr merge-recover <PR> <OUTCOME_OID> --confirmed-operator-recoveryunder the standing authorization above or explicit operator authorization for one new attempt. This is an operator decision, never proof that the prior request did not execute. It requires the exact current retained intent with either no recorded acceptance or confirmed auto cancellation, the same prepared head and merge method, fresh ordinary admission, and an immediate route for the new attempt. For a newly reviewed/prepared replacement head within the existing landing scope, explicitly select and append--replacement-head <SHA>with that exact full lowercase 40-character SHA; standing landing authority covers that repair without another chat approval. A scope or merge-method change still needs its own authorization. Never infer the selected head from PR/main/prep state. Replacement requires matching PR review stamps and prepare context, prepared-tree/gate bindings, completed CI proof, and a completed ClawSweeper review under the canonical freshness policy. Local evidence must remain unchanged through admission. Without the argument, changed-head recovery still fails. Recovery never rebases or bypasses CI, contributor-ancestry, branch, or owner gates. The successor-intent CAS consumes the exact outcome OID, retains the old outcome and objects as ancestry, and records the operator plus the explicit replacement head when supplied; each dispatch has its own capture. Replacement intent also snapshots existing regular capture files in its private Git tree under their original basenames, preserving the bytes through successful worktree cleanup and GC; symlink captures fail closed. Reusing the old outcome OID fails. Never infer authorization from an error string, an OPEN read, or a routine retry, and never clear the outcome ref. - A confirmed-cancelled auto squash may recover an explicitly selected reviewed head, including the unchanged retained head, with
merge-recover <PR> <OUTCOME_OID> --confirmed-operator-recovery --replacement-head <SHA> --admin-evidence <path> --confirmed-operator-admin. Current selected-head review, exact-headgithub_pendingpreparation, and all live prior-CI admin checks are required. The successor retains the original intent, cancellation, captures, and explicit replacement through the existing CAS ancestry; it never invents provider-rejection or pre-dispatch-refusal evidence. CI failure attribution must bind the selected head and current attempt. Explicit--replacement-headselection is still required for a retired-auto admin transition even when the SHA is unchanged; the no-argument rejected-admin recovery route stays separate. Unconfirmed retirement, renewed auto/queue requests, stale outcome/head/artifact bindings, and unrelated admin routes remain refused. - Explicit same-head prior-CI admin recovery may pair
merge-recover <PR> <OUTCOME_OID> --confirmed-operator-recoverywith--admin-evidence <path> --confirmed-operator-adminonly for the complete known GitHub REST HTTP 405 base-modified rejection and matchingghdiagnostic. The request was sent;recovery.providerRejectionrecords its qualified rejection separately from local pre-dispatch refusals. Preserve the exact original regular capture and any previously qualified captures. The successor CAS retains those bytes and the prior intent, including through acceptance, completion, worktree cleanup, and GC. Fresh review, security, current admin authority, evidence integrity, merge-tree, and exact-head checks remain required; the same-headgithub_pendingstamp is never rewritten as success. Unknown extra captures, changed or symlink captures, other 405s, timeouts, 5xx/mixed/truncated responses, accepted intents, other admin routes, and head replacement remain refused. Each further recovery requires its new outcome OID and explicit confirmation; never infer authority from an OPEN read or retry automatically. - An unaccepted prior-CI REST admin intent on a retired head may transition to a fresh current-head auto request only with
merge-recover <PR> <OUTCOME_OID> --confirmed-operator-recovery --replacement-head <SHA> --auto-merge. The explicit replacement must differ from the retained admin head. Its exact reviewed/preparedgithub_pendinghead must still be OPEN,MERGEABLE/BLOCKEDorMERGEABLE/BEHIND, free of an existing auto/queue request, and pass the normal required-check, ClawSweeper, security, and owner gates. This route does not use--admin-evidence: the old exact-SHA REST request remains uncertain but is fenced from the different live head, whilerecovery.staleHeadRetirementretains its intent ancestry and every regular capture without interpreting its bytes; an empty capture is valid. The successor route isauto, never admin or immediate. Same-head, accepted, stale-OID, changed-head, missing/symlink/extra-capture, admission-time capture mutation, route/method, lifecycle, gate, and authority drift remain refused. - An inspected Octopool refusal for a retained, unaccepted auto squash intent may use
merge-recover <PR> <OUTCOME_OID> --confirmed-operator-recovery --pre-dispatch-refusal <evidence-directory>with an optional explicitly selected, reviewed/prepared replacement head under the landing authority above. Preserve the sole originalmerge-output.<attempt>.logandqualification.jsonin that directory. Qualification pinsoutcome, the capture Git blob OID ascapture, andinspected: true; it is an operator attestation, never an automatic retry inferred from an error. Forkind: "octopool-0.6.10-auto-refusal", include the inspectedversion,sourceRevision,parserSha256, and exactargsarray: the helper accepts only the audited 0.6.10 parser that rejected--autobefore dispatch and its complete refusal.kind: "octopool-0.7.1-missing-subject-refusal"uses those same fields, pinned to version0.7.1, source7ab9b348c99a7be4fdc82c75cb06ebce44e0007e, and parser SHA-256b32cb960537f5ffa1336a7689674afba9b4a2485b05e449acd2684a251ff8970; it accepts only the complete refusal for the inspected auto-squash invocation with its body file but no--subject, which that parser rejects before starting the mutation child.kind: "octopool-0.7.1-policy-timeout-refusal"qualifies only the inspected initial policy-fetch timeout from the exact released 0.7.1 Darwin arm64 executable and eight source files pinned inmerge-pre-dispatch-refusal.mjs. In addition to the common pins, attestproducer: "octopool",command: "pr merge",diagnosticsEnabled: true,version,sourceRevision,executableSha256, and the exactsourceSha256path/digest map. This branch accepts only the complete singleclass=timeouterror line; diagnostics, policy denials, and additional output remain refused. The retained native intent binds repo/PR/head/method/route; no uncaptured subject or full argv is required. That source revision returns this initial error before diagnostic setup or mutation dispatch; never manufacture a diagnostic. The new attempt still passes the actual string-rewrite policy and every existing review, CI, authority, and receipt gate. Forkind: "octopool-merge-diagnostics", attestproducer: "octopool"anddiagnosticsEnabled: true; the capture must contain exactly one complete diagnostic withchild_started=false, a known pre-start outcome, no mutation exit status, and no response headers. Native GraphQL dispatch requests these diagnostics and supplies the captured current-head squash headline and body to the publication guard. Missing, changed, symlink, extra, accepted, or ambiguous evidence stays fenced. Recovery retains the qualification and capture bytes with the prior outcome ancestry. Same-head and replacement qualification both bind local review, preparation, and gates to the approved head, then recheck artifact bytes and the prepared branch after awaited admission. A head-boundgithub_pendingstamp may be used only for this qualified recovery once the enforced CI gate and all required checks pass, with freshMERGEABLE/CLEANimmediate admission; the stamp is never rewritten as successful proof, and pending, admin, queue, or auto recovery is refused. - An operator-qualified legacy gh v2.98 DIRTY refusal may use
merge-recover <PR> <CAPTURE_BLOB_OID> --confirmed-operator-recovery --legacy-refusal <evidence-directory> --replacement-head <SHA>. Freeze the originalmerge-output.log,prep.env,prep.md, andgates.envbefore refreshing preparation. The command requires no existing outcome, the sole original regular capture, its exact Git blob hash and complete source-qualified pre-dispatch refusal, historical PR/head/base identity, and all ordinary current review/preparation/CI/admission gates. Empty, uncertain, extra, changed or symlink captures remain blocked. Standing recovery authority and inspected caller/source evidence qualify the refusal; matching text alone does not authorize a retry. The new current intent retains original files and source commits with explicitlegacyRefusalprovenance; it never invents a historical intent or deletes the original capture. Subsequent recovery uses the recorded current outcome normally. - Fresh direct PR/main reads and stable re-reads narrow races; they do not provide global exactly-once execution or a base-OID merge precondition. Immediate squash refuses
NO NET CHANGEwithout claiming the PR merged or all intended content remains present. Normal squash/merge receipts reconstruct the tree at the actual landed parent; merge also requires prepared-head ancestry. Rebase and queue receipts verify the aggregate reviewed delta in the historical landed tree using the unique source fork base of retained main/head, not the final rebased parent or recorded main itself. Missing or ambiguous source bases fail closed. Later main advancement or reverts do not authorize reapplication. A normal recorded squash that was empty at its landed parent warns for investigation; its receipt remains confirmed, with no resubmission or automatic revert. - Worktree cleanup (including GC dry-run and stale-entry provisioning) preserves
.local/merge-output.logat its original path unless the corresponding outcome passes local record and retained-object validation. Empty captures and dangling capture symlinks count. Preserve the worktree, metadata, and local branches for manual reconciliation; do not trash, rename, or manufacture a receipt.CLOSED/MERGEDand an absent process do not resolve dispatch uncertainty. Valid retained outcomes remain available after eligible cleanup; cleanup does not reconcile them or change process-lock recovery. - Before recording intent,
--auto-mergeselects immediate pinned squash forMERGEABLE/CLEANor auto forMERGEABLE/BEHINDorMERGEABLE/BLOCKED, subject to all admission gates and queue policy. Accepted auto/queue requests are visible pending outcomes, not completion. The request carries--match-head-commit; confirmation still requires the exact attempted head. This is a submission-time head precondition, not a server-side freeze against later collaborator pushes. Existing or ambiguous requests are never automatically cancelled, re-armed, or followed by an immediate fallback; an explicitly investigated accepted auto request uses the cancellation recovery above. Ordinarygh pr mergecan enqueue when queue policy applies. - A confirmed merge receipt precedes audits, comments, and cleanup. A comment POST has one attempt marker and is never blindly repeated. Recovery searches authoritative comments for that marker, reports completion pending, and leaves cleanup to the operator after ownership checks; it works without the original worktree/prep artifacts. Normal uninterrupted completion preserves comments and cleanup, with exact-head leased remote deletion. Authoritative branch absence completes cleanup; inspect warnings for advanced or inaccessible branches. Delayed recovery never deletes a recreated branch by name.
- After ownership-checked cleanup, explicitly finish a verified receipt with
scripts/pr merge-complete <PR> <OUTCOME_OID> --confirmed-operator-completion. It revalidates the exact retained receipt and remote merge, requires native worktree/local branch/remote head branch absence, and never dispatches a merge or deletes resources. Amergedreceipt may post its first completion comment;commenting/commentedrequire the existing exact attempt marker and never repost. Missing or ambiguous comments preserve pending state. For a retained prior-CI admin receipt, delayed completion reconstructs the historical parent comparison from retained admission main and the verified landed commit before checking cleanup absence. It labels the audit reconstructed after merge, claims no original at-landing audit, and retains the historical CI caveat. Legacy admin receipts without retained prior-CI proof still require owner review; no original audit is fabricated. Read the current outcome OID again after a state transition; stale OIDs are refused. Defaultmerge-runremains reconciliation-only.
Execution Gotchas
These commands apply on the host permitted by the task and its workflow; they do not authorize local execution or a broader test plan.
- For fs-safe dependency trouble, follow on-demand vendoring instructions; keep vendor contents local and registry dependencies as the default.
- Restore missing dependencies in a trusted normal checkout with
pnpm install, then retry once before diagnosing a code defect. Never reconcile a shared/worktree install while other jobs use it. - Run the CLI through
pnpm openclaw ...orpnpm dev, nevernode --import tsx src/index.ts; the supported wrappers own build freshness and process setup. - Use installed
oxfmtfor formatting and the repository'stsgolanes for typechecking. Inspect scope withpnpm changed:lanes --json; use targeted tests/checks. When avoiding worktree reconciliation, usenode scripts/check-changed.mjsornode scripts/run-vitest.mjswith ready dependencies. Host restrictions still apply.