@rqml/cli 0.3.0 → 0.4.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 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
- --json Machine-readable output (REQ-CLI-JSON)
23
- --strictness <level> relaxed | standard | strict | certified
24
- --base-dir <dir> Resolve the spec and implements code links against <dir>
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 matches = [];
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
- matches.push(allmatches);
26
+ matches2.push(allmatches);
27
27
  match = regex.exec(string);
28
28
  }
29
- return matches;
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 matches = util.getAllMatches(attrStr, validAttrStrRegxp);
307
+ const matches2 = util.getAllMatches(attrStr, validAttrStrRegxp);
308
308
  const attrNames = {};
309
- for (let i = 0; i < matches.length; i++) {
310
- if (matches[i][1].length === 0) {
311
- return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' has no space in starting.", getPositionFromMatch(matches[i]));
312
- } else if (matches[i][3] !== void 0 && matches[i][4] === void 0) {
313
- return getErrorObject("InvalidAttr", "Attribute '" + matches[i][2] + "' is without value.", getPositionFromMatch(matches[i]));
314
- } else if (matches[i][3] === void 0 && !options.allowBooleanAttributes) {
315
- return getErrorObject("InvalidAttr", "boolean attribute '" + matches[i][2] + "' is not allowed.", getPositionFromMatch(matches[i]));
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 = matches[i][2];
317
+ const attrName = matches2[i][2];
318
318
  if (!validateAttrName(attrName)) {
319
- return getErrorObject("InvalidAttr", "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(matches[i]));
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(matches[i]));
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 matches = util.getAllMatches(attrStr, attrsRegx);
1062
- const len = matches.length;
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(matches[i][1]);
1065
+ const attrName = this.resolveNameSpace(matches2[i][1]);
1066
1066
  if (this.ignoreAttributesFn(attrName, jPath)) {
1067
1067
  continue;
1068
1068
  }
1069
- let oldVal = matches[i][4];
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 matches = val.match(entity.regx);
1332
- if (matches) {
1333
- this.entityExpansionCount += matches.length;
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 matches = val.match(entity.regex);
1355
- if (matches) {
1356
- this.entityExpansionCount += matches.length;
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 matches = val.match(entity.regex);
1370
- if (matches) {
1371
- this.entityExpansionCount += matches.length;
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\n```\n[Elicit] \u2192 [Specify] \u2192 [Implement] \u2192 [Verify] \u2192 [Trace]\n \u2191____________________\u2190______________________|\n```\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\n---\n\n## Workflow\n\n### 1. Elicit\nAsk clarifying questions until you understand the goal, scope, acceptance criteria, and constraints. Don\'t assume\u2014capture assumptions as `<notes>` or `<issue>` elements.\n\n### 2. Specify\n**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 before proceeding\n\n### 3. Implement\nRead the requirement first: `rqml show REQ-XXX`. Check blast radius before changing existing artifacts: `rqml impact REQ-XXX`. 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### 4. Verify\nAdd tests that reference requirement IDs, then record verification:\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| Elicitation | Major features | Testable reqs | Edge cases | Formal |\n| Spec-first | Recommended | Required | Required | Approved first |\n| Code traces | Optional | New features | All changes | With metadata |\n| 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**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';
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 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":