@aperant/framework 0.16.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/CHANGELOG.md +185 -1
  2. package/dist/cli/commands/ci-watch.mjs +49 -2
  3. package/dist/cli/commands/ci-watch.mjs.map +1 -1
  4. package/dist/cli/commands/commit.mjs +3 -3
  5. package/dist/cli/commands/commit.mjs.map +1 -1
  6. package/dist/cli/commands/event.mjs +16 -16
  7. package/dist/cli/commands/event.mjs.map +1 -1
  8. package/dist/cli/commands/merge-integrate.mjs +3 -3
  9. package/dist/cli/commands/merge-integrate.mjs.map +1 -1
  10. package/dist/cli/commands/produce.d.mts +9 -0
  11. package/dist/cli/commands/produce.d.mts.map +1 -0
  12. package/dist/cli/commands/produce.mjs +1345 -0
  13. package/dist/cli/commands/produce.mjs.map +1 -0
  14. package/dist/cli/commands/spar-resolve-partner.d.mts +9 -0
  15. package/dist/cli/commands/spar-resolve-partner.d.mts.map +1 -0
  16. package/dist/cli/commands/spar-resolve-partner.mjs +82 -0
  17. package/dist/cli/commands/spar-resolve-partner.mjs.map +1 -0
  18. package/dist/cli/commands/task.d.mts +16 -1
  19. package/dist/cli/commands/task.d.mts.map +1 -1
  20. package/dist/cli/commands/task.mjs +434 -266
  21. package/dist/cli/commands/task.mjs.map +1 -1
  22. package/dist/cli/config/gitignore-drift.d.mts.map +1 -1
  23. package/dist/cli/config/gitignore-drift.mjs +1 -0
  24. package/dist/cli/config/gitignore-drift.mjs.map +1 -1
  25. package/dist/cli/config/post-merge-sweep.d.mts.map +1 -1
  26. package/dist/cli/config/post-merge-sweep.mjs +1 -0
  27. package/dist/cli/config/post-merge-sweep.mjs.map +1 -1
  28. package/dist/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
  29. package/dist/cli/coordination/auto-emit-artifact-ready.mjs.map +1 -1
  30. package/dist/cli/coordination/event-schema.d.mts +5 -3
  31. package/dist/cli/coordination/event-schema.d.mts.map +1 -1
  32. package/dist/cli/coordination/event-schema.mjs +245 -21
  33. package/dist/cli/coordination/event-schema.mjs.map +1 -1
  34. package/dist/cli/coordination/store.d.mts +2 -2
  35. package/dist/cli/coordination/store.mjs +4 -4
  36. package/dist/cli/coordination/store.mjs.map +1 -1
  37. package/dist/cli/dispatch.d.mts.map +1 -1
  38. package/dist/cli/dispatch.mjs +4 -0
  39. package/dist/cli/dispatch.mjs.map +1 -1
  40. package/dist/cli/help.d.mts.map +1 -1
  41. package/dist/cli/help.mjs +31 -0
  42. package/dist/cli/help.mjs.map +1 -1
  43. package/dist/cli/host/codex-config.d.mts +78 -0
  44. package/dist/cli/host/codex-config.d.mts.map +1 -0
  45. package/dist/cli/host/codex-config.mjs +169 -0
  46. package/dist/cli/host/codex-config.mjs.map +1 -0
  47. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  48. package/dist/cli/install/legacy-paths.mjs +2 -0
  49. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  50. package/dist/cli/produce/blind-probe.d.mts +85 -0
  51. package/dist/cli/produce/blind-probe.d.mts.map +1 -0
  52. package/dist/cli/produce/blind-probe.mjs +217 -0
  53. package/dist/cli/produce/blind-probe.mjs.map +1 -0
  54. package/dist/cli/produce/claim.d.mts +188 -0
  55. package/dist/cli/produce/claim.d.mts.map +1 -0
  56. package/dist/cli/produce/claim.mjs +518 -0
  57. package/dist/cli/produce/claim.mjs.map +1 -0
  58. package/dist/cli/produce/done-gate.d.mts +87 -0
  59. package/dist/cli/produce/done-gate.d.mts.map +1 -0
  60. package/dist/cli/produce/done-gate.mjs +200 -0
  61. package/dist/cli/produce/done-gate.mjs.map +1 -0
  62. package/dist/cli/produce/events.d.mts +77 -0
  63. package/dist/cli/produce/events.d.mts.map +1 -0
  64. package/dist/cli/produce/events.mjs +126 -0
  65. package/dist/cli/produce/events.mjs.map +1 -0
  66. package/dist/cli/produce/evidence-oracle.d.mts +63 -0
  67. package/dist/cli/produce/evidence-oracle.d.mts.map +1 -0
  68. package/dist/cli/produce/evidence-oracle.mjs +122 -0
  69. package/dist/cli/produce/evidence-oracle.mjs.map +1 -0
  70. package/dist/cli/produce/ledger.d.mts +140 -0
  71. package/dist/cli/produce/ledger.d.mts.map +1 -0
  72. package/dist/cli/produce/ledger.mjs +272 -0
  73. package/dist/cli/produce/ledger.mjs.map +1 -0
  74. package/dist/cli/produce/probe-family.d.mts +53 -0
  75. package/dist/cli/produce/probe-family.d.mts.map +1 -0
  76. package/dist/cli/produce/probe-family.mjs +160 -0
  77. package/dist/cli/produce/probe-family.mjs.map +1 -0
  78. package/dist/cli/produce/projection.d.mts +55 -0
  79. package/dist/cli/produce/projection.d.mts.map +1 -0
  80. package/dist/cli/produce/projection.mjs +97 -0
  81. package/dist/cli/produce/projection.mjs.map +1 -0
  82. package/dist/cli/produce/run-id.d.mts +42 -0
  83. package/dist/cli/produce/run-id.d.mts.map +1 -0
  84. package/dist/cli/produce/run-id.mjs +79 -0
  85. package/dist/cli/produce/run-id.mjs.map +1 -0
  86. package/dist/cli/produce/saga.d.mts +180 -0
  87. package/dist/cli/produce/saga.d.mts.map +1 -0
  88. package/dist/cli/produce/saga.mjs +290 -0
  89. package/dist/cli/produce/saga.mjs.map +1 -0
  90. package/dist/cli/produce/scheduler.d.mts +165 -0
  91. package/dist/cli/produce/scheduler.d.mts.map +1 -0
  92. package/dist/cli/produce/scheduler.mjs +399 -0
  93. package/dist/cli/produce/scheduler.mjs.map +1 -0
  94. package/dist/cli/produce/setpoint.d.mts +52 -0
  95. package/dist/cli/produce/setpoint.d.mts.map +1 -0
  96. package/dist/cli/produce/setpoint.mjs +113 -0
  97. package/dist/cli/produce/setpoint.mjs.map +1 -0
  98. package/dist/cli/produce/verification-ttl.d.mts +75 -0
  99. package/dist/cli/produce/verification-ttl.d.mts.map +1 -0
  100. package/dist/cli/produce/verification-ttl.mjs +169 -0
  101. package/dist/cli/produce/verification-ttl.mjs.map +1 -0
  102. package/dist/cli/roadmap/{conductor-view.d.mts → showrunner-view.d.mts} +3 -3
  103. package/dist/cli/roadmap/showrunner-view.d.mts.map +1 -0
  104. package/dist/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +7 -7
  105. package/dist/cli/roadmap/showrunner-view.mjs.map +1 -0
  106. package/dist/cli/wfrun/run.d.mts.map +1 -1
  107. package/dist/cli/wfrun/run.mjs +1 -0
  108. package/dist/cli/wfrun/run.mjs.map +1 -1
  109. package/dist/plugin/.claude-plugin/plugin.json +2 -1
  110. package/dist/plugin/skills/apt/SKILL.md +112 -38
  111. package/dist/plugin/skills/apt-debug/SKILL.md +14 -24
  112. package/dist/plugin/skills/apt-fan-out/SKILL.md +4 -4
  113. package/dist/plugin/skills/apt-handoff/SKILL.md +1 -1
  114. package/dist/plugin/skills/apt-plan/SKILL.md +5 -5
  115. package/dist/plugin/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
  116. package/dist/plugin/skills/apt-produce/SKILL.md +606 -0
  117. package/dist/plugin/skills/apt-quick/SKILL.md +14 -22
  118. package/dist/plugin/skills/apt-run/SKILL.md +126 -3
  119. package/dist/plugin/skills/apt-ship/SKILL.md +2 -0
  120. package/dist/plugin/skills/apt-spar/SKILL.md +19 -9
  121. package/dist/plugin/skills/apt-watch-ci/SKILL.md +4 -4
  122. package/package.json +4 -4
  123. package/prompts/{conductor-framework-context.md → showrunner-framework-context.md} +1 -1
  124. package/prompts/{conductor-status-check.md → showrunner-status-check.md} +1 -1
  125. package/prompts/{conductor-sub-agent.md → showrunner-sub-agent.md} +6 -6
  126. package/prompts/{conductor-system.md → showrunner-system.md} +8 -8
  127. package/skills/apt/SKILL.md +112 -38
  128. package/skills/apt-debug/SKILL.md +14 -24
  129. package/skills/apt-fan-out/SKILL.md +4 -4
  130. package/skills/apt-handoff/SKILL.md +1 -1
  131. package/skills/apt-plan/SKILL.md +5 -5
  132. package/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
  133. package/skills/apt-produce/SKILL.md +606 -0
  134. package/skills/apt-quick/SKILL.md +14 -22
  135. package/skills/apt-run/SKILL.md +126 -3
  136. package/skills/apt-ship/SKILL.md +2 -0
  137. package/skills/apt-spar/SKILL.md +19 -9
  138. package/skills/apt-watch-ci/SKILL.md +4 -4
  139. package/src/cli/commands/ci-watch.mjs +51 -2
  140. package/src/cli/commands/commit.mjs +3 -3
  141. package/src/cli/commands/event.mjs +16 -16
  142. package/src/cli/commands/merge-integrate.mjs +3 -3
  143. package/src/cli/commands/produce.mjs +1466 -0
  144. package/src/cli/commands/spar-resolve-partner.mjs +85 -0
  145. package/src/cli/commands/task.mjs +482 -285
  146. package/src/cli/config/gitignore-drift.mjs +1 -0
  147. package/src/cli/config/post-merge-sweep.mjs +1 -0
  148. package/src/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
  149. package/src/cli/coordination/event-schema.d.ts +4 -2
  150. package/src/cli/coordination/event-schema.mjs +276 -21
  151. package/src/cli/coordination/store.mjs +4 -4
  152. package/src/cli/dispatch.mjs +4 -0
  153. package/src/cli/help.mjs +31 -0
  154. package/src/cli/host/codex-config.mjs +165 -0
  155. package/src/cli/install/legacy-paths.mjs +2 -0
  156. package/src/cli/produce/blind-probe.mjs +245 -0
  157. package/src/cli/produce/claim.mjs +543 -0
  158. package/src/cli/produce/done-gate.mjs +238 -0
  159. package/src/cli/produce/events.mjs +131 -0
  160. package/src/cli/produce/evidence-oracle.mjs +133 -0
  161. package/src/cli/produce/ledger.mjs +284 -0
  162. package/src/cli/produce/probe-family.mjs +168 -0
  163. package/src/cli/produce/projection.mjs +105 -0
  164. package/src/cli/produce/run-id.mjs +84 -0
  165. package/src/cli/produce/saga.mjs +303 -0
  166. package/src/cli/produce/scheduler.mjs +423 -0
  167. package/src/cli/produce/setpoint.mjs +122 -0
  168. package/src/cli/produce/verification-ttl.mjs +191 -0
  169. package/src/cli/roadmap/showrunner-view.d.ts +10 -0
  170. package/src/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +6 -6
  171. package/src/cli/wfrun/run.mjs +1 -0
  172. package/dist/cli/roadmap/conductor-view.d.mts.map +0 -1
  173. package/dist/cli/roadmap/conductor-view.mjs.map +0 -1
  174. package/src/cli/roadmap/conductor-view.d.ts +0 -10
