cclaw-cli 0.55.2 → 2.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 +59 -1
- package/dist/artifact-linter/design.js +46 -1
- package/dist/artifact-linter/plan.js +22 -1
- package/dist/artifact-linter/review.js +35 -1
- package/dist/artifact-linter/scope.js +33 -9
- package/dist/artifact-linter/shared.d.ts +12 -10
- package/dist/artifact-linter/shared.js +102 -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/artifact-linter.js +11 -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/hook-events.js +1 -2
- package/dist/content/hook-manifest.d.ts +3 -4
- package/dist/content/hook-manifest.js +22 -25
- package/dist/content/hooks.js +54 -14
- 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/meta-skill.js +4 -3
- package/dist/content/node-hooks.js +368 -164
- package/dist/content/observe.js +3 -3
- package/dist/content/opencode-plugin.js +12 -32
- 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-elicitation.d.ts +1 -0
- package/dist/content/skills-elicitation.js +123 -0
- package/dist/content/skills.d.ts +1 -0
- package/dist/content/skills.js +54 -2
- package/dist/content/stage-schema.js +107 -63
- package/dist/content/stages/brainstorm.js +7 -3
- package/dist/content/stages/design.js +4 -0
- package/dist/content/stages/review.js +8 -8
- package/dist/content/stages/schema-types.d.ts +2 -2
- package/dist/content/stages/scope.js +7 -3
- package/dist/content/stages/ship.js +1 -1
- package/dist/content/start-command.js +4 -4
- 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 +108 -6
- package/dist/content/utility-skills.js +26 -97
- package/dist/flow-state.d.ts +12 -6
- package/dist/flow-state.js +5 -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/hook-schemas/claude-hooks.v1.json +2 -3
- package/dist/hook-schemas/codex-hooks.v1.json +1 -1
- package/dist/hook-schemas/cursor-hooks.v1.json +1 -1
- package/dist/install.js +50 -7
- package/dist/internal/advance-stage/advance.js +22 -2
- package/dist/internal/advance-stage/parsers.d.ts +1 -0
- package/dist/internal/advance-stage/parsers.js +6 -0
- 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.d.ts +1 -1
- package/dist/run-persistence.js +43 -111
- 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 +9647 -0
- package/dist/track-heuristics.d.ts +7 -1
- package/dist/track-heuristics.js +12 -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
|
@@ -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): {
|
package/dist/knowledge-store.js
CHANGED
|
@@ -32,9 +32,8 @@ export function effectiveCompoundThreshold(baseThreshold, archivedRunsCount) {
|
|
|
32
32
|
*
|
|
33
33
|
* Clustering key: `(type, normalizeText(trigger), normalizeText(action))`
|
|
34
34
|
* which mirrors the compound readiness clustering in runtime state.
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* as ready.
|
|
35
|
+
* The readiness surface intentionally stays simple: no maturity/supersede
|
|
36
|
+
* suppression logic in this pass.
|
|
38
37
|
*/
|
|
39
38
|
export function computeCompoundReadiness(entries, options = {}) {
|
|
40
39
|
const thresholdRaw = options.threshold ?? DEFAULT_COMPOUND_RECURRENCE_THRESHOLD;
|
|
@@ -54,8 +53,6 @@ export function computeCompoundReadiness(entries, options = {}) {
|
|
|
54
53
|
const { threshold, relaxationApplied } = effectiveCompoundThreshold(baseThreshold, archivedRunsCount);
|
|
55
54
|
const buckets = new Map();
|
|
56
55
|
for (const entry of entries) {
|
|
57
|
-
if (entry.maturity === "lifted-to-enforcement" || entry.superseded_by !== undefined)
|
|
58
|
-
continue;
|
|
59
56
|
const key = [
|
|
60
57
|
entry.type,
|
|
61
58
|
normalizeText(entry.trigger),
|
|
@@ -71,15 +68,13 @@ export function computeCompoundReadiness(entries, options = {}) {
|
|
|
71
68
|
entryCount: 1,
|
|
72
69
|
severity: entry.severity,
|
|
73
70
|
lastSeenTs: entry.last_seen_ts,
|
|
74
|
-
types: new Set([entry.type])
|
|
75
|
-
maturity: new Set([entry.maturity])
|
|
71
|
+
types: new Set([entry.type])
|
|
76
72
|
});
|
|
77
73
|
continue;
|
|
78
74
|
}
|
|
79
75
|
bucket.recurrence += frequency;
|
|
80
76
|
bucket.entryCount += 1;
|
|
81
77
|
bucket.types.add(entry.type);
|
|
82
|
-
bucket.maturity.add(entry.maturity);
|
|
83
78
|
if (entry.severity === "critical") {
|
|
84
79
|
bucket.severity = "critical";
|
|
85
80
|
}
|
|
@@ -104,8 +99,7 @@ export function computeCompoundReadiness(entries, options = {}) {
|
|
|
104
99
|
qualification: criticalOverride && !meetsRecurrence ? "critical_override" : "recurrence",
|
|
105
100
|
...(bucket.severity ? { severity: bucket.severity } : {}),
|
|
106
101
|
lastSeenTs: bucket.lastSeenTs,
|
|
107
|
-
types: Array.from(bucket.types).sort()
|
|
108
|
-
maturity: Array.from(bucket.maturity).sort()
|
|
102
|
+
types: Array.from(bucket.types).sort()
|
|
109
103
|
});
|
|
110
104
|
}
|
|
111
105
|
ready.sort((a, b) => {
|
|
@@ -143,8 +137,8 @@ export function computeCompoundReadiness(entries, options = {}) {
|
|
|
143
137
|
const KNOWLEDGE_TYPE_SET = new Set(["rule", "pattern", "lesson", "compound"]);
|
|
144
138
|
const KNOWLEDGE_CONFIDENCE_SET = new Set(["high", "medium", "low"]);
|
|
145
139
|
const KNOWLEDGE_SEVERITY_SET = new Set(["critical", "important", "suggestion"]);
|
|
146
|
-
const
|
|
147
|
-
const
|
|
140
|
+
const LEGACY_KNOWLEDGE_UNIVERSALITY_SET = new Set(["project", "personal", "universal"]);
|
|
141
|
+
const LEGACY_KNOWLEDGE_MATURITY_SET = new Set(["raw", "lifted-to-rule", "lifted-to-enforcement"]);
|
|
148
142
|
const KNOWLEDGE_SOURCE_SET = new Set([
|
|
149
143
|
"stage",
|
|
150
144
|
"retro",
|
|
@@ -158,19 +152,21 @@ const KNOWLEDGE_REQUIRED_KEYS = [
|
|
|
158
152
|
"trigger",
|
|
159
153
|
"action",
|
|
160
154
|
"confidence",
|
|
161
|
-
"domain",
|
|
162
155
|
"stage",
|
|
163
156
|
"origin_stage",
|
|
164
|
-
"origin_run",
|
|
165
157
|
"frequency",
|
|
166
|
-
"universality",
|
|
167
|
-
"maturity",
|
|
168
158
|
"created",
|
|
169
159
|
"first_seen_ts",
|
|
170
160
|
"last_seen_ts",
|
|
171
161
|
"project"
|
|
172
162
|
];
|
|
173
163
|
const KNOWLEDGE_ALLOWED_KEYS = new Set(KNOWLEDGE_REQUIRED_KEYS);
|
|
164
|
+
// Legacy keys are accepted for backwards compatibility when reading historical
|
|
165
|
+
// knowledge entries, but no longer required for newly materialized rows.
|
|
166
|
+
KNOWLEDGE_ALLOWED_KEYS.add("domain");
|
|
167
|
+
KNOWLEDGE_ALLOWED_KEYS.add("origin_run");
|
|
168
|
+
KNOWLEDGE_ALLOWED_KEYS.add("universality");
|
|
169
|
+
KNOWLEDGE_ALLOWED_KEYS.add("maturity");
|
|
174
170
|
KNOWLEDGE_ALLOWED_KEYS.add("source");
|
|
175
171
|
KNOWLEDGE_ALLOWED_KEYS.add("severity");
|
|
176
172
|
KNOWLEDGE_ALLOWED_KEYS.add("supersedes");
|
|
@@ -178,6 +174,28 @@ KNOWLEDGE_ALLOWED_KEYS.add("superseded_by");
|
|
|
178
174
|
function keyAllowedInKnowledgeEntry(key) {
|
|
179
175
|
return KNOWLEDGE_ALLOWED_KEYS.has(key);
|
|
180
176
|
}
|
|
177
|
+
function normalizeParsedKnowledgeEntry(obj) {
|
|
178
|
+
const normalized = {
|
|
179
|
+
type: obj.type,
|
|
180
|
+
trigger: obj.trigger,
|
|
181
|
+
action: obj.action,
|
|
182
|
+
confidence: obj.confidence,
|
|
183
|
+
stage: obj.stage,
|
|
184
|
+
origin_stage: obj.origin_stage,
|
|
185
|
+
frequency: obj.frequency,
|
|
186
|
+
created: obj.created,
|
|
187
|
+
first_seen_ts: obj.first_seen_ts,
|
|
188
|
+
last_seen_ts: obj.last_seen_ts,
|
|
189
|
+
project: obj.project
|
|
190
|
+
};
|
|
191
|
+
if (obj.severity !== undefined) {
|
|
192
|
+
normalized.severity = obj.severity;
|
|
193
|
+
}
|
|
194
|
+
if (obj.source !== undefined) {
|
|
195
|
+
normalized.source = obj.source;
|
|
196
|
+
}
|
|
197
|
+
return normalized;
|
|
198
|
+
}
|
|
181
199
|
function knowledgePath(projectRoot) {
|
|
182
200
|
return path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl");
|
|
183
201
|
}
|
|
@@ -198,16 +216,11 @@ function dedupeKey(entry) {
|
|
|
198
216
|
entry.type,
|
|
199
217
|
normalizeText(entry.trigger),
|
|
200
218
|
normalizeText(entry.action),
|
|
201
|
-
entry.domain === null ? "null" : normalizeText(entry.domain),
|
|
202
219
|
entry.stage ?? "null",
|
|
203
220
|
entry.origin_stage ?? "null",
|
|
204
|
-
entry.origin_run === null ? "null" : normalizeText(entry.origin_run),
|
|
205
|
-
entry.universality,
|
|
206
221
|
entry.project === null ? "null" : normalizeText(entry.project),
|
|
207
222
|
entry.source === undefined || entry.source === null ? "null" : entry.source,
|
|
208
|
-
entry.severity === undefined ? "none" : entry.severity
|
|
209
|
-
Array.isArray(entry.supersedes) ? entry.supersedes.map(normalizeText).sort().join(",") : "none",
|
|
210
|
-
entry.superseded_by === undefined ? "none" : normalizeText(entry.superseded_by)
|
|
223
|
+
entry.severity === undefined ? "none" : entry.severity
|
|
211
224
|
].join("|");
|
|
212
225
|
}
|
|
213
226
|
function emptyKnowledgeSnapshot() {
|
|
@@ -236,7 +249,7 @@ function parseKnowledgeSnapshot(raw) {
|
|
|
236
249
|
malformedLines += 1;
|
|
237
250
|
continue;
|
|
238
251
|
}
|
|
239
|
-
const entry = parsed;
|
|
252
|
+
const entry = normalizeParsedKnowledgeEntry(parsed);
|
|
240
253
|
entries.push(entry);
|
|
241
254
|
const key = dedupeKey(entry);
|
|
242
255
|
if (!keyToIndex.has(key)) {
|
|
@@ -321,7 +334,7 @@ export function validateKnowledgeEntry(entry) {
|
|
|
321
334
|
(typeof obj.severity !== "string" || !KNOWLEDGE_SEVERITY_SET.has(obj.severity))) {
|
|
322
335
|
errors.push("severity must be one of: critical, important, suggestion.");
|
|
323
336
|
}
|
|
324
|
-
if (!isNullableString(obj.domain)) {
|
|
337
|
+
if (obj.domain !== undefined && !isNullableString(obj.domain)) {
|
|
325
338
|
errors.push("domain must be string or null.");
|
|
326
339
|
}
|
|
327
340
|
if (!isNullableStage(obj.stage)) {
|
|
@@ -331,7 +344,7 @@ export function validateKnowledgeEntry(entry) {
|
|
|
331
344
|
errors.push(`origin_stage must be one of ${FLOW_STAGES.join(", ")} or null.`);
|
|
332
345
|
}
|
|
333
346
|
const originRun = obj.origin_run;
|
|
334
|
-
if (!isNullableString(originRun)) {
|
|
347
|
+
if (originRun !== undefined && !isNullableString(originRun)) {
|
|
335
348
|
errors.push("origin_run must be string or null.");
|
|
336
349
|
}
|
|
337
350
|
if (typeof obj.frequency !== "number" ||
|
|
@@ -339,10 +352,12 @@ export function validateKnowledgeEntry(entry) {
|
|
|
339
352
|
obj.frequency < 1) {
|
|
340
353
|
errors.push("frequency must be an integer >= 1.");
|
|
341
354
|
}
|
|
342
|
-
if (
|
|
355
|
+
if (obj.universality !== undefined &&
|
|
356
|
+
(typeof obj.universality !== "string" || !LEGACY_KNOWLEDGE_UNIVERSALITY_SET.has(obj.universality))) {
|
|
343
357
|
errors.push("universality must be one of: project, personal, universal.");
|
|
344
358
|
}
|
|
345
|
-
if (
|
|
359
|
+
if (obj.maturity !== undefined &&
|
|
360
|
+
(typeof obj.maturity !== "string" || !LEGACY_KNOWLEDGE_MATURITY_SET.has(obj.maturity))) {
|
|
346
361
|
errors.push("maturity must be one of: raw, lifted-to-rule, lifted-to-enforcement.");
|
|
347
362
|
}
|
|
348
363
|
for (const timestampField of ["created", "first_seen_ts", "last_seen_ts"]) {
|
|
@@ -378,20 +393,15 @@ export function materializeKnowledgeEntry(seed, defaults = {}) {
|
|
|
378
393
|
const now = normalizeUtcIso(defaults.nowIso ?? nowUtcIso());
|
|
379
394
|
const stage = seed.stage ?? defaults.stage ?? null;
|
|
380
395
|
const originStage = seed.origin_stage ?? defaults.originStage ?? stage ?? null;
|
|
381
|
-
const originRun = seed.origin_run ?? defaults.originRun ?? null;
|
|
382
396
|
const source = seed.source ?? defaults.source ?? null;
|
|
383
397
|
const entry = {
|
|
384
398
|
type: seed.type,
|
|
385
399
|
trigger: seed.trigger.trim(),
|
|
386
400
|
action: seed.action.trim(),
|
|
387
401
|
confidence: seed.confidence,
|
|
388
|
-
domain: seed.domain ?? null,
|
|
389
402
|
stage,
|
|
390
403
|
origin_stage: originStage,
|
|
391
|
-
origin_run: originRun,
|
|
392
404
|
frequency: seed.frequency ?? 1,
|
|
393
|
-
universality: seed.universality ?? "project",
|
|
394
|
-
maturity: seed.maturity ?? "raw",
|
|
395
405
|
created: normalizeUtcIso(seed.created ?? now),
|
|
396
406
|
first_seen_ts: normalizeUtcIso(seed.first_seen_ts ?? now),
|
|
397
407
|
last_seen_ts: normalizeUtcIso(seed.last_seen_ts ?? now),
|
|
@@ -400,12 +410,6 @@ export function materializeKnowledgeEntry(seed, defaults = {}) {
|
|
|
400
410
|
if (seed.severity !== undefined) {
|
|
401
411
|
entry.severity = seed.severity;
|
|
402
412
|
}
|
|
403
|
-
if (seed.supersedes !== undefined) {
|
|
404
|
-
entry.supersedes = seed.supersedes.map((value) => value.trim());
|
|
405
|
-
}
|
|
406
|
-
if (seed.superseded_by !== undefined) {
|
|
407
|
-
entry.superseded_by = seed.superseded_by.trim();
|
|
408
|
-
}
|
|
409
413
|
if (source !== null) {
|
|
410
414
|
entry.source = source;
|
|
411
415
|
}
|
|
@@ -514,18 +518,6 @@ function tokenizeText(value) {
|
|
|
514
518
|
function uniqueTokens(values) {
|
|
515
519
|
return [...new Set(values)];
|
|
516
520
|
}
|
|
517
|
-
function supersededTriggerSet(entries) {
|
|
518
|
-
const superseded = new Set();
|
|
519
|
-
for (const entry of entries) {
|
|
520
|
-
for (const trigger of entry.supersedes ?? []) {
|
|
521
|
-
superseded.add(normalizeText(trigger));
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
return superseded;
|
|
525
|
-
}
|
|
526
|
-
function isSupersededLearning(entry, supersededTriggers) {
|
|
527
|
-
return entry.superseded_by !== undefined || supersededTriggers.has(normalizeText(entry.trigger));
|
|
528
|
-
}
|
|
529
521
|
function pathTokens(paths) {
|
|
530
522
|
if (!Array.isArray(paths) || paths.length === 0)
|
|
531
523
|
return [];
|
|
@@ -547,9 +539,7 @@ export async function selectRelevantLearnings(projectRoot, options = {}) {
|
|
|
547
539
|
const limit = typeof options.limit === "number" && Number.isFinite(options.limit) && options.limit > 0
|
|
548
540
|
? Math.floor(options.limit)
|
|
549
541
|
: 8;
|
|
550
|
-
const
|
|
551
|
-
const activeEntries = entries.filter((entry) => !isSupersededLearning(entry, staleTriggers));
|
|
552
|
-
const ranked = activeEntries.map((entry, index) => {
|
|
542
|
+
const ranked = entries.map((entry, index) => {
|
|
553
543
|
let score = 0;
|
|
554
544
|
let stageScore = 0;
|
|
555
545
|
if (stage) {
|
|
@@ -567,13 +557,9 @@ export async function selectRelevantLearnings(projectRoot, options = {}) {
|
|
|
567
557
|
score += 1;
|
|
568
558
|
if (entry.frequency >= 3)
|
|
569
559
|
score += 1;
|
|
570
|
-
if (entry.maturity === "lifted-to-enforcement")
|
|
571
|
-
score -= 1;
|
|
572
560
|
const searchable = [
|
|
573
|
-
...tokenizeText(entry.domain),
|
|
574
561
|
...tokenizeText(entry.trigger),
|
|
575
562
|
...tokenizeText(entry.action),
|
|
576
|
-
...tokenizeText(entry.origin_run),
|
|
577
563
|
...tokenizeText(entry.project)
|
|
578
564
|
];
|
|
579
565
|
const searchSet = new Set(searchable);
|
package/dist/policy.js
CHANGED
|
@@ -58,7 +58,7 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
58
58
|
const utilitySkillChecks = [
|
|
59
59
|
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "strict JSONL schema", name: "utility_skill:learnings:jsonl_schema" },
|
|
60
60
|
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "knowledge.jsonl", name: "utility_skill:learnings:jsonl_store" },
|
|
61
|
-
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "type, trigger, action, confidence,
|
|
61
|
+
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "type, trigger, action, confidence, stage, origin_stage, frequency, created, first_seen_ts, last_seen_ts, project", name: "utility_skill:learnings:field_order" },
|
|
62
62
|
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "## Manual Actions", name: "utility_skill:learnings:manual_actions" },
|
|
63
63
|
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:learnings:hard_gate" },
|
|
64
64
|
{ file: runtimeFile("commands/start.md"), needle: "## Algorithm", name: "utility_command:start:algorithm" },
|
|
@@ -71,7 +71,7 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
71
71
|
{ file: runtimeFile("skills/flow-view/SKILL.md"), needle: "## Diff Subcommand", name: "utility_skill:view:diff_section" },
|
|
72
72
|
{ file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:sdd:hard_gate" },
|
|
73
73
|
{ file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## Status Contract", name: "utility_skill:sdd:status_contract" },
|
|
74
|
-
{ file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "
|
|
74
|
+
{ file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "slice-implementer", name: "utility_skill:sdd:implementer_template" },
|
|
75
75
|
{ file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## Model & Harness Routing Notes", name: "utility_skill:sdd:routing_notes" },
|
|
76
76
|
{ file: runtimeFile("skills/parallel-dispatch/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:parallel:hard_gate" },
|
|
77
77
|
{ file: runtimeFile("skills/parallel-dispatch/SKILL.md"), needle: "Review Army", name: "utility_skill:parallel:review_army" },
|
|
@@ -89,7 +89,7 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
89
89
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Task classification", name: "meta_skill:task_classification" },
|
|
90
90
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Stage quick map", name: "meta_skill:stage_quick_map" },
|
|
91
91
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Whole flow map", name: "meta_skill:whole_flow_map" },
|
|
92
|
-
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "
|
|
92
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "post_ship_review -> archive", name: "meta_skill:closeout_chain" },
|
|
93
93
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Contextual Skill Activation", name: "meta_skill:contextual_skills" },
|
|
94
94
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Protocol Behavior", name: "meta_skill:protocol_behavior" },
|
|
95
95
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Failure guardrails", name: "meta_skill:failure_guardrails" },
|
package/dist/retro-gate.js
CHANGED
|
@@ -1,34 +1,19 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { RUNTIME_ROOT } from "./constants.js";
|
|
4
|
-
import { exists
|
|
5
|
-
import { readKnowledgeSafely } from "./knowledge-store.js";
|
|
4
|
+
import { exists } from "./fs-utils.js";
|
|
6
5
|
function activeArtifactsPath(projectRoot) {
|
|
7
6
|
return path.join(projectRoot, RUNTIME_ROOT, "artifacts");
|
|
8
7
|
}
|
|
9
8
|
function retroArtifactPath(projectRoot) {
|
|
10
9
|
return path.join(activeArtifactsPath(projectRoot), "09-retro.md");
|
|
11
10
|
}
|
|
12
|
-
// Fallback window for compound-entry scanning when `retroDraftedAt` /
|
|
13
|
-
// `retroAcceptedAt` are not set (legacy runs or imports): use the retro
|
|
14
|
-
// artifact's mtime ± 7 days. 24h was too narrow for long-running retros
|
|
15
|
-
// that are edited over several days or runs imported from another
|
|
16
|
-
// machine with slightly different clocks; 7 days is still tight enough
|
|
17
|
-
// that entries from an unrelated future run are excluded.
|
|
18
|
-
const RETRO_ARTIFACT_MTIME_FALLBACK_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
19
11
|
function parseIsoTimestamp(value) {
|
|
20
12
|
if (!value || value.trim().length === 0)
|
|
21
13
|
return null;
|
|
22
14
|
const parsed = Date.parse(value);
|
|
23
15
|
return Number.isFinite(parsed) ? parsed : null;
|
|
24
16
|
}
|
|
25
|
-
function inInclusiveWindow(timestamp, windowStartMs, windowEndMs) {
|
|
26
|
-
if (windowStartMs !== null && timestamp < windowStartMs)
|
|
27
|
-
return false;
|
|
28
|
-
if (windowEndMs !== null && timestamp > windowEndMs)
|
|
29
|
-
return false;
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
17
|
export async function evaluateRetroGate(projectRoot, state) {
|
|
33
18
|
const required = state.completedStages.includes("ship");
|
|
34
19
|
const artifactFile = retroArtifactPath(projectRoot);
|
|
@@ -42,83 +27,16 @@ export async function evaluateRetroGate(projectRoot, state) {
|
|
|
42
27
|
hasRetroArtifact = false;
|
|
43
28
|
}
|
|
44
29
|
}
|
|
45
|
-
let compoundEntries = 0;
|
|
46
|
-
let windowStartMs = parseIsoTimestamp(state.closeout.retroDraftedAt);
|
|
47
|
-
let windowEndMs = parseIsoTimestamp(state.closeout.retroAcceptedAt) ?? parseIsoTimestamp(state.retro.completedAt);
|
|
48
|
-
if (hasRetroArtifact &&
|
|
49
|
-
windowStartMs === null &&
|
|
50
|
-
windowEndMs === null) {
|
|
51
|
-
try {
|
|
52
|
-
const stats = await fs.stat(artifactFile);
|
|
53
|
-
const anchor = stats.mtimeMs;
|
|
54
|
-
if (Number.isFinite(anchor) && anchor > 0) {
|
|
55
|
-
windowStartMs = anchor - RETRO_ARTIFACT_MTIME_FALLBACK_WINDOW_MS;
|
|
56
|
-
windowEndMs = anchor + RETRO_ARTIFACT_MTIME_FALLBACK_WINDOW_MS;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
// fallback scan remains disabled when mtime cannot be read
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
const shouldScanCompoundEvidence = windowStartMs !== null || windowEndMs !== null;
|
|
64
|
-
if (shouldScanCompoundEvidence) {
|
|
65
|
-
const countIfEligible = (parsed) => {
|
|
66
|
-
if (parsed.type !== "compound") {
|
|
67
|
-
return 0;
|
|
68
|
-
}
|
|
69
|
-
const created = typeof parsed.created === "string" ? parseIsoTimestamp(parsed.created) : null;
|
|
70
|
-
if (created === null || !inInclusiveWindow(created, windowStartMs, windowEndMs)) {
|
|
71
|
-
return 0;
|
|
72
|
-
}
|
|
73
|
-
const source = typeof parsed.source === "string"
|
|
74
|
-
? parsed.source.trim().toLowerCase()
|
|
75
|
-
: null;
|
|
76
|
-
const legacyRetroStage = parsed.stage === "retro";
|
|
77
|
-
return source === "retro" || legacyRetroStage ? 1 : 0;
|
|
78
|
-
};
|
|
79
|
-
try {
|
|
80
|
-
const knowledgeFile = path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl");
|
|
81
|
-
const { entries } = await readKnowledgeSafely(projectRoot);
|
|
82
|
-
for (const parsed of entries) {
|
|
83
|
-
compoundEntries += countIfEligible(parsed);
|
|
84
|
-
}
|
|
85
|
-
// Backward compatibility for historical/hand-edited rows that don't pass
|
|
86
|
-
// strict knowledge schema validation but still carry retro evidence.
|
|
87
|
-
if (compoundEntries === 0 && (await exists(knowledgeFile))) {
|
|
88
|
-
const raw = stripBom(await fs.readFile(knowledgeFile, "utf8"));
|
|
89
|
-
for (const line of raw.split(/\r?\n/)) {
|
|
90
|
-
const trimmed = line.trim();
|
|
91
|
-
if (!trimmed)
|
|
92
|
-
continue;
|
|
93
|
-
try {
|
|
94
|
-
const parsed = JSON.parse(trimmed);
|
|
95
|
-
compoundEntries += countIfEligible(parsed);
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
// ignore malformed lines for retro gate calculation
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
compoundEntries = 0;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// A retro is considered complete when any of:
|
|
108
|
-
// - the retro artifact exists AND (at least one compound learning was
|
|
109
|
-
// promoted during the retro window OR compound was explicitly skipped
|
|
110
|
-
// after reviewing the draft), or
|
|
111
|
-
// - the operator explicitly skipped the retro step itself
|
|
112
|
-
// (`retroSkipped === true` with a non-empty reason). `retroSkipped` is an
|
|
113
|
-
// operator-level override of the artifact requirement, so it must
|
|
114
|
-
// bypass `hasRetroArtifact` — otherwise a run that legitimately had
|
|
115
|
-
// nothing worth retro-ing dead-locks at closeout waiting for a
|
|
116
|
-
// file that will never exist.
|
|
117
30
|
const retroSkipReason = state.closeout.retroSkipReason?.trim() ?? "";
|
|
118
31
|
const retroSkipped = state.closeout.retroSkipped === true && retroSkipReason.length > 0;
|
|
32
|
+
const retroAccepted = hasRetroArtifact && parseIsoTimestamp(state.closeout.retroAcceptedAt) !== null;
|
|
119
33
|
const compoundSkipped = state.closeout.compoundSkipped === true;
|
|
120
|
-
const
|
|
121
|
-
|
|
34
|
+
const compoundReviewed = parseIsoTimestamp(state.closeout.compoundCompletedAt) !== null ||
|
|
35
|
+
state.closeout.compoundPromoted > 0;
|
|
36
|
+
const compoundEntries = compoundReviewed ? Math.max(0, Math.floor(state.closeout.compoundPromoted)) : 0;
|
|
37
|
+
// Keep retro-gate deterministic from closeout state only:
|
|
38
|
+
// retroComplete = (retroAccepted || retroSkipped) && (compoundReviewed || compoundSkipped)
|
|
39
|
+
const completed = required ? (retroAccepted || retroSkipped) && (compoundReviewed || compoundSkipped) : true;
|
|
122
40
|
return {
|
|
123
41
|
required,
|
|
124
42
|
completed,
|
package/dist/run-archive.js
CHANGED
|
@@ -17,12 +17,10 @@ const STATE_SNAPSHOT_EXCLUDE = new Set([
|
|
|
17
17
|
]);
|
|
18
18
|
const DELEGATION_LOG_FILE = "delegation-log.json";
|
|
19
19
|
const TDD_CYCLE_LOG_FILE = "tdd-cycle-log.jsonl";
|
|
20
|
-
const RECONCILIATION_NOTICES_FILE = "reconciliation-notices.json";
|
|
21
20
|
const CRITICAL_STATE_SNAPSHOT_FILES = new Set([
|
|
22
21
|
"flow-state.json",
|
|
23
22
|
DELEGATION_LOG_FILE,
|
|
24
|
-
TDD_CYCLE_LOG_FILE
|
|
25
|
-
RECONCILIATION_NOTICES_FILE
|
|
23
|
+
TDD_CYCLE_LOG_FILE
|
|
26
24
|
]);
|
|
27
25
|
function archiveRoot(projectRoot) {
|
|
28
26
|
return path.join(projectRoot, ARCHIVE_DIR_REL_PATH);
|
|
@@ -89,7 +87,6 @@ async function resetCarryoverStateFiles(projectRoot, activeRunId) {
|
|
|
89
87
|
await ensureDir(stateDir);
|
|
90
88
|
await writeFileSafe(path.join(stateDir, DELEGATION_LOG_FILE), `${JSON.stringify({ runId: activeRunId, entries: [] }, null, 2)}\n`, { mode: 0o600 });
|
|
91
89
|
await writeFileSafe(path.join(stateDir, TDD_CYCLE_LOG_FILE), "", { mode: 0o600 });
|
|
92
|
-
await writeFileSafe(path.join(stateDir, RECONCILIATION_NOTICES_FILE), `${JSON.stringify({ schemaVersion: 1, notices: [] }, null, 2)}\n`, { mode: 0o600 });
|
|
93
90
|
}
|
|
94
91
|
async function restoreStateSnapshot(projectRoot, archiveStatePath) {
|
|
95
92
|
if (!(await exists(archiveStatePath)))
|
|
@@ -42,5 +42,5 @@ export declare function flowStateLockPathFor(projectRoot: string): string;
|
|
|
42
42
|
interface EnsureRunSystemOptions {
|
|
43
43
|
createIfMissing?: boolean;
|
|
44
44
|
}
|
|
45
|
-
export declare function ensureRunSystem(projectRoot: string,
|
|
45
|
+
export declare function ensureRunSystem(projectRoot: string, options?: EnsureRunSystemOptions): Promise<FlowState>;
|
|
46
46
|
export {};
|