@rqml/cli 0.3.0 → 0.4.1
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 +10 -4
- package/dist/index.js +260 -29
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -18,10 +18,16 @@ rqml <command> [spec.rqml] [options]
|
|
|
18
18
|
validate [path] XML well-formedness, XSD, and referential integrity
|
|
19
19
|
status [path] Spec, coverage, and lint summary
|
|
20
20
|
check [path] Deterministic enforcement gate (validation + coverage + drift)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
show <id> One artifact: statement, acceptance criteria, trace neighborhood
|
|
22
|
+
impact <id> What is affected, transitively, if this artifact changes
|
|
23
|
+
matrix [path] Traceability matrix: status, goals, code, tests, coverage gaps
|
|
24
|
+
link <id> <uri> Record an implements/verifiedBy edge and its drift baseline
|
|
25
|
+
skeleton <kind> Print a schema-valid snippet (req|edge|testCase|stateMachine)
|
|
26
|
+
|
|
27
|
+
--json Machine-readable output (REQ-CLI-JSON)
|
|
28
|
+
--strictness <level> relaxed | standard | strict | certified
|
|
29
|
+
--base-dir <dir> Resolve the spec and implements code links against <dir>
|
|
30
|
+
--status/--type/--warning Filter matrix rows (comma-separated, e.g. --warning unverified)
|
|
25
31
|
```
|
|
26
32
|
|
|
27
33
|
When no spec path is given, the lone `*.rqml` in the working directory is used
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var require_util = __commonJS({
|
|
|
14
14
|
var nameRegexp = "[" + nameStartChar + "][" + nameChar + "]*";
|
|
15
15
|
var regexName = new RegExp("^" + nameRegexp + "$");
|
|
16
16
|
var getAllMatches = function(string, regex) {
|
|
17
|
-
const
|
|
17
|
+
const matches2 = [];
|
|
18
18
|
let match = regex.exec(string);
|
|
19
19
|
while (match) {
|
|
20
20
|
const allmatches = [];
|
|
@@ -23,10 +23,10 @@ var require_util = __commonJS({
|
|
|
23
23
|
for (let index = 0; index < len; index++) {
|
|
24
24
|
allmatches.push(match[index]);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
matches2.push(allmatches);
|
|
27
27
|
match = regex.exec(string);
|
|
28
28
|
}
|
|
29
|
-
return
|
|
29
|
+
return matches2;
|
|
30
30
|
};
|
|
31
31
|
var isName = function(string) {
|
|
32
32
|
const match = regexName.exec(string);
|
|
@@ -304,24 +304,24 @@ var require_validator = __commonJS({
|
|
|
304
304
|
}
|
|
305
305
|
var validAttrStrRegxp = new RegExp(`(\\s*)([^\\s=]+)(\\s*=)?(\\s*(['"])(([\\s\\S])*?)\\5)?`, "g");
|
|
306
306
|
function validateAttributeString(attrStr, options) {
|
|
307
|
-
const
|
|
307
|
+
const matches2 = util.getAllMatches(attrStr, validAttrStrRegxp);
|
|
308
308
|
const attrNames = {};
|
|
309
|
-
for (let i = 0; i <
|
|
310
|
-
if (
|
|
311
|
-
return getErrorObject("InvalidAttr", "Attribute '" +
|
|
312
|
-
} else if (
|
|
313
|
-
return getErrorObject("InvalidAttr", "Attribute '" +
|
|
314
|
-
} else if (
|
|
315
|
-
return getErrorObject("InvalidAttr", "boolean attribute '" +
|
|
309
|
+
for (let i = 0; i < matches2.length; i++) {
|
|
310
|
+
if (matches2[i][1].length === 0) {
|
|
311
|
+
return getErrorObject("InvalidAttr", "Attribute '" + matches2[i][2] + "' has no space in starting.", getPositionFromMatch(matches2[i]));
|
|
312
|
+
} else if (matches2[i][3] !== void 0 && matches2[i][4] === void 0) {
|
|
313
|
+
return getErrorObject("InvalidAttr", "Attribute '" + matches2[i][2] + "' is without value.", getPositionFromMatch(matches2[i]));
|
|
314
|
+
} else if (matches2[i][3] === void 0 && !options.allowBooleanAttributes) {
|
|
315
|
+
return getErrorObject("InvalidAttr", "boolean attribute '" + matches2[i][2] + "' is not allowed.", getPositionFromMatch(matches2[i]));
|
|
316
316
|
}
|
|
317
|
-
const attrName =
|
|
317
|
+
const attrName = matches2[i][2];
|
|
318
318
|
if (!validateAttrName(attrName)) {
|
|
319
|
-
return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(
|
|
319
|
+
return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(matches2[i]));
|
|
320
320
|
}
|
|
321
321
|
if (!attrNames.hasOwnProperty(attrName)) {
|
|
322
322
|
attrNames[attrName] = 1;
|
|
323
323
|
} else {
|
|
324
|
-
return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is repeated.", getPositionFromMatch(
|
|
324
|
+
return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is repeated.", getPositionFromMatch(matches2[i]));
|
|
325
325
|
}
|
|
326
326
|
}
|
|
327
327
|
return true;
|
|
@@ -1058,15 +1058,15 @@ var require_OrderedObjParser = __commonJS({
|
|
|
1058
1058
|
var attrsRegx = new RegExp(`([^\\s=]+)\\s*(=\\s*(['"])([\\s\\S]*?)\\3)?`, "gm");
|
|
1059
1059
|
function buildAttributesMap(attrStr, jPath, tagName) {
|
|
1060
1060
|
if (this.options.ignoreAttributes !== true && typeof attrStr === "string") {
|
|
1061
|
-
const
|
|
1062
|
-
const len =
|
|
1061
|
+
const matches2 = util.getAllMatches(attrStr, attrsRegx);
|
|
1062
|
+
const len = matches2.length;
|
|
1063
1063
|
const attrs = {};
|
|
1064
1064
|
for (let i = 0; i < len; i++) {
|
|
1065
|
-
const attrName = this.resolveNameSpace(
|
|
1065
|
+
const attrName = this.resolveNameSpace(matches2[i][1]);
|
|
1066
1066
|
if (this.ignoreAttributesFn(attrName, jPath)) {
|
|
1067
1067
|
continue;
|
|
1068
1068
|
}
|
|
1069
|
-
let oldVal =
|
|
1069
|
+
let oldVal = matches2[i][4];
|
|
1070
1070
|
let aName = this.options.attributeNamePrefix + attrName;
|
|
1071
1071
|
if (attrName.length) {
|
|
1072
1072
|
if (this.options.transformAttributeName) {
|
|
@@ -1328,9 +1328,9 @@ var require_OrderedObjParser = __commonJS({
|
|
|
1328
1328
|
}
|
|
1329
1329
|
for (let entityName in this.docTypeEntities) {
|
|
1330
1330
|
const entity = this.docTypeEntities[entityName];
|
|
1331
|
-
const
|
|
1332
|
-
if (
|
|
1333
|
-
this.entityExpansionCount +=
|
|
1331
|
+
const matches2 = val.match(entity.regx);
|
|
1332
|
+
if (matches2) {
|
|
1333
|
+
this.entityExpansionCount += matches2.length;
|
|
1334
1334
|
if (entityConfig.maxTotalExpansions && this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
1335
1335
|
throw new Error(
|
|
1336
1336
|
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
@@ -1351,9 +1351,9 @@ var require_OrderedObjParser = __commonJS({
|
|
|
1351
1351
|
if (val.indexOf("&") === -1) return val;
|
|
1352
1352
|
for (const entityName of Object.keys(this.lastEntities)) {
|
|
1353
1353
|
const entity = this.lastEntities[entityName];
|
|
1354
|
-
const
|
|
1355
|
-
if (
|
|
1356
|
-
this.entityExpansionCount +=
|
|
1354
|
+
const matches2 = val.match(entity.regex);
|
|
1355
|
+
if (matches2) {
|
|
1356
|
+
this.entityExpansionCount += matches2.length;
|
|
1357
1357
|
if (entityConfig.maxTotalExpansions && this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
1358
1358
|
throw new Error(
|
|
1359
1359
|
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
@@ -1366,9 +1366,9 @@ var require_OrderedObjParser = __commonJS({
|
|
|
1366
1366
|
if (this.options.htmlEntities) {
|
|
1367
1367
|
for (const entityName of Object.keys(this.htmlEntities)) {
|
|
1368
1368
|
const entity = this.htmlEntities[entityName];
|
|
1369
|
-
const
|
|
1370
|
-
if (
|
|
1371
|
-
this.entityExpansionCount +=
|
|
1369
|
+
const matches2 = val.match(entity.regex);
|
|
1370
|
+
if (matches2) {
|
|
1371
|
+
this.entityExpansionCount += matches2.length;
|
|
1372
1372
|
if (entityConfig.maxTotalExpansions && this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
1373
1373
|
throw new Error(
|
|
1374
1374
|
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
@@ -3453,6 +3453,77 @@ function updateTraceEdge(xml, request) {
|
|
|
3453
3453
|
previousUri: external.uri
|
|
3454
3454
|
};
|
|
3455
3455
|
}
|
|
3456
|
+
function collectTitles(doc) {
|
|
3457
|
+
const m = /* @__PURE__ */ new Map();
|
|
3458
|
+
const set = (id, title) => {
|
|
3459
|
+
if (id !== void 0 && title !== void 0 && title !== "" && !m.has(id)) {
|
|
3460
|
+
m.set(id, title);
|
|
3461
|
+
}
|
|
3462
|
+
};
|
|
3463
|
+
for (const p of doc.meta.profiles ?? []) set(p.id, p.type);
|
|
3464
|
+
const c = doc.catalogs;
|
|
3465
|
+
if (c) {
|
|
3466
|
+
for (const t of c.glossary ?? []) set(t.id, t.name);
|
|
3467
|
+
for (const a of c.actors ?? []) set(a.id, a.name);
|
|
3468
|
+
for (const s of c.stakeholders ?? []) set(s.id, s.name);
|
|
3469
|
+
for (const x of c.constraints ?? []) set(x.id, x.statement);
|
|
3470
|
+
for (const x of c.policies ?? []) set(x.id, x.obligation);
|
|
3471
|
+
for (const x of c.decisions ?? []) set(x.id, x.decision);
|
|
3472
|
+
for (const x of c.risks ?? []) set(x.id, x.statement);
|
|
3473
|
+
}
|
|
3474
|
+
const d = doc.domain;
|
|
3475
|
+
if (d) {
|
|
3476
|
+
for (const e of d.entities ?? []) {
|
|
3477
|
+
set(e.id, e.name);
|
|
3478
|
+
for (const a of e.attrs ?? []) set(a.id, a.name);
|
|
3479
|
+
}
|
|
3480
|
+
for (const r of d.businessRules ?? []) set(r.id, r.statement);
|
|
3481
|
+
}
|
|
3482
|
+
const g = doc.goals;
|
|
3483
|
+
if (g) {
|
|
3484
|
+
for (const x of g.goals ?? []) set(x.id, x.title);
|
|
3485
|
+
for (const x of g.qualityGoals ?? []) set(x.id, x.title);
|
|
3486
|
+
for (const x of g.obstacles ?? []) set(x.id, x.title);
|
|
3487
|
+
for (const x of g.goalLinks ?? []) set(x.id, x.id);
|
|
3488
|
+
}
|
|
3489
|
+
const sc = doc.scenarios;
|
|
3490
|
+
if (sc) {
|
|
3491
|
+
for (const x of sc.scenarios ?? []) set(x.id, x.title);
|
|
3492
|
+
for (const x of sc.misuseCases ?? []) set(x.id, x.title);
|
|
3493
|
+
for (const x of sc.edgeCases ?? []) set(x.id, x.title);
|
|
3494
|
+
}
|
|
3495
|
+
for (const p of doc.packages) {
|
|
3496
|
+
set(p.id, p.title);
|
|
3497
|
+
for (const r of p.requirements) set(r.id, r.title);
|
|
3498
|
+
}
|
|
3499
|
+
for (const r of doc.looseRequirements) set(r.id, r.title);
|
|
3500
|
+
const b = doc.behavior;
|
|
3501
|
+
if (b) {
|
|
3502
|
+
for (const sm of b.stateMachines ?? []) {
|
|
3503
|
+
set(sm.id, sm.name);
|
|
3504
|
+
for (const st of sm.states) set(st.id, st.name);
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
const it = doc.interfaces;
|
|
3508
|
+
if (it) {
|
|
3509
|
+
for (const api of it.apis ?? []) {
|
|
3510
|
+
set(api.id, api.name);
|
|
3511
|
+
for (const ep of api.endpoints ?? []) set(ep.id, `${ep.method} ${ep.path}`);
|
|
3512
|
+
}
|
|
3513
|
+
for (const ev of it.events ?? []) set(ev.id, ev.name);
|
|
3514
|
+
}
|
|
3515
|
+
const v = doc.verification;
|
|
3516
|
+
if (v) {
|
|
3517
|
+
for (const ts of v.testSuites ?? []) set(ts.id, ts.title);
|
|
3518
|
+
for (const tc of v.testCases ?? []) set(tc.id, tc.title);
|
|
3519
|
+
}
|
|
3520
|
+
const gv = doc.governance;
|
|
3521
|
+
if (gv) {
|
|
3522
|
+
for (const x of gv.issues ?? []) set(x.id, x.statement);
|
|
3523
|
+
for (const x of gv.approvals ?? []) set(x.id, x.role);
|
|
3524
|
+
}
|
|
3525
|
+
return m;
|
|
3526
|
+
}
|
|
3456
3527
|
function endpointKey2(locator) {
|
|
3457
3528
|
return locator.kind === "local" ? locator.id : locator.uri;
|
|
3458
3529
|
}
|
|
@@ -3794,6 +3865,131 @@ function computeCoverage(doc) {
|
|
|
3794
3865
|
function finding(source, rule, message) {
|
|
3795
3866
|
return { source, severity: "warning", rule, message };
|
|
3796
3867
|
}
|
|
3868
|
+
function refFromEndpoint(ep, titleById) {
|
|
3869
|
+
const loc = ep.locator;
|
|
3870
|
+
if (loc.kind === "local") {
|
|
3871
|
+
const ref2 = { id: loc.id };
|
|
3872
|
+
const title = ep.requirement?.title ?? loc.title ?? titleById.get(loc.id);
|
|
3873
|
+
if (title !== void 0) ref2.title = title;
|
|
3874
|
+
if (ep.target === void 0) ref2.broken = true;
|
|
3875
|
+
return ref2;
|
|
3876
|
+
}
|
|
3877
|
+
const ref = {
|
|
3878
|
+
id: loc.kind === "doc" ? `${loc.uri}#${loc.id}` : loc.uri,
|
|
3879
|
+
external: true
|
|
3880
|
+
};
|
|
3881
|
+
if (loc.title !== void 0) ref.title = loc.title;
|
|
3882
|
+
return ref;
|
|
3883
|
+
}
|
|
3884
|
+
function matches(row, filter) {
|
|
3885
|
+
if (filter === void 0) return true;
|
|
3886
|
+
if (filter.status && !filter.status.includes(row.status)) return false;
|
|
3887
|
+
if (filter.type && !filter.type.includes(row.type)) return false;
|
|
3888
|
+
if (filter.warning && !filter.warning.some((w) => row.warnings.includes(w)))
|
|
3889
|
+
return false;
|
|
3890
|
+
return true;
|
|
3891
|
+
}
|
|
3892
|
+
function buildMatrix(doc, filter) {
|
|
3893
|
+
const cov = computeCoverage(doc);
|
|
3894
|
+
const reqById = new Map(allRequirements(doc).map((r) => [r.id, r]));
|
|
3895
|
+
const titleById = collectTitles(doc);
|
|
3896
|
+
const edgeById = new Map(resolveTrace(doc).edges.map((re) => [re.edge.id, re]));
|
|
3897
|
+
const unverified = new Set(cov.unverifiedRequirements);
|
|
3898
|
+
const unimplemented = new Set(cov.unimplementedRequirements);
|
|
3899
|
+
const orphans = new Set(cov.orphanRequirements);
|
|
3900
|
+
const premature = new Set(cov.prematureImplementations.map((p) => p.requirementId));
|
|
3901
|
+
const rows = [];
|
|
3902
|
+
for (const ac of cov.requirements) {
|
|
3903
|
+
const req = reqById.get(ac.id);
|
|
3904
|
+
if (req === void 0) continue;
|
|
3905
|
+
const goals = (ac.outgoing.satisfies ?? []).flatMap((edgeId) => {
|
|
3906
|
+
const re = edgeById.get(edgeId);
|
|
3907
|
+
return re ? [refFromEndpoint(re.to, titleById)] : [];
|
|
3908
|
+
});
|
|
3909
|
+
const implementations = (ac.incoming.implements ?? []).flatMap((edgeId) => {
|
|
3910
|
+
const re = edgeById.get(edgeId);
|
|
3911
|
+
return re ? [refFromEndpoint(re.from, titleById)] : [];
|
|
3912
|
+
});
|
|
3913
|
+
const tests = (ac.outgoing.verifiedBy ?? []).flatMap((edgeId) => {
|
|
3914
|
+
const re = edgeById.get(edgeId);
|
|
3915
|
+
return re ? [refFromEndpoint(re.to, titleById)] : [];
|
|
3916
|
+
});
|
|
3917
|
+
const verification = unverified.has(ac.id) ? "unverified" : "verified";
|
|
3918
|
+
const implementation = premature.has(ac.id) ? "premature" : unimplemented.has(ac.id) ? "unimplemented" : "implemented";
|
|
3919
|
+
const warnings = [];
|
|
3920
|
+
if (unverified.has(ac.id)) warnings.push("unverified");
|
|
3921
|
+
if (unimplemented.has(ac.id)) warnings.push("unimplemented");
|
|
3922
|
+
if (orphans.has(ac.id)) warnings.push("orphan");
|
|
3923
|
+
if (premature.has(ac.id)) warnings.push("premature");
|
|
3924
|
+
if ([...goals, ...implementations, ...tests].some((r) => r.broken))
|
|
3925
|
+
warnings.push("broken-trace");
|
|
3926
|
+
const row = {
|
|
3927
|
+
id: ac.id,
|
|
3928
|
+
title: req.title,
|
|
3929
|
+
type: req.type,
|
|
3930
|
+
status: req.status ?? "draft",
|
|
3931
|
+
goals,
|
|
3932
|
+
implementations,
|
|
3933
|
+
tests,
|
|
3934
|
+
verification,
|
|
3935
|
+
implementation,
|
|
3936
|
+
warnings
|
|
3937
|
+
};
|
|
3938
|
+
if (req.priority !== void 0) row.priority = req.priority;
|
|
3939
|
+
rows.push(row);
|
|
3940
|
+
}
|
|
3941
|
+
const kept = rows.filter((r) => matches(r, filter));
|
|
3942
|
+
const summary = {
|
|
3943
|
+
total: kept.length,
|
|
3944
|
+
verified: kept.filter((r) => r.verification === "verified").length,
|
|
3945
|
+
unverified: kept.filter((r) => r.verification === "unverified").length,
|
|
3946
|
+
implemented: kept.filter((r) => r.implementation === "implemented").length,
|
|
3947
|
+
unimplemented: kept.filter((r) => r.implementation === "unimplemented").length,
|
|
3948
|
+
premature: kept.filter((r) => r.implementation === "premature").length,
|
|
3949
|
+
orphans: kept.filter((r) => r.warnings.includes("orphan")).length,
|
|
3950
|
+
brokenTraces: kept.filter((r) => r.warnings.includes("broken-trace")).length
|
|
3951
|
+
};
|
|
3952
|
+
return { rows: kept, summary };
|
|
3953
|
+
}
|
|
3954
|
+
function escapeCell2(value) {
|
|
3955
|
+
return value.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
3956
|
+
}
|
|
3957
|
+
function refLabel(r) {
|
|
3958
|
+
const base = r.title !== void 0 ? `${r.id} (${r.title})` : r.id;
|
|
3959
|
+
return r.broken ? `${base} [broken]` : base;
|
|
3960
|
+
}
|
|
3961
|
+
function matrixToMarkdown(report) {
|
|
3962
|
+
const s = report.summary;
|
|
3963
|
+
const lines = [
|
|
3964
|
+
"# Traceability matrix",
|
|
3965
|
+
"",
|
|
3966
|
+
`- **Requirements:** ${s.total}`,
|
|
3967
|
+
`- **Verified:** ${s.verified} \xB7 **Unverified:** ${s.unverified}`,
|
|
3968
|
+
`- **Implemented:** ${s.implemented} \xB7 **Unimplemented:** ${s.unimplemented} \xB7 **Premature:** ${s.premature}`,
|
|
3969
|
+
`- **Orphans:** ${s.orphans} \xB7 **Broken traces:** ${s.brokenTraces}`,
|
|
3970
|
+
"",
|
|
3971
|
+
"| ID | Title | Type | Status | Verify | Impl | Goals | Implemented by | Verified by | Warnings |",
|
|
3972
|
+
"| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |"
|
|
3973
|
+
];
|
|
3974
|
+
for (const row of report.rows) {
|
|
3975
|
+
const cells = [
|
|
3976
|
+
row.id,
|
|
3977
|
+
row.title,
|
|
3978
|
+
row.type,
|
|
3979
|
+
row.status,
|
|
3980
|
+
row.verification,
|
|
3981
|
+
row.implementation,
|
|
3982
|
+
row.goals.map(refLabel).join(", "),
|
|
3983
|
+
row.implementations.map(refLabel).join(", "),
|
|
3984
|
+
row.tests.map(refLabel).join(", "),
|
|
3985
|
+
row.warnings.join(", ")
|
|
3986
|
+
].map(escapeCell2);
|
|
3987
|
+
lines.push(`| ${cells.join(" | ")} |`);
|
|
3988
|
+
}
|
|
3989
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
3990
|
+
return `${lines.join("\n")}
|
|
3991
|
+
`;
|
|
3992
|
+
}
|
|
3797
3993
|
var BASELINE_PATH = ".rqml/baseline.json";
|
|
3798
3994
|
function hashFileAt(filePath) {
|
|
3799
3995
|
try {
|
|
@@ -4091,7 +4287,7 @@ async function runImpact(rest) {
|
|
|
4091
4287
|
}
|
|
4092
4288
|
|
|
4093
4289
|
// ../schema/dist/index.js
|
|
4094
|
-
var AGENTS_default = '# RQML Agent Guidelines\n\n## Strictness: `standard`\n\n| Level | Description |\n|-------|-------------|\n| `relaxed` | Prototyping. Spec is advisory. Quick iteration allowed. |\n| `standard` | Production default. Spec-first for features. Core traces. |\n| `strict` | Full traceability. All behavior specified. No ghost features. |\n| `certified` | Regulated/safety-critical. Audit-grade traces with metadata. |\n\n---\n\nThis project uses **RQML** as the single source of truth for system intent. Familiarize yourself with the documentation at https://rqml.org/docs/user-guide/\n\n**Specification file:** Specification lives in a single .rqml file in the root of the project - convention is `requirements.rqml`. Multiple .rqml files may be employed in multirepo projects, in such cases a .rqml spec applies to everything that is higher in the project tree, unless overridden by another .rqml file.\n\n**Schema file:**\nThe RQML XSD schema is at https://rqml.org/schema/rqml-2.1.0.xsd (insert correct version number). Make sure to adhere to the schema at all times and follow guidelines in schema comments. Use as much of the RQML tagset as is necessary to capture and describe high quality requirements.\n\n---\n\n## Toolchain\n\nThe spec-first loop is enforced by the `rqml` CLI (npm: `@rqml/cli`; the `@rqml/mcp` server exposes the same engine as agent tools):\n\n```bash\nrqml check # deterministic gate: validation + coverage + drift (exit 0 = pass)\nrqml status # re-anchor: spec, coverage, and drift state\nrqml show <REQ-ID> # one requirement: statement, acceptance criteria, trace neighborhood\nrqml impact <ID> # what is affected, transitively, if this artifact changes\nrqml link <REQ-ID> <path> # record an implements edge + drift baseline (--type verifiedBy for tests)\nrqml skeleton <kind> # schema-valid snippet: req | edge | testCase | stateMachine\n```\n\nRun `rqml status` when you start a session to re-anchor on the spec. Run `rqml check` before finishing any task \u2014 it must exit 0.\n\n---\n\n## Core Principle: Spec-First Development\n\
|
|
4290
|
+
var AGENTS_default = '# RQML Agent Guidelines\n\n## Strictness: `standard`\n\n| Level | Description |\n|-------|-------------|\n| `relaxed` | Prototyping. Spec is advisory. Quick iteration allowed. |\n| `standard` | Production default. Spec-first for features. Core traces. |\n| `strict` | Full traceability. All behavior specified. No ghost features. |\n| `certified` | Regulated/safety-critical. Audit-grade traces with metadata. |\n\n---\n\nThis project uses **RQML** as the single source of truth for system intent. Familiarize yourself with the documentation at https://rqml.org/docs/user-guide/ and the development process at https://rqml.org/docs/development-process/\n\n**Specification file:** Specification lives in a single .rqml file in the root of the project - convention is `requirements.rqml`. Multiple .rqml files may be employed in multirepo projects, in such cases a .rqml spec applies to everything that is higher in the project tree, unless overridden by another .rqml file.\n\n**Schema file:**\nThe RQML XSD schema is at https://rqml.org/schema/rqml-2.1.0.xsd (insert correct version number). Make sure to adhere to the schema at all times and follow guidelines in schema comments. Use as much of the RQML tagset as is necessary to capture and describe high quality requirements.\n\n---\n\n## Toolchain\n\nThe spec-first loop is enforced by the `rqml` CLI (npm: `@rqml/cli`; the `@rqml/mcp` server exposes the same engine as agent tools):\n\n```bash\nrqml check # deterministic gate: validation + coverage + drift (exit 0 = pass)\nrqml status # re-anchor: spec, coverage, and drift state\nrqml show <REQ-ID> # one requirement: statement, acceptance criteria, trace neighborhood\nrqml impact <ID> # what is affected, transitively, if this artifact changes\nrqml matrix # traceability matrix: status, goals, code, tests, coverage gaps\nrqml link <REQ-ID> <path> # record an implements edge + drift baseline (--type verifiedBy for tests)\nrqml skeleton <kind> # schema-valid snippet: req | edge | testCase | stateMachine\n```\n\nRun `rqml status` when you start a session to re-anchor on the spec. Run `rqml check` before finishing any task \u2014 it must exit 0.\n\n---\n\n## Core Principle: Spec-First Development\n\nCode follows specification, not the reverse. If code and spec diverge, the spec is authoritative\u2014update the code or negotiate a spec change with the developer.\n\nRQML organizes work into a **five-stage process** (https://rqml.org/docs/development-process/). Each stage produces a durable artifact in version control; verification feeds back to the spec, so it is a loop:\n\n| Stage | Task | Output |\n|-------|------|--------|\n| **Spec** | Capture intent as requirements | `requirements.rqml` |\n| **Design** | Decide architecture, record decisions | ADRs in `.rqml/adr/` |\n| **Plan** | Break work into agent-sized stages | `.rqml/plan.md` |\n| **Code** | Implement specified behavior, keep traces current | code + tests |\n| **Verify** | Prove coverage and catch drift | trace graph + `rqml check` |\n\nNever skip ahead: do not implement behavior that is not specified, and do not make a significant architectural choice without recording it as an ADR.\n\n---\n\n## Workflow\n\n### 1. Spec\nAsk clarifying questions until you understand the goal, scope, acceptance criteria, and constraints. Don\'t assume\u2014capture assumptions as `<notes>` or `<issue>` elements. **Never implement unspecified behavior.** Update the `.rqml` file before coding:\n- Add a `<req>` with statement and acceptance criteria\n- Set appropriate `type`, `priority`, and `status="draft"`\n- Get developer confirmation; only `status="approved"` requirements drive implementation\n\n### 2. Design\nBefore building, decide *how*. Record each significant architectural decision as an **Architecture Decision Record (ADR)** in `.rqml/adr/`, following the canonical format (https://rqml.org/docs/development-process/design): `NNNN-kebab-case-slug.md`, with Status, Classification, Context, Options considered, Decision, and Consequences. A decision is ADR-worthy when there are real alternatives or the choice constrains future work; skip ADRs for low-level implementation details. ADRs are immutable once accepted\u2014supersede, don\'t edit.\n\n### 3. Plan\nBreak approved requirements into a staged implementation plan at `.rqml/plan.md`, framed for coding agents: each stage names its goal, the requirement IDs it addresses, the files it touches, and how to verify it.\n\n### 4. Code (Implement)\nRead the requirement first: `rqml show REQ-XXX`. Check blast radius before changing existing artifacts: `rqml impact REQ-XXX`. Honor the ADRs. If you discover missing requirements, stop and add them to the spec first. After implementing, record the trace link:\n\n```bash\nrqml link REQ-XXX src/path/to/implementation.ts\n```\n\n### 5. Verify\nAdd tests that reference requirement IDs, then record verification and run the gate:\n\n```bash\nrqml link REQ-XXX test/path/to/test.ts --type verifiedBy\nrqml check # must exit 0 before you are done\n```\n\n---\n\n## When Code and Spec Diverge\n\n1. **Spec gap** (code has behavior not in spec): Propose adding the requirement, mark as `status="review"`\n2. **Code bug** (code doesn\'t match spec): Fix the code\n3. **Spec bug** (spec is wrong): Propose correction, wait for developer confirmation\n\n**Never silently change the spec to match code.**\n\n---\n\n## Strictness Reference\n\n| Aspect | relaxed | standard | strict | certified |\n|--------|---------|----------|--------|-----------|\n| Spec (elicitation) | Major features | Testable reqs | Edge cases | Formal |\n| Spec-first | Recommended | Required | Required | Approved first |\n| Design (ADRs) | Optional | Significant choices | All architectural choices | With approval |\n| Plan | Optional | For multi-stage work | Required | Required |\n| Code traces | Optional | New features | All changes | With metadata |\n| Verify (test traces) | Optional | New reqs | All reqs | Full matrix |\n| Ghost features | Allowed | Blocked | Blocked | Blocked |\n\n---\n\n## Change Summary Template\n\nFor PRs and commits:\n\n```\n## RQML Trace Summary\n\n**Requirements:** REQ-xxx (added/modified/implemented)\n**Design:** ADR-xxxx \u2014 decision recorded (if any)\n**Implementation:** `path/to/file` \u2014 what changed\n**Verification:** `path/to/test` \u2014 what it verifies\n**Open items:** gaps, assumptions, follow-ups\n```\n\n---\n\n## Schema Validation\n\nThe `.rqml` file must remain valid XML conforming to the version of RQML referenced in the version attribute in the spec document.\n\n**To validate:** Use the toolchain \u2014 it validates offline against the bundled schema and also checks referential integrity the XSD alone cannot enforce:\n```bash\nrqml validate\n```\n\nIf the `rqml` CLI is not installed, `npx @rqml/cli validate` works without installation. As a last resort, xmllint (pre-installed on macOS/Linux) checks XSD validity only:\n```bash\nxmllint --schema https://rqml.org/schema/rqml-2.1.0.xsd <rqml-file-name> --noout\n```\n\n**IDE validation:** If the `.rqml` file includes `xsi:schemaLocation`, XML-aware editors (VS Code with XML extension, IntelliJ) validate automatically.\n\nThe schema comments contain detailed guidance on document structure, ID conventions, and requirement quality criteria.\n\n**If unsure:** Ask the developer before making structural changes to the spec.\n';
|
|
4095
4291
|
var AGENTS_TEMPLATE = AGENTS_default;
|
|
4096
4292
|
|
|
4097
4293
|
// src/commands/init.ts
|
|
@@ -4244,6 +4440,37 @@ function runRefresh(args, edgeId) {
|
|
|
4244
4440
|
return EXIT.OK;
|
|
4245
4441
|
}
|
|
4246
4442
|
|
|
4443
|
+
// src/commands/matrix.ts
|
|
4444
|
+
function list(value) {
|
|
4445
|
+
if (value === void 0) return void 0;
|
|
4446
|
+
const items = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4447
|
+
return items.length > 0 ? items : void 0;
|
|
4448
|
+
}
|
|
4449
|
+
async function runMatrix(rest) {
|
|
4450
|
+
const args = parseArgs(rest);
|
|
4451
|
+
const { path, xml } = readSpec(specArgs(args));
|
|
4452
|
+
const parsed = parse(xml);
|
|
4453
|
+
if (!parsed.ok) {
|
|
4454
|
+
process.stderr.write(`\u2717 ${path}: ${parsed.error.message}
|
|
4455
|
+
`);
|
|
4456
|
+
return EXIT.VALIDATION;
|
|
4457
|
+
}
|
|
4458
|
+
const status = list(flagString(args, "status"));
|
|
4459
|
+
const type = list(flagString(args, "type"));
|
|
4460
|
+
const warning = list(flagString(args, "warning"));
|
|
4461
|
+
const filter = {};
|
|
4462
|
+
if (status) filter.status = status;
|
|
4463
|
+
if (type) filter.type = type;
|
|
4464
|
+
if (warning) filter.warning = warning;
|
|
4465
|
+
const filtered = status !== void 0 || type !== void 0 || warning !== void 0;
|
|
4466
|
+
const matrix = buildMatrix(parsed.document, filtered ? filter : void 0);
|
|
4467
|
+
process.stdout.write(
|
|
4468
|
+
args.json ? `${JSON.stringify(matrix, null, 2)}
|
|
4469
|
+
` : matrixToMarkdown(matrix)
|
|
4470
|
+
);
|
|
4471
|
+
return EXIT.OK;
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4247
4474
|
// src/commands/show.ts
|
|
4248
4475
|
async function runShow(rest) {
|
|
4249
4476
|
const args = parseArgs(rest);
|
|
@@ -4376,10 +4603,12 @@ Commands:
|
|
|
4376
4603
|
re-records only the baseline for an intentional change)
|
|
4377
4604
|
show <id> Extract one artifact with its trace neighborhood
|
|
4378
4605
|
impact <id> What is affected, transitively, if this artifact changes
|
|
4606
|
+
matrix [path] Traceability matrix: status, goals, code, tests, warnings
|
|
4379
4607
|
skeleton <kind> Print a schema-valid snippet (req|edge|testCase|stateMachine)
|
|
4380
4608
|
|
|
4381
4609
|
Options:
|
|
4382
|
-
--json Emit machine-readable JSON (status, check, validate, link, show, impact)
|
|
4610
|
+
--json Emit machine-readable JSON (status, check, validate, link, show, impact, matrix)
|
|
4611
|
+
--status/--type/--warning Filter matrix rows (comma-separated, e.g. --warning unverified)
|
|
4383
4612
|
--strictness <level> relaxed | standard | strict | certified (default: standard)
|
|
4384
4613
|
--base-dir <dir> Directory to resolve the spec and code links against
|
|
4385
4614
|
--spec <path> Explicit spec file (link, show, impact)
|
|
@@ -4411,6 +4640,8 @@ async function main(argv) {
|
|
|
4411
4640
|
return runShow(rest);
|
|
4412
4641
|
case "impact":
|
|
4413
4642
|
return runImpact(rest);
|
|
4643
|
+
case "matrix":
|
|
4644
|
+
return runMatrix(rest);
|
|
4414
4645
|
case "skeleton":
|
|
4415
4646
|
return runSkeleton(rest);
|
|
4416
4647
|
case "-v":
|