@@ -17,7 +17,7 @@ execution_modes:
17
17
  - plan-mode
18
18
  - plan-only
19
19
  allowed-tools: "Bash, Read, Write, Edit, Grep, Glob, Task"
20
- argument-hint: "apt:run [task description] [--skip-review] [--phase-mode auto|agent-team|sub-agents|sequential] [--spar-gates plan[,execute]]"
20
+ argument-hint: "apt:run [task description] [--skip-review] [--phase-mode auto|agent-team|sub-agents|sequential] [--spar-gates plan[,execute]] [--full]"
21
21
  gates:
22
22
  - constitution-read
23
23
  - constitution-coverage
@@ -29,6 +29,8 @@ config_keys:
29
29
  Run the full Aperant pipeline: plan a task, execute all subtasks, verify the implementation, and optionally review the code. This is the one-command experience — from task description to verified, reviewed code.
30
30
 
31
31
  As of C32 the STANDARD and DEEP auto pipelines **terminate on review** (`plan → execute → verify → review`). Pass `--skip-review` to stop after verify; for trivial work the QUICK track still skips planning entirely.
32
+
33
+ Pass `--full` to carry a SINGLE task all the way to **merged + green**: it extends the pipeline past review through **ship** (a PR that auto-merges to main on green — never a direct push) and **watch-CI** (arm the watcher with auto-merge so the change lands once CI passes). `--full` is a thin chain over the existing `/apt:ship` + `/apt:watch-ci` skills — it forces review ON and is hard-guarded by `autonomy.allow_yolo` plus a one-time confirm (§1a). A `/apt:run` invocation WITHOUT `--full` behaves exactly as today (terminates on review).
32
34
  </objective>
33
35
 
34
36
  <your_environment>
