@nimiplatform/nimi-coding 0.2.8 → 0.3.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 +8 -92
- package/CONTRIBUTING.md +5 -34
- package/README.md +47 -350
- package/README.zh-CN.md +33 -287
- package/cli/commands/start.mjs +48 -688
- package/cli/commands/validate-spec-audit.mjs +1 -5
- package/cli/constants.mjs +9 -417
- package/cli/help.mjs +28 -145
- package/cli/index.mjs +5 -33
- package/cli/lib/blueprint-audit.mjs +5 -12
- package/cli/lib/bootstrap.mjs +0 -9
- package/cli/lib/contracts.mjs +0 -163
- package/cli/lib/doctor.mjs +155 -10
- package/cli/lib/entrypoints.mjs +63 -167
- package/cli/lib/internal/contracts-loaders.mjs +0 -85
- package/cli/lib/internal/contracts-parse.mjs +84 -520
- package/cli/lib/internal/governance/ai/check-agents-freshness.mjs +13 -8
- package/cli/lib/internal/surface-taxonomy-validators.mjs +316 -66
- package/cli/lib/internal/validators-shared.mjs +0 -16
- package/cli/lib/internal/validators-spec-helpers.mjs +7 -140
- package/cli/lib/internal/validators-spec.mjs +103 -342
- package/cli/lib/shared.mjs +1 -42
- package/cli/lib/validators.mjs +0 -48
- package/cli/seeds/seed-policy.yaml +0 -3
- package/config/bootstrap.yaml +4 -4
- package/config/spec-generation-inputs.yaml +6 -12
- package/contracts/domain-admission.schema.yaml +3 -3
- package/contracts/migration-inventory.schema.yaml +12 -75
- package/contracts/negative-fixtures.yaml +19 -99
- package/contracts/placement-contract.schema.yaml +49 -78
- package/contracts/projection-edge.schema.yaml +37 -121
- package/contracts/shared-enums.yaml +4 -8
- package/contracts/spec-generation-inputs.schema.yaml +15 -112
- package/contracts/spec-layout.schema.yaml +32 -0
- package/contracts/surface-taxonomy.schema.yaml +43 -94
- package/contracts/table-family.schema.yaml +1 -1
- package/contracts/tracked-output-admission.schema.yaml +13 -66
- package/methodology/core.yaml +33 -25
- package/methodology/four-closure-policy.yaml +18 -26
- package/methodology/role-separation-policy.yaml +21 -27
- package/methodology/spec-reconstruction.yaml +17 -34
- package/package.json +2 -4
- package/spec/_meta/spec-tree-model.yaml +5 -101
- package/spec/product-scope.yaml +25 -57
- package/adapters/README.md +0 -25
- package/adapters/claude/README.md +0 -89
- package/adapters/claude/profile.yaml +0 -70
- package/adapters/codex/README.md +0 -53
- package/adapters/codex/profile.yaml +0 -78
- package/adapters/oh-my-codex/README.md +0 -184
- package/adapters/oh-my-codex/profile.yaml +0 -46
- package/cli/commands/admit-high-risk-decision.mjs +0 -108
- package/cli/commands/audit-sweep.mjs +0 -364
- package/cli/commands/closeout.mjs +0 -186
- package/cli/commands/decide-high-risk-execution.mjs +0 -124
- package/cli/commands/handoff.mjs +0 -123
- package/cli/commands/ingest-high-risk-execution.mjs +0 -95
- package/cli/commands/review-high-risk-execution.mjs +0 -95
- package/cli/commands/sweep-design.mjs +0 -295
- package/cli/commands/sweep.mjs +0 -22
- package/cli/commands/topic-formatters.mjs +0 -382
- package/cli/commands/topic-goal.mjs +0 -33
- package/cli/commands/topic-options-shared.mjs +0 -27
- package/cli/commands/topic-options-workflow.mjs +0 -767
- package/cli/commands/topic-options.mjs +0 -626
- package/cli/commands/topic-runner.mjs +0 -169
- package/cli/commands/topic.mjs +0 -795
- package/cli/commands/validate-acceptance.mjs +0 -5
- package/cli/commands/validate-execution-packet.mjs +0 -5
- package/cli/commands/validate-orchestration-state.mjs +0 -5
- package/cli/commands/validate-prompt.mjs +0 -5
- package/cli/commands/validate-worker-output.mjs +0 -5
- package/cli/lib/adapter-profiles.mjs +0 -403
- package/cli/lib/audit-execution.mjs +0 -52
- package/cli/lib/audit-sweep-runtime/admissions.mjs +0 -508
- package/cli/lib/audit-sweep-runtime/audit-validity.mjs +0 -356
- package/cli/lib/audit-sweep-runtime/chunks.mjs +0 -697
- package/cli/lib/audit-sweep-runtime/claude-auditor.mjs +0 -658
- package/cli/lib/audit-sweep-runtime/closeout.mjs +0 -144
- package/cli/lib/audit-sweep-runtime/codex-auditor-evidence.mjs +0 -654
- package/cli/lib/audit-sweep-runtime/codex-auditor.mjs +0 -527
- package/cli/lib/audit-sweep-runtime/common.mjs +0 -341
- package/cli/lib/audit-sweep-runtime/coverage-quality.mjs +0 -172
- package/cli/lib/audit-sweep-runtime/evidence-assignment.mjs +0 -155
- package/cli/lib/audit-sweep-runtime/format.mjs +0 -57
- package/cli/lib/audit-sweep-runtime/ingest.mjs +0 -486
- package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +0 -425
- package/cli/lib/audit-sweep-runtime/inventory.mjs +0 -798
- package/cli/lib/audit-sweep-runtime/ledger.mjs +0 -315
- package/cli/lib/audit-sweep-runtime/p0p1-profile.mjs +0 -101
- package/cli/lib/audit-sweep-runtime/remediation.mjs +0 -349
- package/cli/lib/audit-sweep-runtime/rerun.mjs +0 -129
- package/cli/lib/audit-sweep-runtime/risk-budget.mjs +0 -300
- package/cli/lib/audit-sweep-runtime/status.mjs +0 -62
- package/cli/lib/audit-sweep-runtime/validators-ledger.mjs +0 -215
- package/cli/lib/audit-sweep-runtime/validators.mjs +0 -797
- package/cli/lib/audit-sweep.mjs +0 -19
- package/cli/lib/authority-convergence.mjs +0 -720
- package/cli/lib/closeout.mjs +0 -732
- package/cli/lib/codex-sdk-runner.mjs +0 -76
- package/cli/lib/external-execution.mjs +0 -101
- package/cli/lib/handoff.mjs +0 -812
- package/cli/lib/high-risk-admission.mjs +0 -512
- package/cli/lib/high-risk-decision.mjs +0 -353
- package/cli/lib/high-risk-ingest.mjs +0 -321
- package/cli/lib/high-risk-review.mjs +0 -267
- package/cli/lib/internal/contracts-parse-high-risk.mjs +0 -131
- package/cli/lib/internal/contracts-validators.mjs +0 -399
- package/cli/lib/internal/doctor-bootstrap-surface.mjs +0 -406
- package/cli/lib/internal/doctor-delegated-surface.mjs +0 -256
- package/cli/lib/internal/doctor-finalize.mjs +0 -383
- package/cli/lib/internal/doctor-format.mjs +0 -286
- package/cli/lib/internal/doctor-inspectors.mjs +0 -327
- package/cli/lib/internal/doctor-state.mjs +0 -205
- package/cli/lib/internal/validators-artifacts.mjs +0 -515
- package/cli/lib/sweep-design-runtime/common.mjs +0 -246
- package/cli/lib/sweep-design-runtime/engine.mjs +0 -733
- package/cli/lib/sweep-design-runtime/fix-topic.mjs +0 -414
- package/cli/lib/sweep-design-runtime/lifecycle.mjs +0 -54
- package/cli/lib/sweep-design-runtime/results.mjs +0 -324
- package/cli/lib/sweep-design.mjs +0 -8
- package/cli/lib/topic-artifacts.mjs +0 -186
- package/cli/lib/topic-authority-coverage.mjs +0 -73
- package/cli/lib/topic-closeout.mjs +0 -560
- package/cli/lib/topic-common.mjs +0 -404
- package/cli/lib/topic-decisions.mjs +0 -332
- package/cli/lib/topic-draft-packets.mjs +0 -167
- package/cli/lib/topic-execution.mjs +0 -533
- package/cli/lib/topic-goal.mjs +0 -440
- package/cli/lib/topic-ledger.mjs +0 -281
- package/cli/lib/topic-lifecycle-artifacts.mjs +0 -173
- package/cli/lib/topic-root-validation.mjs +0 -288
- package/cli/lib/topic-runner-commands.mjs +0 -174
- package/cli/lib/topic-runner-deferral.mjs +0 -532
- package/cli/lib/topic-runner-stale-gates.mjs +0 -114
- package/cli/lib/topic-runner-validation.mjs +0 -138
- package/cli/lib/topic-runner.mjs +0 -727
- package/cli/lib/topic-scaffold.mjs +0 -252
- package/cli/lib/topic-waves.mjs +0 -403
- package/cli/lib/topic.mjs +0 -81
- package/config/audit-execution-artifacts.yaml +0 -20
- package/config/external-execution-artifacts.yaml +0 -16
- package/config/host-adapter.yaml +0 -30
- package/config/host-profile.yaml +0 -29
- package/config/installer-evidence.yaml +0 -31
- package/config/skill-installer.yaml +0 -23
- package/config/skill-manifest.yaml +0 -48
- package/config/skills.yaml +0 -30
- package/contracts/acceptance.schema.yaml +0 -16
- package/contracts/admission-checklist.schema.yaml +0 -15
- package/contracts/audit-chunk.schema.yaml +0 -123
- package/contracts/audit-closeout.schema.yaml +0 -51
- package/contracts/audit-finding.schema.yaml +0 -61
- package/contracts/audit-ledger.schema.yaml +0 -138
- package/contracts/audit-plan.schema.yaml +0 -138
- package/contracts/audit-remediation-map.schema.yaml +0 -52
- package/contracts/audit-rerun.schema.yaml +0 -31
- package/contracts/audit-sweep-result.yaml +0 -53
- package/contracts/authority-convergence-audit.schema.yaml +0 -19
- package/contracts/closeout.schema.yaml +0 -25
- package/contracts/decision-review.schema.yaml +0 -16
- package/contracts/doc-spec-audit-result.yaml +0 -19
- package/contracts/execution-packet.schema.yaml +0 -49
- package/contracts/external-host-compatibility.yaml +0 -22
- package/contracts/forbidden-shortcuts.catalog.yaml +0 -23
- package/contracts/high-risk-admission.schema.yaml +0 -23
- package/contracts/high-risk-execution-result.yaml +0 -20
- package/contracts/orchestration-state.schema.yaml +0 -41
- package/contracts/overflow-continuation.schema.yaml +0 -12
- package/contracts/packet.schema.yaml +0 -30
- package/contracts/pending-note.schema.yaml +0 -17
- package/contracts/prompt.schema.yaml +0 -12
- package/contracts/remediation.schema.yaml +0 -16
- package/contracts/result.schema.yaml +0 -24
- package/contracts/spec-reconstruction-result.yaml +0 -41
- package/contracts/sweep-design-result.yaml +0 -349
- package/contracts/topic-goal.schema.yaml +0 -87
- package/contracts/topic-run-ledger.schema.yaml +0 -72
- package/contracts/topic-step-decision.schema.yaml +0 -45
- package/contracts/topic.schema.yaml +0 -65
- package/contracts/true-close.schema.yaml +0 -15
- package/contracts/wave.schema.yaml +0 -29
- package/contracts/worker-output.schema.yaml +0 -15
- package/contracts/workflow-consumer.schema.yaml +0 -110
- package/methodology/audit-sweep-p0p1-recall.yaml +0 -45
- package/methodology/authority-convergence-policy.yaml +0 -42
- package/methodology/overflow-continuation-policy.yaml +0 -14
- package/methodology/skill-exchange-projection.yaml +0 -114
- package/methodology/skill-handoff.yaml +0 -34
- package/methodology/skill-installer-result.yaml +0 -27
- package/methodology/skill-installer-summary-projection.yaml +0 -181
- package/methodology/skill-runtime.yaml +0 -23
- package/methodology/spec-target-truth-profile.yaml +0 -46
- package/methodology/topic-lifecycle-report.yaml +0 -144
- package/methodology/topic-lifecycle.yaml +0 -37
- package/methodology/topic-naming-ontology.yaml +0 -21
- package/methodology/topic-ontology.yaml +0 -38
- package/methodology/topic-validation-policy.yaml +0 -9
- package/methodology/wave-dag-policy.yaml +0 -14
- package/spec/_meta/command-gating-matrix.yaml +0 -143
- package/spec/_meta/generate-drift-migration-checklist.yaml +0 -137
- package/spec/_meta/governance-routing-cutover-checklist.yaml +0 -35
- package/spec/_meta/phase2-impacted-surface-matrix.yaml +0 -44
- package/spec/_meta/spec-authority-cutover-readiness.yaml +0 -102
- package/spec/bootstrap-state.yaml +0 -99
|
@@ -1,332 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildDispatchPrompt,
|
|
3
|
-
buildPostUpdateReviewDecision,
|
|
4
|
-
buildPreImplementationDecision,
|
|
5
|
-
loadAuthorityConvergencePolicy,
|
|
6
|
-
} from "./authority-convergence.mjs";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
|
|
9
|
-
import { findUniqueFreezableDraftPacket } from "./topic-draft-packets.mjs";
|
|
10
|
-
import { loadTopicRuntimeAuthority, toPortableRelativePath } from "./topic-common.mjs";
|
|
11
|
-
import { findDeterministicNextWave, getTopicWaves, loadTopicReport } from "./topic-scaffold.mjs";
|
|
12
|
-
import { validateTopicGraph, validateWaveAdmission } from "./topic-waves.mjs";
|
|
13
|
-
import { listWavePackets, listWaveResults } from "./topic-artifacts.mjs";
|
|
14
|
-
|
|
15
|
-
export function buildTopicStepDecision(topic, wave, values) {
|
|
16
|
-
const waveId = wave?.wave_id ?? null;
|
|
17
|
-
return {
|
|
18
|
-
decision_id: `${topic.topic_id}:${waveId ?? "topic"}:${values.reasonCode}`,
|
|
19
|
-
topic_id: topic.topic_id,
|
|
20
|
-
wave_id: waveId,
|
|
21
|
-
decision_kind: "topic_next_step",
|
|
22
|
-
stop_class: values.stopClass,
|
|
23
|
-
recommended_action: values.recommendedAction,
|
|
24
|
-
reason_code: values.reasonCode,
|
|
25
|
-
requires_human_confirmation: values.stopClass === "require_human_confirmation",
|
|
26
|
-
recommended_decision: values.recommendedDecision,
|
|
27
|
-
recommendation_rationale: values.recommendationRationale,
|
|
28
|
-
expected_artifacts: values.expectedArtifacts ?? [],
|
|
29
|
-
next_command_ref: values.nextCommandRef ?? null,
|
|
30
|
-
blocking_checks: values.blockingChecks ?? [],
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export function commandRef(parts) {
|
|
34
|
-
return ["nimicoding", "topic", ...parts].join(" ");
|
|
35
|
-
}
|
|
36
|
-
export async function buildDecisionForSelectedWave(projectRoot, loaded, graphReport, wave) {
|
|
37
|
-
const failedGraphChecks = (graphReport.checks ?? []).filter((entry) => !entry.ok);
|
|
38
|
-
if (!graphReport.ok)
|
|
39
|
-
return buildTopicStepDecision(loaded.topic, wave, {
|
|
40
|
-
stopClass: "blocked",
|
|
41
|
-
recommendedAction: "no_action",
|
|
42
|
-
reasonCode: "topic_graph_validation_failed",
|
|
43
|
-
recommendedDecision: "fix_topic_graph_before_continuing",
|
|
44
|
-
recommendationRationale:
|
|
45
|
-
"The wave graph is the dispatch authority for topic execution and must validate before any next step is emitted.",
|
|
46
|
-
blockingChecks: failedGraphChecks,
|
|
47
|
-
});
|
|
48
|
-
if (["closed", "retired", "superseded"].includes(wave.state))
|
|
49
|
-
return buildTopicStepDecision(loaded.topic, wave, {
|
|
50
|
-
stopClass: "blocked",
|
|
51
|
-
recommendedAction: "no_action",
|
|
52
|
-
reasonCode: "selected_wave_is_terminal",
|
|
53
|
-
recommendedDecision: "select_a_non_terminal_wave_or_close_the_topic",
|
|
54
|
-
recommendationRationale: `Selected wave ${wave.wave_id} is ${wave.state} and cannot be dispatched.`,
|
|
55
|
-
});
|
|
56
|
-
if (wave.state === "needs_revision")
|
|
57
|
-
return buildTopicStepDecision(loaded.topic, wave, {
|
|
58
|
-
stopClass: "blocked",
|
|
59
|
-
recommendedAction: "open_remediation",
|
|
60
|
-
reasonCode: "revise",
|
|
61
|
-
recommendedDecision: "fix",
|
|
62
|
-
recommendationRationale: "Block.",
|
|
63
|
-
});
|
|
64
|
-
if (["candidate", "preflight_draft"].includes(wave.state)) {
|
|
65
|
-
const admission = await validateWaveAdmission(projectRoot, loaded.topicId, wave.wave_id);
|
|
66
|
-
return admission.ok
|
|
67
|
-
? buildTopicStepDecision(loaded.topic, wave, {
|
|
68
|
-
stopClass: "continue",
|
|
69
|
-
recommendedAction: "admit_wave",
|
|
70
|
-
reasonCode: "wave_admission_ready",
|
|
71
|
-
recommendedDecision: "admit_wave",
|
|
72
|
-
recommendationRationale: "Admission is mechanical.",
|
|
73
|
-
nextCommandRef: commandRef(["wave", "admit", loaded.topicId, wave.wave_id]),
|
|
74
|
-
})
|
|
75
|
-
: buildTopicStepDecision(loaded.topic, wave, {
|
|
76
|
-
stopClass: "blocked",
|
|
77
|
-
recommendedAction: "admit_wave",
|
|
78
|
-
reasonCode: "wave_admission_validation_failed",
|
|
79
|
-
recommendedDecision: "repair_admission_blockers",
|
|
80
|
-
recommendationRationale:
|
|
81
|
-
"The selected wave cannot be admitted until its admission checks pass.",
|
|
82
|
-
nextCommandRef: commandRef([
|
|
83
|
-
"validate",
|
|
84
|
-
"admission",
|
|
85
|
-
loaded.topicId,
|
|
86
|
-
wave.wave_id,
|
|
87
|
-
"--json",
|
|
88
|
-
]),
|
|
89
|
-
blockingChecks: (admission.checks ?? []).filter((entry) => !entry.ok),
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
if (
|
|
93
|
-
["preflight_admitted", "implementation_admitted", "continuation_packet_open"].includes(
|
|
94
|
-
wave.state,
|
|
95
|
-
)
|
|
96
|
-
)
|
|
97
|
-
return buildTopicStepDecision(
|
|
98
|
-
loaded.topic,
|
|
99
|
-
wave,
|
|
100
|
-
await buildPreImplementationDecision({
|
|
101
|
-
projectRoot,
|
|
102
|
-
loaded,
|
|
103
|
-
wave,
|
|
104
|
-
commandRef,
|
|
105
|
-
listWavePackets,
|
|
106
|
-
listWaveResults,
|
|
107
|
-
findUniqueFreezableDraftPacket,
|
|
108
|
-
loadTopicRuntimeAuthority,
|
|
109
|
-
}),
|
|
110
|
-
);
|
|
111
|
-
if (wave.state === "implementation_active") {
|
|
112
|
-
const waveResults = await listWaveResults(loaded.topicDir, wave.wave_id),
|
|
113
|
-
implementationResult = waveResults.find(
|
|
114
|
-
(entry) => entry.result?.result_kind === "implementation",
|
|
115
|
-
);
|
|
116
|
-
if (!implementationResult)
|
|
117
|
-
return buildTopicStepDecision(loaded.topic, wave, {
|
|
118
|
-
stopClass: "await_external_evidence",
|
|
119
|
-
recommendedAction: "record_result",
|
|
120
|
-
reasonCode: "awaiting_implementation_result",
|
|
121
|
-
recommendedDecision: "ingest_the_implementation_result_when_available",
|
|
122
|
-
recommendationRationale:
|
|
123
|
-
"The wave is active, but closeout requires explicit implementation result lineage.",
|
|
124
|
-
expectedArtifacts: ["result-<wave-id>-implementation.md"],
|
|
125
|
-
nextCommandRef: commandRef([
|
|
126
|
-
"result",
|
|
127
|
-
"record",
|
|
128
|
-
loaded.topicId,
|
|
129
|
-
"--kind",
|
|
130
|
-
"implementation",
|
|
131
|
-
"--verdict",
|
|
132
|
-
"<verdict>",
|
|
133
|
-
"--from",
|
|
134
|
-
"<path>",
|
|
135
|
-
"--verified-at",
|
|
136
|
-
"<utc>",
|
|
137
|
-
]),
|
|
138
|
-
});
|
|
139
|
-
const postUpdateReviewDecision = await buildPostUpdateReviewDecision({
|
|
140
|
-
projectRoot,
|
|
141
|
-
topicDir: loaded.topicDir,
|
|
142
|
-
topicId: loaded.topicId,
|
|
143
|
-
wave,
|
|
144
|
-
packets: await listWavePackets(loaded.topicDir, wave.wave_id),
|
|
145
|
-
results: waveResults,
|
|
146
|
-
policy: await loadAuthorityConvergencePolicy(projectRoot),
|
|
147
|
-
commandRef,
|
|
148
|
-
});
|
|
149
|
-
return postUpdateReviewDecision
|
|
150
|
-
? buildTopicStepDecision(loaded.topic, wave, postUpdateReviewDecision)
|
|
151
|
-
: buildTopicStepDecision(loaded.topic, wave, {
|
|
152
|
-
stopClass: "continue",
|
|
153
|
-
recommendedAction: "closeout_wave",
|
|
154
|
-
reasonCode: "wave_has_result_lineage_ready_for_closeout",
|
|
155
|
-
recommendedDecision: "closeout_wave",
|
|
156
|
-
recommendationRationale:
|
|
157
|
-
"Wave closeout is a deterministic phase transition once lineage-backed result evidence exists.",
|
|
158
|
-
nextCommandRef: commandRef([
|
|
159
|
-
"closeout",
|
|
160
|
-
"wave",
|
|
161
|
-
loaded.topicId,
|
|
162
|
-
wave.wave_id,
|
|
163
|
-
"--authority",
|
|
164
|
-
"closed",
|
|
165
|
-
"--semantic",
|
|
166
|
-
"closed",
|
|
167
|
-
"--consumer",
|
|
168
|
-
"closed",
|
|
169
|
-
"--drift-resistance",
|
|
170
|
-
"closed",
|
|
171
|
-
"--disposition",
|
|
172
|
-
"complete",
|
|
173
|
-
]),
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
return wave.state === "overflowed"
|
|
177
|
-
? buildTopicStepDecision(loaded.topic, wave, {
|
|
178
|
-
stopClass: "require_human_confirmation",
|
|
179
|
-
recommendedAction: "continue_overflow",
|
|
180
|
-
reasonCode: "overflow_requires_manager_judgement",
|
|
181
|
-
recommendedDecision: "approve_continuation_only_if_the_same_owner_domain_rule_still_holds",
|
|
182
|
-
recommendationRationale:
|
|
183
|
-
"Overflow is not pass or rollback; continuation requires explicit manager judgement.",
|
|
184
|
-
nextCommandRef: commandRef([
|
|
185
|
-
"overflow",
|
|
186
|
-
"continue",
|
|
187
|
-
loaded.topicId,
|
|
188
|
-
"--packet",
|
|
189
|
-
"<continuation-packet-id>",
|
|
190
|
-
"--overflowed-packet",
|
|
191
|
-
"<packet-id>",
|
|
192
|
-
"--manager-judgement",
|
|
193
|
-
"<text>",
|
|
194
|
-
"--same-owner-domain",
|
|
195
|
-
]),
|
|
196
|
-
})
|
|
197
|
-
: buildTopicStepDecision(loaded.topic, wave, {
|
|
198
|
-
stopClass: "blocked",
|
|
199
|
-
recommendedAction: "no_action",
|
|
200
|
-
reasonCode: "unsupported_wave_state",
|
|
201
|
-
recommendedDecision: "repair_or_review_the_selected_wave_state",
|
|
202
|
-
recommendationRationale: `No next-step rule is defined for wave state ${wave.state}.`,
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
export async function decideTopicNextStep(projectRoot, input = null) {
|
|
206
|
-
const loaded = await loadTopicReport(projectRoot, input);
|
|
207
|
-
if (!loaded.ok) return loaded;
|
|
208
|
-
const { validateTopicRoot } = await import("./topic-root-validation.mjs");
|
|
209
|
-
const rootValidation = await validateTopicRoot(projectRoot, input),
|
|
210
|
-
selectedWave =
|
|
211
|
-
getTopicWaves(loaded.topic).find(
|
|
212
|
-
(entry) => entry.wave_id === loaded.topic.selected_next_target,
|
|
213
|
-
) ?? null;
|
|
214
|
-
if (!rootValidation.ok)
|
|
215
|
-
return {
|
|
216
|
-
ok: true,
|
|
217
|
-
topicId: loaded.topicId,
|
|
218
|
-
topicRef: toPortableRelativePath(path.relative(projectRoot, loaded.topicDir)),
|
|
219
|
-
decision: buildTopicStepDecision(loaded.topic, selectedWave, {
|
|
220
|
-
stopClass: "blocked",
|
|
221
|
-
recommendedAction: "no_action",
|
|
222
|
-
reasonCode: "topic_root_validation_failed",
|
|
223
|
-
recommendedDecision: "repair_topic_root_before_continuing",
|
|
224
|
-
recommendationRationale:
|
|
225
|
-
"The topic root must validate before a next execution step can be selected.",
|
|
226
|
-
blockingChecks: (rootValidation.checks ?? []).filter((entry) => !entry.ok),
|
|
227
|
-
}),
|
|
228
|
-
};
|
|
229
|
-
if (loaded.topic.state === "pending")
|
|
230
|
-
return {
|
|
231
|
-
ok: true,
|
|
232
|
-
topicId: loaded.topicId,
|
|
233
|
-
topicRef: toPortableRelativePath(path.relative(projectRoot, loaded.topicDir)),
|
|
234
|
-
decision: buildTopicStepDecision(loaded.topic, selectedWave, {
|
|
235
|
-
stopClass: "await_external_evidence",
|
|
236
|
-
recommendedAction: "resume_topic",
|
|
237
|
-
reasonCode: "topic_pending_wait",
|
|
238
|
-
recommendedDecision: "resume_only_when_the_pending_note_reopen_criteria_are_met",
|
|
239
|
-
recommendationRationale:
|
|
240
|
-
"Pending is an explicit wait state and must not be bypassed by the loop.",
|
|
241
|
-
nextCommandRef: commandRef(["resume", loaded.topicId, "--criteria-met", "<text>"]),
|
|
242
|
-
}),
|
|
243
|
-
};
|
|
244
|
-
if (
|
|
245
|
-
!loaded.topic.selected_next_target ||
|
|
246
|
-
loaded.topic.selected_next_target === "topic_design_baseline"
|
|
247
|
-
) {
|
|
248
|
-
const allWavesTerminal =
|
|
249
|
-
getTopicWaves(loaded.topic).filter(
|
|
250
|
-
(entry) => !["closed", "retired", "superseded"].includes(entry.state),
|
|
251
|
-
).length === 0 && getTopicWaves(loaded.topic).length > 0,
|
|
252
|
-
deterministicNextWave = allWavesTerminal ? null : findDeterministicNextWave(loaded.topic);
|
|
253
|
-
return {
|
|
254
|
-
ok: true,
|
|
255
|
-
topicId: loaded.topicId,
|
|
256
|
-
topicRef: toPortableRelativePath(path.relative(projectRoot, loaded.topicDir)),
|
|
257
|
-
decision: buildTopicStepDecision(
|
|
258
|
-
loaded.topic,
|
|
259
|
-
deterministicNextWave,
|
|
260
|
-
allWavesTerminal
|
|
261
|
-
? {
|
|
262
|
-
stopClass: "completed",
|
|
263
|
-
recommendedAction: "closeout_topic",
|
|
264
|
-
reasonCode: "all_waves_terminal",
|
|
265
|
-
recommendedDecision: "run_topic_closeout_and_true_close_checks",
|
|
266
|
-
recommendationRationale:
|
|
267
|
-
"All waves are terminal, so the next step is topic-level closeout review.",
|
|
268
|
-
nextCommandRef: commandRef([
|
|
269
|
-
"closeout",
|
|
270
|
-
"topic",
|
|
271
|
-
loaded.topicId,
|
|
272
|
-
"--authority",
|
|
273
|
-
"closed",
|
|
274
|
-
"--semantic",
|
|
275
|
-
"closed",
|
|
276
|
-
"--consumer",
|
|
277
|
-
"closed",
|
|
278
|
-
"--drift-resistance",
|
|
279
|
-
"closed",
|
|
280
|
-
"--disposition",
|
|
281
|
-
"complete",
|
|
282
|
-
]),
|
|
283
|
-
}
|
|
284
|
-
: deterministicNextWave
|
|
285
|
-
? {
|
|
286
|
-
stopClass: "continue",
|
|
287
|
-
recommendedAction: "admit_wave",
|
|
288
|
-
reasonCode: "deterministic_next_wave_ready",
|
|
289
|
-
recommendedDecision: "admit_wave",
|
|
290
|
-
recommendationRationale:
|
|
291
|
-
"The first dependency-ready non-terminal wave in topic.yaml waves[] order is selected mechanically.",
|
|
292
|
-
nextCommandRef: commandRef([
|
|
293
|
-
"wave",
|
|
294
|
-
"admit",
|
|
295
|
-
loaded.topicId,
|
|
296
|
-
deterministicNextWave.wave_id,
|
|
297
|
-
]),
|
|
298
|
-
}
|
|
299
|
-
: {
|
|
300
|
-
stopClass: "require_human_confirmation",
|
|
301
|
-
recommendedAction: "admit_wave",
|
|
302
|
-
reasonCode: "no_selected_next_target",
|
|
303
|
-
recommendedDecision: "select_the_next_wave_or_hold_the_topic",
|
|
304
|
-
recommendationRationale:
|
|
305
|
-
"The loop cannot choose among possible next waves without manager judgement.",
|
|
306
|
-
nextCommandRef: commandRef(["wave", "select", loaded.topicId, "<wave-id>"]),
|
|
307
|
-
},
|
|
308
|
-
),
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
if (!selectedWave)
|
|
312
|
-
return {
|
|
313
|
-
ok: true,
|
|
314
|
-
topicId: loaded.topicId,
|
|
315
|
-
topicRef: toPortableRelativePath(path.relative(projectRoot, loaded.topicDir)),
|
|
316
|
-
decision: buildTopicStepDecision(loaded.topic, null, {
|
|
317
|
-
stopClass: "blocked",
|
|
318
|
-
recommendedAction: "no_action",
|
|
319
|
-
reasonCode: "selected_next_target_does_not_resolve",
|
|
320
|
-
recommendedDecision: "repair_topic_selected_next_target",
|
|
321
|
-
recommendationRationale: `selected_next_target does not resolve to a declared wave: ${loaded.topic.selected_next_target}`,
|
|
322
|
-
}),
|
|
323
|
-
};
|
|
324
|
-
const graphReport = await validateTopicGraph(projectRoot, input);
|
|
325
|
-
return {
|
|
326
|
-
ok: true,
|
|
327
|
-
topicId: loaded.topicId,
|
|
328
|
-
topicRef: toPortableRelativePath(path.relative(projectRoot, loaded.topicDir)),
|
|
329
|
-
decision: await buildDecisionForSelectedWave(projectRoot, loaded, graphReport, selectedWave),
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
const RUN_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { readdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import YAML from "yaml";
|
|
4
|
-
|
|
5
|
-
import { readTextIfFile } from "./fs-helpers.mjs";
|
|
6
|
-
import {
|
|
7
|
-
packetAuthorityCoverage,
|
|
8
|
-
packetAuthorityCoverageError,
|
|
9
|
-
sourceSweepDesignAuthorityRefs,
|
|
10
|
-
} from "./topic-authority-coverage.mjs";
|
|
11
|
-
import { parseYamlText } from "./yaml-helpers.mjs";
|
|
12
|
-
|
|
13
|
-
function toPortableRelativePath(filePath) {
|
|
14
|
-
return filePath.split(path.sep).join("/");
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function parsePacketDraft(text) {
|
|
18
|
-
if (!text) return null;
|
|
19
|
-
if (text.startsWith("---\n")) {
|
|
20
|
-
const closing = text.indexOf("\n---\n", 4);
|
|
21
|
-
if (closing !== -1) {
|
|
22
|
-
return parseYamlText(text.slice(4, closing));
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return parseYamlText(text);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function nonEmptyStringArray(value) {
|
|
29
|
-
return Array.isArray(value)
|
|
30
|
-
? value.filter((entry) => typeof entry === "string" && entry.length > 0)
|
|
31
|
-
: [];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function singleStringList(value) {
|
|
35
|
-
return typeof value === "string" && value.length > 0 ? [value] : nonEmptyStringArray(value);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function missingSweepDraftFields(wave) {
|
|
39
|
-
const source = wave?.source_sweep_design;
|
|
40
|
-
if (!source || typeof source !== "object") {
|
|
41
|
-
return ["source_sweep_design"];
|
|
42
|
-
}
|
|
43
|
-
const missing = [];
|
|
44
|
-
if (singleStringList(source.authority_owner).length === 0) missing.push("source_sweep_design.authority_owner");
|
|
45
|
-
if (nonEmptyStringArray(source.validation_commands).length === 0) missing.push("source_sweep_design.validation_commands");
|
|
46
|
-
if (nonEmptyStringArray(source.negative_checks).length === 0) missing.push("source_sweep_design.negative_checks");
|
|
47
|
-
if (nonEmptyStringArray(source.closeout_criteria).length === 0) missing.push("source_sweep_design.closeout_criteria");
|
|
48
|
-
if (nonEmptyStringArray(source.source_design_packet_refs).length === 0) missing.push("source_sweep_design.source_design_packet_refs");
|
|
49
|
-
if (nonEmptyStringArray(source.design_auditor_result_refs).length === 0) missing.push("source_sweep_design.design_auditor_result_refs");
|
|
50
|
-
if (nonEmptyStringArray(source.revision_ledger_entry_refs).length === 0) missing.push("source_sweep_design.revision_ledger_entry_refs");
|
|
51
|
-
if (nonEmptyStringArray(source.blocked_gate_refs).length > 0) missing.push("source_sweep_design.blocked_gate_refs_empty");
|
|
52
|
-
return missing;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function maybeGenerateSweepFixDraft(projectRoot, loaded, wave) {
|
|
56
|
-
const missing = missingSweepDraftFields(wave);
|
|
57
|
-
if (missing.length > 0) {
|
|
58
|
-
return {
|
|
59
|
-
ok: false,
|
|
60
|
-
reasonCode: missing.includes("source_sweep_design.blocked_gate_refs_empty")
|
|
61
|
-
? "admitted_wave_has_blocked_gate_refs"
|
|
62
|
-
: missing.includes("source_sweep_design.validation_commands")
|
|
63
|
-
? "admitted_wave_missing_validation_commands"
|
|
64
|
-
: "admitted_wave_requires_packet",
|
|
65
|
-
missing,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const draftPath = path.join(loaded.topicDir, `draft-${wave.wave_id}-implementation.yaml`);
|
|
70
|
-
if (await readTextIfFile(draftPath) !== null) {
|
|
71
|
-
return {
|
|
72
|
-
ok: false,
|
|
73
|
-
reasonCode: "admitted_wave_has_ambiguous_draft_packets",
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const source = wave.source_sweep_design;
|
|
78
|
-
const sourceAuthorityRefs = sourceSweepDesignAuthorityRefs(source);
|
|
79
|
-
const packet = {
|
|
80
|
-
packet_id: `${wave.wave_id}-implementation`,
|
|
81
|
-
topic_id: loaded.topicId,
|
|
82
|
-
wave_id: wave.wave_id,
|
|
83
|
-
packet_kind: "implementation",
|
|
84
|
-
status: "draft",
|
|
85
|
-
authority_owner: sourceAuthorityRefs.length > 0 ? sourceAuthorityRefs : singleStringList(source.authority_owner),
|
|
86
|
-
canonical_seams: [
|
|
87
|
-
...(sourceAuthorityRefs.length > 0 ? sourceAuthorityRefs : singleStringList(source.authority_owner)),
|
|
88
|
-
...nonEmptyStringArray(source.source_design_packet_refs),
|
|
89
|
-
...nonEmptyStringArray(source.design_auditor_result_refs),
|
|
90
|
-
...nonEmptyStringArray(source.revision_ledger_entry_refs),
|
|
91
|
-
],
|
|
92
|
-
forbidden_shortcuts: nonEmptyStringArray(loaded.topic?.forbidden_shortcuts),
|
|
93
|
-
acceptance_invariants: [
|
|
94
|
-
...nonEmptyStringArray(source.closeout_criteria),
|
|
95
|
-
...nonEmptyStringArray(source.validation_commands).map((command) => `validation command required: ${command}`),
|
|
96
|
-
],
|
|
97
|
-
negative_tests: nonEmptyStringArray(source.negative_checks),
|
|
98
|
-
reopen_conditions: [
|
|
99
|
-
...nonEmptyStringArray(source.drift_resistance_checks),
|
|
100
|
-
"source_sweep_design provenance changes require packet regeneration",
|
|
101
|
-
],
|
|
102
|
-
source_sweep_design_run_id: source.run_id ?? null,
|
|
103
|
-
source_authority_refs: sourceAuthorityRefs,
|
|
104
|
-
source_authority_coverage_policy: "authority_owner_and_canonical_seams_cover_union_of_source_sweep_design_authority_refs",
|
|
105
|
-
source_design_packet_refs: nonEmptyStringArray(source.source_design_packet_refs),
|
|
106
|
-
design_auditor_result_refs: nonEmptyStringArray(source.design_auditor_result_refs),
|
|
107
|
-
revision_ledger_entry_refs: nonEmptyStringArray(source.revision_ledger_entry_refs),
|
|
108
|
-
validation_commands: nonEmptyStringArray(source.validation_commands),
|
|
109
|
-
closeout_criteria: nonEmptyStringArray(source.closeout_criteria),
|
|
110
|
-
};
|
|
111
|
-
if (packet.forbidden_shortcuts.length === 0) {
|
|
112
|
-
return {
|
|
113
|
-
ok: false,
|
|
114
|
-
reasonCode: "admitted_wave_requires_packet",
|
|
115
|
-
missing: ["topic.forbidden_shortcuts"],
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
await writeFile(draftPath, YAML.stringify(packet), "utf8");
|
|
119
|
-
return {
|
|
120
|
-
ok: true,
|
|
121
|
-
packet,
|
|
122
|
-
draftRef: toPortableRelativePath(path.relative(projectRoot, draftPath)),
|
|
123
|
-
generated: true,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export async function findUniqueFreezableDraftPacket(projectRoot, loaded, wave, authority) {
|
|
128
|
-
const matches = [];
|
|
129
|
-
for (const entry of await readdir(loaded.topicDir, { withFileTypes: true })) {
|
|
130
|
-
if (!entry.isFile() || !/^draft.*\.(ya?ml|md)$/u.test(entry.name.toLowerCase())) continue;
|
|
131
|
-
const draftPath = path.join(loaded.topicDir, entry.name);
|
|
132
|
-
const packet = parsePacketDraft(await readTextIfFile(draftPath) ?? "");
|
|
133
|
-
if (!packet || typeof packet !== "object") continue;
|
|
134
|
-
if (packet.topic_id !== loaded.topicId || packet.wave_id !== wave.wave_id) continue;
|
|
135
|
-
if (!authority.packetFreezeAllowedStatuses.includes(packet.status)) continue;
|
|
136
|
-
if (authority.packetRequiredFields.some((field) => {
|
|
137
|
-
const value = packet[field];
|
|
138
|
-
return value == null || value === "" || (Array.isArray(value) && value.length === 0);
|
|
139
|
-
})) continue;
|
|
140
|
-
const coverage = packetAuthorityCoverage(packet, wave);
|
|
141
|
-
if (!coverage.ok) {
|
|
142
|
-
return {
|
|
143
|
-
ok: false,
|
|
144
|
-
reasonCode: "admitted_wave_packet_authority_coverage_incomplete",
|
|
145
|
-
missing: [
|
|
146
|
-
...coverage.missingAuthorityOwnerRefs.map((ref) => `authority_owner:${ref}`),
|
|
147
|
-
...coverage.missingCanonicalSeamRefs.map((ref) => `canonical_seams:${ref}`),
|
|
148
|
-
],
|
|
149
|
-
error: packetAuthorityCoverageError(coverage),
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
matches.push({
|
|
153
|
-
packet,
|
|
154
|
-
draftRef: toPortableRelativePath(path.relative(projectRoot, draftPath)),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
if (matches.length === 1) {
|
|
158
|
-
return { ok: true, ...matches[0] };
|
|
159
|
-
}
|
|
160
|
-
if (matches.length > 1) {
|
|
161
|
-
return {
|
|
162
|
-
ok: false,
|
|
163
|
-
reasonCode: "admitted_wave_has_ambiguous_draft_packets",
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
return maybeGenerateSweepFixDraft(projectRoot, loaded, wave);
|
|
167
|
-
}
|