@smartmemory/compose 0.1.1-beta → 0.1.3-beta
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/.claude/skills/bug-fix/SKILL.md +143 -0
- package/.claude/skills/compose/SKILL.md +604 -0
- package/.compose-deps.json +89 -0
- package/README.md +47 -983
- package/bin/compose.js +473 -0
- package/contracts/comp-obs-contract.schema.json +362 -0
- package/contracts/cross-model-review-result.json +78 -0
- package/contracts/review-result.json +126 -0
- package/dist/assets/{_baseUniq-CQwX6VLz.js → _baseUniq-D-avYfn5.js} +1 -1
- package/dist/assets/{arc-SxJ2J1sh.js → arc-BC4dfQ-X.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-BykunY1F.js → architectureDiagram-Q4EWVU46-BZmFXnGI.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-ohAKBOUw.js → blockDiagram-DXYQGD6D-DlfWSuux.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-DBDC3ENB.js → c4Diagram-AHTNJAMY-Y__uJrRx.js} +1 -1
- package/dist/assets/channel-LRG9kHqJ.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-Cv93Z7uM.js → chunk-4BX2VUAB-BfMePfTp.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-DE0WBDkj.js → chunk-4TB4RGXK-BdlMSdEA.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-CE1EXenG.js → chunk-55IACEB6-vrQHZTdv.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-DA7Ana6H.js → chunk-EDXVE4YY-B8wioVlW.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-CTDIPA3p.js → chunk-FMBD7UC4-Cd6Hrux2.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-uGBaPaTX.js → chunk-OYMX7WX6-CfrhdQXY.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-CYlnXuUO.js → chunk-QZHKN3VN-B9JQerOU.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-ojGkzcZK.js → chunk-YZCP3GAM-DFN9X99H.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BC9a6pDE.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-BC9a6pDE.js +1 -0
- package/dist/assets/clone-dRxgFrBv.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-Bktn9hL-.js → cose-bilkent-S5V4N54A-BAn0ap_E.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-DFaSzuRF.js → dagre-KV5264BT-DyxnVq1g.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-DnfmDzEm.js → diagram-5BDNPKRD-XCrzqski.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-Bm8W9YnG.js → diagram-G4DWMVQ6-MBCAXft_.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-B5-TSKvp.js → diagram-MMDJMWI5-DbtB2yS6.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-ls4rqlky.js → diagram-TYMM5635-Bb5NzX61.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-giG6WO-r.js → erDiagram-SMLLAGMA-CpIeCOh2.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-XvlUuz-7.js → flowDiagram-DWJPFMVM-CHyoKnhW.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-hLBV57oV.js → ganttDiagram-T4ZO3ILL-DErKteO_.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-BHu3s_Gn.js → gitGraphDiagram-UUTBAWPF-KFVAtj2F.js} +1 -1
- package/dist/assets/{graph-D0Cfv00Y.js → graph-CRnO_ifT.js} +1 -1
- package/dist/assets/index-DKBsEUJ-.css +1 -0
- package/dist/assets/index-DkRKLuNr.js +1144 -0
- package/dist/assets/{infoDiagram-42DDH7IO-DbqRsOo3.js → infoDiagram-42DDH7IO-BZFnuSp5.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-DnCdx7zb.js → ishikawaDiagram-UXIWVN3A-4Xe2Szde.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-CfD7eNcP.js → journeyDiagram-VCZTEJTY-CZRByfS-.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-BYaO9-mK.js → kanban-definition-6JOO6SKY-B95sk6Fk.js} +1 -1
- package/dist/assets/{layout-Bj72wOEB.js → layout-BqNQzxWT.js} +1 -1
- package/dist/assets/{linear-BRFo114D.js → linear-CUh7qb64.js} +1 -1
- package/dist/assets/{min-GCHnKlJS.js → min-wXgOS3ig.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-n0PMebY4.js → mindmap-definition-QFDTVHPH-DB6iaAbO.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-pN4CljHF.js → pieDiagram-DEJITSTG-CHkZHrTW.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-DNoAy8-D.js → quadrantDiagram-34T5L4WZ-DoTEO8e3.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-BhtY05PT.js → requirementDiagram-MS252O5E-Dn8peXYp.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-B6AD-16A.js → sankeyDiagram-XADWPNL6-DRXs6Ipb.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-DShHM-uk.js → sequenceDiagram-FGHM5R23-wBBYZ0aq.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-DMxn7HTo.js → stateDiagram-FHFEXIEX-DPlBNGmf.js} +1 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-BW0ezXb4.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-Cdu6uq52.js → timeline-definition-GMOUNBTQ-CbbyTlHk.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-CpK29iRe.js → vennDiagram-DHZGUBPP-Bj4GaFfj.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-BQgSkdcO.js → wardley-RL74JXVD-RtNzq8KU.js} +55 -55
- package/dist/assets/{wardleyDiagram-NUSXRM2D-DJHYev6O.js → wardleyDiagram-NUSXRM2D-CDfE3zSj.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-1d75pbaO.js → xychartDiagram-5P7HB3ND-CZXHHYD5.js} +1 -1
- package/dist/index.html +2 -2
- package/lib/budget-ledger.js +45 -0
- package/lib/bug-bisect.js +292 -0
- package/lib/bug-checkpoint.js +191 -0
- package/lib/bug-escalation.js +306 -0
- package/lib/bug-index-gen.js +136 -0
- package/lib/bug-ledger.js +126 -0
- package/lib/build-stream-schema.js +176 -0
- package/lib/build-stream-writer.js +3 -1
- package/lib/build.js +854 -284
- package/lib/connector-factory-shim.js +167 -0
- package/lib/constants.js +18 -0
- package/lib/debug-discipline.js +176 -27
- package/lib/deps.js +205 -0
- package/lib/health-score.js +4 -4
- package/lib/import.js +26 -13
- package/lib/inject-schema.js +21 -0
- package/lib/new.js +27 -53
- package/lib/result-normalizer.js +160 -144
- package/lib/review-lenses.js +5 -5
- package/lib/review-normalize.js +413 -0
- package/lib/review-prompt.js +163 -0
- package/lib/sections.js +325 -0
- package/lib/step-prompt.js +21 -1
- package/lib/step-validator.js +5 -3
- package/lib/stratum-mcp-client.js +172 -7
- package/package.json +14 -3
- package/pipelines/bug-fix.stratum.yaml +39 -1
- package/pipelines/build.stratum.yaml +28 -45
- package/pipelines/review-fix.stratum.yaml +1 -1
- package/presets/team-review.stratum.yaml +21 -14
- package/server/build-stream-bridge.js +28 -0
- package/server/cc-session-feature-resolver.js +111 -0
- package/server/cc-session-reader.js +327 -0
- package/server/cc-session-watcher.js +318 -0
- package/server/compose-mcp-tools.js +0 -125
- package/server/compose-mcp.js +2 -4
- package/server/contract-diff.js +192 -0
- package/server/decision-event-emit.js +175 -0
- package/server/decision-event-id.js +64 -0
- package/server/decision-events-snapshot.js +166 -0
- package/server/design-routes.js +92 -49
- package/server/drift-axes.js +365 -0
- package/server/drift-emit.js +121 -0
- package/server/gate-log-store.js +102 -0
- package/server/lifecycle-phase-history.js +44 -0
- package/server/open-loops-store.js +102 -0
- package/server/schema-validator.js +49 -0
- package/server/status-emit.js +27 -0
- package/server/status-snapshot.js +218 -0
- package/server/vision-routes.js +332 -4
- package/server/vision-server.js +104 -12
- package/server/vision-store.js +21 -0
- package/dist/assets/channel-DGElom1e.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-KqWP9wWZ.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-KqWP9wWZ.js +0 -1
- package/dist/assets/clone-DUJKJXd7.js +0 -1
- package/dist/assets/index-CUd6pFGF.css +0 -1
- package/dist/assets/index-DReRlzZI.js +0 -1144
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-o6PnCs4e.js +0 -1
- package/server/connectors/agent-connector.js +0 -78
- package/server/connectors/claude-sdk-connector.js +0 -198
- package/server/connectors/codex-connector.js +0 -240
- package/server/connectors/connector-discovery.js +0 -18
- package/server/connectors/connector-runtime.js +0 -13
- package/server/connectors/opencode-connector.js +0 -200
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bug-ledger.js — COMP-FIX-HARD hypothesis ledger persistence.
|
|
3
|
+
*
|
|
4
|
+
* Persists hypothesis entries to docs/bugs/<bug-code>/hypotheses.jsonl
|
|
5
|
+
* (append-only JSONL). One JSON object per line.
|
|
6
|
+
*
|
|
7
|
+
* Idempotent on (attempt, ts): repeated writes with the same key are skipped
|
|
8
|
+
* (the first writer wins). Tolerates malformed lines on read (skip + warn).
|
|
9
|
+
*
|
|
10
|
+
* Pattern reference: server/gate-log-store.js (gate log JSONL helpers).
|
|
11
|
+
*
|
|
12
|
+
* Entry shape:
|
|
13
|
+
* Required: attempt (number), ts (ISO string), hypothesis (string),
|
|
14
|
+
* verdict ('confirmed' | 'rejected' | 'inconclusive')
|
|
15
|
+
* Optional: evidence_for[], evidence_against[], next_to_try, agent,
|
|
16
|
+
* tokens_used, findings[]
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { appendFileSync, readFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
20
|
+
import { join, dirname } from 'node:path';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the on-disk path for a bug's hypothesis ledger.
|
|
24
|
+
* @param {string} cwd — repository root
|
|
25
|
+
* @param {string} bugCode — bug identifier (e.g. "BUG-123")
|
|
26
|
+
* @returns {string} absolute path to hypotheses.jsonl
|
|
27
|
+
*/
|
|
28
|
+
export function getHypothesesPath(cwd, bugCode) {
|
|
29
|
+
return join(cwd, 'docs', 'bugs', bugCode, 'hypotheses.jsonl');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Append one hypothesis entry to the bug's ledger.
|
|
34
|
+
* Idempotent: if an entry with the same (attempt, ts) already exists, the
|
|
35
|
+
* write is skipped. Creates the parent directory if missing.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} cwd
|
|
38
|
+
* @param {string} bugCode
|
|
39
|
+
* @param {object} entry — must have attempt, ts, hypothesis, verdict
|
|
40
|
+
*/
|
|
41
|
+
export function appendHypothesisEntry(cwd, bugCode, entry) {
|
|
42
|
+
const filePath = getHypothesesPath(cwd, bugCode);
|
|
43
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
44
|
+
|
|
45
|
+
// Idempotency check: scan existing entries for matching (attempt, ts).
|
|
46
|
+
// Volume per bug is small so a linear scan is fine.
|
|
47
|
+
if (existsSync(filePath)) {
|
|
48
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
49
|
+
for (const line of raw.split('\n')) {
|
|
50
|
+
const trimmed = line.trim();
|
|
51
|
+
if (!trimmed) continue;
|
|
52
|
+
try {
|
|
53
|
+
const obj = JSON.parse(trimmed);
|
|
54
|
+
if (obj.attempt === entry.attempt && obj.ts === entry.ts) return; // already written
|
|
55
|
+
} catch {
|
|
56
|
+
// malformed line — skip
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
appendFileSync(filePath, JSON.stringify(entry) + '\n', 'utf8');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Read all hypothesis entries for a bug.
|
|
66
|
+
* Returns [] if the file does not exist. Tolerates malformed lines: each
|
|
67
|
+
* unparseable line is skipped with a stderr warning, valid lines returned.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} cwd
|
|
70
|
+
* @param {string} bugCode
|
|
71
|
+
* @returns {object[]}
|
|
72
|
+
*/
|
|
73
|
+
export function readHypotheses(cwd, bugCode) {
|
|
74
|
+
const filePath = getHypothesesPath(cwd, bugCode);
|
|
75
|
+
if (!existsSync(filePath)) return [];
|
|
76
|
+
|
|
77
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
78
|
+
const entries = [];
|
|
79
|
+
|
|
80
|
+
for (const line of raw.split('\n')) {
|
|
81
|
+
const trimmed = line.trim();
|
|
82
|
+
if (!trimmed) continue;
|
|
83
|
+
try {
|
|
84
|
+
entries.push(JSON.parse(trimmed));
|
|
85
|
+
} catch {
|
|
86
|
+
console.warn('[bug-ledger] malformed line skipped:', trimmed.slice(0, 80));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return entries;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Render rejected hypotheses as a markdown block, suitable for splicing into
|
|
95
|
+
* a bug-fix prompt so the next attempt avoids re-trying dead ends.
|
|
96
|
+
*
|
|
97
|
+
* Returns "" if the input has no entries with verdict === 'rejected'.
|
|
98
|
+
*
|
|
99
|
+
* @param {object[]} entries
|
|
100
|
+
* @returns {string}
|
|
101
|
+
*/
|
|
102
|
+
export function formatRejectedHypotheses(entries) {
|
|
103
|
+
if (!Array.isArray(entries) || entries.length === 0) return '';
|
|
104
|
+
const rejected = entries.filter((e) => e && e.verdict === 'rejected');
|
|
105
|
+
if (rejected.length === 0) return '';
|
|
106
|
+
|
|
107
|
+
const blocks = rejected.map((e) => {
|
|
108
|
+
const lines = [];
|
|
109
|
+
lines.push(`### Attempt ${e.attempt} (${e.ts})`);
|
|
110
|
+
lines.push(`**Hypothesis:** ${e.hypothesis}`);
|
|
111
|
+
if (Array.isArray(e.evidence_against) && e.evidence_against.length > 0) {
|
|
112
|
+
lines.push('**Evidence against:**');
|
|
113
|
+
for (const ev of e.evidence_against) lines.push(`- ${ev}`);
|
|
114
|
+
}
|
|
115
|
+
if (Array.isArray(e.evidence_for) && e.evidence_for.length > 0) {
|
|
116
|
+
lines.push('**Evidence for:**');
|
|
117
|
+
for (const ev of e.evidence_for) lines.push(`- ${ev}`);
|
|
118
|
+
}
|
|
119
|
+
if (e.next_to_try) {
|
|
120
|
+
lines.push(`**Next to try:** ${e.next_to_try}`);
|
|
121
|
+
}
|
|
122
|
+
return lines.join('\n');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return ['## Previously Rejected Hypotheses', '', ...blocks].join('\n\n') + '\n';
|
|
126
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* build-stream-schema.js — JSON Schema validator for BuildStreamEvent envelopes.
|
|
3
|
+
*
|
|
4
|
+
* STRAT-PAR-STREAM-CONSUMER-VALIDATE: validates incoming progress notifications
|
|
5
|
+
* in stratum-mcp-client.js#dispatchEvent against the v0.2.6 envelope schema.
|
|
6
|
+
*
|
|
7
|
+
* Design decisions:
|
|
8
|
+
* - Uses AJV (already in compose deps) compiled once at module load.
|
|
9
|
+
* - On validation failure the caller should warn+drop — never throw.
|
|
10
|
+
* - KNOWN_VERSIONS: set of accepted schema_version strings. v0.2.5 accepted for
|
|
11
|
+
* one-cycle backward compatibility; v0.2.6 is current.
|
|
12
|
+
* - reply_required (Option A, STRAT-PAR-STREAM-CONSUMER-VALIDATE design):
|
|
13
|
+
* optional boolean reserved for future gate/permission/question kinds.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
17
|
+
|
|
18
|
+
export const KNOWN_VERSIONS = new Set(['0.2.5', '0.2.6']);
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Envelope schema (top-level fields only; metadata shape is kind-specific)
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
const ENVELOPE_SCHEMA = {
|
|
25
|
+
type: 'object',
|
|
26
|
+
required: ['schema_version', 'flow_id', 'step_id', 'seq', 'ts', 'kind', 'metadata'],
|
|
27
|
+
// Closed envelope — matches the producer contract exactly (events.py + build-stream-event.v0.2.6.schema.json).
|
|
28
|
+
// Unknown top-level fields are rejected so envelope-level producer drift is caught.
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
properties: {
|
|
31
|
+
schema_version: { type: 'string' },
|
|
32
|
+
flow_id: { type: 'string' },
|
|
33
|
+
step_id: { type: 'string' },
|
|
34
|
+
seq: { type: 'integer', minimum: 0 },
|
|
35
|
+
ts: { type: 'string' },
|
|
36
|
+
kind: { type: 'string' },
|
|
37
|
+
// task_id is optional (omitted when null by producer — events.py#to_json drops None).
|
|
38
|
+
// Type is string only; null task_id should be omitted, not sent.
|
|
39
|
+
task_id: { type: 'string' },
|
|
40
|
+
reply_required: { type: 'boolean' },
|
|
41
|
+
metadata: { type: 'object' },
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Per-kind closed metadata schemas (v0.2.6 CONTRACT)
|
|
47
|
+
// Source of truth: stratum-mcp/contracts/build-stream-event.v0.2.6.schema.json
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
const KIND_METADATA_SCHEMAS = {
|
|
51
|
+
capability_profile: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
required: ['agent', 'template'],
|
|
54
|
+
additionalProperties: false,
|
|
55
|
+
properties: {
|
|
56
|
+
agent: { type: 'string' },
|
|
57
|
+
template: { type: ['string', 'null'] },
|
|
58
|
+
allowedTools: { type: ['array', 'null'], items: { type: 'string' } },
|
|
59
|
+
disallowedTools: { type: ['array', 'null'], items: { type: 'string' } },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
capability_violation: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
required: ['agent', 'template', 'detail', 'severity'],
|
|
65
|
+
additionalProperties: false,
|
|
66
|
+
properties: {
|
|
67
|
+
agent: { type: 'string' },
|
|
68
|
+
template: { type: ['string', 'null'] },
|
|
69
|
+
detail: { type: 'string' },
|
|
70
|
+
severity: { type: 'string', enum: ['violation', 'warning'] },
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
step_usage: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
required: ['stepId', 'input_tokens', 'output_tokens', 'cost_usd'],
|
|
76
|
+
additionalProperties: false,
|
|
77
|
+
properties: {
|
|
78
|
+
stepId: { type: 'string' },
|
|
79
|
+
input_tokens: { type: 'number', minimum: 0 },
|
|
80
|
+
output_tokens: { type: 'number', minimum: 0 },
|
|
81
|
+
cache_creation_input_tokens: { type: 'number', minimum: 0 },
|
|
82
|
+
cache_read_input_tokens: { type: 'number', minimum: 0 },
|
|
83
|
+
cost_usd: { type: 'number', minimum: 0 },
|
|
84
|
+
model: { type: ['string', 'null'] },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
gate_tier_result: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
required: ['stepId', 'tierId', 'passed'],
|
|
90
|
+
additionalProperties: false,
|
|
91
|
+
properties: {
|
|
92
|
+
stepId: { type: 'string' },
|
|
93
|
+
tierId: { type: 'string' },
|
|
94
|
+
passed: { type: 'boolean' },
|
|
95
|
+
details: { type: ['string', 'null'] },
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
health_score: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
required: ['score', 'breakdown'],
|
|
101
|
+
additionalProperties: false,
|
|
102
|
+
properties: {
|
|
103
|
+
score: { type: 'number', minimum: 0, maximum: 100 },
|
|
104
|
+
breakdown: { type: 'object', additionalProperties: { type: 'number' } },
|
|
105
|
+
missing: { type: 'array', items: { type: 'string' } },
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
build_end: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
required: ['status', 'featureCode'],
|
|
111
|
+
additionalProperties: false,
|
|
112
|
+
properties: {
|
|
113
|
+
status: { type: 'string', enum: ['complete', 'killed', 'crashed', 'failed'] },
|
|
114
|
+
featureCode: { type: 'string' },
|
|
115
|
+
total_input_tokens: { type: 'number', minimum: 0 },
|
|
116
|
+
total_output_tokens: { type: 'number', minimum: 0 },
|
|
117
|
+
total_cost_usd: { type: 'number', minimum: 0 },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// AJV setup — compile once (Ajv2020 for draft-2020-12 feature support)
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
const ajv = new Ajv2020({ strict: false });
|
|
127
|
+
const validateEnvelope = ajv.compile(ENVELOPE_SCHEMA);
|
|
128
|
+
|
|
129
|
+
const compiledKindValidators = new Map(
|
|
130
|
+
Object.entries(KIND_METADATA_SCHEMAS).map(([kind, schema]) => [kind, ajv.compile(schema)])
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Public API
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Validate a parsed BuildStreamEvent envelope.
|
|
139
|
+
*
|
|
140
|
+
* Returns { valid: true } on success.
|
|
141
|
+
* Returns { valid: false, error: string } on failure.
|
|
142
|
+
*
|
|
143
|
+
* Validation rules:
|
|
144
|
+
* 1. Envelope must pass top-level schema (required fields, correct types).
|
|
145
|
+
* 2. schema_version must be a known accepted version.
|
|
146
|
+
* 3. If the kind has a closed metadata schema, metadata must pass it.
|
|
147
|
+
*
|
|
148
|
+
* @param {object} envelope Parsed event object
|
|
149
|
+
* @returns {{ valid: boolean, error?: string }}
|
|
150
|
+
*/
|
|
151
|
+
export function validateBuildStreamEvent(envelope) {
|
|
152
|
+
// 1. Top-level envelope shape
|
|
153
|
+
if (!validateEnvelope(envelope)) {
|
|
154
|
+
const errMsg = ajv.errorsText(validateEnvelope.errors);
|
|
155
|
+
return { valid: false, error: `envelope schema: ${errMsg}` };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 2. schema_version check (accepts both 0.2.5 for backward compat and 0.2.6)
|
|
159
|
+
if (!KNOWN_VERSIONS.has(envelope.schema_version)) {
|
|
160
|
+
return {
|
|
161
|
+
valid: false,
|
|
162
|
+
error: `unknown schema_version "${envelope.schema_version}" (accepted: ${[...KNOWN_VERSIONS].join(', ')})`,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 3. Kind-specific metadata validation (only for the 6 closed kinds)
|
|
167
|
+
const kindValidator = compiledKindValidators.get(envelope.kind);
|
|
168
|
+
if (kindValidator) {
|
|
169
|
+
if (!kindValidator(envelope.metadata)) {
|
|
170
|
+
const errMsg = ajv.errorsText(kindValidator.errors);
|
|
171
|
+
return { valid: false, error: `metadata for kind "${envelope.kind}": ${errMsg}` };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return { valid: true };
|
|
176
|
+
}
|
|
@@ -94,14 +94,16 @@ export class BuildStreamWriter {
|
|
|
94
94
|
* @param {string} agent Full agent string
|
|
95
95
|
* @param {string} templateName Active template name
|
|
96
96
|
* @param {string} detail Description of the violation
|
|
97
|
+
* @param {'violation'|'warning'} [severity='violation'] Severity from checkCapabilityViolation
|
|
97
98
|
*/
|
|
98
|
-
writeViolation(stepId, agent, templateName, detail) {
|
|
99
|
+
writeViolation(stepId, agent, templateName, detail, severity = 'violation') {
|
|
99
100
|
this.write({
|
|
100
101
|
type: 'capability_violation',
|
|
101
102
|
stepId,
|
|
102
103
|
agent,
|
|
103
104
|
template: templateName,
|
|
104
105
|
detail,
|
|
106
|
+
severity,
|
|
105
107
|
});
|
|
106
108
|
}
|
|
107
109
|
|