Use for Waves implementation workspace safety, PR preparation, review remediation, authorized merge/deploy verification, and interrupted-task handoff. Apply only the stages authorized by the task. Do not activate merely because an audit or explanation mentions shipping.
Resources
2Install
npx skillscat add wavespestcontrolfl/waves-customer-portal/waves-ship Install via the SkillsCat registry.
Waves Ship — PR lifecycle for the portal and astro repos
Purpose
Ship code changes through the Waves review/deploy pipeline without triggering the failure modes that have burned past sessions: wrong-branch commits, hijacked pushes, premature merges, silently-lost commits, and broken Railway builds. The quality system is the Codex pre-push hook, the @codex GitHub bot, the GitHub Actions tests workflow (.github/workflows/tests.yml, since 2026-07-12 — server/client/gates/native jobs on every PR), and Railway's prebuild gates — so this procedure is load-bearing. ⚠️ The tests workflow SILENTLY NEVER FIRES for a CONFLICTING PR (the pull_request merge ref can't be built), leaving a stale green from the old head — that check is automated in §4.
When to Use
- Starting any implementation task on waves-customer-portal or wavespestcontrol-astro.
- Pushing commits, opening PRs, responding to Codex reviews, merging, or verifying deploys.
- Deciding how to hand off fixes to a branch another session owns.
Scope and completion
Identify the requested outcome and existing approval before applying the
procedure. A local implementation does not require push, PR, merge, deploy,
or a gate flip to be complete. A read-only task does not authorize writes.
Tool permissions and the explicit merge, blast-radius, gate, and customer
communication boundaries remain in force.
Report the achieved state precisely: audit complete; implementation
complete locally; verification blocked; PR ready for review; merge-ready,
awaiting authorization; or deployed and verified. Include missing evidence
without implying it was obtained.
Run required checks for the applicable stage. Reuse recorded results only
when the checked code, environment, and relevant inputs are unchanged.
Repeat checks for changed code, failures, or unresolved risk; preserve
all final-HEAD CI and Codex requirements.
Procedure
1. Start clean
- Record the repository/worktree path, branch, HEAD, and existing changes.
Verify checkout state; do not assume a named path is bare, stale, or idle. - For implementation, use a task-owned worktree. Reuse the selected
task-owned worktree when suitable; create another only when the authorized
work requires isolation. Preserve unrelated changes. - When creating a new branch and worktree, resolve and record the user-selected base, or
a refreshedorigin/mainwhen no base was selected. Pass that revision
explicitly:git worktree add <path> -b <branch> <base-sha>; never rely
on the current checkout's HEAD as the implicit base. - When attaching an existing selected branch, verify its recorded SHA and
checkgit worktree listbeforegit worktree add <path> <branch>.
Do not recreate or reset the branch with-b/-B, or use--forceto
bypass another worktree's checkout. If it is already checked out, reuse
only a suitable task-owned worktree or follow the §7 handoff rule. - Record whether this task created or reused the worktree. Selecting or
reusing an existing worktree does not authorize removing it. - For audits and reviews, use the user's selected branch, worktree, diff,
or commit. Record the resolved SHA(s). Read-only review does not require
creating a worktree. - For an audit explicitly seeking current production defects, establish
the deployed revision and reachability. State whether origin/main was
refreshed and whether deployment was verified; never silently substitute
origin/main for another selected target. - Check dependency availability before installing. Follow the selected
checkout's setup procedure only when execution requires it. Write only
at the task-owned worktree path. For portal runtime verification, followdocs/development.md: runworktree:setupin manually created worktrees
and the relevantdev:doctorbefore managed startup.worktree:create
creates a new branch from refreshed origin/main and installs dependencies;
use it only when those effects and that base match the authorized task.
2. Before every commit
git branch --show-current— auto-applied CLAUDE.md edits and parallel sessions can silently flip HEAD.- Stage explicit paths. Never
git add -A(the external Codex process leaves stray files, and a live-edit fetch-stub underclient/public/__stub/is REAL prod payload — a stub once reached the public repo and forced a token rotation; gitignoreclient/public/__stub/in every worktree that uses the rig BEFORE the first commit). - Write the commit message from a FILE (
git commit -F), never reconstructed fromgit log— squash-merge uses the COMMIT MESSAGE, not the PR title, so this text is what lands on main.
3. Before every push
- If the diff touches
client/:npm run check:portal-brand— one violation kills EVERY Railway build for everyone. - If the diff changes a public API contract (
server/routes/*request shape, a newly required field, an endpoint rename): sweep EXTERNAL consumers before shipping — the astro repo (wavespestcontrol-astro) posts to these routes from BookingForm/QuoteForm/ChatWidget/EstimateForm and more.grep -rn "<route path>" ../wavespestcontrol-astro/src/for every touched route; a consumer that doesn't send the new required field must be updated in the SAME change window. (July 2026: #2572 made /booking/confirm require slot_sig, the astro form was missed, and online booking silently 409'd for 8 days.) - If the diff touches blog schema:
npm run verify:blog-schema(both run in Railwayprebuild; catching it locally is the only pre-deploy chance). - The pre-push hook runs a blocking LLM audit and blocks on P0. Budget for it: Codex alone is usually ~30–60s, but a ref that falls through to the Claude fallback can take up to ~13 minutes (180s Codex ceiling, then a 600s Claude budget covering both of its attempts), and a multi-ref push pays that per ref. The fallback prints its own ceiling when it starts, so the terminal tells you what it is waiting on. That is the expected shape when Codex is out of quota, not a hang — do not reach for the bypass because it is slow. Only bypass (
SKIP_CODEX_REVIEW=1 git push --no-verify --no-thin) when the external branch-hijack strikes — see REFERENCE.md. - Round 0 has two auditors. Codex is primary; when it cannot produce a verdict (usage limit, timeout, empty or malformed output, not installed) the hook falls back to a sandboxed
claude -prun using the same schema and the same P0 gate, and the verdict header names which one ran. A pass-through is only real when the output saysUN-AUDITED, meaning BOTH auditors failed — that is the only case where the GitHub@codexround is the sole audit, and it still must be stated in the PR body. - The fallback auditor is sandboxed because the diff it reads is untrusted input (
--restricted --strict-mcp-configplus an explicit disallow list that includesToolSearch). After anyclaudeCLI upgrade runscripts/verify-fallback-auditor-sandbox.sh— it proves the auditor cannot read a file, write a file, load a tool through ToolSearch, or reach a local HTTP canary, each decided by ground truth on disk or in the listener's log rather than the model's self-report, and it fails rather than reporting green if its own control probes cannot detect a tool that IS present. You do not have to remember: the hook readsscripts/.fallback-auditor-verifiedon every fallback run and warns when the installed CLI version or the sandbox definition has moved since it was last proved. The warning does not block, because refusing on an unrecognised CLI would re-darken round 0 on the day after every upgrade.scripts/test-verdict-guards.shcovers the three functions that decide whether a fallback verdict is trustworthy, andscripts/test-sandbox-fingerprint.shcovers the drift tripwire itself — the fingerprint hashes the extractedclaude -pinvocation as well as the flags, env allowlist and disallow list, so deletingenv -ifrom the shipping command trips the warning instead of passing silently, while merely re-indenting it does not.scripts/test-referenced-context.shcovers the third: the fallback has no tools, so the hook inlines the tracked, in-repo files the diff's addedrequire/importlines point at (capped atCLAUDE_CONTEXT_MAX_FILES/CLAUDE_CONTEXT_MAX_BYTES,CLAUDE_CONTEXT_MAX_FILES=0kills it) so it stops hedging about files it cannot open. Those paths come out of an untrusted diff, so that suite is a security test: it pins path-escape, symlink-escape, untracked-file (.env), node_modules and extension rejection. All three are no model calls, about a second — run them before changing any of the functions they cover. - Split advisory: a diff over 25 files or 120 KB prints a SPLIT ADVISORY at push time. It never blocks. Treat it as the prompt to cut siblings off main before the first
@codextag — §4's round cap otherwise discovers the same thing at round 5, after those rounds are spent. - After EVERY push:
git ls-remote origin <branch>and confirm the remote tip is your SHA. Re-check ~2 minutes later — an external Codex process has reset branches torefs/codex/curated-syncmid-push. - Before any FORCE-push or recovery push (re-pushing a SHA that was already the tip), capture
export VERIFY_PR_PUSH_AFTER=$(date -u +%Y-%m-%dT%H:%M:%SZ)FIRST and pass it toverify-pr-checks.shafterwards. A leftover CI run from the earlier push looks identical to a fresh one, and the script's reflog-based detection can't see a hijack that moved the remote without your tracking ref being fetched. Normal pushes of new commits need nothing extra. Never pipe a push through| tail— it swallows the hook's verdict. - If the pre-push hook blocked or produced no output: the findings JSON + codex logs survive at
$(git rev-parse --absolute-git-dir)/codex-review-last/— read them there instead of re-rolling the audit (its counts are nondeterministic run-to-run).
4. PR and Codex review
gh pr create --head <branch> --base main— always explicit flags (shared-worktree sessions have opened PRs from the wrong head).- Screenshots and recordings go on the PR as native attachments — never a local path, a pasted base64 blob, or a third-party image host.
gh≥ 2.99.0 (2026-09-01) adds a repeatable--attach <file>togh pr create/gh pr edit/gh pr comment(andgh issue create/edit/comment). Alt text via a#suffix:--attach './login.png#Login error state'. Formats: PNG, JPEG, GIF, WebP, SVG, MP4, MOV, WebM; 10 MB for images/GIFs, 100 MB for video on a paid plan. A Markdown image reference in the body to an attached file is rewritten in place; unreferenced attachments append at the end. Needs write access and an OAuth or classic PAT token; GitHub Enterprise Server is unsupported. Usegh pr create --attachon a new PR andgh pr edit --attachto update an existing PR's description (gh pr comment --attachadds a separate comment and leaves the description stale). Checkgh --versionfirst — older builds reject the flag. Fallback below 2.99 (Codex environments, machines where the Homebrew bottle has not caught up): put the vision findings in the PR body as text, state that the screenshots were reviewed in-session but not attached, and upgradeghat the next opportunity; still never paste base64 or link an external image host. UI PRs attach the ui-verify desktop + mobile screenshots this way. Source: https://github.blog/changelog/2026-09-01-github-cli-media-in-issues-pull-requests-and-comments/ - PORTAL ONLY — after every push once the PR exists, RUN
scripts/verify-pr-checks.sh(don't just remember the rule) — it verifies the remote tip is your SHA, the PR head matches, the PR is not CONFLICTING, and atestspull_request run exists for your head SHA, attributable to this push (re-verifying both heads after the wait; it prints whether attribution isexactorinferred). The script lives in the portal repo and hardcodes that repo +tests.yml; the astro repo has no equivalent — there, check mergeable and the Pages build by hand (see Astro-repo differences below). A CONFLICTING PR makes the tests workflow silently never fire (bit #3251 and #3253 the same night) — whenever CI looks green-but-quiet, this script is the check. On failure it prints the fix (merge origin/main, push, re-tag@codex review). - Review tier — pick it before the PR opens and name it in the PR body. Full is the default; Light only when every touched file qualifies, and a single touched file on the Full list makes the whole PR Full.
- Light: docs, tests,
ops/scripts,.claude/instructions, and code that ships dark behind an unsetGATE_*flag with no schema change. Pre-push hook + Codex rounds on the final head until one is clean, like any other PR — Light changes how much pre-review is needed, never the final-head gate below. - Full: anything on the CLAUDE.md rule-18 list or an AGENTS.md P0 domain (money, customer comms, schema/CHECK values, public token routes, webhook payloads, admin auth, app-consumed endpoints, spoke-fleet posts and feeds, persisted identifiers), plus any PR over the size rule. Full ladder below.
- Light: docs, tests,
- Size rule: over ~600 changed lines → split BEFORE opening the PR. Every PR that needed 5+ rounds in the 2026-09-04..07 window was over 600 lines, and the round cap only catches it after four rounds are spent. Mechanical bulk (fixtures, generated files, lockfiles) does not count toward the line total.
- Review map in the PR body (every Full PR; one line per requirement is enough for Light):
requirement → files carrying it → invariant that could break → exact command that proves it. Codex reads the body before the diff; a round spent on misread intent is the commonest wasted round. - Tag only a settled head: pre-push hook clean,
scripts/verify-pr-checks.shpassed (portal; astro = the manual mergeable + Pages-build check below), and every fix you already know about is pushed. Never tag mid-fix or on a head you expect to replace — that round is spent on a stale SHA. Tag bare@codexon a fresh PR; after each subsequent push, post@codex review(a bare re-tag is a no-op; a quote-reply "> @codex" spawns a cloud code-editing task, not a review). - For authorized local shipping tasks, if the local session supervisor is installed, enroll this PR from its owning session after the PR head matches the worktree. Follow
docs/session-supervisor.mdfor the exact UUID/worktree enrollment and pause/finish commands. It resumes exited processes only and each background continuation is limited to its enrolled PR; it does not replace the live review wait or live-session lane continuation below. Pause its job before a deliberate stop, an owner-decision handoff, or a scope cancellation; finish its job after merge/deploy verification and before worktree cleanup. Never enroll another session or an audit/proposal-only task. - Own review follow-through. For an authorized shipping task, tagging Codex starts a wait, not a handoff to Adam. Keep checking CI, paginated reviews, issue comments, and inline comments until the round is fully delivered under the gate below; then fix, rebut, or defer findings and re-tag as required. Use available waits or scheduled wakeups and keep progress updates flowing. Pending review or CI alone is never a reason to end the task or ask Adam to relay comments. If a quota, permission, or infrastructure failure prevents continuing, record the concrete blocker and resumable state under §7; never treat silence as clean or retry a denied action indefinitely.
- Siblings off
mainbeat a stack. Default to siblings. A stack re-reviews every level as fixes propagate upward, serialises the merges bottom-up, and pays a rebase on each one — a chain like #4029 → #4260 → #4254 reviews the same code several times over. Cut independent PRs againstmaininstead and let them converge in parallel; the re-cut of #4249 into five siblings (#4357–#4361) is the worked example. Stack only when a child genuinely cannot compile or be reviewed without its parent, and then keep it two deep at most. - Stacked PRs, when you do stack: open children as DRAFT and retarget them to
mainBEFORE the parent squash-merges — squash merges strand children (recurred 4×; GitHub only auto-retargets on head-branch deletion, which squash-merge flows don't guarantee). - Run the full merge gate in CHECKLIST.md. Core rules:
- Codex-complete on a HEAD = either the clean ISSUE comment whose Reviewed-commit SHA matches your final HEAD, or a review whose inline threads anchor to it (Codex never emits both — REFERENCE.md). A clean top-level with unresolved inline threads on the current head is NOT complete — check
original_commit_id. - Carried-over clean verdict: a clean issue comment on an earlier commit
Ralso completes the final HEADHwhen no Codex finding is open and every commit on the first-parent chain fromRtoHis a clean automatic merge of the base branch. Each one has exactly two parents, its first parent is the previous commit on the chain (Rfor the first), its second parent is on the base branch, and its tree equalsgit merge-tree --write-tree <parent1> <parent2>with no conflicts. Any hand edit, conflict resolution, rebase, or ordinary commit afterRfails this and needs a fresh round. CI must still pass onH. Owner ruling 2026-09-26: changes on the base branch that the PR depends on are not re-reviewed under this rule; final-head CI is the backstop. Locally,~/.claude/bin/codex-statusreports this asCLEAN_EQUIV; anything it cannot prove staysSTALE. - Inline findings lag the top-level wrapper — usually 1–2 minutes, but up to ~12 minutes on a heavy round (bit #3669 r4: three findings landed 12 min after the wrapper, after the fix for the first had already been pushed). Poll until comment count is stable for 15 minutes after the wrapper before treating a round as fully delivered.
- Severity scales. Two exist: the GitHub
@codexreview badges findings P0–P3 (P3 = lightgrey advisory badge); the pre-push hook's schema (.github/codex-review-schema.json) has only P0–P2, where its P2 covers what GitHub splits into P2 and P3. Findings are read on the GitHub scale: P0 = security/money/data loss, P1 = correctness/footgun, P2 = should-fix quality (AGENTS.md flags speculative abstraction and leftover compat shims here), P3 = advisory. The merge gate itself is zero findings (P3 advisory aside); the autonomous blog lane's own code bar (server/services/content/codex-remediation.js: P0/P1 block, a P2/P3-only round merges after one remediation round) is separate and governs only that lane. - Merge-ready = a Codex round on the final HEAD with zero findings, P2 included (owner exceptions per §5). For P0/P1 specifically: one is resolved only when it is FIXED, or when its inline rebuttal (file:line evidence) has been ACCEPTED — by Adam in-session, or by a subsequent Codex round on that same head that does not re-dispute it. An unevaluated rebuttal is unresolved: after rebutting a P0/P1, post
@codex reviewand wait for the confirmation round (if Codex will not re-review an unchanged head, use the empty nudge commit from REFERENCE.md). That is one confirmation round, not fishing. - Carry earlier findings forward until the fixing diff or accepted rebuttal is verified. A later clean summary or a stale inline anchor does not resolve an unchanged defect; reconcile the paginated review history against the final code before merging.
- P2s block a merge like any other finding: fix it, or decline it on its thread with the reason (needs a schema change or an owner decision), then re-tag until a round on the head comes back clean. A
Deferred P2slist in the PR body is not a resolution. P3s are advisory: no action or listing required. A per-PR owner stopping rule (memoryopen-stopping-rules) can relax this for that PR only. - A rebutted or declined finding is not a clean round: re-tag, and merge only once a later round on the head returns zero findings. Adam accepting a rebuttal in-session is an owner exception for that PR: record it under
Open for Adam→ resolved in the PR body, and it counts like a per-PR stopping rule. If Codex re-disputes a P0/P1 rebuttal, stop re-arguing and do not push again for it: it stays UNRESOLVED and needs Adam's decision before any merge (standing authorization included) — record it under anOpen for Adamheading in the PR body. A re-disputed P2 is handled the same way. - Round cap: if round 5 or later still surfaces a NEW P0/P1, the PR is too large to converge — stop, report the round history, and propose a split. Never push to fish for a zero-comment round: Codex's counts are nondeterministic run-to-run (REFERENCE.md), so a zero is a lucky draw, not evidence.
- NEVER merge until Codex has completed on the final commit, directly or through the carried-over clean verdict above. Green CI + COMMENTED is not enough.
- During Codex usage limits, a bounced re-tag is not queued — post a fresh one after reset. >15–20 min of silence on a heavy day usually means limits, not clean.
- Codex-complete on a HEAD = either the clean ISSUE comment whose Reviewed-commit SHA matches your final HEAD, or a review whose inline threads anchor to it (Codex never emits both — REFERENCE.md). A clean top-level with unresolved inline threads on the current head is NOT complete — check
5. Merge and verify
- Merge when clean is the standing default for authorized shipping tasks. The session that owns a self-authored PR merges it once the full CHECKLIST.md gate passes on its final HEAD; no separate merge prompt is needed. Honor an explicit local-only, proposal-only, PR-only, or do-not-merge scope. This default does not authorize shipping unrelated work.
- Clean = the §4 gate on the final HEAD plus successful final-HEAD CI: a Codex round on the final HEAD with zero findings, P2 included (
codex-statusCLEAN or CLEAN_EQUIV), unless a per-PR owner stopping rule or an in-session owner acceptance (recorded in the PR body) relaxes it. A round-cap stop (new P0/P1 at round 5+) does NOT — that PR goes back to Adam with a split proposal. - Blast-radius diffs (an AGENTS.md P0 domain or ANY CLAUDE.md rule-18 contract — read rule 18; its list is the whole list) are covered too, on judgment, after the clean gate above (owner ruling 2026-10-01). Ask: if this merges and nobody flips anything, does it charge, refund or message customers right away, or run a migration that can't be undone? Ships dark behind a gate, or reverts with no lasting effect → merge. Otherwise stop at MERGE-READY with the severity summary and ask Adam. For money diffs, also check rows written before the change and cron/job ordering. Flipping a prod gate and sending customer comms stay Adam's.
- After merge: confirm your final commit actually landed — "PR merged" doesn't prove it. Squash merges rewrite SHAs, so ancestry checks fail even on success: check
gh pr view <n> --json state,headRefOid,mergeCommitand confirmheadRefOidequals your final push SHA. Only for true merge commits doesgit merge-base --is-ancestor <final-sha> <merge-sha>apply. If your last push isn't in the merged head, recover via cherry-pick. - For an authorized merged release, confirm the Railway deploy went green
before reporting it deployed. Local and PR-only tasks use the completion
states above. - Continue the authorized lane. After verifying the merge and applicable deployment, start the next already-authorized PR in the same lane without waiting for another prompt. Confirm its scope and ownership from the approved plan or handoff; do not take over another session's branch or treat a proposed follow-up as authorization. Report completion when the lane is done, or the specific decision/blocker when it cannot continue.
- When the lane closes, remove only a worktree created by this task, after
confirming its changes are preserved, no other session is using it, and
no process or shared dependency still needs it. Never force removal.
Leave reused worktrees in place unless the owner explicitly authorizes
their removal.
6. Dark-ship pattern (user-visible features)
- New user-visible behavior ships behind an env-var gate (
GATE_*) or query param, default OFF in prod. Name the kill switch in the PR body. - Owner (Adam) flips gates — never flip a customer-facing gate without his authorization.
- After a gate flip, verify in prod (the gated page/flow renders; the kill switch works).
7. Parallel sessions
- Another session's hot branch (tip moved recently): deliver fixes as committable PR review suggestions, never competing pushes. One PR = one session.
- Pull the LIVE PR state before commenting or re-tagging @codex — pasted snapshots go stale.
- Paginate every Codex comment read:
gh api --paginate "repos/{owner}/{repo}/pulls/N/comments?per_page=100". The REST default is 30 per page; once threads plus replies pass 30, the newest findings fall off the end and a round reads as 2 findings when it was 3 (#3818 r5 missed a P1 this way, 2026-09-03). Counting bycreated_at > tag timedoes not help — the cut-off is positional.
When interrupted or handing off, record:
- Objective and acceptance criteria.
- Repository/worktree, branch, HEAD, and relevant dirty/index state.
- Changes made and unrelated work to preserve.
- Checks, exact checked state, and results.
- First concrete blocker.
- Existing approval, its source, and scope.
- Next safe action.
Keep secrets, customer records, and raw transcripts out. On resume,
revalidate the code state and ownership before relying on the handoff.
A quiet worktree or old memory note alone does not establish ownership.
Astro-repo differences
- Every push fans out builds across the whole Cloudflare Pages fleet (1 concurrent build account-wide; hub lags 30–45 min) — batch changes, pace commits.
- On any content edit bump
updated:onschemaVersion: 2blog posts andmodified:on other content; a post carrying both keeps them in sync (drives sitemap lastmod — astroCLAUDE.md§ SEO / deploy). - Brand-isolation CI fails the PR on hardcoded "Waves Pest Control"/hub URLs in spoke-shared content — use
{{brandName}}-style tokens.
Verification
Before reporting a shipped change as done, all of: final-commit-in-merge check passed; Codex gate passed on final HEAD (zero unresolved P0/P1 — every P0/P1 rebuttal confirmed by a later Codex round or accepted by Adam, none re-disputed and awaiting Adam; zero findings on the final head's round, P2 included, unless a per-PR stopping rule relaxes it); Railway deploy green (or Pages build green for astro); gate/kill-switch documented for user-visible features; remote tip verified after last push. If any step was skipped or impossible, say so explicitly — never imply it happened.
Failure Modes
- Merging on a stale clean signal (Codex reviewed an earlier commit).
- Stopping a clean, authorized shipping task for a redundant merge prompt, or self-merging a blast-radius diff that goes live on merge (charges, refunds, customer messages, irreversible migrations).
- Ending an otherwise runnable shipping task with CI or Codex pending and leaving Adam to relay the result.
- Re-pushing to fish for a zero-comment round with no real fix (a P2 is fixed or declined on its thread, then re-tagged once), or ignoring the round cap.
- Piping jest through
grep/tailfor pass/fail — it masks the exit code (a failing test shipped this way once). Run bare and check the exit code. - Trusting "PR merged" as proof your last push landed.
- Pushing client/ changes without the brand check.
- Editing files outside the selected task-owned worktree or overwriting unrelated work.
- Claiming "deployed" when only "merged".
git checkout <ref> -- <path>to read an old version (overwrites working tree) — usegit show <ref>:<path>.
Escalation
Bring unresolved design, functionality, or intended-behavior decisions to Adam. Routine technical choices within the approved scope — review fixes, PR splits, tagging, and clean merges (blast-radius ones on the §5 judgment test) — belong to the agent. Preserve the specific boundaries above: blast-radius diffs that go live on merge (§5), round-cap stops and re-disputed P0/P1 rebuttals (§4), customer-facing gate flips, direct pushes to main, and customer communications. Reuse existing applicable authorization; do not ask for it again. Record a required owner decision under Open for Adam in the PR body, or in the handoff if no PR exists; record execution blockers under §7.