Delivery — P8: Conflict Probe, PR, CI, Checkpoint 2
Owns the P8 phase (CONCEPT.md §4): the last autonomous phase before the
second human checkpoint. Everything human-facing here is rendered from
data — the PR body comes from state.json + findings.json via a
template script, never freehand. The human's job at CP2 is reviewing
and merging; this skill's job is making that reviewable.
Codex Adaptation
This skill is aligned with the Claude variant. In Codex: use
spawn_agent roles (worker or default) for fix tasks named micro-fixer —
do not assume Claude's Agent tool, subagent_type, or
run_in_background exist. Reusable skill assets live under
~/.codex/skills/....
Delegation Contract
Whenever Codex subagents are available and permitted, every delivery-time code, test, and defect edit MUST be worker-owned, including semantic-conflict resolution, CI/CP2 fixes, trivial edits, and integration corrections. The delivery lead owns decomposition, dispatch, integration, deterministic verification, gates, and operational records. Dispatch independent tasks with disjoint ownership concurrently; serialize dependent or overlapping work (or give the overlap to one worker). Send failed verification or integration work back as a narrowly scoped follow-up task. The lead may edit covered files locally only when delegation is unavailable or prohibited, and MUST report that reason explicitly. State, ledger, PR metadata, commits, and other operational records remain lead-owned through their existing commands.
Input
specs/PROJ-<X>-<theme>/state.jsonatP7:done(orP8:*when resuming)specs/PROJ-<X>-<theme>/findings.json— the ledger (debt section source)- The PROJ branch
proj/PROJ-<X>with all commits including docs .worktreemetadata from P0: dependencies are isolated in the persistent PROJ worktree;.env.local, development database, hosted-auth limits, and any configured dev port remain deliberately shared as recorded
Workflow
0. Gate + phase transition
bash scripts/state.sh get <X> <theme> '.phase + ":" + .status' must be
P7:done (or an interrupted P8 state when resuming). Then:
bash scripts/state.sh transition <X> <theme> P8 running
1. Conflict probe against main
Run bash scripts/conflict-probe.sh <X> <theme> (throwaway worktree —
it never touches real branches) and read the JSON verdict:
none→ continue.trivial(lockfiles/generated only) → mergemaininto the PROJ branch, regenerate the trivial files (e.g. re-run the package manager), commit, re-run the probe. It must now reportnone.semantic→ ONE bounded resolution attempt usingapi-contracts.md/architecture-delta as the reference. Resolved → commit + re-probe. Unresolved → record the affected files as a risk assessment in state (.summaryaddendum) so it renders into the PR body, and continue to PR creation — the human decides at CP2. A semantic conflict is a planning signal; note it for the retrospective.
2. Complete the ledger before rendering
bash scripts/harvest-debt.sh <X> <theme>— collectponytail:markersnode scripts/ledger.mjs auto-defer <X> <theme>— open Medium/Low → debt (§8)node scripts/ledger.mjs stats <X> <theme>— open_blocking MUST be 0. If not, P8 must not deliver: route back to the P6 fix loop; after three failed repairs on the same finding it is a stop condition.
3. Create the PR — body rendered, never freehand
Never write the PR description by hand and never edit the rendered body — fix the data (state/ledger) and re-render instead. Mode B (Jira) sync-back is Stage 3 — do not improvise ticket comments.
4. CI polling with a bounded fix loop
Run bash scripts/ci-poll.sh <pr-number>:
- Exit 0 (green) →
bash scripts/state.sh set <X> <theme> .pr.ci green, continue. - Exit 1 (red) → the script printed every currently failing check and its
verbatim
--log-failedoutput in one pass. Dispatch a fix task namedmicro-fixerper failing check immediately, concurrently (disjoint ownership — §5 spawn tiering, no context pack), with EXACTLY that check's verbatim output + affected file paths. Each fix task commits its own fix. Push once, only after every fix task in this round has committed — a push before the round is complete retriggers checks (including third-party ones like CodeRabbit) that already finished, restarting the wait for no reason. Then re-poll once. Max 3 attempts (one attempt = one dispatch-fix-push-repoll round); the 4th red on the same check is a stop condition (§8). - Exit 2 (timeout) → stop condition; record
.pr.ci timeout.
5. Seal the autonomous part
The P8 seal changes the PR head, so the earlier green result no longer proves the final commit. Transition, commit, and push the seal, then poll CI again:
bash scripts/state.sh set <X> <theme> .worktree.cleanup_status removed
bash scripts/state.sh set <X> <theme> .worktree.cleanup_reason \
'"removed after final CI, upstream-head, and clean-tree verification"'
bash scripts/state.sh transition <X> <theme> P8 done
git add specs/PROJ-<X>-<theme>/state.json
git commit -m "chore(PROJ-<X>): seal P8 delivery"
git push
FINAL_CI_HEAD=$(git rev-parse HEAD)
bash scripts/ci-poll.sh <pr-number> 1800 "$FINAL_CI_HEAD"
[ "$(git rev-parse HEAD)" = "$FINAL_CI_HEAD" ]
[ "$(gh pr view <pr-number> --json headRefOid --jq .headRefOid)" = "$FINAL_CI_HEAD" ]
Only exit 0 from that final poll makes P8:done valid. A red/timeout reopens
delivery work; immediately run
scripts/worktree.sh retain <X> <theme> "<exact final-CI reason>" so the
preserved worktree records why cleanup was refused. Do not claim the previous
head's CI as evidence for this one.
After exit 0, keep the persistent worktree while the PR is open so review
follow-up can use it. After the human merges, rerun P8 (or auto) to repeat
the exact-head checks and remove the worktree only through the safety helper:
scripts/worktree.sh cleanup <X> <theme> --ci-verified-head "$FINAL_CI_HEAD"
The helper exits 2 without changing P8:done while the authoritative
gh pr view state is OPEN; runner-managed P8 treats that as a successful
waiting state and prints the post-merge rerun command. Cleanup proceeds only
when the authoritative PR state is MERGED.
The poll is bound to the pre-poll local/PR head and rechecks the PR head after
all workflows finish; the two explicit comparisons above also reject a local
or remote head change between poll and cleanup. The helper additionally
requires .pr.ci == "green", P8:done, authoritative MERGED PR state, the sealed
removed intent, the expected branch/registration, an identical pushed
upstream commit, and a clean tree. It removes only the registered worktree
(including its reproducible ignored dependencies and managed env symlink) and
keeps the branch. On any failed predicate it changes the retained worktree to
cleanup_status: "retained" via state.sh, records the exact reason, moves a
sealed P8 to P8:blocked, and prints the safe P8-resume action. Resume P8 so it
reseals, pushes, and re-polls the new exact HEAD; a direct cleanup retry against
the now-dirty retained state is intentionally impossible. Never substitute
git worktree remove --force by hand.
When SKILLCHAIN_RUNNER_MANAGED=1, do not call cleanup in the writer lane.
The runner repeats the final CI poll after the lane exits and performs this
same guarded cleanup; this avoids deleting the runner's current filesystem
before it has verified the P8 seal.
The run is then waiting on the human. A runner-managed run emits the final PR, CI head, open-PR waiting status, and post-merge rerun command from the sealed state/helper result; nothing further happens autonomously.
→ NEXT ACTION: human reviews and merges the PR (Checkpoint 2 — for
overnight runs, in the morning via the morning report). After the merge,
set this PROJ's Status to shipped in specs/product-roadmap.md if that
file exists — it is what tells chain-guide (0) that dependent PROJs are
unblocked.
6. Checkpoint 2 — reconcile PR review comments (when they arrive)
Apply the checkpoint (4a) reconcile loop to the PR comments, via
gh pr view --comments / gh api:
- Classify each comment, point by point: fix now / debt / reject with rationale.
fix now→ dispatch a fix task namedmicro-fixerper comment immediately, concurrently for disjoint files (comment verbatim + file paths each), verify, commit. Do not push per comment.debt→ponytail:marker + ledger record (node scripts/ledger.mjs add <X> <theme>with statusdeferred), reply on the comment with the finding id.reject→ reply on the PR with the rationale — never silently ignore.- Once every comment in the round is classified and its
fix nowwork committed, append the round tospecs/PROJ-<X>-<theme>/decisions.md(decisions template frame), push once, re-request review. - Principle-level feedback ("I never want to see this again") → AGENTS.md/GUIDELINES candidate through the existing approval pipeline (documentation skill owns the merge).
Repeat per review round until merge. state.json stays P8:done; the
merge itself transitions nothing — after merge, done:done may be set
for bookkeeping: bash scripts/state.sh transition <X> <theme> done done.
Completion Checklist
- Conflict probe verdict handled (
nonereached, or semantic risk rendered into the PR) - Debt harvested + Medium/Low auto-deferred;
open_blocking= 0 - PR body rendered from data; no hand-written or hand-edited description
- PR created;
.prblock in state.json - CI green, or bounded fix loop / stop condition recorded truthfully
- state.json
P8:done - final CI verified the exact pushed seal commit
- Open PR retains the PROJ worktree; merged PR removes it safely, or records an exact failure reason and retry command
- CP2 comment rounds reconciled point by point with decision-log entries
Git Commit Format
chore(PROJ-<X>): P8 delivery — conflict probe, PR #<N>, CI green
fix(PROJ-<X>): CP2 round <NN> — <short summary of applied fixes>