@@ -58,7 +60,7 @@ As of C32 the STANDARD and DEEP auto pipelines **terminate on review** (`plan
58
60
 
59
61
  1. Read `AGENTS.md` if it exists
60
62
  2. Parse task description from `$ARGUMENTS` (everything before flags)
61
- 3. Parse flags: `--skip-review`, `--dry-run`, `--phase-mode <mode>`, `--spar-gates <comma-list>`.
63
+ 3. Parse flags: `--skip-review`, `--dry-run`, `--phase-mode <mode>`, `--spar-gates <comma-list>`, `--full`.
62
64
  - `--spar-gates <plan|execute|plan,execute>` (FRAMEWORK-RFC-001) — comma-separated stage list to auto-fire `/apt:spar` after. Per-invocation override of `.aperant/config.local.json` `spar.enabled`. On QUICK track, print the verbatim hard-reject line and discard the flag (Fast Path Guarantee — CLAUDE.md):
63
65
 
64
66
  ```
@@ -71,6 +73,52 @@ As of C32 the STANDARD and DEEP auto pipelines **terminate on review** (`plan
71
73
  node packages/framework/bin/apt-tools.mjs state .
72
74
  ```
73
75
 
76
+ ## 1a. `--full` flag — surface + gates
77
+
78
+ This section runs ONLY when `--full` was parsed in §1. When `--full` is absent, skip this entire section and run the pipeline exactly as today (manual mode is byte-behavior-unchanged).
79
+
80
+ **Contradiction handling (resolve in THIS precedence order — the "discard" cases win first, so a `--full` that is discarded never triggers the conflict or autonomy gates):**
81
+
82
+ 0. **QUICK track wins first (Fast Path Guarantee).** If the task is QUICK track, discard `--full` with the Fast-Path line (item 4 below) and run the QUICK one-shot path unchanged — do NOT evaluate the `--skip-review` conflict (item 1) or the autonomy gate. On QUICK, `--full` is treated as never passed.
83
+
84
+ 1. **`--full` + `--skip-review` → hard reject.** `--full` requires review before auto-ship, so the two flags are contradictory. Do NOT silently let one win — print the verbatim line and exit:
85
+
86
+ ```
87
+ [apt:run] --full and --skip-review conflict (--full requires review before auto-ship). Pick one.
88
+ ```
89
+
90
+ 2. **`--full` IMPLIES review ON.** Treat `--skip-review` as never set for the rest of the run; §6 Review always executes.
91
+
92
+ 3. **`--full` + `--dry-run` → dry-run wins.** Run the plan stage only and report (the existing §3 dry-run exit), then stop. No execute, no ship — `--full` is discarded for this run.
93
+
94
+ 4. **QUICK track → discard `--full` with the Fast-Path line.** On QUICK track `--full` is hard-exempt (CLAUDE.md Fast Path Guarantee). Print the verbatim line and run the QUICK one-shot path unchanged:
95
+
96
+ ```
97
+ [apt:run] --full ignored for QUICK track (Fast Path Guarantee)
98
+ ```
99
+
100
+ `--full` composes normally with `--phase-mode` and `--spar-gates`.
101
+
102
+ **Autonomy gate (load-bearing safety valve, ID-03):**
103
+
104
+ Auto-ship + auto-merge is YOLO-class. Read `.aperant/config.json` for `autonomy.allow_yolo` (boolean) and the autonomy `level` (0–3).
105
+
106
+ 1. **`allow_yolo` hard guard.** If `autonomy.allow_yolo !== true`, STOP before ship with the verbatim line and leave the task at `reviewing` (fully shippable by hand):
107
+
108
+ ```
109
+ [apt:run] --full needs autonomy.allow_yolo=true (auto-ship + auto-merge to main). Enable via /apt:setup or drop --full.
110
+ ```
111
+
112
+ 2. **One-time confirm at autonomy < 3.** Before opening the auto-merging PR (just before §6.1 Ship below), emit ONE confirmation when autonomy `level < 3` AND `--yolo` was not also passed:
113
+
114
+ ```
115
+ [apt:run --full] About to open a PR that auto-merges to main once CI is green. Proceed? [y/N]
116
+ ```
117
+
118
+ At autonomy 3 OR when `--yolo` is passed, degrade to silent (mirrors `/apt:ship`'s autonomy-3 degrade). On `N` / empty, STOP after review with the standard `/apt:ship` hand-off hint — the task is left at `reviewing`, fully shippable by hand.
119
+
120
+ Note: the `allow_yolo` guard + the one-time confirm are evaluated up front here (parse-time gating) and the confirm is RE-SURFACED at the ship boundary (§6.1) so the user confirms immediately before the irreversible auto-merge arm. Review always runs first, and auto-merge always waits for green.
121
+
74
122
  ## 1b. Resolve Orchestration Mode
75
123
 
76
124
  Run reconcile to determine the effective orchestration strategy for this pipeline run. Pass the task's track via `--track <quick|standard|deep>` so per-track defaults from `.aperant/config.json:orchestration.per_track` apply (roundtable 2026-05-14 R7):
@@ -338,6 +386,58 @@ Skip on `spar.enabled === true` (no pre-ship gate exists in v2 — see spec §8
338
386
 
339
387
  **This is the task review, not the PR review.** It runs inline without sub-agents. For heavy multi-agent PR review, use `/apt:pr-review` separately.
340
388
 
389
+ ## 6.1. Stage 5 — Ship (apt:ship) — `--full` only
390
+
391
+ **Run this section ONLY when `--full` was parsed (§1a) AND review passed** — "review passed" means the §6 review `assessment` is `approve`; a `request-changes` assessment STOPS here (task left at `reviewing`, no ship — see the stop-condition table in §6.2). When `--full` is absent, skip §6.1 + §6.2 entirely and go straight to §7 (manual mode — terminates on review).
392
+
393
+ Pipeline order note: the user's phrasing was *"review, verify"* but `/apt:run` runs **verify (§5) → review (§6) → ship (§6.1)**. KEEP this order — verify gates review, and review consumes `qa_signoff.json`. Do NOT silently reorder. `--full` runs `plan → execute → verify → review → ship → watch-ci`.
394
+
395
+ **Pre-ship gate (re-surface the §1a confirm at the boundary):**
396
+
397
+ 1. Re-check `autonomy.allow_yolo === true` (already validated in §1a; re-read here so a mid-run config edit cannot slip an auto-merge past the guard). If false, STOP with the §1a `allow_yolo` line; leave the task at `reviewing`.
398
+ 2. Emit the §1a one-time confirm (`[apt:run --full] About to open a PR that auto-merges to main once CI is green. Proceed? [y/N]`) when autonomy `level < 3` and `--yolo` was not passed. At autonomy 3 / `--yolo`, degrade to silent. On `N` / empty, STOP after review with the standard `/apt:ship` hand-off hint; the task stays at `reviewing`.
399
+
400
+ **Ship (Option A — PR that auto-merges to main on green; NEVER a direct push/merge):**
401
+
402
+ Invoke `/apt:ship` inline. It runs its own hard-gate (verify-approved / review-clean / consistency / gitignore), opens the PR against the task's recorded `base_branch` (main for a task forked from main), and atomically flips `reviewing → shipped-pending-merge` via `task update --pr-url` — which also arms the post-ship watcher per `ci_watch.after_ship`. `--full` MUST NEVER `git push`/`git merge` directly to main (ID-02 Option B is rejected unconditionally): "ship to main" means **a PR that auto-merges to main on green**, not a force-push.
403
+
404
+ Capture the opened PR number `{N}` from the `/apt:ship` result for §6.2.
405
+
406
+ If `/apt:ship`'s hard-gate fails, or the branch is behind and the rebase conflicts with no `resolve_merge_conflicts`, STOP — no PR is opened and the task stays at `reviewing` (see the stop-condition table below).
407
+
408
+ ## 6.2. Stage 6 — Watch-CI arm (auto-merge-on-green) — `--full` only
409
+
410
+ After the PR opens, force auto-merge for THIS run regardless of the config default (`ci_watch.auto_merge_on_green` is `false` by default per the D-08 kill-criteria). `/apt:ship` already armed the watcher via `ci_watch.after_ship`; this call PATCHES that already-armed watch's auto-merge flag to `true` (it does NOT reimplement arming and is idempotent):
411
+
412
+ ```bash
413
+ node packages/framework/bin/apt-tools.mjs ci-watch start . --pr {N} --task-id {task-id} --host {cli} --auto-merge-on-green
414
+ ```
415
+
416
+ **Branch on host cron capability.** Run host-detect and read `host_capabilities.cron`:
417
+
418
+ ```bash
419
+ node packages/framework/bin/apt-tools.mjs host-detect .
420
+ ```
421
+
422
+ - **cron-capable host** (e.g. Claude Code, `cron: true`): the watcher is armed with auto-merge ON; register the recurring tick via the host's `CronCreate` (per `/apt:watch-ci` §3). Then **EXIT — do NOT loop** (respect `/apt:watch-ci`'s single-execution-per-invocation contract). The cron beat drives the watcher to merged + green unattended. Report: watcher armed, auto-merge ON, and the deadline.
423
+ - **non-cron host** (Codex / Gemini / OpenCode): truly-unattended watching is impossible. Arm the watch state + auto-merge (the `ci-watch start --auto-merge-on-green` above) but do NOT impersonate a scheduler — NEVER fake a `while` / `sleep` loop. Print the honest manual-tick instruction:
424
+
425
+ ```
426
+ [apt:run --full] PR opened + CI watcher state created (host has no cron). Advance manually: /apt:watch-ci --tick --pr {N}. Auto-merge-on-green is set; the merge fires on the first green tick you run.
427
+ ```
428
+
429
+ **Stop conditions (every gate is reused; each STOP leaves the task in its current lifecycle state and NEVER merges). `--full` adds NO new gate — it only chains the existing ones and surfaces the stop reason in §7:**
430
+
431
+ | Stop point | Resulting state |
432
+ |---|---|
433
+ | QA `rejected` after max iters (§5) | `verifying`; no ship |
434
+ | Review `request-changes` (§6) | `reviewing`; no ship |
435
+ | Ship hard-gate fail (verify-approved / review-clean / consistency / gitignore) | `reviewing`; no PR |
436
+ | Branch behind + rebase conflict (no `resolve_merge_conflicts`) | `reviewing`; no PR |
437
+ | `allow_yolo=false` or confirm `N` (§1a / §6.1) | `reviewing`; no PR |
438
+ | CI red after 5-attempt / 4h-wall (watcher) | `shipped-pending-merge`; PR comment, NOT merged |
439
+ | Merge conflict mid-watch (`diverged-bailed`) | `shipped-pending-merge`; PR comment, NOT merged |
440
+
341
441
  ## 7. Pipeline Summary
342
442
 
343
443
  Render the phase rail panel verbatim — branch glyphs `┌─ │ └─`, status glyphs `✓` (done) / `●` (active) / `○` (pending/skipped), inline metrics per row drawn from the artifacts written across §3–§6:
@@ -359,7 +459,18 @@ Substitute live values from the on-disk artifacts: `build-progress.json.complete
359
459
  **Always end with a "What's Next" recommendation:**
360
460
 
361
461
  ```
362
- {If review assessment is "approve" and QA approved:}
462
+ {If --full ran and the PR opened (cron host):}
463
+ -> Shipped. PR #{N} opened; CI watcher armed with auto-merge-on-green (deadline {deadline}).
464
+ The cron beat drives it to merged + green. /apt:close-task runs post-merge.
465
+
466
+ {If --full ran and the PR opened (non-cron host):}
467
+ -> Shipped. PR #{N} opened; watcher state created with auto-merge-on-green.
468
+ Advance manually: /apt:watch-ci --tick --pr {N} (merge fires on the first green tick).
469
+
470
+ {If --full was requested but a gate STOPPED before ship:}
471
+ -> {stop reason}. Task left at {lifecycle state}, not merged. {remediation per the stop-condition table}
472
+
473
+ {If review assessment is "approve" and QA approved (no --full):}
363
474
  -> Recommended: /apt:ship — create PR for this task
364
475
  Also: /apt:pr-review (deep multi-agent review) | manual review
365
476
 
@@ -388,5 +499,17 @@ atomic `reviewing → shipped-pending-merge` flip. `/apt:ship` then flips
388
499
  does not need to record or drain anything beyond ensuring each child-skill
389
500
  stage runs to its success-end.
390
501
 
502
+ **`--full` full lifecycle map:** `planning → executing → verifying → reviewing
503
+ → shipped-pending-merge → (CI green + merged) → completed`. The `reviewing →
504
+ shipped-pending-merge` flip is done atomically by ship's `task update --pr-url`
505
+ (§6.1). `shipped-pending-merge → completed` is done by `/apt:close-task`
506
+ post-merge — **`--full` does NOT auto-invoke `/apt:close-task`.** On cron hosts
507
+ the merge happens asynchronously AFTER `--full` has exited, so `--full` cannot
508
+ observe it; closeout is left to the existing path (the watcher's green branch
509
+ suggests `/apt:close-task`, and the user / passive sweeper runs it). Folding
510
+ close-task into `--full` would require it to become a recurring watcher —
511
+ explicitly rejected (it would duplicate the watcher and violate the
512
+ single-execution contract).
513
+
391
514
  </process>
392
515
  </output>
@@ -578,6 +578,8 @@ suggest_mode=$(jq -r '.pr_review.suggest_after_ship // "always"' .aperant/config
578
578
 
579
579
  ### 7.2 Post-ship /apt:watch-ci hook
580
580
 
581
+ > Cross-reference: `/apt:run --full` drives this skill's §6 PR creation + §7.2 watcher arming end-to-end (review → ship → auto-merge-on-green watch). When invoked from that chain, the watcher's auto-merge is force-enabled for the run via `apt-tools ci-watch start --auto-merge-on-green` (see `apt-run/SKILL.md` §6.1–§6.2); `/apt:ship` itself behaves identically whether invoked standalone or by `--full`.
582
+
581
583
  After Section 7.1's hint resolves (whether it ran the deep review or skipped), report or interactively arm the post-ship CI watcher. The hook is controlled by `ci_watch.after_ship` in `.aperant/config.json` (tri-state: `auto` | `ask` | `never`). The default depends on host capability — `auto` on cron-capable hosts (Claude Code, Pi), `never` elsewhere.
582
584
 
583
585
  **Skip entirely** when no PR URL was captured in Section 6 (library `npm publish`-only path — same skip rule as 7.1).
@@ -137,6 +137,8 @@ Do NOT proceed; self-sparring is sycophancy by construction (the host cannot obj
137
137
 
138
138
  Walk the ladder in order; pick the first rung with `detected: true` (or `"runtime-host-only"` for MCP). **MCP rung has a hard timeout** — default 900000ms (15 min), configurable via `apt-spar.mcp_timeout_ms` in `.aperant/config.json`. The MCP rung is a structured tool call, NOT a Bash invocation, so the Bash tool's 600000ms ceiling does NOT apply — the MCP rung can carry heavy-research partner runs (e.g. Claude Code partner doing multiple minutes of Read/Grep/Bash) where the Bash rungs would hit the host ceiling. On MCP timeout, **automatically fall through to rung 2 or rung 3** — do NOT fail the whole spar. Treat MCP timeout as "rung unavailable for this invocation."
139
139
 
140
+ **Codex walk order (anti-staleness — do NOT infer it from prose).** The codex MCP rung is the ONLY codex transport that forces the agent to NAME a model, and a named model is a staleness vector (the upstream MCP server has no access to the user's config and silently substitutes a stale default; the agent could also substitute one from training memory). The self-resolving rungs (plugin shim → raw CLI) pass NO `--model` and let `codex exec` inherit `~/.codex/config.toml`, so they are staleness-proof by construction. Therefore, for the **codex** partner specifically, prefer the self-resolving plugin → CLI rungs in the common case and demote the MCP rung to **LAST**. Do NOT hand-order the rungs from this prose — run `apt-tools spar-resolve-partner --partner codex` and FOLLOW the preference it returns: walk in the order of its `recommended_walk` array (`["plugin","cli","mcp"]`) and treat `prefer_self_resolving: true` as authoritative. The generic `host.partner_transports.codex` array stays MCP-first (it is data shared with other consumers, not a spar walk order); the resolver's preference overrides it for codex only. **claude / gemini stay MCP-first** — their MCP rungs take only `{prompt}` and need no model, so there is no staleness vector and no walk change.
141
+
140
142
  If all three rungs are unavailable (every rung `detected: false`, or MCP `runtime-host-only` timed out AND rungs 2+3 are `detected: false`), print:
141
143
 
142
144
  ```
@@ -183,9 +185,9 @@ The table below shows the canonical command shape per (partner × rung); the act
183
185
 
184
186
  | Partner | Rung | Command | Notes |
185
187
  |---|---|---|---|
186
- | codex | mcp | tool call: `mcp__codex-mcp__codex({prompt, model})` where `model` is parsed from `~/.codex/config.toml`'s `model = "<value>"` line (see §5a below). | Preferred. 900000ms (15 min) timeout (configurable `apt-spar.mcp_timeout_ms`); on timeout fall through to plugin then CLI. If `~/.codex/config.toml` is missing OR the `model =` line is absent/unparseable, treat MCP rung as UNAVAILABLE and fall through to plugin/CLI — never invent a default model (the MCP server's built-in fallback is stale `gpt-5.3-codex`, which is exactly the bug we are fixing). |
187
- | codex | plugin | `node "${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs" task "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)"` | No `--model` flag — `codex-companion.mjs` invokes `codex exec` which inherits `~/.codex/config.toml` automatically. **NO `--write`. NO `--effort` override.** Per SPAR-04 — spar is conversation, not patching. |
188
- | codex | cli | `codex exec "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)"` | Precedent: `apt-pr-review/SKILL.md:1084`. No `--model` flag — `codex exec` inherits `~/.codex/config.toml` automatically. Verify with `codex --help`. |
188
+ | codex | mcp | tool call: `mcp__codex-mcp__codex({prompt, model})` where `model` is the `model` field returned by `apt-tools spar-resolve-partner --partner codex` (see §5a below). | **Demoted to LAST for codex** (see the codex walk-order note in §3) — attempt this rung only when the resolver returns `available: true`. 900000ms (15 min) timeout (configurable `apt-spar.mcp_timeout_ms`); on timeout fall through to plugin then CLI. When the resolver returns `available: false`, treat the MCP rung as UNAVAILABLE and fall through to plugin/CLI — never invent a default model (the MCP server silently substitutes a stale hardcoded default model id, which is exactly the bug we are fixing). |
189
+ | codex | plugin | `node "${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs" task "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)" < /dev/null` | No `--model` flag — `codex-companion.mjs` invokes `codex exec` which inherits `~/.codex/config.toml` automatically. **NO `--write`. NO `--effort` override.** Per SPAR-04 — spar is conversation, not patching. The `< /dev/null` is load-bearing — the shim forwards to `codex exec`, which wedges on stdin EOF under a no-TTY subprocess (see the wedge note below). |
190
+ | codex | cli | `codex exec --sandbox read-only "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)" < /dev/null` | Precedent: `apt-pr-review/SKILL.md:1084`. No `--model` flag — `codex exec` inherits `~/.codex/config.toml` automatically. The `< /dev/null` un-wedges the no-TTY stdin block and `--sandbox read-only` keeps the spar read-only (see the wedge note below). Verify with `codex --help`. |
189
191
  | claude | mcp | tool call: `mcp__claude-code-mcp__claude_code({prompt})` | Preferred. Same 900000ms (15 min) timeout + fallthrough rule. |
190
192
  | claude | plugin | n/a | No plugin shim exists for Claude. Skip this rung. |
191
193
  | claude | cli | `claude -p "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)" --output-format text` | Symmetric. Claude has no `--write` equivalent — asymmetric flags are a smell. |
@@ -193,21 +195,29 @@ The table below shows the canonical command shape per (partner × rung); the act
193
195
  | gemini | plugin | n/a | No plugin shim exists for Gemini. Skip this rung. |
194
196
  | gemini | cli | `gemini "$(cat <<'APT_SPAR_PROMPT_EOF' ... APT_SPAR_PROMPT_EOF\n)"` | No `-m` flag — gemini-cli resolves model via `--model` > `GEMINI_MODEL` env > `~/.gemini/settings.json` > built-in default per `docs/cli/model-routing.md:47-58`. Drop the flag, let the cascade work; users override per-shell via `GEMINI_MODEL=...` or per-project via `settings.json`. |
195
197
 
198
+ **Codex stdin-wedge note (load-bearing).** `codex exec` reads the positional prompt and THEN blocks on stdin EOF under a no-TTY subprocess (the Bash tool), printing `Reading additional input from stdin…` and never returning. Redirecting `< /dev/null` un-wedges it (verified: returns in ~6s instead of hanging, exit 0, and prints the resolved `model:` it used). Both codex Bash rungs (plugin shim + raw CLI) carry `< /dev/null` because the shim forwards to `codex exec`. The `--sandbox read-only` flag on the raw-CLI rung keeps the spar read-only — aligning with the SPAR-04 "NO `--write`" rule; read-only still permits the partner's investigative Read/Grep.
199
+
196
200
  Set the Bash tool's `timeout` parameter to `--timeout-ms` (default 300000).
197
201
 
198
202
  **Timeout calibration.** The 5-min Bash-rung default and 15-min MCP-rung default are sized for heavy-research partner runs — modern partner CLIs commonly spend multiple minutes doing real Read/Grep/Bash investigation before producing a round response. The two defaults are intentionally asymmetric: the Bash rungs (plugin shim, raw CLI) are capped at the Claude Code Bash tool's 600000ms (10 min) host ceiling, while the MCP rung — a structured tool call rather than a Bash invocation — has no host ceiling and can carry the worst-case 15-min Claude Code partner. For tighter budgets (e.g. CI), pass `--timeout-ms` explicitly. The MCP-rung override is downward-only: if you observe consistent partner-CLI wedges (auth handshakes, hosted-API stalls, CI lanes wanting fast failure), set `apt-spar.mcp_timeout_ms` BELOW the 15-min default in `.aperant/config.json` (e.g. `120000` for a 2-min fast-fail budget) — raising it above 15 min is unnecessary because the default already covers the documented worst case. Future versions may introduce per-partner defaults.
199
203
 
200
204
  ## 5a. Codex MCP model resolution
201
205
 
202
- The codex MCP rung (table row above) requires an explicit `model` arg sourced from the user's `~/.codex/config.toml`. The MCP server (`codex-mcp-server`) does NOT read `~/.codex/config.toml` itself — when no `model` is passed it falls back to a hardcoded stale `gpt-5.3-codex`, silently overriding the user's actual configured model. Resolve the model exactly once per spar invocation, before the rung-table walk:
206
+ The codex MCP rung (table row above) requires an explicit `model` arg sourced from the user's `~/.codex/config.toml`. The MCP server (`codex-mcp-server`) does NOT read `~/.codex/config.toml` itself — when no `model` is passed it silently substitutes a stale hardcoded default model id, overriding the user's actual configured model. **Aperant holds zero model names** — resolve the model LIVE via the deterministic resolver exactly once per spar invocation, before the rung-table walk:
203
207
 
204
208
  ```bash
205
- # Resolve the codex MCP model exactly once per spar invocation, before the rung table walk
206
- CODEX_MCP_MODEL="$(grep -E '^model = "[^"]+"' "$HOME/.codex/config.toml" 2>/dev/null | head -1 | sed -E 's/^model = "([^"]+)".*/\1/')"
207
- # If empty codex MCP rung is UNAVAILABLE; fall through to plugin/CLI.
209
+ # Resolve the codex partner model + transport preference LIVE, once per spar
210
+ # invocation, before the rung-table walk. apt-tools reads ~/.codex/config.toml
211
+ # fresh and never bakes a default.
212
+ apt-tools spar-resolve-partner --partner codex
208
213
  ```
209
214
 
210
- If `$CODEX_MCP_MODEL` is empty (file missing, `model =` line absent, or value not double-quoted), treat the MCP rung as UNAVAILABLE and fall through to plugin → CLI. **Never invent a default** — the upstream MCP server's built-in `gpt-5.3-codex` fallback is exactly the bug this resolution path exists to fix.
215
+ The command emits a JSON envelope `{available, model, source, reason, prefer_self_resolving, recommended_walk}`:
216
+
217
+ - When `available: true`, pass the returned `model` field **VERBATIM** into the codex MCP tool call's `model` arg — do NOT transform it, and do NOT substitute a model from your own training memory.
218
+ - When `available: false` (the envelope carries a machine `reason` of `config_missing` / `no_model_key` / `unparseable`), treat the codex MCP rung as **UNAVAILABLE** and fall through to plugin → CLI. **Never invent a default** — substituting any model name is exactly the staleness bug this resolution path exists to fix. The plugin and raw-CLI rungs pass no `--model` and let `codex exec` self-resolve from the user's live config, so the fall-through is always staleness-proof.
219
+
220
+ Follow the envelope's `recommended_walk` / `prefer_self_resolving` for the rung order (see the codex walk-order note in §3) rather than ordering the rungs by hand.
211
221
 
212
222
  **Prompt-quoting safety.** The prompt is user-controlled (it comes from `[topic]` and/or conversation context) and may legitimately contain double quotes, dollar signs, backticks, or `$(...)` substitution that bash would otherwise expand or break on. NEVER interpolate `<prompt>` directly inside a double-quoted shell argument. Instead, pipe via a here-document so the shell treats the body as opaque text:
213
223
 
@@ -216,7 +226,7 @@ If `$CODEX_MCP_MODEL` is empty (file missing, `model =` line absent, or value no
216
226
  node "${CLAUDE_PLUGIN_ROOT}/scripts/codex-companion.mjs" task "$(cat <<'APT_SPAR_PROMPT_EOF'
217
227
  <prompt body — opaque, no shell expansion>
218
228
  APT_SPAR_PROMPT_EOF
219
- )"
229
+ )" < /dev/null
220
230
 
221
231
  # Claude direction
222
232
  claude -p "$(cat <<'APT_SPAR_PROMPT_EOF'
@@ -37,7 +37,7 @@ The locked decisions backing every section are in `.aperant/context/notes/framew
37
37
  - **Working directory:** the task's worktree (or the project root if this is a non-worktree task).
38
38
  - **State files:** `.aperant/ci-watches/{pr}.json` (per-PR state, D-04); `.aperant/ci-watches/{pr}.lock` (per-PR atomic lock, D-05). Both ephemeral — they are NOT committed.
39
39
  - **Events:** every tick appends a `ci-watch.tick` event to `.aperant/events/{today}.jsonl`; `start` appends `ci-watch.armed`; the fix-and-push path appends `ci-watch.fix-pushed`; terminal actions append `ci-watch.stopped` with a `reason`.
40
- - **CLI surface:** `apt-tools ci-watch start|stop|status|tick --pr N`. The skill body shells out to those subcommands — DO NOT re-implement the classifier in your prompt; the cmd already does it.
40
+ - **CLI surface:** `apt-tools ci-watch start|stop|status|tick --pr N`. `start` also accepts `--auto-merge-on-green` (per-run override — forces `auto_merge_on_green: true` on the snapshot without persisting `ci_watch.auto_merge_on_green` into config; on an already-armed watch it PATCHES the snapshot rather than no-opping). `/apt:run --full` uses this flag to guarantee auto-merge for a single PR after ship's config-driven arm runs first. The skill body shells out to those subcommands — DO NOT re-implement the classifier in your prompt; the cmd already does it.
41
41
 
42
42
  ---
43
43
 
@@ -87,7 +87,7 @@ For the `--tick` mode, execute exactly the following sequence — once — insid
87
87
 
88
88
  When `action ∈ {green, merged, closed, attempts-cap, wall-cap, identical-log}`:
89
89
 
90
- - **green** — print "CI green for PR {N}." If `ci_watch.auto_merge_on_green` is `true` in `.aperant/config.json`, run `gh pr merge {N} --auto --squash` then suggest `/apt:close-task`.
90
+ - **green** — print "CI green for PR {N}." If `ci_watch.auto_merge_on_green` is `true` in `.aperant/config.json`, run `gh pr merge {N} --squash` then suggest `/apt:close-task`. Use a PLAIN merge here, NOT `--auto`: the classifier has already confirmed every check is `SUCCESS`/`SKIPPED` (`stop-matrix.mjs` `allGreen`) before this branch fires, so green is gated by the watcher, not by GitHub branch protection. `--auto` bought nothing on this path — in a repo WITHOUT required status checks `gh pr merge --auto` degrades to an immediate merge anyway — and dropping it removes the classify→merge ambiguity in unprotected repos.
91
91
  - **DO NOT pass `--delete-branch`** to `gh pr merge` from this auto-merge path — ever, unconditionally. The watcher's cwd is the task worktree, where `gh`'s `--delete-branch` post-merge cleanup tries to `git switch <base>` and fails because `<base>` is checked out in the primary worktree. The remote merge has already succeeded by then; local branch + worktree cleanup is `/apt:close-task`'s job via `apt-tools task close-merged` → `computeWorktreeCleanup` (see FRAMEWORK-BUG-019).
92
92
 
93
93
  Else print the exact D-08 text:
@@ -127,7 +127,7 @@ When `action === 'fix'` (or `diverged-rebased`/`diverged-bailed`):
127
127
 
128
128
  For `diverged-rebased`: the cmd already returned the verb; the orchestrator runs `git -C {worktree} fetch && git rebase origin/{base}` BEFORE the fix step. On conflict the action becomes `diverged-bailed`.
129
129
 
130
- For `diverged-bailed`: default behavior is to exit with a PR comment summarizing the conflict. Self-heal is **opt-in** via `ci_watch.resolve_merge_conflicts` (default false) in `.aperant/config.json`. When enabled, instead of bailing, hand off to **`/apt:merge-conflict-resolution`** (`--base origin/{base}`) for the worktree; it resolves the rebase conflict inline and re-probes mergeability. On success, continue to the §5 fix-and-push step (`git push origin {branch}`) and reschedule per §3 — the watcher keeps running. On its non-convergence STOP (it aborts the rebase to a clean tree first), fall back to the bail-with-PR-comment path. The resolved branch still goes through CI; the watcher never auto-merges except via the existing config-gated `gh pr merge --auto`.
130
+ For `diverged-bailed`: default behavior is to exit with a PR comment summarizing the conflict. Self-heal is **opt-in** via `ci_watch.resolve_merge_conflicts` (default false) in `.aperant/config.json`. When enabled, instead of bailing, hand off to **`/apt:merge-conflict-resolution`** (`--base origin/{base}`) for the worktree; it resolves the rebase conflict inline and re-probes mergeability. On success, continue to the §5 fix-and-push step (`git push origin {branch}`) and reschedule per §3 — the watcher keeps running. On its non-convergence STOP (it aborts the rebase to a clean tree first), fall back to the bail-with-PR-comment path. The resolved branch still goes through CI; the watcher never auto-merges except via the existing config-gated green-branch `gh pr merge --squash` (§4).
131
131
 
132
132
  ---
133
133
 
@@ -164,5 +164,5 @@ If you find yourself wanting to add "and then wait 5 minutes and run again," STO
164
164
  ## Notes for the orchestrator
165
165
 
166
166
  - All gh CLI calls run with the host's existing auth. The watcher introduces no new secret material (per task `dimensions_covered.token-encryption.crossed: false`).
167
- - `auto_merge_on_green` is snapshotted from `.aperant/config.json` at `start` time (state file's `auto_merge_on_green` field). Changing the config mid-watch does NOT retroactively affect armed watches — re-run `start` to pick up the new value.
167
+ - `auto_merge_on_green` is snapshotted from `.aperant/config.json` at `start` time (state file's `auto_merge_on_green` field). Changing the config mid-watch does NOT retroactively affect armed watches — re-run `start` to pick up the new value. The `--auto-merge-on-green` start flag forces it `true` for one run regardless of the config default (it patches an already-armed snapshot too); this is how `/apt:run --full` drives auto-merge without flipping the global config.
168
168
  - The fix-CLI shell-out is the cleanest abstraction we have for portability. It's NOT a sub-agent. On Claude Code that means another `claude` invocation; on Codex/Gemini/OpenCode/Pi, the equivalent binary. Detection lives in `apt-tools host-detect`'s `cli` field.
@@ -154,12 +154,60 @@ function doStart(root, flags) {
154
154
  const taskId = flags.get('task-id')
155
155
  if (!taskId) return err('Required: --task-id <id>')
156
156
  const host = flags.get('host') || 'unknown'
157
+ // Per-run auto-merge override (ID-08): --auto-merge-on-green forces
158
+ // auto_merge_on_green:true on the new snapshot AND patches an already-armed
159
+ // watch instead of no-opping, because `/apt:run --full` runs ship's
160
+ // config-driven arm (default false) FIRST, then this call to guarantee
161
+ // auto-merge for this single run without persisting ci_watch.auto_merge_on_green.
162
+ const forceAutoMerge = flags.has('auto-merge-on-green')
157
163
 
158
164
  // Refuse to start a second watcher for the same PR — exit 2 with a
159
165
  // busy envelope so the caller knows the PR is already armed. The
160
166
  // orchestrator skill prints the holder's session and exits.
161
167
  const existingState = readState(root, pr)
162
168
  if (existingState && !existingState.error) {
169
+ // --auto-merge-on-green on an already-armed watch PATCHES the snapshot's
170
+ // auto_merge_on_green to true and returns armed (NOT a busy no-op), so the
171
+ // --full chain can force auto-merge after ship's default-false config arm.
172
+ if (forceAutoMerge) {
173
+ const patchSessionId = newSessionId()
174
+ const patchLock = acquireLock(root, pr, patchSessionId)
175
+ if (patchLock.status !== 'ok') {
176
+ return exitWith(
177
+ {
178
+ status: 'busy',
179
+ command: 'ci-watch-start',
180
+ pr: Number(pr),
181
+ reason: 'lock-held',
182
+ holder: patchLock.holder,
183
+ },
184
+ 2,
185
+ )
186
+ }
187
+ try {
188
+ // Re-read INSIDE the lock so a concurrent tick's attempts/deadline/
189
+ // last_poll update is not clobbered by the stale pre-lock snapshot.
190
+ // Mirrors the main start path below, which also re-reads under the
191
+ // lock before writing.
192
+ const current = readState(root, pr)
193
+ const base = current && !current.error ? current : existingState
194
+ const patched =
195
+ base.auto_merge_on_green === true ? base : { ...base, auto_merge_on_green: true }
196
+ if (patched !== base) writeState(root, pr, patched)
197
+ return ok({
198
+ status: 'ok',
199
+ command: 'ci-watch-start',
200
+ pr: Number(pr),
201
+ host: base.host,
202
+ scheduler_handle: patched.scheduler_handle ?? null,
203
+ deadline: patched.deadline,
204
+ auto_merge_on_green: true,
205
+ patched: patched !== base,
206
+ })
207
+ } finally {
208
+ releaseLock(root, pr, patchSessionId)
209
+ }
210
+ }
163
211
  return exitWith(
164
212
  {
165
213
  status: 'busy',
@@ -202,8 +250,9 @@ function doStart(root, flags) {
202
250
  const startedAt = existingObj.started_at || nowIso()
203
251
  const deadline = existingObj.deadline || new Date(Date.now() + WALL_BUDGET_MS).toISOString()
204
252
  // Snapshot auto_merge from config at start so config changes mid-watch don't
205
- // surprise the orchestrator (spec.md §8).
206
- const autoMergeFromConfig = readAutoMergeFromConfig(root)
253
+ // surprise the orchestrator (spec.md §8). --auto-merge-on-green forces it
254
+ // true for this run regardless of the config default (ID-08).
255
+ const autoMergeFromConfig = forceAutoMerge || readAutoMergeFromConfig(root)
207
256
  const state = {
208
257
  pr: Number(pr),
209
258
  task_id: taskId,
@@ -49,10 +49,10 @@ export function cmdCommit(args) {
49
49
  return err(`Commit failed: ${e.message}`)
50
50
  }
51
51
 
52
- // Conductor v2 (post live verification 2026-05-15) — auto-emit
52
+ // Showrunner v2 (post live verification 2026-05-15) — auto-emit
53
53
  // `artifact.ready{kind:'plan'}` when the commit landed a fresh
54
54
  // `implementation_plan.json` under .aperant/tasks/{id}/ AND the
55
- // process runs under a Conductor PTY (APERANT_TERMINAL_ID set).
55
+ // process runs under a Showrunner PTY (APERANT_TERMINAL_ID set).
56
56
  // Code-enforced replacement for the markdown-discipline emit step
57
57
  // that QUICK-track tasks correctly skipped per Fast Path Guarantee.
58
58
  // Best-effort: emit failures do NOT roll back the commit.
@@ -66,7 +66,7 @@ export function cmdCommit(args) {
66
66
  )
67
67
  }
68
68
  } catch {
69
- // Non-fatal — the commit succeeded. The Conductor will fall back
69
+ // Non-fatal — the commit succeeded. The Showrunner will fall back
70
70
  // to filesystem polling (slightly higher latency, same outcome).
71
71
  }
72
72
 
@@ -6,7 +6,7 @@ import { appendFileSync, mkdirSync, readdirSync, readFileSync, writeFileSync } f
6
6
  import { hostname } from 'node:os'
7
7
  import { join, resolve } from 'node:path'
8
8
  import { readLifecycleEventsForDay } from '../coordination/event-log.mjs'
9
- import { validateConductorEvent } from '../coordination/event-schema.mjs'
9
+ import { validateShowrunnerEvent } from '../coordination/event-schema.mjs'
10
10
  import { createCoordinationStore } from '../coordination/store.mjs'
11
11
  import { parseFlags } from '../util/args.mjs'
12
12
  import { err, ok } from '../util/result.mjs'
@@ -28,8 +28,8 @@ const VALID_EVENT_TYPES = [
28
28
  'ci-watch.tick',
29
29
  'ci-watch.fix-pushed',
30
30
  'ci-watch.stopped',
31
- // Phase 2.5 — Conductor-readable ops emitted by APT skills so the
32
- // Conductor's readEventLog tool can drive its observe loop. These
31
+ // Phase 2.5 — Showrunner-readable ops emitted by APT skills so the
32
+ // Showrunner's readEventLog tool can drive its observe loop. These
33
33
  // events route to JSONL (like ci-watch.*) and carry terminal_id
34
34
  // auto-stamped from APERANT_TERMINAL_ID when present.
35
35
  'phase.changed',
@@ -40,9 +40,9 @@ const VALID_EVENT_TYPES = [
40
40
  'budget.exceeded',
41
41
  'terminal.enrolled',
42
42
  'terminal.unenrolled',
43
- 'conductor.intent',
44
- 'conductor.cross_check',
45
- 'conductor.realignment',
43
+ 'showrunner.intent',
44
+ 'showrunner.cross_check',
45
+ 'showrunner.realignment',
46
46
  ]
47
47
 
48
48
  const CI_WATCH_EVENT_TYPES = new Set([
@@ -52,7 +52,7 @@ const CI_WATCH_EVENT_TYPES = new Set([
52
52
  'ci-watch.stopped',
53
53
  ])
54
54
 
55
- const CONDUCTOR_EVENT_TYPES = new Set([
55
+ const SHOWRUNNER_EVENT_TYPES = new Set([
56
56
  'phase.changed',
57
57
  'artifact.ready',
58
58
  'awaiting-input',
@@ -61,9 +61,9 @@ const CONDUCTOR_EVENT_TYPES = new Set([
61
61
  'budget.exceeded',
62
62
  'terminal.enrolled',
63
63
  'terminal.unenrolled',
64
- 'conductor.intent',
65
- 'conductor.cross_check',
66
- 'conductor.realignment',
64
+ 'showrunner.intent',
65
+ 'showrunner.cross_check',
66
+ 'showrunner.realignment',
67
67
  ])
68
68
 
69
69
  /**
@@ -142,22 +142,22 @@ export function cmdEvent(subcommand, projectDir, extraArgs) {
142
142
  return ok({ status: 'ok', command: 'event-append', event_file: jsonlFile, event })
143
143
  }
144
144
 
145
- // Phase 2.5 — Conductor-readable events route to the per-day JSONL
146
- // path with the canonical envelope the Conductor's readEventLog
145
+ // Phase 2.5 — Showrunner-readable events route to the per-day JSONL
146
+ // path with the canonical envelope the Showrunner's readEventLog
147
147
  // expects: { t, agent, pid, hostname, incarnation, op, task, data }.
148
148
  // terminal_id is auto-stamped from APERANT_TERMINAL_ID env when set,
149
- // so APT skills running in a Conductor-spawned PTY automatically
149
+ // so APT skills running in a Showrunner-spawned PTY automatically
150
150
  // tag their events with the originating terminal.
151
- if (CONDUCTOR_EVENT_TYPES.has(type)) {
151
+ if (SHOWRUNNER_EVENT_TYPES.has(type)) {
152
152
  const terminalId = process.env.APERANT_TERMINAL_ID
153
153
  if (terminalId && data && typeof data === 'object' && !data.terminal_id) {
154
154
  data.terminal_id = terminalId
155
155
  }
156
156
  // Step 1 — schema gate: reject payloads that violate the per-op
157
157
  // contract before the line lands on disk. The same validator
158
- // runs at the framework store seam and the core conductor
158
+ // runs at the framework store seam and the core showrunner
159
159
  // writer; gating here catches CLI emitters (APT skills).
160
- const validation = validateConductorEvent(type, data)
160
+ const validation = validateShowrunnerEvent(type, data)
161
161
  if (!validation.ok) {
162
162
  return err(`Schema violation for ${type}: ${validation.error}`)
163
163
  }
@@ -8,7 +8,7 @@
8
8
  * exist, else stable by task id), and probes each branch's mergeability against
9
9
  * the CURRENT base via the EXACT same `git merge-tree` detection the `detect`
10
10
  * subcommand uses (#395). It emits a structured integration report the
11
- * conductor renders and the `apt:merge-conflict-resolution` flow consumes for
11
+ * showrunner renders and the `apt:merge-conflict-resolution` flow consumes for
12
12
  * any conflicted branch.
13
13
  *
14
14
  * READ-ONLY, like `detect`/`markers`: NO `git merge`, NO `git rebase`, NO index
@@ -33,7 +33,7 @@
33
33
  * apt-tools merge integrate <project-dir> --batch <batch-id> [--base <ref>]
34
34
  *
35
35
  * --tasks Explicit canonical task-id list (what `fan-out resolve` returns and
36
- * the conductor already holds). Primary form.
36
+ * the showrunner already holds). Primary form.
37
37
  * --batch Recover the task-id list from the batch's worker rows
38
38
  * `.aperant/team-status/fanout-<batch>-<task>.json` (the agent-id
39
39
  * format fan-out.mjs spawns workers with: `fanout-${batch}-${task}`).
@@ -291,7 +291,7 @@ export function mergeIntegrate(targetDir, extraArgs) {
291
291
  const task = state?.active_tasks?.[id] || null
292
292
 
293
293
  // Tasks with no shipped branch can't be integrated — record them so the
294
- // conductor sees why, but never block the rest of the batch.
294
+ // showrunner sees why, but never block the rest of the batch.
295
295
  if (!task) {
296
296
  needsHuman++
297
297
  tasks.push({ task_id: id, status: 'unknown', reason: 'task not found in state.json' })