@aperant/framework 0.17.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 (152) hide show
  1. package/CHANGELOG.md +152 -0
  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/task.d.mts +16 -1
  15. package/dist/cli/commands/task.d.mts.map +1 -1
  16. package/dist/cli/commands/task.mjs +434 -266
  17. package/dist/cli/commands/task.mjs.map +1 -1
  18. package/dist/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
  19. package/dist/cli/coordination/auto-emit-artifact-ready.mjs.map +1 -1
  20. package/dist/cli/coordination/event-schema.d.mts +5 -3
  21. package/dist/cli/coordination/event-schema.d.mts.map +1 -1
  22. package/dist/cli/coordination/event-schema.mjs +245 -21
  23. package/dist/cli/coordination/event-schema.mjs.map +1 -1
  24. package/dist/cli/coordination/store.d.mts +2 -2
  25. package/dist/cli/coordination/store.mjs +4 -4
  26. package/dist/cli/coordination/store.mjs.map +1 -1
  27. package/dist/cli/dispatch.d.mts.map +1 -1
  28. package/dist/cli/dispatch.mjs +2 -0
  29. package/dist/cli/dispatch.mjs.map +1 -1
  30. package/dist/cli/help.d.mts.map +1 -1
  31. package/dist/cli/help.mjs +30 -0
  32. package/dist/cli/help.mjs.map +1 -1
  33. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  34. package/dist/cli/install/legacy-paths.mjs +2 -0
  35. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  36. package/dist/cli/produce/blind-probe.d.mts +85 -0
  37. package/dist/cli/produce/blind-probe.d.mts.map +1 -0
  38. package/dist/cli/produce/blind-probe.mjs +217 -0
  39. package/dist/cli/produce/blind-probe.mjs.map +1 -0
  40. package/dist/cli/produce/claim.d.mts +188 -0
  41. package/dist/cli/produce/claim.d.mts.map +1 -0
  42. package/dist/cli/produce/claim.mjs +518 -0
  43. package/dist/cli/produce/claim.mjs.map +1 -0
  44. package/dist/cli/produce/done-gate.d.mts +87 -0
  45. package/dist/cli/produce/done-gate.d.mts.map +1 -0
  46. package/dist/cli/produce/done-gate.mjs +200 -0
  47. package/dist/cli/produce/done-gate.mjs.map +1 -0
  48. package/dist/cli/produce/events.d.mts +77 -0
  49. package/dist/cli/produce/events.d.mts.map +1 -0
  50. package/dist/cli/produce/events.mjs +126 -0
  51. package/dist/cli/produce/events.mjs.map +1 -0
  52. package/dist/cli/produce/evidence-oracle.d.mts +63 -0
  53. package/dist/cli/produce/evidence-oracle.d.mts.map +1 -0
  54. package/dist/cli/produce/evidence-oracle.mjs +122 -0
  55. package/dist/cli/produce/evidence-oracle.mjs.map +1 -0
  56. package/dist/cli/produce/ledger.d.mts +140 -0
  57. package/dist/cli/produce/ledger.d.mts.map +1 -0
  58. package/dist/cli/produce/ledger.mjs +272 -0
  59. package/dist/cli/produce/ledger.mjs.map +1 -0
  60. package/dist/cli/produce/probe-family.d.mts +53 -0
  61. package/dist/cli/produce/probe-family.d.mts.map +1 -0
  62. package/dist/cli/produce/probe-family.mjs +160 -0
  63. package/dist/cli/produce/probe-family.mjs.map +1 -0
  64. package/dist/cli/produce/projection.d.mts +55 -0
  65. package/dist/cli/produce/projection.d.mts.map +1 -0
  66. package/dist/cli/produce/projection.mjs +97 -0
  67. package/dist/cli/produce/projection.mjs.map +1 -0
  68. package/dist/cli/produce/run-id.d.mts +42 -0
  69. package/dist/cli/produce/run-id.d.mts.map +1 -0
  70. package/dist/cli/produce/run-id.mjs +79 -0
  71. package/dist/cli/produce/run-id.mjs.map +1 -0
  72. package/dist/cli/produce/saga.d.mts +180 -0
  73. package/dist/cli/produce/saga.d.mts.map +1 -0
  74. package/dist/cli/produce/saga.mjs +290 -0
  75. package/dist/cli/produce/saga.mjs.map +1 -0
  76. package/dist/cli/produce/scheduler.d.mts +165 -0
  77. package/dist/cli/produce/scheduler.d.mts.map +1 -0
  78. package/dist/cli/produce/scheduler.mjs +399 -0
  79. package/dist/cli/produce/scheduler.mjs.map +1 -0
  80. package/dist/cli/produce/setpoint.d.mts +52 -0
  81. package/dist/cli/produce/setpoint.d.mts.map +1 -0
  82. package/dist/cli/produce/setpoint.mjs +113 -0
  83. package/dist/cli/produce/setpoint.mjs.map +1 -0
  84. package/dist/cli/produce/verification-ttl.d.mts +75 -0
  85. package/dist/cli/produce/verification-ttl.d.mts.map +1 -0
  86. package/dist/cli/produce/verification-ttl.mjs +169 -0
  87. package/dist/cli/produce/verification-ttl.mjs.map +1 -0
  88. package/dist/cli/roadmap/{conductor-view.d.mts → showrunner-view.d.mts} +3 -3
  89. package/dist/cli/roadmap/showrunner-view.d.mts.map +1 -0
  90. package/dist/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +7 -7
  91. package/dist/cli/roadmap/showrunner-view.mjs.map +1 -0
  92. package/dist/plugin/.claude-plugin/plugin.json +2 -1
  93. package/dist/plugin/skills/apt/SKILL.md +112 -38
  94. package/dist/plugin/skills/apt-debug/SKILL.md +14 -24
  95. package/dist/plugin/skills/apt-fan-out/SKILL.md +4 -4
  96. package/dist/plugin/skills/apt-handoff/SKILL.md +1 -1
  97. package/dist/plugin/skills/apt-plan/SKILL.md +5 -5
  98. package/dist/plugin/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
  99. package/dist/plugin/skills/apt-produce/SKILL.md +606 -0
  100. package/dist/plugin/skills/apt-quick/SKILL.md +14 -22
  101. package/dist/plugin/skills/apt-run/SKILL.md +126 -3
  102. package/dist/plugin/skills/apt-ship/SKILL.md +2 -0
  103. package/dist/plugin/skills/apt-spar/SKILL.md +5 -3
  104. package/dist/plugin/skills/apt-watch-ci/SKILL.md +4 -4
  105. package/package.json +4 -4
  106. package/prompts/{conductor-framework-context.md → showrunner-framework-context.md} +1 -1
  107. package/prompts/{conductor-status-check.md → showrunner-status-check.md} +1 -1
  108. package/prompts/{conductor-sub-agent.md → showrunner-sub-agent.md} +6 -6
  109. package/prompts/{conductor-system.md → showrunner-system.md} +8 -8
  110. package/skills/apt/SKILL.md +112 -38
  111. package/skills/apt-debug/SKILL.md +14 -24
  112. package/skills/apt-fan-out/SKILL.md +4 -4
  113. package/skills/apt-handoff/SKILL.md +1 -1
  114. package/skills/apt-plan/SKILL.md +5 -5
  115. package/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
  116. package/skills/apt-produce/SKILL.md +606 -0
  117. package/skills/apt-quick/SKILL.md +14 -22
  118. package/skills/apt-run/SKILL.md +126 -3
  119. package/skills/apt-ship/SKILL.md +2 -0
  120. package/skills/apt-spar/SKILL.md +5 -3
  121. package/skills/apt-watch-ci/SKILL.md +4 -4
  122. package/src/cli/commands/ci-watch.mjs +51 -2
  123. package/src/cli/commands/commit.mjs +3 -3
  124. package/src/cli/commands/event.mjs +16 -16
  125. package/src/cli/commands/merge-integrate.mjs +3 -3
  126. package/src/cli/commands/produce.mjs +1466 -0
  127. package/src/cli/commands/task.mjs +482 -285
  128. package/src/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
  129. package/src/cli/coordination/event-schema.d.ts +4 -2
  130. package/src/cli/coordination/event-schema.mjs +276 -21
  131. package/src/cli/coordination/store.mjs +4 -4
  132. package/src/cli/dispatch.mjs +2 -0
  133. package/src/cli/help.mjs +30 -0
  134. package/src/cli/install/legacy-paths.mjs +2 -0
  135. package/src/cli/produce/blind-probe.mjs +245 -0
  136. package/src/cli/produce/claim.mjs +543 -0
  137. package/src/cli/produce/done-gate.mjs +238 -0
  138. package/src/cli/produce/events.mjs +131 -0
  139. package/src/cli/produce/evidence-oracle.mjs +133 -0
  140. package/src/cli/produce/ledger.mjs +284 -0
  141. package/src/cli/produce/probe-family.mjs +168 -0
  142. package/src/cli/produce/projection.mjs +105 -0
  143. package/src/cli/produce/run-id.mjs +84 -0
  144. package/src/cli/produce/saga.mjs +303 -0
  145. package/src/cli/produce/scheduler.mjs +423 -0
  146. package/src/cli/produce/setpoint.mjs +122 -0
  147. package/src/cli/produce/verification-ttl.mjs +191 -0
  148. package/src/cli/roadmap/showrunner-view.d.ts +10 -0
  149. package/src/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +6 -6
  150. package/dist/cli/roadmap/conductor-view.d.mts.map +0 -1
  151. package/dist/cli/roadmap/conductor-view.mjs.map +0 -1
  152. package/src/cli/roadmap/conductor-view.d.ts +0 -10
