| name | harness-pr-ops |
| description | Use while DRIVING a harness change onto trunk — opening/merging PRs, especially several at once (a roadmap or /retro batch), or the moment a guard unexpectedly BLOCKS a git/gh/bin-harness command mid-flow. Encodes the operating mechanics the enforcement guards + GitHub impose that aren't obvious until they bite: stacked-PR merge order, running locked-path commands cleanly, and what to expect at the human gate. Pairs with harness-pr (the PR template), windows-host-paths (token mechanics), worktree. |
Operating a harness change through the guards
The PR content rules live in harness-pr + harness-authoring. This is the
mechanics of getting the change to land without fighting the guards or GitHub —
the footguns that cost re-work the first time you hit them. Each rule has a receipt.
Standing grants — check BEFORE re-asking a settled point
Before ANY AskUserQuestion / "over to you" hand-back about approval mid-flow, run
"$HARNESS/bin/harness" approve --list. An active standing grant covering the scope
means the point is SETTLED: proceed (record marker grants against it verbatim), do
not re-ask. When the user gives blanket words ("full approval for everything",
"you're approved on X"), record them immediately: approve --standing --scope "<what kind of work>" --grant "<their verbatim words>" — that is what makes the
user-model's standing-grant test (evidence 4) mechanical instead of remembered.
FLOORS ALWAYS OUTRANK: guard-weakening and destructive/irreversible actions need
fresh explicit words no matter what --list shows.
receipt: roadmap item 2 (proposals/resolved/P-2026-039-product-ux-roadmap.md); the
re-ask friction fired 3x on 2026-06-27 and again 2026-07-05.
Stacked PRs — retarget the child BEFORE deleting the base
When PR-B is stacked on PR-A's branch (B's base = A's branch), merging A with
gh pr merge A --delete-branch deletes the branch B targets. GitHub does NOT
reliably retarget B — it auto-CLOSES B with a dangling base, and a closed PR
whose base branch is gone cannot be reopened or rebased (gh pr edit --base
and gh pr reopen both fail). You must recreate it as a new PR.
Do instead, in order: merge A → gh pr edit B --base main (retarget while B is
still open) → then merge/delete. Or simplest: merge A without --delete-branch,
retarget B, merge B, then delete both branches. Prefer not to stack at all when the
PRs touch different files (branch each off origin/main independently).
receipt: session edd67875, 2026-06-28 — merged #200 with --delete-branch; its
stacked child #201 auto-closed un-reopenably and had to be re-created as #203.
After retargeting the child to main post-SQUASH-merge, expect gh pr update-branch
to FAIL ("Cannot update PR branch due to conflicts"): the squash rewrote the base's
history, so files both branches touched become add/add conflicts. Recover locally:
git fetch origin && git merge origin/main; for files where the child is strictly
newer (progress logs, loop state) take the branch tip via git show HEAD:<file> > <file> (NOT git checkout --ours — the dirty-revert guard blocks checkout/restore
path forms), confirm zero <<<<<<< markers, commit the merge; then PROVE no content
drift — git diff <pre-merge-tip> HEAD --stat must be empty — and push; CI re-runs.
receipt: session 4acb66e4, 2026-07-02 — #220 squash-merged; stacked #221's
update-branch conflicted add/add on two loop-state files; resolved locally to the
branch tip, git diff da71709 HEAD --stat clean, CI green, merged.
Run locked-path commands on their OWN Bash call
The enforcement-layer guard blocks a Bash command when a working-tree-mutating /
file-write token co-occurs with a locked absolute-root path substring (…/bin/harness,
…/hooks/…, …) — it then reads the locked path as a WRITE target even when you only
EXECUTE it read-only. The triggering tokens are git checkout / git restore, rm /
mv / cp / tee / sed -i, a > / >> redirect, open(…,'w') — NOT git merge
or git commit (those are allowed alongside a locked path; verified empirically). Safe
habit: run a locked-path command (bin/harness predict/outcome/…) as its OWN call, never
chained after a git checkout/restore or a file write.
receipt: session edd67875 — git checkout main && git fetch && git merge … && python …/bin/harness outcome … was blocked ("'bin' is enforcement-layer"); the git checkout
was the trigger (a bare git merge … && python …/bin/harness … is NOT blocked). Splitting
bin/harness onto its own call ran clean.
A locked-layer build that adds test files: wire ci.yml in the SAME approve cycle
When an enforcement-gated change ALSO adds tracked test_*.py (a new file or whole new package),
test_ci_coverage.py requires each to be wired into .github/workflows/ci.yml (or excused in
INTENTIONALLY_UNWIRED) — and ci.yml is itself locked (.github/). So the ci.yml wiring is a
SECOND locked edit. Discover it UP FRONT and batch it into the same bin/harness approve →
edit-all-locked-files → --revoke cycle as your primary locked edit, instead of finding it after
the first commit and paying a second approve/revoke round-trip. Run python3 tests/test_ci_coverage.py
locally as part of pre-push validation so the requirement surfaces before CI, not after.
receipt: session 89bd318f, 2026-06-30 — an bin/harness delegation landed first; only then did
test_ci_coverage reveal 7 new fleet/test_*.py needed wiring into the (locked) ci.yml,
costing a second approve cycle. Excused fleet/test_mcp.py (needs the mcp SDK CI lacks).
The human gate is the EXPECTED terminus of locked-layer work — don't forecast auto-land
A change touching the locked layer (hooks/ lint/ evals/ bin/ .github/ autonomy.json settings.json templates/) does NOT auto-merge. Even auditor-APPROVED, the binding gate
is the human PR merge (harness-pr step 6); the agent must not self-merge. Likewise a
proposed enforcement gate may be redesigned NON-locked, and /run-evals may be
proportionately WAIVED for an additive read-only change. So when you predict such a task,
the calibrated expectation is "stops at the human gate / redesigned / eval-waived," NOT
"lands this session." Over-forecasting clean completion missed TWICE in one session
(predictions 9e2786ec, 5309dd57 — both enforcement-hooks/harness-authoring, the
session's lowest-hit categories).
Already documented elsewhere — go there, don't re-derive
- Guard-bypass tokens must LEAD the command (
HARNESS_TRUNK_LEASE_OK=1 no-ops behind
a cd … or any non-leading position; cwd already persists, so never prefix cd):
skill windows-host-paths §A (Manifestation A).
- A commit message / PR body that NAMES the enforcement marker trips the prose-scan
on inline
-m/heredoc text — write it to a file and use git commit -F FILE /
gh pr create --body-file FILE: skill harness-authoring §"Mentioning the enforcement
marker in a commit/PR body".
- Branch hygiene (return to trunk +
--ff-only refresh after a PR): harness-pr step 7.
Merging under branch protection
main requires the lint-and-test check on the CURRENT head AND an up-to-date branch.
After any merge lands, sibling PRs go BEHIND; gh pr merge then fails "Required status
check … is expected." Per PR: gh pr update-branch N → gh pr checks N --watch →
gh pr merge N. Do NOT reach for --admin to skip this — the up-to-date re-run is the
gate working, and bypassing CI on the harness is the reward-hack the kernel forbids.