cclaw-cli 0.55.2 → 1.0.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/README.md +3 -3
- package/dist/artifact-linter/brainstorm.js +45 -1
- package/dist/artifact-linter/design.js +32 -1
- package/dist/artifact-linter/plan.js +22 -1
- package/dist/artifact-linter/review.js +35 -1
- package/dist/artifact-linter/scope.js +19 -9
- package/dist/artifact-linter/shared.d.ts +11 -10
- package/dist/artifact-linter/shared.js +70 -41
- package/dist/artifact-linter/ship.js +36 -0
- package/dist/artifact-linter/spec.js +23 -1
- package/dist/artifact-linter/tdd.js +74 -0
- package/dist/artifact-linter.d.ts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -0
- package/dist/content/closeout-guidance.d.ts +1 -1
- package/dist/content/closeout-guidance.js +10 -11
- package/dist/content/core-agents.d.ts +35 -36
- package/dist/content/core-agents.js +189 -99
- package/dist/content/diff-command.js +1 -1
- package/dist/content/examples.d.ts +0 -3
- package/dist/content/examples.js +197 -752
- package/dist/content/idea.d.ts +60 -0
- package/dist/content/idea.js +404 -0
- package/dist/content/learnings.d.ts +2 -4
- package/dist/content/learnings.js +10 -26
- package/dist/content/node-hooks.js +131 -97
- package/dist/content/opencode-plugin.js +12 -26
- package/dist/content/reference-patterns.js +2 -2
- package/dist/content/runtime-shared-snippets.d.ts +8 -0
- package/dist/content/runtime-shared-snippets.js +80 -0
- package/dist/content/session-hooks.js +1 -1
- package/dist/content/skills.d.ts +1 -0
- package/dist/content/skills.js +50 -0
- package/dist/content/stage-schema.js +107 -63
- package/dist/content/stages/review.js +8 -8
- package/dist/content/stages/schema-types.d.ts +2 -2
- package/dist/content/stages/scope.js +1 -1
- package/dist/content/stages/ship.js +1 -1
- package/dist/content/status-command.js +3 -3
- package/dist/content/subagent-context-skills.js +156 -1
- package/dist/content/subagents.d.ts +0 -5
- package/dist/content/subagents.js +12 -82
- package/dist/content/templates.js +87 -6
- package/dist/content/utility-skills.js +26 -97
- package/dist/flow-state.d.ts +5 -6
- package/dist/flow-state.js +4 -6
- package/dist/gate-evidence.d.ts +0 -31
- package/dist/gate-evidence.js +3 -181
- package/dist/harness-adapters.js +1 -1
- package/dist/install.js +38 -4
- package/dist/internal/advance-stage/advance.js +0 -1
- package/dist/internal/advance-stage/review-loop.js +1 -10
- package/dist/knowledge-store.d.ts +2 -20
- package/dist/knowledge-store.js +43 -57
- package/dist/policy.js +3 -3
- package/dist/retro-gate.js +8 -90
- package/dist/run-archive.js +1 -4
- package/dist/run-persistence.js +14 -109
- package/dist/runtime/run-hook.entry.d.ts +3 -0
- package/dist/runtime/run-hook.entry.js +5 -0
- package/dist/runtime/run-hook.mjs +9477 -0
- package/package.json +4 -2
- package/dist/content/hook-inline-snippets.d.ts +0 -96
- package/dist/content/hook-inline-snippets.js +0 -515
- package/dist/content/idea-command.d.ts +0 -8
- package/dist/content/idea-command.js +0 -322
- package/dist/content/idea-frames.d.ts +0 -31
- package/dist/content/idea-frames.js +0 -140
- package/dist/content/idea-ranking.d.ts +0 -25
- package/dist/content/idea-ranking.js +0 -65
- package/dist/trace-matrix.d.ts +0 -27
- package/dist/trace-matrix.js +0 -226
package/dist/gate-evidence.js
CHANGED
|
@@ -6,14 +6,12 @@ import { readConfig } from "./config.js";
|
|
|
6
6
|
import { RUNTIME_ROOT } from "./constants.js";
|
|
7
7
|
import { stageSchema } from "./content/stage-schema.js";
|
|
8
8
|
import { readDelegationLedger } from "./delegation.js";
|
|
9
|
-
import {
|
|
9
|
+
import { exists } from "./fs-utils.js";
|
|
10
10
|
import { computeEarlyLoopStatus, isEarlyLoopStage, normalizeEarlyLoopMaxIterations } from "./early-loop.js";
|
|
11
11
|
import { detectPublicApiChanges } from "./internal/detect-public-api-changes.js";
|
|
12
12
|
import { readFlowState, writeFlowState } from "./runs.js";
|
|
13
13
|
import { parseTddCycleLog, validateTddCycleOrder } from "./tdd-cycle.js";
|
|
14
14
|
import { validateTddVerificationEvidence } from "./tdd-verification-evidence.js";
|
|
15
|
-
import { buildTraceMatrix } from "./trace-matrix.js";
|
|
16
|
-
import { FLOW_STAGES } from "./types.js";
|
|
17
15
|
async function currentStageArtifactExists(projectRoot, stage, track) {
|
|
18
16
|
const resolved = await resolveArtifactPath(stage, {
|
|
19
17
|
projectRoot,
|
|
@@ -107,8 +105,7 @@ async function discoverRealTestCommands(projectRoot) {
|
|
|
107
105
|
return unique(commands);
|
|
108
106
|
}
|
|
109
107
|
async function verifyDiscoveredCommandEvidence(projectRoot, stage, gateId, flowState) {
|
|
110
|
-
if (!(stage === "tdd" && gateId === "tdd_verified_before_complete")
|
|
111
|
-
!(stage === "review" && gateId === "review_trace_matrix_clean")) {
|
|
108
|
+
if (!(stage === "tdd" && gateId === "tdd_verified_before_complete")) {
|
|
112
109
|
return null;
|
|
113
110
|
}
|
|
114
111
|
const commands = await discoverRealTestCommands(projectRoot);
|
|
@@ -207,8 +204,6 @@ async function readEarlyLoopGateSnapshot(projectRoot, flowState) {
|
|
|
207
204
|
};
|
|
208
205
|
}
|
|
209
206
|
}
|
|
210
|
-
const RECONCILIATION_NOTICES_FILE = "reconciliation-notices.json";
|
|
211
|
-
const RECONCILIATION_NOTICES_SCHEMA_VERSION = 1;
|
|
212
207
|
const DESIGN_RESEARCH_REQUIRED_SECTIONS = [
|
|
213
208
|
"Stack Analysis",
|
|
214
209
|
"Features & Patterns",
|
|
@@ -216,123 +211,6 @@ const DESIGN_RESEARCH_REQUIRED_SECTIONS = [
|
|
|
216
211
|
"Pitfalls & Risks",
|
|
217
212
|
"Synthesis"
|
|
218
213
|
];
|
|
219
|
-
export const RECONCILIATION_NOTICES_REL_PATH = `${RUNTIME_ROOT}/state/${RECONCILIATION_NOTICES_FILE}`;
|
|
220
|
-
function isFlowStageValue(value) {
|
|
221
|
-
return typeof value === "string" && FLOW_STAGES.includes(value);
|
|
222
|
-
}
|
|
223
|
-
function reconciliationNoticesPath(projectRoot) {
|
|
224
|
-
return path.join(projectRoot, RUNTIME_ROOT, "state", RECONCILIATION_NOTICES_FILE);
|
|
225
|
-
}
|
|
226
|
-
function defaultReconciliationNoticesPayload() {
|
|
227
|
-
return {
|
|
228
|
-
schemaVersion: RECONCILIATION_NOTICES_SCHEMA_VERSION,
|
|
229
|
-
notices: [],
|
|
230
|
-
parseOk: true,
|
|
231
|
-
schemaOk: true
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
function sanitizeReconciliationNotice(raw) {
|
|
235
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
const typed = raw;
|
|
239
|
-
if (typeof typed.id !== "string" ||
|
|
240
|
-
typeof typed.runId !== "string" ||
|
|
241
|
-
!isFlowStageValue(typed.stage) ||
|
|
242
|
-
typeof typed.gateId !== "string" ||
|
|
243
|
-
typeof typed.reason !== "string" ||
|
|
244
|
-
typeof typed.demotedAt !== "string") {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
const kind = typed.kind === "closeout_substate_demotion"
|
|
248
|
-
? "closeout_substate_demotion"
|
|
249
|
-
: "gate_demotion";
|
|
250
|
-
let payload;
|
|
251
|
-
if (kind === "closeout_substate_demotion" && typed.payload && typeof typed.payload === "object" && !Array.isArray(typed.payload)) {
|
|
252
|
-
const payloadTyped = typed.payload;
|
|
253
|
-
if (typeof payloadTyped.previous === "string" &&
|
|
254
|
-
typeof payloadTyped.next === "string" &&
|
|
255
|
-
typeof payloadTyped.reason === "string") {
|
|
256
|
-
payload = {
|
|
257
|
-
previous: payloadTyped.previous,
|
|
258
|
-
next: payloadTyped.next,
|
|
259
|
-
reason: payloadTyped.reason
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return {
|
|
264
|
-
id: typed.id,
|
|
265
|
-
runId: typed.runId,
|
|
266
|
-
stage: typed.stage,
|
|
267
|
-
gateId: typed.gateId,
|
|
268
|
-
reason: typed.reason,
|
|
269
|
-
demotedAt: typed.demotedAt,
|
|
270
|
-
kind,
|
|
271
|
-
payload
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
export async function readReconciliationNotices(projectRoot) {
|
|
275
|
-
const filePath = reconciliationNoticesPath(projectRoot);
|
|
276
|
-
if (!(await exists(filePath))) {
|
|
277
|
-
return defaultReconciliationNoticesPayload();
|
|
278
|
-
}
|
|
279
|
-
try {
|
|
280
|
-
const raw = JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
281
|
-
const schemaOk = raw.schemaVersion === RECONCILIATION_NOTICES_SCHEMA_VERSION;
|
|
282
|
-
const notices = Array.isArray(raw.notices)
|
|
283
|
-
? raw.notices
|
|
284
|
-
.map((value) => sanitizeReconciliationNotice(value))
|
|
285
|
-
.filter((value) => value !== null)
|
|
286
|
-
: [];
|
|
287
|
-
return {
|
|
288
|
-
schemaVersion: RECONCILIATION_NOTICES_SCHEMA_VERSION,
|
|
289
|
-
notices,
|
|
290
|
-
parseOk: true,
|
|
291
|
-
schemaOk
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
catch {
|
|
295
|
-
return {
|
|
296
|
-
...defaultReconciliationNoticesPayload(),
|
|
297
|
-
parseOk: false,
|
|
298
|
-
schemaOk: false
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
async function writeReconciliationNotices(projectRoot, payload) {
|
|
303
|
-
const filePath = reconciliationNoticesPath(projectRoot);
|
|
304
|
-
await ensureDir(path.dirname(filePath));
|
|
305
|
-
await writeFileSafe(filePath, `${JSON.stringify({
|
|
306
|
-
schemaVersion: RECONCILIATION_NOTICES_SCHEMA_VERSION,
|
|
307
|
-
notices: payload.notices
|
|
308
|
-
}, null, 2)}\n`, { mode: 0o600 });
|
|
309
|
-
}
|
|
310
|
-
export function classifyReconciliationNotices(flowState, notices) {
|
|
311
|
-
const activeBlocked = [];
|
|
312
|
-
const currentStageBlocked = [];
|
|
313
|
-
const unsynced = [];
|
|
314
|
-
const staleRun = [];
|
|
315
|
-
for (const notice of notices) {
|
|
316
|
-
if (notice.runId !== flowState.activeRunId) {
|
|
317
|
-
staleRun.push(notice);
|
|
318
|
-
continue;
|
|
319
|
-
}
|
|
320
|
-
if (notice.kind === "closeout_substate_demotion") {
|
|
321
|
-
continue;
|
|
322
|
-
}
|
|
323
|
-
const stageCatalog = flowState.stageGateCatalog[notice.stage];
|
|
324
|
-
const blocked = stageCatalog.blocked.includes(notice.gateId);
|
|
325
|
-
if (!blocked) {
|
|
326
|
-
unsynced.push(notice);
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
activeBlocked.push(notice);
|
|
330
|
-
if (notice.stage === flowState.currentStage) {
|
|
331
|
-
currentStageBlocked.push(notice);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
return { activeBlocked, currentStageBlocked, unsynced, staleRun };
|
|
335
|
-
}
|
|
336
214
|
export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
337
215
|
const stage = flowState.currentStage;
|
|
338
216
|
const schema = stageSchema(stage, flowState.track);
|
|
@@ -448,23 +326,6 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
|
448
326
|
if (!securityAttestation.ok) {
|
|
449
327
|
issues.push(`review security attestation failed: ${securityAttestation.errors.join("; ")}`);
|
|
450
328
|
}
|
|
451
|
-
const traceGateRequired = schema.requiredGates.some((gate) => gate.id === "review_trace_matrix_clean" && gate.tier === "required");
|
|
452
|
-
if (traceGateRequired) {
|
|
453
|
-
const trace = await buildTraceMatrix(projectRoot);
|
|
454
|
-
const traceIssues = [];
|
|
455
|
-
if (trace.orphanedCriteria.length > 0) {
|
|
456
|
-
traceIssues.push(`orphaned criteria: ${trace.orphanedCriteria.join(", ")}`);
|
|
457
|
-
}
|
|
458
|
-
if (trace.orphanedTasks.length > 0) {
|
|
459
|
-
traceIssues.push(`orphaned tasks: ${trace.orphanedTasks.join(", ")}`);
|
|
460
|
-
}
|
|
461
|
-
if (trace.orphanedTests.length > 0) {
|
|
462
|
-
traceIssues.push(`orphaned tests: ${trace.orphanedTests.join(", ")}`);
|
|
463
|
-
}
|
|
464
|
-
if (traceIssues.length > 0) {
|
|
465
|
-
issues.push(`review trace-matrix gate blocked (review_trace_matrix_clean): ${traceIssues.join("; ")}.`);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
329
|
}
|
|
469
330
|
if (stage === "design") {
|
|
470
331
|
const researchGateRequired = schema.requiredGates.some((gate) => gate.id === "design_research_complete" && gate.tier === "required");
|
|
@@ -739,47 +600,8 @@ export function reconcileCurrentStageGateCatalog(flowState) {
|
|
|
739
600
|
export async function reconcileAndWriteCurrentStageGateCatalog(projectRoot) {
|
|
740
601
|
const state = await readFlowState(projectRoot);
|
|
741
602
|
const { nextState, reconciliation } = reconcileCurrentStageGateCatalog(state);
|
|
742
|
-
const effectiveState = reconciliation.changed ? nextState : state;
|
|
743
603
|
if (reconciliation.changed) {
|
|
744
|
-
await writeFlowState(projectRoot,
|
|
745
|
-
}
|
|
746
|
-
const noticesPayload = await readReconciliationNotices(projectRoot);
|
|
747
|
-
let noticesChanged = false;
|
|
748
|
-
const noticeBuckets = classifyReconciliationNotices(effectiveState, noticesPayload.notices);
|
|
749
|
-
if (noticeBuckets.unsynced.length > 0 || noticeBuckets.staleRun.length > 0) {
|
|
750
|
-
const dropIds = new Set([...noticeBuckets.unsynced, ...noticeBuckets.staleRun].map((notice) => notice.id));
|
|
751
|
-
noticesPayload.notices = noticesPayload.notices.filter((notice) => !dropIds.has(notice.id));
|
|
752
|
-
noticesChanged = true;
|
|
753
|
-
}
|
|
754
|
-
if (reconciliation.demotedGateIds.length > 0) {
|
|
755
|
-
const existing = new Set(noticesPayload.notices.map((notice) => `${notice.runId}:${notice.stage}:${notice.gateId}:${notice.kind ?? "gate_demotion"}`));
|
|
756
|
-
for (const gateId of reconciliation.demotedGateIds) {
|
|
757
|
-
const dedupeKey = `${effectiveState.activeRunId}:${reconciliation.stage}:${gateId}:gate_demotion`;
|
|
758
|
-
if (existing.has(dedupeKey)) {
|
|
759
|
-
continue;
|
|
760
|
-
}
|
|
761
|
-
const ts = new Date().toISOString();
|
|
762
|
-
noticesPayload.notices.push({
|
|
763
|
-
id: `${dedupeKey}:${ts}`,
|
|
764
|
-
runId: effectiveState.activeRunId,
|
|
765
|
-
stage: reconciliation.stage,
|
|
766
|
-
gateId,
|
|
767
|
-
reason: "demoted from passed to blocked during gate reconciliation (missing evidence)",
|
|
768
|
-
demotedAt: ts,
|
|
769
|
-
kind: "gate_demotion"
|
|
770
|
-
});
|
|
771
|
-
existing.add(dedupeKey);
|
|
772
|
-
noticesChanged = true;
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
if (noticesChanged) {
|
|
776
|
-
noticesPayload.notices.sort((a, b) => {
|
|
777
|
-
if (a.demotedAt === b.demotedAt) {
|
|
778
|
-
return a.id.localeCompare(b.id);
|
|
779
|
-
}
|
|
780
|
-
return a.demotedAt.localeCompare(b.demotedAt);
|
|
781
|
-
});
|
|
782
|
-
await writeReconciliationNotices(projectRoot, noticesPayload);
|
|
604
|
+
await writeFlowState(projectRoot, nextState);
|
|
783
605
|
}
|
|
784
606
|
return {
|
|
785
607
|
...reconciliation,
|
package/dist/harness-adapters.js
CHANGED
|
@@ -378,7 +378,7 @@ When in doubt, prefer **non-trivial** — the quick track is opt-in and only saf
|
|
|
378
378
|
Knowledge capture and curation run automatically as part of stage completion
|
|
379
379
|
protocols via the internal \`learnings\` skill — no user-facing command.
|
|
380
380
|
Reusable entries land in \`.cclaw/knowledge.jsonl\` as strict JSONL with
|
|
381
|
-
\`type\`, \`trigger\`, \`action\`, and \`
|
|
381
|
+
\`type\`, \`trigger\`, \`action\`, \`confidence\`, \`stage\`, and \`origin_stage\` metadata.
|
|
382
382
|
|
|
383
383
|
**Stage order:** brainstorm > scope > design > spec > plan > tdd > review > ship, then closeout: retro > compound > archive. Use \`/cc\` to keep moving through normal work and post-ship closeout; use \`/cc-cancel\` for cancelled/abandoned runs. Gates must pass before handoff.
|
|
384
384
|
|
package/dist/install.js
CHANGED
|
@@ -6,7 +6,7 @@ import { CCLAW_VERSION, FLOW_VERSION, REQUIRED_DIRS, RUNTIME_ROOT } from "./cons
|
|
|
6
6
|
import { writeConfig, createDefaultConfig, readConfig, configPath, detectLanguageRulePacks, detectAdvancedKeys } from "./config.js";
|
|
7
7
|
import { learnSkillMarkdown } from "./content/learnings.js";
|
|
8
8
|
import { stageCommandShimMarkdown } from "./content/stage-command.js";
|
|
9
|
-
import { ideaCommandContract, ideaCommandSkillMarkdown } from "./content/idea
|
|
9
|
+
import { ideaCommandContract, ideaCommandSkillMarkdown } from "./content/idea.js";
|
|
10
10
|
import { startCommandContract, startCommandSkillMarkdown } from "./content/start-command.js";
|
|
11
11
|
import { viewCommandContract, viewCommandSkillMarkdown } from "./content/view-command.js";
|
|
12
12
|
import { cancelCommandContract, cancelCommandSkillMarkdown } from "./content/cancel-command.js";
|
|
@@ -19,7 +19,7 @@ import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.j
|
|
|
19
19
|
import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
|
|
20
20
|
import { STATE_CONTRACTS } from "./content/state-contracts.js";
|
|
21
21
|
import { REVIEW_PROMPTS } from "./content/review-prompts.js";
|
|
22
|
-
import { stageSkillFolder, stageSkillMarkdown } from "./content/skills.js";
|
|
22
|
+
import { stageSkillFolder, stageSkillMarkdown, executingWavesSkillMarkdown } from "./content/skills.js";
|
|
23
23
|
import { LANGUAGE_RULE_PACK_DIR, LANGUAGE_RULE_PACK_FILES, LANGUAGE_RULE_PACK_GENERATORS, LEGACY_LANGUAGE_RULE_PACK_FOLDERS } from "./content/utility-skills.js";
|
|
24
24
|
import { RESEARCH_PLAYBOOKS } from "./content/research-playbooks.js";
|
|
25
25
|
import { SUBAGENT_CONTEXT_SKILLS } from "./content/subagent-context-skills.js";
|
|
@@ -470,6 +470,9 @@ async function writeArtifactTemplates(projectRoot) {
|
|
|
470
470
|
await writeFileSafe(runtimePath(projectRoot, "templates", "state-contracts", fileName), content);
|
|
471
471
|
}));
|
|
472
472
|
}
|
|
473
|
+
async function writeWavePlansScaffold(projectRoot) {
|
|
474
|
+
await writeFileSafe(runtimePath(projectRoot, "wave-plans", ".gitkeep"), "");
|
|
475
|
+
}
|
|
473
476
|
async function writeSkills(projectRoot, config) {
|
|
474
477
|
const skillTrack = config?.defaultTrack ?? "standard";
|
|
475
478
|
for (const stage of FLOW_STAGES) {
|
|
@@ -486,6 +489,7 @@ async function writeSkills(projectRoot, config) {
|
|
|
486
489
|
await writeFileSafe(runtimePath(projectRoot, "skills", "parallel-dispatch", "SKILL.md"), parallelAgentsSkill());
|
|
487
490
|
await writeFileSafe(runtimePath(projectRoot, "skills", "session", "SKILL.md"), sessionHooksSkillMarkdown());
|
|
488
491
|
await writeFileSafe(runtimePath(projectRoot, "skills", "iron-laws", "SKILL.md"), ironLawsSkillMarkdown());
|
|
492
|
+
await writeFileSafe(runtimePath(projectRoot, "skills", "executing-waves", "SKILL.md"), executingWavesSkillMarkdown());
|
|
489
493
|
await writeFileSafe(runtimePath(projectRoot, "skills", META_SKILL_NAME, "SKILL.md"), usingCclawSkillMarkdown());
|
|
490
494
|
// In-thread research procedures (no YAML frontmatter, not delegated personas).
|
|
491
495
|
for (const [fileName, markdown] of Object.entries(RESEARCH_PLAYBOOKS)) {
|
|
@@ -897,6 +901,33 @@ async function writeMergedHookJson(projectRoot, hookFilePath, generatedJson) {
|
|
|
897
901
|
}
|
|
898
902
|
await writeFileSafe(hookFilePath, `${JSON.stringify(mergedDoc, null, 2)}\n`);
|
|
899
903
|
}
|
|
904
|
+
async function readBundledRunHookRuntimeScript(options) {
|
|
905
|
+
const bundleUrl = new URL("./runtime/run-hook.mjs", import.meta.url);
|
|
906
|
+
try {
|
|
907
|
+
await fs.stat(bundleUrl);
|
|
908
|
+
}
|
|
909
|
+
catch {
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
try {
|
|
913
|
+
const moduleUrl = `${bundleUrl.href}?ts=${Date.now()}`;
|
|
914
|
+
const loaded = await import(moduleUrl);
|
|
915
|
+
const factory = typeof loaded.buildRunHookRuntimeScript === "function"
|
|
916
|
+
? loaded.buildRunHookRuntimeScript
|
|
917
|
+
: typeof loaded.default === "function"
|
|
918
|
+
? loaded.default
|
|
919
|
+
: null;
|
|
920
|
+
if (!factory)
|
|
921
|
+
return null;
|
|
922
|
+
const script = factory(options);
|
|
923
|
+
if (typeof script !== "string")
|
|
924
|
+
return null;
|
|
925
|
+
return script.trim().length > 0 ? script : null;
|
|
926
|
+
}
|
|
927
|
+
catch {
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
900
931
|
async function writeHooks(projectRoot, config) {
|
|
901
932
|
const harnesses = config.harnesses;
|
|
902
933
|
const hooksDir = runtimePath(projectRoot, "hooks");
|
|
@@ -911,14 +942,16 @@ async function writeHooks(projectRoot, config) {
|
|
|
911
942
|
await writeFileSafe(path.join(hooksDir, "stage-complete.mjs"), stageCompleteScript());
|
|
912
943
|
await writeFileSafe(path.join(hooksDir, "start-flow.mjs"), startFlowScript());
|
|
913
944
|
await writeFileSafe(path.join(hooksDir, "cancel-run.mjs"), cancelRunScript());
|
|
914
|
-
|
|
945
|
+
const hookRuntimeOptions = {
|
|
915
946
|
strictness: effectiveStrictness,
|
|
916
947
|
tddTestPathPatterns: config.tdd?.testPathPatterns ?? config.tddTestGlobs,
|
|
917
948
|
tddProductionPathPatterns: config.tdd?.productionPathPatterns,
|
|
918
949
|
compoundRecurrenceThreshold: config.compound?.recurrenceThreshold,
|
|
919
950
|
earlyLoopEnabled: config.earlyLoop?.enabled,
|
|
920
951
|
earlyLoopMaxIterations: config.earlyLoop?.maxIterations
|
|
921
|
-
}
|
|
952
|
+
};
|
|
953
|
+
const bundledHookRuntime = await readBundledRunHookRuntimeScript(hookRuntimeOptions);
|
|
954
|
+
await writeFileSafe(path.join(hooksDir, "run-hook.mjs"), bundledHookRuntime ?? nodeHookRuntimeScript(hookRuntimeOptions));
|
|
922
955
|
await writeFileSafe(path.join(hooksDir, "run-hook.cmd"), runHookCmdScript());
|
|
923
956
|
await writeFileSafe(path.join(hooksDir, "delegation-record.mjs"), delegationRecordScript());
|
|
924
957
|
const opencodePluginSource = opencodePluginJs();
|
|
@@ -1196,6 +1229,7 @@ async function materializeRuntime(projectRoot, config, forceStateReset, operatio
|
|
|
1196
1229
|
writeEntryCommands(projectRoot),
|
|
1197
1230
|
writeSkills(projectRoot, config),
|
|
1198
1231
|
writeArtifactTemplates(projectRoot),
|
|
1232
|
+
writeWavePlansScaffold(projectRoot),
|
|
1199
1233
|
writeRulebook(projectRoot)
|
|
1200
1234
|
]);
|
|
1201
1235
|
await writeState(projectRoot, config, forceStateReset);
|
|
@@ -168,7 +168,6 @@ export async function harvestStageLearnings(projectRoot, stage, track) {
|
|
|
168
168
|
const appendResult = await appendKnowledge(projectRoot, parsed.entries, {
|
|
169
169
|
stage,
|
|
170
170
|
originStage: stage,
|
|
171
|
-
originRun: null,
|
|
172
171
|
project: path.basename(projectRoot)
|
|
173
172
|
});
|
|
174
173
|
if (appendResult.invalid > 0) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SHIP_FINALIZATION_MODES } from "../../constants.js";
|
|
2
|
-
import {
|
|
2
|
+
import { validateTddVerificationEvidence } from "../../tdd-verification-evidence.js";
|
|
3
3
|
import { asRecord } from "./helpers.js";
|
|
4
4
|
export const AUTO_REVIEW_LOOP_GATE_BY_STAGE = {
|
|
5
5
|
design: "design_architecture_locked"
|
|
@@ -134,15 +134,6 @@ export function validateUserApprovalEvidence(evidence) {
|
|
|
134
134
|
// guaranteed to carry the structural breadcrumbs downstream tooling
|
|
135
135
|
// expects. Previously only `tdd:tdd_verified_before_complete` was checked.
|
|
136
136
|
const GATE_EVIDENCE_VALIDATORS = {
|
|
137
|
-
"review:review_trace_matrix_clean": (evidence) => {
|
|
138
|
-
if (!TEST_COMMAND_HINT_PATTERN.test(evidence)) {
|
|
139
|
-
return "must include the fresh verification command that was run before ship handoff (for example `npm test`, `pytest`, `go test`, or equivalent).";
|
|
140
|
-
}
|
|
141
|
-
if (!PASS_STATUS_PATTERN.test(evidence)) {
|
|
142
|
-
return "must include explicit success status (for example `PASS` or `GREEN`).";
|
|
143
|
-
}
|
|
144
|
-
return null;
|
|
145
|
-
},
|
|
146
137
|
"ship:ship_finalization_executed": (evidence) => {
|
|
147
138
|
if (!SHIP_FINALIZATION_MODE_PATTERN.test(evidence)) {
|
|
148
139
|
return `must name the finalization mode that ran (for example ${SHIP_FINALIZATION_MODE_HINT}).`;
|
|
@@ -2,8 +2,6 @@ import { type FlowStage } from "./types.js";
|
|
|
2
2
|
export type KnowledgeEntryType = "rule" | "pattern" | "lesson" | "compound";
|
|
3
3
|
export type KnowledgeEntryConfidence = "high" | "medium" | "low";
|
|
4
4
|
export type KnowledgeEntrySeverity = "critical" | "important" | "suggestion";
|
|
5
|
-
export type KnowledgeEntryUniversality = "project" | "personal" | "universal";
|
|
6
|
-
export type KnowledgeEntryMaturity = "raw" | "lifted-to-rule" | "lifted-to-enforcement";
|
|
7
5
|
export type KnowledgeEntrySource = "stage" | "retro" | "compound" | "idea" | "manual";
|
|
8
6
|
export interface KnowledgeEntry {
|
|
9
7
|
type: KnowledgeEntryType;
|
|
@@ -11,20 +9,14 @@ export interface KnowledgeEntry {
|
|
|
11
9
|
action: string;
|
|
12
10
|
confidence: KnowledgeEntryConfidence;
|
|
13
11
|
severity?: KnowledgeEntrySeverity;
|
|
14
|
-
domain: string | null;
|
|
15
12
|
stage: FlowStage | null;
|
|
16
13
|
origin_stage: FlowStage | null;
|
|
17
|
-
origin_run: string | null;
|
|
18
14
|
frequency: number;
|
|
19
|
-
universality: KnowledgeEntryUniversality;
|
|
20
|
-
maturity: KnowledgeEntryMaturity;
|
|
21
15
|
created: string;
|
|
22
16
|
first_seen_ts: string;
|
|
23
17
|
last_seen_ts: string;
|
|
24
18
|
project: string | null;
|
|
25
19
|
source?: KnowledgeEntrySource | null;
|
|
26
|
-
supersedes?: string[];
|
|
27
|
-
superseded_by?: string;
|
|
28
20
|
}
|
|
29
21
|
export interface KnowledgeSeedEntry {
|
|
30
22
|
type: KnowledgeEntryType;
|
|
@@ -32,25 +24,18 @@ export interface KnowledgeSeedEntry {
|
|
|
32
24
|
action: string;
|
|
33
25
|
confidence: KnowledgeEntryConfidence;
|
|
34
26
|
severity?: KnowledgeEntrySeverity;
|
|
35
|
-
domain?: string | null;
|
|
36
27
|
stage?: FlowStage | null;
|
|
37
28
|
origin_stage?: FlowStage | null;
|
|
38
|
-
origin_run?: string | null;
|
|
39
29
|
frequency?: number;
|
|
40
|
-
universality?: KnowledgeEntryUniversality;
|
|
41
|
-
maturity?: KnowledgeEntryMaturity;
|
|
42
30
|
created?: string;
|
|
43
31
|
first_seen_ts?: string;
|
|
44
32
|
last_seen_ts?: string;
|
|
45
33
|
project?: string | null;
|
|
46
34
|
source?: KnowledgeEntrySource | null;
|
|
47
|
-
supersedes?: string[];
|
|
48
|
-
superseded_by?: string;
|
|
49
35
|
}
|
|
50
36
|
export interface AppendKnowledgeDefaults {
|
|
51
37
|
stage?: FlowStage | null;
|
|
52
38
|
originStage?: FlowStage | null;
|
|
53
|
-
originRun?: string | null;
|
|
54
39
|
project?: string | null;
|
|
55
40
|
source?: KnowledgeEntrySource | null;
|
|
56
41
|
nowIso?: string;
|
|
@@ -98,8 +83,6 @@ export interface CompoundReadinessCluster {
|
|
|
98
83
|
lastSeenTs: string;
|
|
99
84
|
/** Entry types observed (rule/pattern/lesson/compound). */
|
|
100
85
|
types: KnowledgeEntryType[];
|
|
101
|
-
/** Distinct maturity values observed across the cluster. */
|
|
102
|
-
maturity: KnowledgeEntryMaturity[];
|
|
103
86
|
}
|
|
104
87
|
export interface CompoundReadiness {
|
|
105
88
|
schemaVersion: 2;
|
|
@@ -167,9 +150,8 @@ export declare function effectiveCompoundThreshold(baseThreshold: number, archiv
|
|
|
167
150
|
*
|
|
168
151
|
* Clustering key: `(type, normalizeText(trigger), normalizeText(action))`
|
|
169
152
|
* which mirrors the compound readiness clustering in runtime state.
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* as ready.
|
|
153
|
+
* The readiness surface intentionally stays simple: no maturity/supersede
|
|
154
|
+
* suppression logic in this pass.
|
|
173
155
|
*/
|
|
174
156
|
export declare function computeCompoundReadiness(entries: KnowledgeEntry[], options?: ComputeCompoundReadinessOptions): CompoundReadiness;
|
|
175
157
|
export declare function validateKnowledgeEntry(entry: unknown): {
|