@aperant/framework 0.17.0 → 0.20.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.
- package/CHANGELOG.md +261 -0
- package/agents/apt-executor.md +6 -0
- package/agents/apt-planner.md +3 -2
- package/bin/features-reconcile-check.mjs +77 -0
- package/dist/cli/artifacts/self-stage.d.mts.map +1 -1
- package/dist/cli/artifacts/self-stage.mjs +2 -1
- package/dist/cli/artifacts/self-stage.mjs.map +1 -1
- package/dist/cli/cli-wrappers/features-reconcile.d.mts +2 -0
- package/dist/cli/cli-wrappers/features-reconcile.d.mts.map +1 -0
- package/dist/cli/cli-wrappers/features-reconcile.mjs +9 -0
- package/dist/cli/cli-wrappers/features-reconcile.mjs.map +1 -0
- package/dist/cli/commands/ci-watch.mjs +49 -2
- package/dist/cli/commands/ci-watch.mjs.map +1 -1
- package/dist/cli/commands/commit.mjs +3 -3
- package/dist/cli/commands/commit.mjs.map +1 -1
- package/dist/cli/commands/event.mjs +16 -16
- package/dist/cli/commands/event.mjs.map +1 -1
- package/dist/cli/commands/features-reconcile.d.mts +2 -0
- package/dist/cli/commands/features-reconcile.d.mts.map +1 -0
- package/dist/cli/commands/features-reconcile.mjs +143 -0
- package/dist/cli/commands/features-reconcile.mjs.map +1 -0
- package/dist/cli/commands/health-check.d.mts +2 -2
- package/dist/cli/commands/health-check.mjs +2 -2
- package/dist/cli/commands/merge-integrate.mjs +3 -3
- package/dist/cli/commands/merge-integrate.mjs.map +1 -1
- package/dist/cli/commands/produce.d.mts +9 -0
- package/dist/cli/commands/produce.d.mts.map +1 -0
- package/dist/cli/commands/produce.mjs +1345 -0
- package/dist/cli/commands/produce.mjs.map +1 -0
- package/dist/cli/commands/task.d.mts +16 -1
- package/dist/cli/commands/task.d.mts.map +1 -1
- package/dist/cli/commands/task.mjs +434 -266
- package/dist/cli/commands/task.mjs.map +1 -1
- package/dist/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
- package/dist/cli/coordination/auto-emit-artifact-ready.mjs.map +1 -1
- package/dist/cli/coordination/event-schema.d.mts +5 -3
- package/dist/cli/coordination/event-schema.d.mts.map +1 -1
- package/dist/cli/coordination/event-schema.mjs +245 -21
- package/dist/cli/coordination/event-schema.mjs.map +1 -1
- package/dist/cli/coordination/store.d.mts +2 -2
- package/dist/cli/coordination/store.mjs +4 -4
- package/dist/cli/coordination/store.mjs.map +1 -1
- package/dist/cli/dispatch.d.mts.map +1 -1
- package/dist/cli/dispatch.mjs +4 -2
- package/dist/cli/dispatch.mjs.map +1 -1
- package/dist/cli/features/area-shape.d.mts +11 -0
- package/dist/cli/features/area-shape.d.mts.map +1 -0
- package/dist/cli/features/area-shape.mjs +141 -0
- package/dist/cli/features/area-shape.mjs.map +1 -0
- package/dist/cli/features/delta.d.mts +78 -0
- package/dist/cli/features/delta.d.mts.map +1 -0
- package/dist/cli/features/delta.mjs +591 -0
- package/dist/cli/features/delta.mjs.map +1 -0
- package/dist/cli/features/reconcile.d.mts +236 -0
- package/dist/cli/features/reconcile.d.mts.map +1 -0
- package/dist/cli/features/reconcile.mjs +1126 -0
- package/dist/cli/features/reconcile.mjs.map +1 -0
- package/dist/cli/features/surface-map.d.mts +63 -0
- package/dist/cli/features/surface-map.d.mts.map +1 -0
- package/dist/cli/features/surface-map.mjs +191 -0
- package/dist/cli/features/surface-map.mjs.map +1 -0
- package/dist/cli/{commands/features-audit.d.mts → features/write-root.d.mts} +4 -3
- package/dist/cli/features/write-root.d.mts.map +1 -0
- package/dist/cli/features/write-root.mjs +137 -0
- package/dist/cli/features/write-root.mjs.map +1 -0
- package/dist/cli/gate/gates/features-reconciled.d.mts +36 -0
- package/dist/cli/gate/gates/features-reconciled.d.mts.map +1 -0
- package/dist/cli/gate/gates/features-reconciled.mjs +67 -0
- package/dist/cli/gate/gates/features-reconciled.mjs.map +1 -0
- package/dist/cli/gate/gates/index.mjs +1 -0
- package/dist/cli/gate/gates/index.mjs.map +1 -1
- package/dist/cli/help.d.mts.map +1 -1
- package/dist/cli/help.mjs +31 -1
- package/dist/cli/help.mjs.map +1 -1
- package/dist/cli/install/legacy-paths.d.mts.map +1 -1
- package/dist/cli/install/legacy-paths.mjs +2 -0
- package/dist/cli/install/legacy-paths.mjs.map +1 -1
- package/dist/cli/produce/blind-probe.d.mts +85 -0
- package/dist/cli/produce/blind-probe.d.mts.map +1 -0
- package/dist/cli/produce/blind-probe.mjs +217 -0
- package/dist/cli/produce/blind-probe.mjs.map +1 -0
- package/dist/cli/produce/claim.d.mts +188 -0
- package/dist/cli/produce/claim.d.mts.map +1 -0
- package/dist/cli/produce/claim.mjs +518 -0
- package/dist/cli/produce/claim.mjs.map +1 -0
- package/dist/cli/produce/done-gate.d.mts +87 -0
- package/dist/cli/produce/done-gate.d.mts.map +1 -0
- package/dist/cli/produce/done-gate.mjs +200 -0
- package/dist/cli/produce/done-gate.mjs.map +1 -0
- package/dist/cli/produce/events.d.mts +77 -0
- package/dist/cli/produce/events.d.mts.map +1 -0
- package/dist/cli/produce/events.mjs +126 -0
- package/dist/cli/produce/events.mjs.map +1 -0
- package/dist/cli/produce/evidence-oracle.d.mts +63 -0
- package/dist/cli/produce/evidence-oracle.d.mts.map +1 -0
- package/dist/cli/produce/evidence-oracle.mjs +122 -0
- package/dist/cli/produce/evidence-oracle.mjs.map +1 -0
- package/dist/cli/produce/ledger.d.mts +140 -0
- package/dist/cli/produce/ledger.d.mts.map +1 -0
- package/dist/cli/produce/ledger.mjs +272 -0
- package/dist/cli/produce/ledger.mjs.map +1 -0
- package/dist/cli/produce/probe-family.d.mts +53 -0
- package/dist/cli/produce/probe-family.d.mts.map +1 -0
- package/dist/cli/produce/probe-family.mjs +160 -0
- package/dist/cli/produce/probe-family.mjs.map +1 -0
- package/dist/cli/produce/projection.d.mts +55 -0
- package/dist/cli/produce/projection.d.mts.map +1 -0
- package/dist/cli/produce/projection.mjs +97 -0
- package/dist/cli/produce/projection.mjs.map +1 -0
- package/dist/cli/produce/run-id.d.mts +42 -0
- package/dist/cli/produce/run-id.d.mts.map +1 -0
- package/dist/cli/produce/run-id.mjs +79 -0
- package/dist/cli/produce/run-id.mjs.map +1 -0
- package/dist/cli/produce/saga.d.mts +180 -0
- package/dist/cli/produce/saga.d.mts.map +1 -0
- package/dist/cli/produce/saga.mjs +290 -0
- package/dist/cli/produce/saga.mjs.map +1 -0
- package/dist/cli/produce/scheduler.d.mts +165 -0
- package/dist/cli/produce/scheduler.d.mts.map +1 -0
- package/dist/cli/produce/scheduler.mjs +399 -0
- package/dist/cli/produce/scheduler.mjs.map +1 -0
- package/dist/cli/produce/setpoint.d.mts +52 -0
- package/dist/cli/produce/setpoint.d.mts.map +1 -0
- package/dist/cli/produce/setpoint.mjs +113 -0
- package/dist/cli/produce/setpoint.mjs.map +1 -0
- package/dist/cli/produce/verification-ttl.d.mts +75 -0
- package/dist/cli/produce/verification-ttl.d.mts.map +1 -0
- package/dist/cli/produce/verification-ttl.mjs +169 -0
- package/dist/cli/produce/verification-ttl.mjs.map +1 -0
- package/dist/cli/release-notes/ship-autodraft.mjs +2 -2
- package/dist/cli/roadmap/{conductor-view.d.mts → showrunner-view.d.mts} +3 -3
- package/dist/cli/roadmap/showrunner-view.d.mts.map +1 -0
- package/dist/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +7 -7
- package/dist/cli/roadmap/showrunner-view.mjs.map +1 -0
- package/dist/cli/variant/gallery.d.mts.map +1 -1
- package/dist/cli/variant/gallery.mjs +7 -3
- package/dist/cli/variant/gallery.mjs.map +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +2 -1
- package/dist/plugin/agents/apt-executor.md +6 -0
- package/dist/plugin/agents/apt-planner.md +3 -2
- package/dist/plugin/skills/apt/SKILL.md +112 -38
- package/dist/plugin/skills/apt-close-task/SKILL.md +2 -2
- package/dist/plugin/skills/apt-debug/SKILL.md +22 -24
- package/dist/plugin/skills/apt-execute/SKILL.md +35 -26
- package/dist/plugin/skills/apt-fan-out/SKILL.md +4 -4
- package/dist/plugin/skills/apt-handoff/SKILL.md +1 -1
- package/dist/plugin/skills/apt-mockup/SKILL.md +1 -1
- package/dist/plugin/skills/apt-plan/SKILL.md +5 -5
- package/dist/plugin/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
- package/dist/plugin/skills/apt-produce/SKILL.md +606 -0
- package/dist/plugin/skills/apt-quick/SKILL.md +27 -33
- package/dist/plugin/skills/apt-review/SKILL.md +14 -11
- package/dist/plugin/skills/apt-run/SKILL.md +132 -3
- package/dist/plugin/skills/apt-ship/SKILL.md +29 -5
- package/dist/plugin/skills/apt-spar/SKILL.md +5 -3
- package/dist/plugin/skills/apt-watch-ci/SKILL.md +6 -4
- package/dist/schemas/feature-registry.d.ts.map +1 -1
- package/dist/schemas/feature-registry.js +34 -3
- package/dist/schemas/feature-registry.js.map +1 -1
- package/dist/types/config.d.ts +8 -5
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +4 -4
- package/prompts/{conductor-framework-context.md → showrunner-framework-context.md} +1 -1
- package/prompts/{conductor-status-check.md → showrunner-status-check.md} +1 -1
- package/prompts/{conductor-sub-agent.md → showrunner-sub-agent.md} +6 -6
- package/prompts/{conductor-system.md → showrunner-system.md} +8 -8
- package/skills/apt/SKILL.md +112 -38
- package/skills/apt-close-task/SKILL.md +2 -2
- package/skills/apt-debug/SKILL.md +22 -24
- package/skills/apt-execute/SKILL.md +35 -26
- package/skills/apt-fan-out/SKILL.md +4 -4
- package/skills/apt-handoff/SKILL.md +1 -1
- package/skills/apt-mockup/SKILL.md +1 -1
- package/skills/apt-plan/SKILL.md +5 -5
- package/skills/apt-plan/adapters/{conductor.md → showrunner.md} +16 -16
- package/skills/apt-produce/SKILL.md +606 -0
- package/skills/apt-quick/SKILL.md +27 -33
- package/skills/apt-review/SKILL.md +14 -11
- package/skills/apt-run/SKILL.md +132 -3
- package/skills/apt-ship/SKILL.md +29 -5
- package/skills/apt-spar/SKILL.md +5 -3
- package/skills/apt-watch-ci/SKILL.md +6 -4
- package/src/cli/artifacts/self-stage.mjs +2 -1
- package/src/cli/cli-wrappers/features-reconcile.mjs +9 -0
- package/src/cli/commands/ci-watch.mjs +51 -2
- package/src/cli/commands/commit.mjs +3 -3
- package/src/cli/commands/event.mjs +16 -16
- package/src/cli/commands/features-reconcile.mjs +157 -0
- package/src/cli/commands/health-check.mjs +2 -2
- package/src/cli/commands/merge-integrate.mjs +3 -3
- package/src/cli/commands/produce.mjs +1466 -0
- package/src/cli/commands/task.mjs +482 -285
- package/src/cli/coordination/auto-emit-artifact-ready.mjs +5 -5
- package/src/cli/coordination/event-schema.d.ts +4 -2
- package/src/cli/coordination/event-schema.mjs +276 -21
- package/src/cli/coordination/store.mjs +4 -4
- package/src/cli/dispatch.mjs +4 -2
- package/src/cli/features/area-shape.mjs +140 -0
- package/src/cli/features/delta.mjs +625 -0
- package/src/cli/features/reconcile.mjs +1169 -0
- package/src/cli/features/surface-map.mjs +192 -0
- package/src/cli/features/write-root.mjs +140 -0
- package/src/cli/gate/gates/features-reconciled.mjs +70 -0
- package/src/cli/gate/gates/index.mjs +1 -0
- package/src/cli/help.mjs +31 -1
- package/src/cli/install/legacy-paths.mjs +2 -0
- package/src/cli/produce/blind-probe.mjs +245 -0
- package/src/cli/produce/claim.mjs +543 -0
- package/src/cli/produce/done-gate.mjs +238 -0
- package/src/cli/produce/events.mjs +131 -0
- package/src/cli/produce/evidence-oracle.mjs +133 -0
- package/src/cli/produce/ledger.mjs +284 -0
- package/src/cli/produce/probe-family.mjs +168 -0
- package/src/cli/produce/projection.mjs +105 -0
- package/src/cli/produce/run-id.mjs +84 -0
- package/src/cli/produce/saga.mjs +303 -0
- package/src/cli/produce/scheduler.mjs +423 -0
- package/src/cli/produce/setpoint.mjs +122 -0
- package/src/cli/produce/verification-ttl.mjs +191 -0
- package/src/cli/release-notes/ship-autodraft.mjs +2 -2
- package/src/cli/roadmap/showrunner-view.d.ts +10 -0
- package/src/cli/roadmap/{conductor-view.mjs → showrunner-view.mjs} +6 -6
- package/src/cli/variant/gallery.mjs +7 -3
- package/templates/aperant-claude-md-appendix.md +2 -2
- package/workflows/scan-features.md +17 -11
- package/dist/cli/cli-wrappers/features-audit.d.mts +0 -2
- package/dist/cli/cli-wrappers/features-audit.d.mts.map +0 -1
- package/dist/cli/cli-wrappers/features-audit.mjs +0 -8
- package/dist/cli/cli-wrappers/features-audit.mjs.map +0 -1
- package/dist/cli/commands/features-audit.d.mts.map +0 -1
- package/dist/cli/commands/features-audit.mjs +0 -293
- package/dist/cli/commands/features-audit.mjs.map +0 -1
- package/dist/cli/features/registry-audit.d.mts +0 -56
- package/dist/cli/features/registry-audit.d.mts.map +0 -1
- package/dist/cli/features/registry-audit.mjs +0 -264
- package/dist/cli/features/registry-audit.mjs.map +0 -1
- package/dist/cli/roadmap/conductor-view.d.mts.map +0 -1
- package/dist/cli/roadmap/conductor-view.mjs.map +0 -1
- package/src/cli/cli-wrappers/features-audit.mjs +0 -8
- package/src/cli/commands/features-audit.mjs +0 -302
- package/src/cli/features/registry-audit.mjs +0 -254
- package/src/cli/roadmap/conductor-view.d.ts +0 -10
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
* produce/saga.mjs — the plan→execute→verify→review→ship saga + FORWARD
|
|
4
|
+
* compensators (M4, ID-M4-01 / ID-M4-02, the ID-10 semantic).
|
|
5
|
+
*
|
|
6
|
+
* The loop is modeled as a saga: each phase declares a FORWARD compensator via
|
|
7
|
+
* `resolveCompensator(phase)`. The load-bearing invariant is that EVERY
|
|
8
|
+
* compensator carries `rolls_back_merged_pr: false` — a compensator is always a
|
|
9
|
+
* forward compensating transaction. A failed verify/review/ship SHIPS A REVERT
|
|
10
|
+
* (a new forward commit/branch) + reopens the task + re-plans; it NEVER un-merges
|
|
11
|
+
* history other agents/humans already built on. plan/execute are pre-merge and
|
|
12
|
+
* simply reopen + re-plan (no revert), still `rolls_back_merged_pr: false`.
|
|
13
|
+
*
|
|
14
|
+
* The CLI RECORDS the phase transitions + the forward-compensator DESCRIPTOR; it
|
|
15
|
+
* NEVER executes the revert / reopen / re-plan (that dispatch is OOS — the CLI
|
|
16
|
+
* records + computes, the skill/dispatch acts, exactly as M2 records
|
|
17
|
+
* oracle/probe results while the skill drives the LLM steps).
|
|
18
|
+
*
|
|
19
|
+
* `projectSaga` folds `produce.saga.*` ONLY into per-milestone phase state — a
|
|
20
|
+
* SEPARATE fold PARALLEL to the roadmap projection (projection.mjs still folds
|
|
21
|
+
* `produce.roadmap.*` ONLY, so the M1/M2/M3 blindness invariant holds: a saga
|
|
22
|
+
* compensation never mutates a milestone's `done` roadmap status).
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
appendProduceEvent,
|
|
27
|
+
readImplementerFamily,
|
|
28
|
+
readProduceRunEventsMatching,
|
|
29
|
+
} from './events.mjs'
|
|
30
|
+
import { normalizeFamily, resolveProbeFamily } from './probe-family.mjs'
|
|
31
|
+
|
|
32
|
+
/** The ordered saga phases (ID-M4-01). Mirrors ALLOWED_SAGA_PHASES in event-schema.mjs. */
|
|
33
|
+
export const SAGA_PHASES = ['plan', 'execute', 'verify', 'review', 'ship']
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The declared FORWARD-compensator table (ID-M4-02). EVERY descriptor carries
|
|
37
|
+
* `rolls_back_merged_pr: false` — a compensator is always forward. plan/execute
|
|
38
|
+
* are pre-merge (reopen + re-plan, no revert); verify/review/ship SHIP A REVERT
|
|
39
|
+
* + reopen + re-plan. The CLI records the descriptor kind; it never emits a
|
|
40
|
+
* rollback / history-rewrite instruction.
|
|
41
|
+
*/
|
|
42
|
+
const COMPENSATORS = {
|
|
43
|
+
plan: {
|
|
44
|
+
kind: 'rewrite_plan',
|
|
45
|
+
reopens_task: true,
|
|
46
|
+
ships_revert: false,
|
|
47
|
+
rolls_back_merged_pr: false,
|
|
48
|
+
},
|
|
49
|
+
execute: {
|
|
50
|
+
kind: 'discard_branch_replan',
|
|
51
|
+
reopens_task: true,
|
|
52
|
+
ships_revert: false,
|
|
53
|
+
rolls_back_merged_pr: false,
|
|
54
|
+
},
|
|
55
|
+
verify: {
|
|
56
|
+
kind: 'revert_reopen_replan',
|
|
57
|
+
reopens_task: true,
|
|
58
|
+
ships_revert: true,
|
|
59
|
+
rolls_back_merged_pr: false,
|
|
60
|
+
},
|
|
61
|
+
review: {
|
|
62
|
+
kind: 'revert_reopen_replan',
|
|
63
|
+
reopens_task: true,
|
|
64
|
+
ships_revert: true,
|
|
65
|
+
rolls_back_merged_pr: false,
|
|
66
|
+
},
|
|
67
|
+
ship: {
|
|
68
|
+
kind: 'revert_reopen_replan',
|
|
69
|
+
reopens_task: true,
|
|
70
|
+
ships_revert: true,
|
|
71
|
+
rolls_back_merged_pr: false,
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Resolve the declared FORWARD compensator for a saga phase. Returns a fresh
|
|
77
|
+
* copy (the table is never handed out mutable) or null for an out-of-vocabulary
|
|
78
|
+
* phase (fail-closed). The returned descriptor ALWAYS has
|
|
79
|
+
* `rolls_back_merged_pr: false` (ID-M4-02).
|
|
80
|
+
*
|
|
81
|
+
* @param {string} phase
|
|
82
|
+
* @returns {{ kind: string, reopens_task: boolean, ships_revert: boolean, rolls_back_merged_pr: boolean } | null}
|
|
83
|
+
*/
|
|
84
|
+
export function resolveCompensator(phase) {
|
|
85
|
+
const descriptor = COMPENSATORS[phase]
|
|
86
|
+
if (!descriptor) return null
|
|
87
|
+
return { ...descriptor }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Append `produce.saga.phase_entered` for a milestone's saga phase. Validates
|
|
92
|
+
* the phase against SAGA_PHASES fail-closed (an out-of-vocabulary phase appends
|
|
93
|
+
* nothing).
|
|
94
|
+
*
|
|
95
|
+
* @param {string} projectDir
|
|
96
|
+
* @param {{ runId: string, milestoneId: string, phase: string }} args
|
|
97
|
+
* @returns {{ ok: true, phase: string } | { ok: false, reason: string }}
|
|
98
|
+
*/
|
|
99
|
+
export function enterPhase(projectDir, { runId, milestoneId, phase }) {
|
|
100
|
+
if (!SAGA_PHASES.includes(phase)) return { ok: false, reason: 'invalid_phase' }
|
|
101
|
+
appendProduceEvent(projectDir, 'produce.saga.phase_entered', {
|
|
102
|
+
run_id: runId,
|
|
103
|
+
milestone_id: milestoneId,
|
|
104
|
+
phase,
|
|
105
|
+
})
|
|
106
|
+
return { ok: true, phase }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Append `produce.saga.phase_completed` for a milestone's saga phase. Validates
|
|
111
|
+
* the phase against SAGA_PHASES fail-closed.
|
|
112
|
+
*
|
|
113
|
+
* @param {string} projectDir
|
|
114
|
+
* @param {{ runId: string, milestoneId: string, phase: string }} args
|
|
115
|
+
* @returns {{ ok: true, phase: string } | { ok: false, reason: string }}
|
|
116
|
+
*/
|
|
117
|
+
export function completePhase(projectDir, { runId, milestoneId, phase }) {
|
|
118
|
+
if (!SAGA_PHASES.includes(phase)) return { ok: false, reason: 'invalid_phase' }
|
|
119
|
+
appendProduceEvent(projectDir, 'produce.saga.phase_completed', {
|
|
120
|
+
run_id: runId,
|
|
121
|
+
milestone_id: milestoneId,
|
|
122
|
+
phase,
|
|
123
|
+
})
|
|
124
|
+
return { ok: true, phase }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Fire the FORWARD compensator for a failed saga phase (ID-M4-02). Resolves the
|
|
129
|
+
* declared compensator, then appends `produce.saga.compensated {run_id,
|
|
130
|
+
* milestone_id, phase, compensator: <descriptor.kind>, reason}` — carrying the
|
|
131
|
+
* descriptor KIND, never a rollback instruction. Validates the phase fail-closed.
|
|
132
|
+
* The CLI records the forward compensator; it does NOT execute the revert/reopen/
|
|
133
|
+
* re-plan (OOS dispatch) and NEVER mutates the roadmap milestone status.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} projectDir
|
|
136
|
+
* @param {{ runId: string, milestoneId: string, phase: string, reason?: string }} args
|
|
137
|
+
* @returns {{ ok: true, phase: string, compensator: object } | { ok: false, reason: string }}
|
|
138
|
+
*/
|
|
139
|
+
export function compensatePhase(projectDir, { runId, milestoneId, phase, reason }) {
|
|
140
|
+
if (!SAGA_PHASES.includes(phase)) return { ok: false, reason: 'invalid_phase' }
|
|
141
|
+
const compensator = resolveCompensator(phase)
|
|
142
|
+
appendProduceEvent(projectDir, 'produce.saga.compensated', {
|
|
143
|
+
run_id: runId,
|
|
144
|
+
milestone_id: milestoneId,
|
|
145
|
+
phase,
|
|
146
|
+
compensator: compensator.kind,
|
|
147
|
+
reason: reason || `compensate_${phase}`,
|
|
148
|
+
})
|
|
149
|
+
return { ok: true, phase, compensator }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Record a provider-diverse review verdict (ID-M4-04 / ID-08). The review-path
|
|
154
|
+
* analog of evidence-oracle.mjs::recordOracleVerdict. REFUSES fail-closed when
|
|
155
|
+
* the reviewer family equals the recorded implementer family (a same-family
|
|
156
|
+
* reviewer rubber-stamps its own family's work) or when no implementer family is
|
|
157
|
+
* recorded (ID-M2-02). On acceptance appends `produce.saga.reviewed {run_id,
|
|
158
|
+
* milestone_id, reviewer_family, verdict}`.
|
|
159
|
+
*
|
|
160
|
+
* @param {string} projectDir
|
|
161
|
+
* @param {{ runId: string, milestoneId: string, verdict: string, reviewerFamily: string }} args
|
|
162
|
+
* @returns {{ ok: true, verdict: string, reviewer_family: string, implementer_family: string }
|
|
163
|
+
* | { ok: false, reason: string, implementer_family?: string | null }}
|
|
164
|
+
*/
|
|
165
|
+
export function recordReview(projectDir, { runId, milestoneId, verdict, reviewerFamily }) {
|
|
166
|
+
if (verdict !== 'pass' && verdict !== 'fail') return { ok: false, reason: 'invalid_verdict' }
|
|
167
|
+
if (!reviewerFamily) return { ok: false, reason: 'no_reviewer_family' }
|
|
168
|
+
|
|
169
|
+
const implementerFamily = readImplementerFamily(projectDir, runId, milestoneId)
|
|
170
|
+
if (!implementerFamily)
|
|
171
|
+
return { ok: false, reason: 'no_recorded_implementer', implementer_family: null }
|
|
172
|
+
|
|
173
|
+
// Cross-family enforcement (normalized so claude-code === claude).
|
|
174
|
+
if (normalizeFamily(reviewerFamily) === normalizeFamily(implementerFamily))
|
|
175
|
+
return { ok: false, reason: 'same_family_reviewer', implementer_family: implementerFamily }
|
|
176
|
+
|
|
177
|
+
appendProduceEvent(projectDir, 'produce.saga.reviewed', {
|
|
178
|
+
run_id: runId,
|
|
179
|
+
milestone_id: milestoneId,
|
|
180
|
+
reviewer_family: reviewerFamily,
|
|
181
|
+
verdict,
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
ok: true,
|
|
186
|
+
verdict,
|
|
187
|
+
reviewer_family: reviewerFamily,
|
|
188
|
+
implementer_family: implementerFamily,
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Resolve a REVIEW-phase reviewer family DISJOINT from the recorded implementer
|
|
194
|
+
* (ID-M4-04 / ID-08), a THIN wrapper over the M2 `resolveProbeFamily` — NO
|
|
195
|
+
* duplicated cross-family / partner-map / model-resolution logic. Reads the
|
|
196
|
+
* implementer family from the recorded `produce.milestone.implemented` trail
|
|
197
|
+
* (fail-closed when absent). Returns the resolution with `probe_family` surfaced
|
|
198
|
+
* as `reviewer_family`. Provider is an OUTPUT; the model is resolved LIVE (null
|
|
199
|
+
* for self-resolving families) — never a hardcoded id.
|
|
200
|
+
*
|
|
201
|
+
* @param {string} projectDir
|
|
202
|
+
* @param {{ runId: string, milestoneId: string, hostFamily?: string | null, availableFamilies?: string[] }} args
|
|
203
|
+
* @returns {{ available: boolean, reviewer_family: string | null, implementer_family: string | null,
|
|
204
|
+
* transport_hint: object | null, model: string | null, source: string | null, reason: string | null }}
|
|
205
|
+
*/
|
|
206
|
+
export function resolveReviewer(projectDir, { runId, milestoneId, hostFamily, availableFamilies }) {
|
|
207
|
+
const implementerFamily = readImplementerFamily(projectDir, runId, milestoneId)
|
|
208
|
+
if (!implementerFamily) {
|
|
209
|
+
return {
|
|
210
|
+
available: false,
|
|
211
|
+
reviewer_family: null,
|
|
212
|
+
implementer_family: null,
|
|
213
|
+
transport_hint: null,
|
|
214
|
+
model: null,
|
|
215
|
+
source: null,
|
|
216
|
+
reason: 'no_recorded_implementer',
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const resolution = resolveProbeFamily({ implementerFamily, hostFamily, availableFamilies })
|
|
221
|
+
// Surface the disjoint probe family as the reviewer family (same selection).
|
|
222
|
+
const { probe_family, ...rest } = resolution
|
|
223
|
+
return { ...rest, reviewer_family: probe_family, implementer_family: implementerFamily }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Fold the run's `produce.saga.*` events into per-milestone saga state — a
|
|
228
|
+
* SEPARATE fold parallel to the roadmap projection (the roadmap projection folds
|
|
229
|
+
* `produce.roadmap.*` ONLY, so saga events never enter the roadmap the Showrunner
|
|
230
|
+
* re-reads each tick — blindness preserved, ID-M4-01). Deterministic: two folds
|
|
231
|
+
* of the same log yield the identical saga state; `updated_at` is the last folded
|
|
232
|
+
* event's timestamp.
|
|
233
|
+
*
|
|
234
|
+
* @param {string} projectDir
|
|
235
|
+
* @param {string} runId
|
|
236
|
+
* @returns {{
|
|
237
|
+
* run_id: string,
|
|
238
|
+
* milestones: Array<{
|
|
239
|
+
* milestone_id: string,
|
|
240
|
+
* current_phase: string | null,
|
|
241
|
+
* phase_status: 'entered' | 'completed' | null,
|
|
242
|
+
* phases: Record<string, 'entered' | 'completed'>,
|
|
243
|
+
* last_compensated: { phase: string | null, compensator: string | null, reason: string | null } | null,
|
|
244
|
+
* reviewed: { reviewer_family: string | null, verdict: string | null } | null,
|
|
245
|
+
* }>,
|
|
246
|
+
* updated_at: string | null,
|
|
247
|
+
* }}
|
|
248
|
+
*/
|
|
249
|
+
export function projectSaga(projectDir, runId) {
|
|
250
|
+
const events = readProduceRunEventsMatching(projectDir, runId, (op) =>
|
|
251
|
+
op.startsWith('produce.saga.'),
|
|
252
|
+
)
|
|
253
|
+
const byId = new Map()
|
|
254
|
+
const order = []
|
|
255
|
+
let updatedAt = null
|
|
256
|
+
|
|
257
|
+
const ensure = (id) => {
|
|
258
|
+
if (!byId.has(id)) {
|
|
259
|
+
byId.set(id, {
|
|
260
|
+
milestone_id: id,
|
|
261
|
+
current_phase: null,
|
|
262
|
+
phase_status: null,
|
|
263
|
+
phases: {},
|
|
264
|
+
last_compensated: null,
|
|
265
|
+
reviewed: null,
|
|
266
|
+
})
|
|
267
|
+
order.push(id)
|
|
268
|
+
}
|
|
269
|
+
return byId.get(id)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
for (const e of events) {
|
|
273
|
+
const d = e.data || {}
|
|
274
|
+
const id = typeof d.milestone_id === 'string' ? d.milestone_id : null
|
|
275
|
+
if (!id) continue
|
|
276
|
+
const m = ensure(id)
|
|
277
|
+
const phase = typeof d.phase === 'string' ? d.phase : null
|
|
278
|
+
|
|
279
|
+
if (e.op === 'produce.saga.phase_entered' && phase) {
|
|
280
|
+
m.current_phase = phase
|
|
281
|
+
m.phase_status = 'entered'
|
|
282
|
+
m.phases[phase] = 'entered'
|
|
283
|
+
} else if (e.op === 'produce.saga.phase_completed' && phase) {
|
|
284
|
+
m.current_phase = phase
|
|
285
|
+
m.phase_status = 'completed'
|
|
286
|
+
m.phases[phase] = 'completed'
|
|
287
|
+
} else if (e.op === 'produce.saga.compensated') {
|
|
288
|
+
m.last_compensated = {
|
|
289
|
+
phase,
|
|
290
|
+
compensator: typeof d.compensator === 'string' ? d.compensator : null,
|
|
291
|
+
reason: typeof d.reason === 'string' ? d.reason : null,
|
|
292
|
+
}
|
|
293
|
+
} else if (e.op === 'produce.saga.reviewed') {
|
|
294
|
+
m.reviewed = {
|
|
295
|
+
reviewer_family: typeof d.reviewer_family === 'string' ? d.reviewer_family : null,
|
|
296
|
+
verdict: typeof d.verdict === 'string' ? d.verdict : null,
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (typeof e.t === 'string') updatedAt = e.t
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return { run_id: runId, milestones: order.map((id) => byId.get(id)), updated_at: updatedAt }
|
|
303
|
+
}
|