@@ -3,8 +3,8 @@
3
3
  * auto-emit-artifact-ready.mjs — code-enforced plan-ready signal.
4
4
  *
5
5
  * Promotes the `artifact.ready{kind:'plan'}` emit from a markdown contract
6
- * in `skills/apt-plan/adapters/conductor.md` to a framework-level
7
- * postcondition of `apt-tools commit`. This closes the Conductor v2 gap
6
+ * in `skills/apt-plan/adapters/showrunner.md` to a framework-level
7
+ * postcondition of `apt-tools commit`. This closes the Showrunner v2 gap
8
8
  * exposed during live verification 2026-05-15: the planner's QUICK /
9
9
  * SIMPLE track is hard-exempt from "new interaction discipline"
10
10
  * (Aperant Fast Path Guarantee ID-05), so the markdown emit step was
@@ -19,7 +19,7 @@
19
19
  * OpenCode, Codex) traverses via `apt-tools commit`
20
20
  *
21
21
  * Conditions for emit (ALL must hold — otherwise no-op):
22
- * 1. `APERANT_TERMINAL_ID` is set in process.env (Conductor PTY context).
22
+ * 1. `APERANT_TERMINAL_ID` is set in process.env (Showrunner PTY context).
23
23
  * 2. The committed files include a path matching
24
24
  * `.aperant/tasks/{task-id}/implementation_plan.json`.
25
25
  *
@@ -32,7 +32,7 @@ import { createHash } from 'node:crypto'
32
32
  import { appendFileSync, mkdirSync, readFileSync } from 'node:fs'
33
33
  import { hostname } from 'node:os'
34
34
  import { join } from 'node:path'
35
- import { validateConductorEvent } from './event-schema.mjs'
35
+ import { validateShowrunnerEvent } from './event-schema.mjs'
36
36
 
37
37
  const PLAN_PATH_RE = /^(?:\.\/)?\.aperant\/tasks\/([^/]+)\/implementation_plan\.json$/
38
38
 
@@ -70,7 +70,7 @@ export function maybeAutoEmitPlanReady({ files, projectRoot }) {
70
70
  terminal_id: terminalId,
71
71
  }
