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 --statclean, 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"); thegit checkoutwas the trigger (a baregit merge … && python …/bin/harness …is NOT blocked). Splittingbin/harnessonto 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/harnessdelegation landed first; only then didtest_ci_coveragereveal 7 newfleet/test_*.pyneeded wiring into the (locked)ci.yml, costing a second approve cycle. Excusedfleet/test_mcp.py(needs themcpSDK 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=1no-ops behind acd …or any non-leading position; cwd already persists, so never prefixcd): skillwindows-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 usegit commit -F FILE/gh pr create --body-file FILE: skillharness-authoring§"Mentioning the enforcement marker in a commit/PR body". - Branch hygiene (return to trunk +
--ff-onlyrefresh after a PR):harness-prstep 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.