72
72
 
73
- const validation = validateConductorEvent('artifact.ready', data)
73
+ const validation = validateShowrunnerEvent('artifact.ready', data)
74
74
  if (!validation.ok) {
75
75
  return { emitted: false, reason: `schema violation: ${validation.error}` }
76
76
  }
@@ -5,9 +5,11 @@
5
5
 
6
6
  export type ValidateResult = { ok: true } | { ok: false; error: string }
7
7
 
8
- export function validateConductorEvent(
8
+ export function validateShowrunnerEvent(
9
9
  op: string,
10
10
  data: Record<string, unknown> | undefined | null,
11
11
  ): ValidateResult
12
12
 
13
- export const CONDUCTOR_VALIDATED_OPS: ReadonlySet<string>
13
+ export const SHOWRUNNER_VALIDATED_OPS: ReadonlySet<string>
14
+
15
+ export const PRODUCE_VALIDATED_OPS: ReadonlySet<string>
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @internal
3
- * event-schema.mjs — single source of truth for conductor event payload shapes.
3
+ * event-schema.mjs — single source of truth for showrunner event payload shapes.
4
4
  *
5
5
  * Why this lives in `framework`, not `core`:
6
6
  * `packages/core` depends on `@aperant/framework`. The schema is shared by
7
7
  * the CLI write path (`commands/event.mjs`), the framework lifecycle write
8
8
  * seam (`coordination/store.mjs:appendEvent`), and the core IPC write seam
9
- * (`packages/core/src/runtime/orchestration/conductor/event-log.ts`).
9
+ * (`packages/core/src/runtime/orchestration/showrunner/event-log.ts`).
10
10
  * Putting it in the lower layer keeps a single rule table that every
11
11
  * writer can import.
12
12
  *
@@ -22,11 +22,11 @@
22
22
  * - `awaiting-input` / `input-rescinded` MUST carry both terminal_id and
23
23
  * request_id so the drawer can pair cards strictly.
24
24
  * - `artifact.ready{kind:'plan'}` is the canonical plan-completion signal
25
- * consumed by the conductor's reviewer queue (Step 5's reviewPlan tool).
25
+ * consumed by the showrunner's reviewer queue (Step 5's reviewPlan tool).
26
26
  */
27
27
 
28
- /** Conductor ops the schema enforces. Other ops pass through unchanged. */
29
- const CONDUCTOR_OPS = new Set([
28
+ /** Showrunner ops the schema enforces. Other ops pass through unchanged. */
29
+ const SHOWRUNNER_OPS = new Set([
30
30
  'phase.changed',
31
31
  'artifact.ready',
32
32
  'awaiting-input',
@@ -35,34 +35,120 @@ const CONDUCTOR_OPS = new Set([
35
35
  'budget.exceeded',
36
36
  'terminal.enrolled',
37
37
  'terminal.unenrolled',
38
- 'conductor.intent',
39
- 'conductor.cross_check',
40
- 'conductor.realignment',
38
+ 'showrunner.intent',
39
+ 'showrunner.cross_check',
40
+ 'showrunner.realignment',
41
41
  // c-4 (action-capable) — gate verdict + boot-replay audit trail
42
- 'conductor.action.executed',
43
- 'conductor.action.gate_blocked',
44
- 'conductor.boot_reconciled',
42
+ 'showrunner.action.executed',
43
+ 'showrunner.action.gate_blocked',
44
+ 'showrunner.boot_reconciled',
45
45
  // AC21 — MCP-server consent decisions audited to .aperant/events/*.jsonl
46
46
  'mcp-consent.approved',
47
47
  'mcp-consent.denied',
48
48
  'mcp-consent.tofu-reprompt',
49
49
  ])
50
50
 
51
+ /**
52
+ * Produce outer-loop ops (M1) — the `/apt:produce` roadmap-mutation vocabulary.
53
+ * Kept as a PARALLEL set (not merged into SHOWRUNNER_OPS) so the two surfaces
54
+ * stay conceptually distinct; both are consulted by the same
55
+ * `validateShowrunnerEvent` entry point that `store.appendEvent` calls, so a
56
+ * malformed produce event is rejected fail-closed on the canonical event log
57
+ * (ID-04 / AC4). Every produce op carries a `run_id` tagging its run.
58
+ */
59
+ const PRODUCE_OPS = new Set([
60
+ 'produce.run.started',
61
+ 'produce.setpoint.frozen',
62
+ 'produce.roadmap.milestone_added',
63
+ 'produce.roadmap.milestone_status_changed',
64
+ 'produce.tick',
65
+ // M2 — the oracle + held-out cross-family blind-probe forensic trail. Every
66
+ // oracle/probe decision lands on the same append-only log, fail-closed
67
+ // against malformed entries (US-1 / AC1 / AC12). The probe ops are
68
+ // STRUCTURALLY blind: they carry only a hash/result/family, NEVER the probe
69
+ // content (ID-M2-03). The projection still folds `produce.roadmap.*` ONLY,
70
+ // so these never enter the roadmap the Showrunner re-reads each tick.
71
+ 'produce.milestone.implemented',
72
+ 'produce.probe.frozen',
73
+ 'produce.probe.run',
74
+ 'produce.oracle.judged',
75
+ 'produce.oracle.verdict',
76
+ 'produce.oracle.probe_gap',
77
+ // M3 — the Lyapunov acceptance-criteria ledger + scope-change forensic trail.
78
+ // Every criteria-open / criteria-close / scope-change decision lands on the
79
+ // same append-only log, fail-closed against malformed entries (US-4 / AC1). V
80
+ // (open acceptance criteria) = declared − closed, folded by ledger.mjs. The
81
+ // projection still folds `produce.roadmap.*` ONLY — criteria/scope ops are a
82
+ // SEPARATE fold, so the M1/M2 roadmap-blindness invariant is preserved.
83
+ 'produce.criteria.declared',
84
+ 'produce.criteria.closed',
85
+ 'produce.scope.changed',
86
+ // M4 — the saga + verification-TTL forensic trail. Each plan→execute→verify→
87
+ // review→ship phase transition, every FORWARD compensation, every diverse
88
+ // review verdict, and every TTL re-verify / regression lands on the same
89
+ // append-only log, fail-closed against malformed entries (US-2 / AC1). The
90
+ // saga fold is SEPARATE from the roadmap projection (which still folds
91
+ // `produce.roadmap.*` ONLY), so the M1/M2/M3 blindness invariant holds. The
92
+ // saga-phase ops enforce a closed `phase` vocabulary (ALLOWED_SAGA_PHASES),
93
+ // mirroring the phase.changed.to / artifact.ready.kind allowed-value pattern.
94
+ 'produce.saga.phase_entered',
95
+ 'produce.saga.phase_completed',
96
+ 'produce.saga.compensated',
97
+ 'produce.saga.reviewed',
98
+ 'produce.ttl.reverified',
99
+ 'produce.ttl.regression_detected',
100
+ // M5 — the WSJF/WIP scheduler's audited decision. The wake `tick` emits ONE
101
+ // `produce.schedule.computed` alongside its `produce.tick` so the scheduler's
102
+ // pick is a recorded fact on the append-only log (the event-sourced ethos). It
103
+ // uses the `produce.schedule.*` prefix — the roadmap projection folds
104
+ // `produce.roadmap.*` ONLY, so this op NEVER enters the roadmap the Showrunner
105
+ // re-reads (the M1/M2 blindness invariant holds automatically — ID-M5-06).
106
+ 'produce.schedule.computed',
107
+ // M6 — the Claim Envelope lifecycle forensic trail. A claim's CONTENT lives in
108
+ // the frozen `claims/<claim-id>.json` file, referenced by `content_hash` — it
109
+ // NEVER touches the log (ID-M6-07, mirrors produce.probe.frozen /
110
+ // produce.setpoint.frozen). `declared` seeds the claim ledger, `expired` drops
111
+ // it from the active set, `consumed` records a bounded down-projected delta
112
+ // (target_kind value-checked against the closed set). The claim ledger is a
113
+ // SEPARATE fold (projectClaims) — the roadmap projection still folds
114
+ // `produce.roadmap.*` ONLY, so these ops NEVER enter the roadmap the Showrunner
115
+ // re-reads (the M1/M2 blindness invariant holds automatically).
116
+ 'produce.claim.declared',
117
+ 'produce.claim.expired',
118
+ 'produce.claim.consumed',
119
+ ])
120
+
51
121
  /** Allowed values for `phase.changed.to` — PTY busy/idle only. */
52
122
  const ALLOWED_PHASE_VALUES = new Set(['busy', 'idle'])
53
123
 
124
+ /**
125
+ * Allowed values for the saga `phase` on `produce.saga.phase_entered` /
126
+ * `produce.saga.phase_completed` / `produce.saga.compensated` (M4). The saga
127
+ * models plan→execute→verify→review→ship; an out-of-vocabulary phase is rejected
128
+ * fail-closed, mirroring the ALLOWED_PHASE_VALUES value check for phase.changed.
129
+ */
130
+ const ALLOWED_SAGA_PHASES = new Set(['plan', 'execute', 'verify', 'review', 'ship'])
131
+
132
+ /**
133
+ * Allowed values for the claim `target_kind` on `produce.claim.consumed` (M6).
134
+ * Consumption is a DELTA down-projection onto ONE acceptance-criterion / subtask /
135
+ * proof-obligation (ID-M6-06); an out-of-vocabulary kind is rejected fail-closed,
136
+ * mirroring the ALLOWED_SAGA_PHASES value check for the saga-phase ops.
137
+ */
138
+ const ALLOWED_CLAIM_TARGET_KINDS = new Set(['acceptance_criterion', 'subtask', 'proof_obligation'])
139
+
54
140
  /** Allowed values for `artifact.ready.kind`. */
55
141
  const ALLOWED_ARTIFACT_KINDS = new Set(['plan', 'spec', 'build', 'review'])
56
142
 
57
143
  /**
58
- * Validate the data payload of a conductor event against the op's contract.
144
+ * Validate the data payload of a showrunner event against the op's contract.
59
145
  *
60
146
  * @param {string} op
61
147
  * @param {Record<string, unknown> | undefined | null} data
62
148
  * @returns {{ ok: true } | { ok: false, error: string }}
63
149
  */
64
- export function validateConductorEvent(op, data) {
65
- if (!CONDUCTOR_OPS.has(op)) return { ok: true }
150
+ export function validateShowrunnerEvent(op, data) {
151
+ if (!SHOWRUNNER_OPS.has(op) && !PRODUCE_OPS.has(op)) return { ok: true }
66
152
  const d = data && typeof data === 'object' ? data : {}
67
153
 
68
154
  switch (op) {
@@ -105,13 +191,13 @@ export function validateConductorEvent(op, data) {
105
191
  return { ok: true }
106
192
  }
107
193
 
108
- case 'conductor.intent':
194
+ case 'showrunner.intent':
109
195
  return requireFields(op, d, ['terminal_id', 'request_id', 'action', 'reasoning'])
110
196
 
111
- case 'conductor.cross_check':
197
+ case 'showrunner.cross_check':
112
198
  return requireFields(op, d, ['terminal_id', 'request_id'])
113
199
 
114
- case 'conductor.realignment': {
200
+ case 'showrunner.realignment': {
115
201
  const r = requireFields(op, d, ['terminal_id', 'request_id_orig'])
116
202
  if (!r.ok) return r
117
203
  if (d.attempt_n === undefined || d.attempt_n === null)
@@ -125,10 +211,10 @@ export function validateConductorEvent(op, data) {
125
211
  case 'budget.exceeded':
126
212
  return requireFields(op, d, ['terminal_id'])
127
213
 
128
- case 'conductor.action.executed':
214
+ case 'showrunner.action.executed':
129
215
  return requireFields(op, d, ['terminal_id', 'request_id', 'action_kind', 'idempotency_key'])
130
216
 
131
- case 'conductor.action.gate_blocked':
217
+ case 'showrunner.action.gate_blocked':
132
218
  return requireFields(op, d, [
133
219
  'terminal_id',
134
220
  'request_id',
@@ -152,7 +238,130 @@ export function validateConductorEvent(op, data) {
152
238
  'session_id',
153
239
  ])
154
240
 
155
- case 'conductor.boot_reconciled': {
241
+ // Produce outer-loop (M1) — roadmap-mutation + tick vocabulary. Every
242
+ // op requires its `run_id`; roadmap ops additionally carry the
243
+ // milestone identity / status the projection folds (ID-04 / AC4).
244
+ case 'produce.run.started':
245
+ return requireFields(op, d, ['run_id', 'setpoint_hash'])
246
+
247
+ case 'produce.setpoint.frozen':
248
+ return requireFields(op, d, ['run_id', 'setpoint_hash', 'setpoint_path'])
249
+
250
+ case 'produce.roadmap.milestone_added':
251
+ return requireFields(op, d, ['run_id', 'milestone_id', 'title'])
252
+
253
+ case 'produce.roadmap.milestone_status_changed':
254
+ return requireFields(op, d, ['run_id', 'milestone_id', 'status'])
255
+
256
+ case 'produce.tick':
257
+ return requireFields(op, d, ['run_id'])
258
+
259
+ // Produce M2 — oracle + held-out cross-family blind-probe vocabulary.
260
+ // The implementer family is the recorded disjointness anchor (ID-M2-02);
261
+ // the probe ops are content-blind (only hash/result/family — ID-M2-03).
262
+
263
+ // The Showrunner records who implemented a milestone once per milestone;
264
+ // the cross-family rule is enforced against THIS recorded fact.
265
+ case 'produce.milestone.implemented':
266
+ return requireFields(op, d, ['run_id', 'milestone_id', 'implementer_family'])
267
+
268
+ // The frozen blind-probe SET is announced by hash + author family ONLY —
269
+ // NEVER the probe content (structural probe-blindness, AC5).
270
+ case 'produce.probe.frozen':
271
+ return requireFields(op, d, ['run_id', 'milestone_id', 'probe_set_hash', 'author_family'])
272
+
273
+ // A blind-probe RUN carries only its pass/fail result, the running family,
274
+ // and the HEAD sha it ran against — no probe content.
275
+ case 'produce.probe.run':
276
+ return requireFields(op, d, ['run_id', 'milestone_id', 'result', 'probe_family', 'head_sha'])
277
+
278
+ // The independent evidence-quality oracle's verdict over verify-proof's
279
+ // captured evidence, tagged with the (cross-family) oracle family.
280
+ case 'produce.oracle.judged':
281
+ return requireFields(op, d, ['run_id', 'milestone_id', 'verdict', 'oracle_family'])
282
+
283
+ // The two-gate done verdict (visible AND blind agree) — the ONLY path to
284
+ // `done` (ID-03: the Showrunner never self-attests).
285
+ case 'produce.oracle.verdict':
286
+ return requireFields(op, d, ['run_id', 'milestone_id', 'verdict'])
287
+
288
+ // The reward-hacking canary near-miss: visible gate passed, blind gate
289
+ // failed → the milestone stays open (ID-M2-06 / AC9).
290
+ case 'produce.oracle.probe_gap':
291
+ return requireFields(op, d, ['run_id', 'milestone_id'])
292
+
293
+ // Produce M3 — the Lyapunov acceptance-criteria ledger (V = declared −
294
+ // closed) + the scope-change escape hatch (ID-M3-01 / ID-M3-04 / AC1). A
295
+ // criterion binds to a milestone; it opens on `declared` (V += 1), closes
296
+ // on `closed` (V −= 1). `produce.scope.changed` is the ONLY sanctioned
297
+ // post-descent V increase — `milestone_id` is optional (a scope change may
298
+ // not target one milestone), so only `run_id` + `reason` are required.
299
+ case 'produce.criteria.declared':
300
+ return requireFields(op, d, ['run_id', 'milestone_id', 'criterion_id', 'text'])
301
+
302
+ case 'produce.criteria.closed':
303
+ return requireFields(op, d, ['run_id', 'milestone_id', 'criterion_id'])
304
+
305
+ case 'produce.scope.changed':
306
+ return requireFields(op, d, ['run_id', 'reason'])
307
+
308
+ // Produce M4 — the saga phase model + FORWARD compensators + verification-TTL.
309
+ // The saga-phase ops (entered/completed/compensated) enforce the closed
310
+ // `phase` vocabulary (ALLOWED_SAGA_PHASES), mirroring the phase.changed.to
311
+ // value check. No op names a probe/review CONTENT field — the roadmap
312
+ // projection still folds `produce.roadmap.*` ONLY (blindness preserved).
313
+ case 'produce.saga.phase_entered':
314
+ case 'produce.saga.phase_completed': {
315
+ const r = requireFields(op, d, ['run_id', 'milestone_id', 'phase'])
316
+ if (!r.ok) return r
317
+ return checkSagaPhase(op, d)
318
+ }
319
+
320
+ case 'produce.saga.compensated': {
321
+ const r = requireFields(op, d, ['run_id', 'milestone_id', 'phase', 'compensator'])
322
+ if (!r.ok) return r
323
+ return checkSagaPhase(op, d)
324
+ }
325
+
326
+ // A diverse-review verdict over the milestone's diff, tagged with the
327
+ // (cross-family) reviewer family. Content-free — verdict + family only.
328
+ case 'produce.saga.reviewed':
329
+ return requireFields(op, d, ['run_id', 'milestone_id', 'reviewer_family', 'verdict'])
330
+
331
+ // A scheduled post-`done` TTL re-verify RUN against HEAD — result/head_sha
332
+ // only, never probe content. A regression fires the forward compensator.
333
+ case 'produce.ttl.reverified':
334
+ return requireFields(op, d, ['run_id', 'milestone_id', 'head_sha', 'result'])
335
+
336
+ case 'produce.ttl.regression_detected':
337
+ return requireFields(op, d, ['run_id', 'milestone_id', 'head_sha'])
338
+
339
+ // Produce M5 — the WSJF/WIP scheduler's recorded pick. Only `run_id` is
340
+ // required (the `picked` ids + `wip_limit` are optional context); the
341
+ // roadmap projection folds `produce.roadmap.*` ONLY, so it never sees this
342
+ // op (blindness preserved — ID-M5-06).
343
+ case 'produce.schedule.computed':
344
+ return requireFields(op, d, ['run_id'])
345
+
346
+ // Produce M6 — the Claim Envelope lifecycle. `declared` carries a
347
+ // `content_hash` ONLY (the full envelope lives in the frozen
348
+ // claims/<claim-id>.json file — ID-M6-07); `consumed.target_kind` is
349
+ // value-checked against the closed set (mirrors ALLOWED_SAGA_PHASES /
350
+ // checkSagaPhase). No op names a claim-content field — the roadmap
351
+ // projection still folds `produce.roadmap.*` ONLY (blindness preserved).
352
+ case 'produce.claim.declared':
353
+ return requireFields(op, d, ['run_id', 'profile', 'claim_id', 'content_hash'])
354
+
355
+ case 'produce.claim.expired':
356
+ return requireFields(op, d, ['run_id', 'claim_id', 'reason'])
357
+
358
+ case 'produce.claim.consumed': {
359
+ const r = requireFields(op, d, ['run_id', 'claim_id', 'target_kind'])
360
+ if (!r.ok) return r
361
+ return checkClaimTargetKind(op, d)
362
+ }
363
+
364
+ case 'showrunner.boot_reconciled': {
156
365
  // Fleet-wide event — numeric fields only. All optional but if
157
366
  // present must be finite numbers (zero allowed).
158
367
  const numFields = ['cleaned_leases', 'replayed', 'start_offset']
@@ -174,7 +383,10 @@ export function validateConductorEvent(op, data) {
174
383
  }
175
384
 
176
385
  /** Set of ops the schema validates — exported for test enumeration. */
177
- export const CONDUCTOR_VALIDATED_OPS = CONDUCTOR_OPS
386
+ export const SHOWRUNNER_VALIDATED_OPS = SHOWRUNNER_OPS
387
+
388
+ /** Produce outer-loop ops the schema validates — exported for test enumeration. */
389
+ export const PRODUCE_VALIDATED_OPS = PRODUCE_OPS
178
390
 
179
391
  /**
180
392
  * @param {string} op
@@ -191,3 +403,46 @@ function requireFields(op, d, fields) {
191
403
  if (missing.length === 0) return { ok: true }
192
404
  return { ok: false, error: `${op} missing required field(s): ${missing.join(', ')}` }
193
405
  }
406
+
407
+ /**
408
+ * Validate the saga `phase` value against ALLOWED_SAGA_PHASES (M4). Mirrors the
409
+ * inline phase.changed.to value check — fail-closed on an out-of-vocabulary
410
+ * phase so a saga event can never carry an unknown phase onto the log.
411
+ *
412
+ * @param {string} op
413
+ * @param {Record<string, unknown>} d
414
+ * @returns {{ ok: true } | { ok: false, error: string }}
415
+ */
416
+ function checkSagaPhase(op, d) {
417
+ if (!ALLOWED_SAGA_PHASES.has(String(d.phase))) {
418
+ return {
419
+ ok: false,
420
+ error: `${op}.phase must be one of [${[...ALLOWED_SAGA_PHASES].join(', ')}], got: ${String(
421
+ d.phase,
422
+ )}`,
423
+ }
424
+ }
425
+ return { ok: true }
426
+ }
427
+
428
+ /**
429
+ * Validate the claim `target_kind` value against ALLOWED_CLAIM_TARGET_KINDS (M6).
430
+ * Mirrors checkSagaPhase — fail-closed on an out-of-vocabulary target kind so a
431
+ * produce.claim.consumed event can never carry an unknown delta target onto the
432
+ * log (ID-M6-06).
433
+ *
434
+ * @param {string} op
435
+ * @param {Record<string, unknown>} d
436
+ * @returns {{ ok: true } | { ok: false, error: string }}
437
+ */
438
+ function checkClaimTargetKind(op, d) {
439
+ if (!ALLOWED_CLAIM_TARGET_KINDS.has(String(d.target_kind))) {
440
+ return {
441
+ ok: false,
442
+ error: `${op}.target_kind must be one of [${[...ALLOWED_CLAIM_TARGET_KINDS].join(
443
+ ', ',
444
+ )}], got: ${String(d.target_kind)}`,
445
+ }
446
+ }
447
+ return { ok: true }
448
+ }
@@ -32,7 +32,7 @@ import { withFileLock } from '../util/fs-lock.mjs'
32
32
  import { atomicWriteJson } from '../util/io.mjs'
33
33
  import { getAgentIdentity, resolveSharedRoot } from './agent-identity.mjs'
34
34
  import { appendLifecycleEvent } from './event-log.mjs'
35
- import { validateConductorEvent } from './event-schema.mjs'
35
+ import { validateShowrunnerEvent } from './event-schema.mjs'
36
36
  import { advanceLastSeen as lastSeenAdvance, readLastSeen as lastSeenRead } from './last-seen.mjs'
37
37
  import {
38
38
  listActive as teamStatusListActive,
@@ -131,16 +131,16 @@ export function createCoordinationStore(projectRoot) {
131
131
  * call via appendLifecycleEvent. Best-effort: filesystem errors are
132
132
  * swallowed so a missing events/ dir never breaks task lifecycle.
133
133
  *
134
- * Step 1 — schema gate: conductor ops written via the store seam are
134
+ * Step 1 — schema gate: showrunner ops written via the store seam are
135
135
  * validated against `event-schema.mjs` before they reach disk.
136
136
  * Schema violations throw (caught here and surfaced via console.warn)
137
- * because a malformed conductor event would silently break the
137
+ * because a malformed showrunner event would silently break the
138
138
  * drawer's pairing logic — we want it loud, not best-effort.
139
139
  */
140
140
  appendEvent(evt) {
141
141
  try {
142
142
  if (evt && typeof evt.op === 'string') {
143
- const validation = validateConductorEvent(evt.op, evt.data)
143
+ const validation = validateShowrunnerEvent(evt.op, evt.data)
144
144
  if (!validation.ok) {
145
145
  console.warn(`[store.appendEvent] schema violation: ${validation.error}`)
146
146
  return null
@@ -78,6 +78,7 @@ import { cmdPersonas } from './commands/personas.mjs'
78
78
  import { cmdPlanApproval } from './commands/plan-approval.mjs'
79
79
  import { cmdPrReview } from './commands/pr-review.mjs'
80
80
  import { cmdPrecompactHook } from './commands/precompact-hook.mjs'
81
+ import { cmdProduce } from './commands/produce.mjs'
81
82
  import { cmdProductContext } from './commands/product-context.mjs'
82
83
  import { cmdProfile } from './commands/profile.mjs'
83
84
  // cmdRecipeDiff — loaded lazily in the dispatch handler (Fast Path PRF-001)
@@ -326,6 +327,7 @@ export const DISPATCH = {
326
327
  team: (args) => wrap(cmdTeam)(args[1], args[2], args.slice(3)),
327
328
  active: (args) => wrap(cmdActive)(args[1], args[2], args.slice(3)),
328
329
  roadmap: (args) => wrap(cmdRoadmap)(args[1], args[2], args.slice(3)),
330
+ produce: (args) => wrap(cmdProduce)(args[1], args[2], args.slice(3)),
329
331
  seed: (args) => wrap(cmdSeed)(args[1], args[2], args.slice(3)),
330
332
  workflow: (args) => wrap(cmdWorkflow)(args[1], args[2], args.slice(3)),
331
333
  'pr-review': (args) => wrap(cmdPrReview)(args[1], args[2], args.slice(3)),
package/src/cli/help.mjs CHANGED
@@ -29,6 +29,7 @@ Commands:
29
29
  risk-check <dir> --paths <p1> [--operation <type>] Classify file paths by risk level
30
30
 
31
31
  task create <dir> --description <text> [--scope <s>] [--milestone <id>] [--phase <id>] [--roadmap-ref-url <url>] [--depends-on <refs>] [--track <T>] [--autonomy <N>] Create isolated task
32
+ task ensure <dir> --description <text> --intent <create-new|require-existing|self-managed|none> [--track <T>] [--autonomy <N>] [--scope <s>] [--id <id>] Deterministic idempotent allocator: create-new provisions a per-task worktree (idempotent on re-run), require-existing resolves the active task without minting, none/self-managed allocate nothing
32
33
  task list <dir> [--scope <s>] [--blocked-by <id>] List active tasks (optionally filtered)
33
34
  task get <dir> --id <task-id> Get task details and artifacts
34
35
  task update <dir> --id <id> [--lifecycle-phase <p>] [--pr-url <url>] [--subtasks-total <N>] [--depends-on <refs>] Update non-linkage task state; --pr-url may arm ci-watch per ci_watch.after_ship
@@ -136,6 +137,35 @@ Commands:
136
137
  ci-watch start|stop|status|tick <dir> [--pr <N>] [...] /apt:watch-ci primitives — see SKILL.md for full flag matrix
137
138
  ci-watch sweep <dir> --orphaned [--dry-run] BUG-027: enumerate ci-watches state files, query gh per PR, remove terminal-state (MERGED|CLOSED) entries (manual GC; rate-limited)
138
139
 
140
+ produce start <dir> --outcome "<o>" [--run-id <id>] /apt:produce M1 — freeze an immutable acceptance setpoint under .aperant/produce/<run-id>/ (idempotent-reuse on --run-id, never overwrites); emits produce.run.started + produce.setpoint.frozen
141
+ produce add-milestone <dir> --run-id <id> --title "<t>" [--id <mid>] Append produce.roadmap.milestone_added + refresh the derived roadmap.json projection cache
142
+ produce set-milestone-status <dir> --run-id <id> --milestone <mid> --status <planned|in_progress|done> [--verdict-path <p>] Append produce.roadmap.milestone_status_changed; --status done is the M2 two-gate adjudication (visible oracle AND blind probe)
143
+ produce status <dir> --run-id <id> Re-project the roadmap from the append-only event log (no in-memory authority)
144
+ produce tick <dir> --run-id <id> Run exactly ONE wake tick — re-project from disk, emit produce.tick, compute next_action + next_tick_hint, then exit (no loop)
145
+ produce record-implementer <dir> --run-id <id> --milestone <mid> --implementer-family <fam> /apt:produce M2 — record the implementer family (the cross-family disjointness anchor)
146
+ produce resolve-probe-family <dir> --run-id <id> [--milestone <mid>] [--implementer-family <fam>] [--host-family <fam>] [--available-families a,b,c] M2 — resolve a probe/oracle family DISJOINT from the implementer + a LIVE model (never hardcoded); available:false when none
147
+ produce oracle prepare <dir> --run-id <id> --milestone <mid> --verdict-path <p> M2 — extract verify-proof's v2 verdict (must be approved) + tests[] evidence into a cross-family judge payload
148
+ produce oracle record <dir> --run-id <id> --milestone <mid> --verdict <pass|fail> --oracle-family <fam> M2 — record produce.oracle.judged (refused when oracle_family === implementer)
149
+ produce probe freeze <dir> --run-id <id> --milestone <mid> --probe-file <p> --author-family <fam> M2 — freeze an immutable held-out probe set (hash + family only; refused when author_family === implementer)
150
+ produce probe record <dir> --run-id <id> --milestone <mid> --result <pass|fail> --probe-family <fam> --head-sha <sha> M2 — record produce.probe.run (result/family/sha only; refused when probe_family === implementer)
151
+ produce probe sweep <dir> --run-id <id> [--sample N] M2 — deterministic rotating sample of already-done milestones to blind-re-probe against HEAD
152
+ produce canary <dir> --run-id <id> M2 — green-on-visible MINUS green-on-blind + per-milestone breakdown (the reward-hacking canary)
153
+ produce declare-criteria <dir> --run-id <id> --milestone <mid> --criterion "<text>" [--criterion-id <cid>] [--scope-change "<reason>"] /apt:produce M3 — open one acceptance criterion (V += 1); rejected after descent unless --scope-change justifies the increase (anti-windup)
154
+ produce progress <dir> --run-id <id> M3 — re-read the Lyapunov V-series from the log: open-criteria count strictly decreasing across shipped milestones + any un-sanctioned-increase violations (flags, never rejects)
155
+ produce saga enter <dir> --run-id <id> --milestone <mid> --phase <plan|execute|verify|review|ship> /apt:produce M4 — append produce.saga.phase_entered (separate fold from the roadmap projection)
156
+ produce saga complete <dir> --run-id <id> --milestone <mid> --phase <plan|execute|verify|review|ship> M4 — append produce.saga.phase_completed
157
+ produce saga compensate <dir> --run-id <id> --milestone <mid> --phase <p> [--reason "<r>"] M4 — fire the FORWARD compensator (verify|review|ship ships a revert + reopens + re-plans; NEVER rolls back a merged PR); records the descriptor, does not execute it
158
+ produce saga resolve-reviewer <dir> --run-id <id> --milestone <mid> [--host-family <fam>] [--available-families a,b,c] M4 — resolve a REVIEW-phase reviewer family DISJOINT from the implementer + a LIVE model (reuses resolveProbeFamily; never hardcoded); available:false when none
159
+ produce saga review <dir> --run-id <id> --milestone <mid> --verdict <pass|fail> --reviewer-family <fam> M4 — record produce.saga.reviewed (refused when reviewer_family === implementer)
160
+ produce saga status <dir> --run-id <id> M4 — re-project per-milestone saga phase state + pending compensation from the log (no roadmap status mutation)
161
+ produce ttl select <dir> --run-id <id> --head-sha <sha> [--sample N] M4 — deterministic rotating sample of OLD done milestones STALE against HEAD (reuses the M2 rotation; a milestone re-verified against current HEAD is excluded as fresh)
162
+ produce ttl reverify <dir> --run-id <id> --milestone <mid> --result <pass|fail> --probe-family <fam> --head-sha <sha> M4 — cross-family re-verify; --result fail appends ttl.reverified + ttl.regression_detected + fires the FORWARD compensator (ships a revert, not a rollback)
163
+ produce schedule <dir> --run-id <id> [--wip N] /apt:produce M5 — WSJF-rank the ready milestones (cost-of-delay / job-size) + a WIP-limited pick; dependency-aware readiness (blocked / cycle), M4-regression re-entry; read-only
164
+ produce claim declare <dir> --run-id <id> --profile <p> --claim-file <path> [--claim-id <id>] /apt:produce M6 — validate + freeze an immutable Claim Envelope to claims/<claim-id>.json (interop, not a 4th store); rejects a global/unbounded scope, empty preconditions, or missing expiry fail-closed; logs produce.claim.declared (content_hash only)
165
+ produce claim list <dir> --run-id <id> M6 — re-project the claim ledger (declared − expired) folded from the append-only log; produce.claim.* never enters the roadmap projection
166
+ produce claim expire <dir> --run-id <id> --claim-id <id> [--reason "<r>"] M6 — append produce.claim.expired; the claim drops out of the ledger's active set (anti-corrosion manual expiry)
167
+ produce claim consume <dir> --run-id <id> [--milestone <mid>] M6 — emit the STRUCTURED delta {claim_id, targets:[{kind, requirement, ref?}]} derived from resolution_verifier (never a lesson-text paste); skips expired / HEAD-invalidated / precondition-unmatched claims
168
+
139
169
  followups check <dir> --task-id <id> G45: derive the expected follow-up universe (Out of Scope ∪ raw review rows ∪ qa findings) + report missing/unadjudicated/unfixed/undispositioned rows; exit 1 when the ledger is dishonest
140
170
  followups disposition <dir> --task-id <id> --row <row-id> --kind <filed_task|executed_commit|parked_note|dropped_by_user> --ref <ref> [--reason <text>] G45: flip a ledger row to dispositioned (atomic; dropped_by_user requires --reason)
141
171
  followups policy <dir> --task-id <id> G45: effective disposition policy (ask|file|chain + max_chain_depth) from config.followups when set, else autonomy-derived
@@ -108,6 +108,7 @@ export const LEGACY_INSTALL_PATHS = Object.freeze({
108
108
  'agents/apt-personas.md',
109
109
  'agents/apt-plan.md',
110
110
  'agents/apt-pr-review.md',
111
+ 'agents/apt-produce.md',
111
112
  'agents/apt-prototype.md',
112
113
  'agents/apt-quick.md',
113
114
  'agents/apt-release-notes.md',
@@ -153,6 +154,7 @@ export const LEGACY_INSTALL_PATHS = Object.freeze({
153
154
  'commands/apt-personas.md',
154
155
  'commands/apt-plan.md',
155
156
  'commands/apt-pr-review.md',
157
+ 'commands/apt-produce.md',
156
158
  'commands/apt-prototype.md',
157
159
  'commands/apt-quick.md',
158
160
  'commands/apt-release-notes.md',