@rsaglobaltech/specgate 0.8.1 → 0.9.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.
Files changed (121) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +8 -8
  3. package/bin/specgate.ts +13 -2
  4. package/dist/bin/specgate.js +11 -1
  5. package/dist/bin/specgate.js.map +1 -1
  6. package/dist/packages/core/src/application/GenerateAgentPromptUseCase.js +56 -0
  7. package/dist/packages/core/src/application/GenerateAgentPromptUseCase.js.map +1 -1
  8. package/dist/packages/core/src/application/ValidateProjectUseCase.js +54 -0
  9. package/dist/packages/core/src/application/ValidateProjectUseCase.js.map +1 -1
  10. package/dist/packages/core/src/domain/AdversarialProbe.js +75 -0
  11. package/dist/packages/core/src/domain/AdversarialProbe.js.map +1 -0
  12. package/dist/packages/core/src/domain/AgentPrompt.js +52 -3
  13. package/dist/packages/core/src/domain/AgentPrompt.js.map +1 -1
  14. package/dist/packages/core/src/domain/DeclaredArtifacts.js +25 -0
  15. package/dist/packages/core/src/domain/DeclaredArtifacts.js.map +1 -1
  16. package/dist/packages/core/src/domain/DoneVerification.js +68 -0
  17. package/dist/packages/core/src/domain/DoneVerification.js.map +1 -0
  18. package/dist/packages/core/src/domain/GateCommandIntegrity.js +139 -0
  19. package/dist/packages/core/src/domain/GateCommandIntegrity.js.map +1 -0
  20. package/dist/packages/core/src/domain/HarnessConfig.js +6 -0
  21. package/dist/packages/core/src/domain/HarnessConfig.js.map +1 -1
  22. package/dist/packages/core/src/domain/MultiStack.js +96 -0
  23. package/dist/packages/core/src/domain/MultiStack.js.map +1 -0
  24. package/dist/packages/core/src/domain/Precedents.js +96 -0
  25. package/dist/packages/core/src/domain/Precedents.js.map +1 -0
  26. package/dist/packages/core/src/domain/RequirementRemoval.js +161 -0
  27. package/dist/packages/core/src/domain/RequirementRemoval.js.map +1 -0
  28. package/dist/packages/core/src/domain/ScenarioCoverage.js +178 -0
  29. package/dist/packages/core/src/domain/ScenarioCoverage.js.map +1 -0
  30. package/dist/packages/core/src/domain/SpecSections.js +206 -0
  31. package/dist/packages/core/src/domain/SpecSections.js.map +1 -0
  32. package/dist/packages/core/src/domain/TestCommand.js +82 -0
  33. package/dist/packages/core/src/domain/TestCommand.js.map +1 -0
  34. package/dist/packages/core/src/domain/TraceabilityFormat.js +37 -1
  35. package/dist/packages/core/src/domain/TraceabilityFormat.js.map +1 -1
  36. package/dist/packages/core/src/infrastructure/HarnessConfigFile.js +31 -0
  37. package/dist/packages/core/src/infrastructure/HarnessConfigFile.js.map +1 -1
  38. package/dist/packages/core/test/unit/adversarial-probe.test.js +81 -0
  39. package/dist/packages/core/test/unit/adversarial-probe.test.js.map +1 -0
  40. package/dist/packages/core/test/unit/done-verification.test.js +83 -0
  41. package/dist/packages/core/test/unit/done-verification.test.js.map +1 -0
  42. package/dist/packages/core/test/unit/gate-command-integrity.test.js +114 -0
  43. package/dist/packages/core/test/unit/gate-command-integrity.test.js.map +1 -0
  44. package/dist/packages/core/test/unit/multi-stack.test.js +86 -0
  45. package/dist/packages/core/test/unit/multi-stack.test.js.map +1 -0
  46. package/dist/packages/core/test/unit/precedents.test.js +99 -0
  47. package/dist/packages/core/test/unit/precedents.test.js.map +1 -0
  48. package/dist/packages/core/test/unit/requirement-removal.test.js +124 -0
  49. package/dist/packages/core/test/unit/requirement-removal.test.js.map +1 -0
  50. package/dist/packages/core/test/unit/scenario-coverage.test.js +146 -0
  51. package/dist/packages/core/test/unit/scenario-coverage.test.js.map +1 -0
  52. package/dist/packages/core/test/unit/spec-sections.test.js +146 -0
  53. package/dist/packages/core/test/unit/spec-sections.test.js.map +1 -0
  54. package/dist/packages/core/test/unit/test-command.test.js +81 -0
  55. package/dist/packages/core/test/unit/test-command.test.js.map +1 -0
  56. package/dist/packages/core/test/unit/validate-project.test.js +21 -0
  57. package/dist/packages/core/test/unit/validate-project.test.js.map +1 -1
  58. package/dist/scripts/agents/commands.js +1 -1
  59. package/dist/scripts/agents/commands.js.map +1 -1
  60. package/dist/scripts/agents/init.js +1 -1
  61. package/dist/scripts/agents/init.js.map +1 -1
  62. package/dist/scripts/change/instructions.js +2 -2
  63. package/dist/scripts/change/instructions.js.map +1 -1
  64. package/dist/scripts/cli/commands/change/ChangeCommand.js +2 -2
  65. package/dist/scripts/cli/commands/change/ChangeCommand.js.map +1 -1
  66. package/dist/scripts/cli/commands/config/CiInitCommand.js +1 -1
  67. package/dist/scripts/cli/commands/config/CiInitCommand.js.map +1 -1
  68. package/dist/scripts/cli/commands/harness/RunCommand.js +203 -4
  69. package/dist/scripts/cli/commands/harness/RunCommand.js.map +1 -1
  70. package/dist/scripts/cli/commands/pack/InferPackCommand.js +331 -31
  71. package/dist/scripts/cli/commands/pack/InferPackCommand.js.map +1 -1
  72. package/dist/scripts/cli/commands/project/AdoptCommand.js +81 -4
  73. package/dist/scripts/cli/commands/project/AdoptCommand.js.map +1 -1
  74. package/dist/scripts/cli/commands/project/InitProjectCommand.js +245 -45
  75. package/dist/scripts/cli/commands/project/InitProjectCommand.js.map +1 -1
  76. package/dist/scripts/cli/commands/quality/DoctorCommand.js +20 -2
  77. package/dist/scripts/cli/commands/quality/DoctorCommand.js.map +1 -1
  78. package/dist/scripts/cli/commands/quality/FixCommand.js +1 -1
  79. package/dist/scripts/cli/commands/quality/FixCommand.js.map +1 -1
  80. package/dist/scripts/cli/commands/quality/ReportCommand.js +1 -1
  81. package/dist/scripts/cli/commands/quality/ReportCommand.js.map +1 -1
  82. package/dist/scripts/cli/commands/quality/ValidateSpecsCommand.js +340 -18
  83. package/dist/scripts/cli/commands/quality/ValidateSpecsCommand.js.map +1 -1
  84. package/dist/scripts/cli/commands/spec/DoneCommand.js +108 -0
  85. package/dist/scripts/cli/commands/spec/DoneCommand.js.map +1 -1
  86. package/dist/scripts/cli/commands/spec/PlanCommand.js +7 -1
  87. package/dist/scripts/cli/commands/spec/PlanCommand.js.map +1 -1
  88. package/dist/scripts/cli/commands/spec/ReqCommand.js +112 -2
  89. package/dist/scripts/cli/commands/spec/ReqCommand.js.map +1 -1
  90. package/dist/scripts/cli/commands/spec/StatusCommand.js +2 -2
  91. package/dist/scripts/cli/commands/spec/StatusCommand.js.map +1 -1
  92. package/dist/scripts/docs/build-site.js +145 -35
  93. package/dist/scripts/docs/build-site.js.map +1 -1
  94. package/dist/scripts/docs/components.js +208 -0
  95. package/dist/scripts/docs/components.js.map +1 -0
  96. package/dist/scripts/docs/highlight.js +180 -0
  97. package/dist/scripts/docs/highlight.js.map +1 -0
  98. package/dist/scripts/docs/icons.js +63 -0
  99. package/dist/scripts/docs/icons.js.map +1 -0
  100. package/dist/scripts/docs/nav.js +9 -3
  101. package/dist/scripts/docs/nav.js.map +1 -1
  102. package/dist/scripts/gen_mcp_tools.js +22 -2
  103. package/dist/scripts/gen_mcp_tools.js.map +1 -1
  104. package/dist/scripts/harness/init.js +101 -40
  105. package/dist/scripts/harness/init.js.map +1 -1
  106. package/dist/scripts/harness/worktree-lock.js +161 -0
  107. package/dist/scripts/harness/worktree-lock.js.map +1 -0
  108. package/dist/scripts/infer_pack.js +4 -1
  109. package/dist/scripts/infer_pack.js.map +1 -1
  110. package/dist/scripts/lib/surface.js +35 -7
  111. package/dist/scripts/lib/surface.js.map +1 -1
  112. package/package.json +5 -2
  113. package/templates/adopt/AI_RULES.md.tpl +1 -1
  114. package/templates/adopt/spec.md.tpl +4 -4
  115. package/templates/base/spec.md.tpl +12 -0
  116. package/templates/ci/azure.yml.tpl +2 -2
  117. package/templates/ci/github.yml.tpl +2 -2
  118. package/templates/ci/gitlab.yml.tpl +1 -1
  119. package/templates/ci/jenkins.tpl +1 -1
  120. package/templates/contracts/test-strategy.md.tpl +2 -2
  121. package/templates/harness/prompt-prefix.md.tpl +2 -5
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Choosing the precedent (D2).
5
+ *
6
+ * The prompt is entirely normative — facts, Gherkin, rules, definition of done.
7
+ * None of it shows an agent what an accepted implementation looks like *here*,
8
+ * so it invents a house style and the next attempt is spent correcting it.
9
+ *
10
+ * The rules below are the ones that decide whether a precedent helps or hurts:
11
+ * a precedent from another bounded context teaches conventions that do not
12
+ * apply, with the authority of "this was accepted".
13
+ */
14
+ const test = require("node:test");
15
+ const assert = require("node:assert/strict");
16
+ const { choosePrecedent, excerpt } = require("../../src/domain/Precedents");
17
+ const row = (id, status, testArtifact = "", technicalArtifact = "") => ({
18
+ requirementId: id,
19
+ status,
20
+ testArtifact,
21
+ technicalArtifact,
22
+ });
23
+ test("the most recent accepted requirement in the same context wins", () => {
24
+ const rows = [
25
+ row("REQ-001", "Verified", "t/one.test.ts", "src/one.ts"),
26
+ row("REQ-002", "Verified", "t/two.test.ts", "src/two.ts"),
27
+ ];
28
+ const contexts = { "REQ-001": "Billing", "REQ-002": "Billing", "REQ-003": "Billing" };
29
+ const choice = choosePrecedent(rows, contexts, "REQ-003");
30
+ assert.equal(choice.requirementId, "REQ-002");
31
+ assert.equal(choice.testArtifact, "t/two.test.ts");
32
+ assert.equal(choice.technicalArtifact, "src/two.ts");
33
+ });
34
+ test("a different bounded context is not a precedent", () => {
35
+ // Worse than none: it teaches conventions that do not apply, with the
36
+ // authority of having been accepted.
37
+ const rows = [row("REQ-001", "Verified", "t/one.test.ts", "src/one.ts")];
38
+ const contexts = { "REQ-001": "Billing", "REQ-002": "Shipping" };
39
+ assert.equal(choosePrecedent(rows, contexts, "REQ-002"), null);
40
+ });
41
+ test("a project with no contexts at all shares one", () => {
42
+ // A codebase nobody has divided yet is one context, not none.
43
+ const rows = [row("REQ-001", "Verified", "t/one.test.ts", "src/one.ts")];
44
+ assert.equal(choosePrecedent(rows, {}, "REQ-002").requirementId, "REQ-001");
45
+ });
46
+ test("Implemented is not Verified", () => {
47
+ // `Implemented` means the code exists; `Verified` means somebody checked it.
48
+ // Copying an unchecked implementation propagates whatever is wrong with it.
49
+ const rows = [row("REQ-001", "Implemented", "t/one.test.ts", "src/one.ts")];
50
+ assert.equal(choosePrecedent(rows, {}, "REQ-002"), null);
51
+ });
52
+ test("status matching ignores case and padding", () => {
53
+ const rows = [row("REQ-001", " verified ", "t/one.test.ts", "src/one.ts")];
54
+ assert.equal(choosePrecedent(rows, {}, "REQ-002").requirementId, "REQ-001");
55
+ });
56
+ test("a row with no artifacts is not a precedent", () => {
57
+ // There is nothing to show. A section naming a requirement and quoting
58
+ // nothing is worse than no section.
59
+ const rows = [row("REQ-001", "Verified", "TBD", "-")];
60
+ assert.equal(choosePrecedent(rows, {}, "REQ-002"), null);
61
+ });
62
+ test("one artifact is enough", () => {
63
+ const rows = [row("REQ-001", "Verified", "t/one.test.ts", "TBD")];
64
+ const choice = choosePrecedent(rows, {}, "REQ-002");
65
+ assert.equal(choice.testArtifact, "t/one.test.ts");
66
+ assert.equal(choice.technicalArtifact, "", "a placeholder must not become a path");
67
+ });
68
+ test("a requirement is never its own precedent", () => {
69
+ const rows = [row("REQ-002", "Verified", "t/two.test.ts", "src/two.ts")];
70
+ assert.equal(choosePrecedent(rows, {}, "REQ-002"), null);
71
+ });
72
+ test("a later requirement is not a precedent for an earlier one", () => {
73
+ // Precedent means already settled. Pointing REQ-001 at REQ-009 would make
74
+ // the example depend on work that may not exist when REQ-001 is attempted.
75
+ const rows = [row("REQ-009", "Verified", "t/nine.test.ts", "src/nine.ts")];
76
+ assert.equal(choosePrecedent(rows, {}, "REQ-001"), null);
77
+ });
78
+ test("backticks around a path are not part of the path", () => {
79
+ const rows = [row("REQ-001", "Verified", "`t/one.test.ts`", "`src/one.ts`")];
80
+ const choice = choosePrecedent(rows, {}, "REQ-002");
81
+ assert.equal(choice.testArtifact, "t/one.test.ts");
82
+ });
83
+ // ── excerpt ──────────────────────────────────────────────────────────────────
84
+ test("a short file is quoted whole", () => {
85
+ assert.equal(excerpt("a\nb\nc"), "a\nb\nc");
86
+ });
87
+ test("a long file is cut and says so", () => {
88
+ // A thousand-line class in the prompt costs the agent the budget it needs
89
+ // for its own work, and the part that teaches the convention is the top.
90
+ const out = excerpt(Array.from({ length: 100 }, (_, i) => `line ${i}`).join("\n"), 10);
91
+ assert.match(out, /^line 0\n/);
92
+ assert.match(out, /… \(90 more lines\)$/);
93
+ assert.doesNotMatch(out, /line 50/);
94
+ });
95
+ test("excerpt survives an empty file", () => {
96
+ assert.equal(excerpt(""), "");
97
+ assert.equal(excerpt(null), "");
98
+ });
99
+ //# sourceMappingURL=precedents.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"precedents.test.js","sourceRoot":"","sources":["../../../../../packages/core/test/unit/precedents.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * Fase 4.1: `req add` had no counterpart, so two cold adoptions repaired the
4
+ * matrix by hand with a script.
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const node_test_1 = require("node:test");
41
+ const assert = __importStar(require("node:assert/strict"));
42
+ const RequirementRemoval_1 = require("../../src/domain/RequirementRemoval");
43
+ const HEADER = "| Requirement | Scenario ID | Feature file | Use Case | Command/Query | Aggregate | Event | Technical artifact | Test artifact | Status |\n" +
44
+ "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |";
45
+ const MATRIX = [
46
+ HEADER,
47
+ "| REQ-001 | SCN-001 | `features/core/health.feature` | UC-001 Health | - | - | - | src/h.js | tests/h.js | Verified |",
48
+ "| REQ-002 | SCN-002 | `features/billing/totals.feature` | UC-002 Totals | - | - | - | src/t.js | tests/t.js | Draft |",
49
+ "| REQ-003 | SCN-003 | `features/core/health.feature` | UC-003 Also health | - | - | - | src/x.js | tests/x.js | Draft |",
50
+ ].join("\n");
51
+ const SPEC = [
52
+ "# Spec",
53
+ "",
54
+ "## 8. Key requirements",
55
+ "",
56
+ "| ID | Requirement | Priority | Status |",
57
+ "|---|---|---|---|",
58
+ "| REQ-002 | Totals are rounded | Must | Draft |",
59
+ "",
60
+ "### REQ-002 — Totals are rounded",
61
+ "",
62
+ "The system MUST satisfy: Totals are rounded.",
63
+ "",
64
+ "### REQ-003 — Also health",
65
+ "",
66
+ "kept",
67
+ ].join("\n");
68
+ (0, node_test_1.test)("the plan names every row the id owns", () => {
69
+ const plan = (0, RequirementRemoval_1.planRemoval)(MATRIX, "REQ-002", SPEC);
70
+ assert.equal(plan.rows.length, 1);
71
+ assert.deepEqual(plan.statuses, ["Draft"]);
72
+ assert.equal(plan.hasProse, true);
73
+ });
74
+ (0, node_test_1.test)("a feature file another row still uses is not orphaned", () => {
75
+ // REQ-001 and REQ-003 share health.feature; removing one leaves it referenced.
76
+ assert.deepEqual((0, RequirementRemoval_1.planRemoval)(MATRIX, "REQ-001", SPEC).orphanedFeatures, []);
77
+ assert.deepEqual((0, RequirementRemoval_1.planRemoval)(MATRIX, "REQ-002", SPEC).orphanedFeatures, [
78
+ "features/billing/totals.feature",
79
+ ]);
80
+ });
81
+ (0, node_test_1.test)("a delivered requirement is flagged, a draft is not", () => {
82
+ // Removing an Implemented row deletes the record that something shipped.
83
+ assert.equal((0, RequirementRemoval_1.isDelivered)("Verified"), true);
84
+ assert.equal((0, RequirementRemoval_1.isDelivered)("Implemented"), true);
85
+ assert.equal((0, RequirementRemoval_1.isDelivered)("Draft"), false);
86
+ assert.equal((0, RequirementRemoval_1.isDelivered)(""), false);
87
+ assert.equal((0, RequirementRemoval_1.planRemoval)(MATRIX, "REQ-001", SPEC).statuses.some(RequirementRemoval_1.isDelivered), true);
88
+ });
89
+ (0, node_test_1.test)("removing takes the row and leaves the others alone", () => {
90
+ const after = (0, RequirementRemoval_1.removeMatrixRows)(MATRIX, "REQ-002");
91
+ assert.ok(!after.includes("REQ-002"));
92
+ assert.ok(after.includes("REQ-001"));
93
+ assert.ok(after.includes("REQ-003"));
94
+ assert.ok(after.includes("| Requirement | Scenario ID |"), "the header survives");
95
+ });
96
+ (0, node_test_1.test)("duplicate rows under one id all go", () => {
97
+ const withDupe = MATRIX + "\n| REQ-002 | SCN-009 | - | UC-002 again | - | - | - | - | - | Draft |";
98
+ assert.equal((0, RequirementRemoval_1.planRemoval)(withDupe, "REQ-002", SPEC).rows.length, 2);
99
+ assert.ok(!(0, RequirementRemoval_1.removeMatrixRows)(withDupe, "REQ-002").includes("REQ-002"));
100
+ });
101
+ (0, node_test_1.test)("the prose goes: both the section and the table row", () => {
102
+ const after = (0, RequirementRemoval_1.removeSpecProse)(SPEC, "REQ-002");
103
+ assert.ok(!after.includes("REQ-002"));
104
+ assert.ok(!after.includes("Totals are rounded"), "the section body goes with its heading");
105
+ assert.ok(after.includes("### REQ-003 — Also health"), "the next requirement survives");
106
+ assert.ok(after.includes("kept"));
107
+ assert.ok(after.includes("## 8. Key requirements"), "the enclosing section survives");
108
+ });
109
+ (0, node_test_1.test)("removing the last requirement does not eat the rest of the document", () => {
110
+ const spec = "## 8. Key requirements\n\n### REQ-002 — Gone\n\nbody\n\n## 9. Non-functional\n\nkept\n";
111
+ const after = (0, RequirementRemoval_1.removeSpecProse)(spec, "REQ-002");
112
+ assert.ok(!after.includes("REQ-002"));
113
+ assert.ok(after.includes("## 9. Non-functional"));
114
+ assert.ok(after.includes("kept"));
115
+ });
116
+ (0, node_test_1.test)("an id that is not there changes nothing", () => {
117
+ assert.equal((0, RequirementRemoval_1.removeMatrixRows)(MATRIX, "REQ-999"), MATRIX);
118
+ assert.equal((0, RequirementRemoval_1.planRemoval)(MATRIX, "REQ-999", SPEC).rows.length, 0);
119
+ });
120
+ (0, node_test_1.test)("REQ-01 does not match REQ-014", () => {
121
+ const m = HEADER + "\n| REQ-014 | - | - | UC | - | - | - | - | - | Draft |";
122
+ assert.equal((0, RequirementRemoval_1.removeMatrixRows)(m, "REQ-01"), m);
123
+ });
124
+ //# sourceMappingURL=requirement-removal.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirement-removal.test.js","sourceRoot":"","sources":["../../../../../packages/core/test/unit/requirement-removal.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * #168: a scenario declared in a feature file that nothing tests.
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const node_test_1 = require("node:test");
40
+ const assert = __importStar(require("node:assert/strict"));
41
+ const ScenarioCoverage_1 = require("../../src/domain/ScenarioCoverage");
42
+ const FEATURE = `Feature: Invoice totals
43
+
44
+ Scenario: SCN-010 subtotal is the sum of line amounts
45
+ Given two lines
46
+ When the invoice is totalled
47
+ Then the subtotal is their sum
48
+
49
+ Scenario: SCN-011 tax is applied per line
50
+ Given two rates
51
+ When the invoice is totalled
52
+ Then each line carries its own rate
53
+
54
+ Scenario: SCN-012 a fully discounted invoice carries no tax
55
+ Given a 100% discount
56
+ When the invoice is totalled
57
+ Then the tax is zero
58
+ `;
59
+ (0, node_test_1.test)("every scenario in the file is found, not just the one the row names", () => {
60
+ assert.deepEqual((0, ScenarioCoverage_1.scenariosIn)(FEATURE).map((s) => s.needles[0]), ["SCN-010", "SCN-011", "SCN-012"]);
61
+ });
62
+ (0, node_test_1.test)("the scenario nothing tests is the one reported", () => {
63
+ // The measured case: an agent wrote tests for the scenarios it could satisfy
64
+ // and skipped the one it could not.
65
+ const tests = [
66
+ `test("SCN-010 subtotal is the sum of line amounts", () => {});
67
+ test("SCN-011 tax is applied per line", () => {});`,
68
+ ];
69
+ const missing = (0, ScenarioCoverage_1.uncoveredScenarios)(FEATURE, tests);
70
+ assert.equal(missing.length, 1);
71
+ assert.match(missing[0].title, /fully discounted/);
72
+ });
73
+ (0, node_test_1.test)("a fully covered feature reports nothing", () => {
74
+ const tests = ["SCN-010 SCN-011 SCN-012"];
75
+ assert.deepEqual((0, ScenarioCoverage_1.uncoveredScenarios)(FEATURE, tests), []);
76
+ });
77
+ (0, node_test_1.test)("a tag identifies a scenario even after the title is reworded", () => {
78
+ // The one edit that otherwise defeats every link to a scenario.
79
+ const tagged = `Feature: F
80
+
81
+ @REQ-001 @SCN-042
82
+ Scenario: something else entirely now
83
+ Given a thing
84
+ When it happens
85
+ Then it holds
86
+ `;
87
+ assert.deepEqual((0, ScenarioCoverage_1.scenariosIn)(tagged)[0].needles, ["SCN-042"]);
88
+ assert.deepEqual((0, ScenarioCoverage_1.uncoveredScenarios)(tagged, ["covers @SCN-042 here"]), []);
89
+ });
90
+ (0, node_test_1.test)("without an id anywhere, the title is the identity", () => {
91
+ const untagged = `Feature: F
92
+
93
+ Scenario: the cart rejects a negative quantity
94
+ Given a cart
95
+ When a negative quantity is added
96
+ Then it is rejected
97
+ `;
98
+ assert.deepEqual((0, ScenarioCoverage_1.scenariosIn)(untagged)[0].needles, ["the cart rejects a negative quantity"]);
99
+ assert.deepEqual((0, ScenarioCoverage_1.uncoveredScenarios)(untagged, ['test("The cart rejects a negative quantity!", ...)']), [], "punctuation and case must not break a title match");
100
+ assert.equal((0, ScenarioCoverage_1.uncoveredScenarios)(untagged, ["something unrelated"]).length, 1);
101
+ });
102
+ (0, node_test_1.test)("a Scenario Outline counts — the widest scenarios must not be the unchecked ones", () => {
103
+ const outline = `Feature: F
104
+
105
+ Scenario Outline: SCN-050 rounding across currencies
106
+ Given <amount>
107
+ When it is rounded
108
+ Then it is <result>
109
+
110
+ Examples:
111
+ | amount | result |
112
+ | 1.005 | 1.01 |
113
+ `;
114
+ assert.equal((0, ScenarioCoverage_1.scenariosIn)(outline).length, 1);
115
+ assert.equal((0, ScenarioCoverage_1.uncoveredScenarios)(outline, ["nothing"]).length, 1);
116
+ });
117
+ (0, node_test_1.test)("a scenario proved in any of several test files is proved", () => {
118
+ const missing = (0, ScenarioCoverage_1.uncoveredScenarios)(FEATURE, ["SCN-010", "SCN-011", "SCN-012 over here"]);
119
+ assert.deepEqual(missing, []);
120
+ });
121
+ (0, node_test_1.test)("no test sources at all leaves every scenario uncovered", () => {
122
+ assert.equal((0, ScenarioCoverage_1.uncoveredScenarios)(FEATURE, []).length, 3);
123
+ });
124
+ (0, node_test_1.test)("normalise strips what should not decide a match", () => {
125
+ assert.equal((0, ScenarioCoverage_1.normalise)("SCN-010: Subtotal!"), (0, ScenarioCoverage_1.normalise)("scn010 subtotal"));
126
+ });
127
+ // ── Fase 1.2: a link that exists but lies ────────────────────────────────────
128
+ (0, node_test_1.test)("a test artifact naming the requirement is evidence enough", () => {
129
+ assert.equal((0, ScenarioCoverage_1.linkIsUnevidenced)("REQ-014", "", ['test("REQ-014 vets are listed")']), false);
130
+ });
131
+ (0, node_test_1.test)("a test artifact naming one of the requirement's scenarios is evidence", () => {
132
+ assert.equal((0, ScenarioCoverage_1.linkIsUnevidenced)("REQ-010", FEATURE, ["SCN-011 tax"]), false);
133
+ });
134
+ (0, node_test_1.test)("the measured lie: a seeded row pointed at somebody else's test", () => {
135
+ // A "Vet" requirement declaring PetValidatorTests.java as its proof. The row
136
+ // has no scenario, so there is nothing for the scenario check to match on —
137
+ // which is exactly why those rows lie most easily.
138
+ assert.equal((0, ScenarioCoverage_1.linkIsUnevidenced)("REQ-014", "", ["class PetTypeFormatterTests { void formatsAName() {} }"]), true);
139
+ });
140
+ (0, node_test_1.test)("no test sources at all is not evidence", () => {
141
+ assert.equal((0, ScenarioCoverage_1.linkIsUnevidenced)("REQ-014", FEATURE, []), true);
142
+ });
143
+ (0, node_test_1.test)("a row with no requirement id is not this check's business", () => {
144
+ assert.equal((0, ScenarioCoverage_1.linkIsUnevidenced)("", FEATURE, ["anything"]), false);
145
+ });
146
+ //# sourceMappingURL=scenario-coverage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenario-coverage.test.js","sourceRoot":"","sources":["../../../../../packages/core/test/unit/scenario-coverage.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * Fase 3.1: `req add` wrote a matrix row and no prose anywhere.
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const node_test_1 = require("node:test");
40
+ const assert = __importStar(require("node:assert/strict"));
41
+ const SpecSections_1 = require("../../src/domain/SpecSections");
42
+ (0, node_test_1.test)("the generated section carries an obligation keyword", () => {
43
+ // Otherwise the section fails --strict-requirements, and a project would be
44
+ // failing a gate over text the tool itself just wrote.
45
+ const section = (0, SpecSections_1.renderRequirementSection)("REQ-014", "Vets are listed alphabetically");
46
+ assert.match(section, /^## REQ-014 — Vets are listed alphabetically$/m);
47
+ assert.match(section, /\bMUST\b/);
48
+ });
49
+ (0, node_test_1.test)("the section says it is a draft, rather than pretending to be finished", () => {
50
+ assert.match((0, SpecSections_1.renderRequirementSection)("REQ-014", "Vets are listed"), /Replace this sentence/);
51
+ });
52
+ (0, node_test_1.test)("a title starting with an identifier still reads as a sentence", () => {
53
+ // `MUST res.redirect sets Location` is not English. `MUST satisfy:` is, for
54
+ // every title, without a heuristic that guesses at grammar.
55
+ assert.match((0, SpecSections_1.renderRequirementSection)("REQ-002", "res.redirect sets Location and status 302"), /MUST satisfy: res\.redirect sets Location and status 302\./);
56
+ assert.match((0, SpecSections_1.renderRequirementSection)("REQ-003", "HTTP caching is respected"), /MUST satisfy: HTTP caching is respected\./);
57
+ });
58
+ (0, node_test_1.test)("an existing section is found, and a different id is not", () => {
59
+ const spec = "# Spec\n\n## REQ-001 — One\n\ntext\n\n## REQ-014 — Fourteen\n\ntext\n";
60
+ assert.equal((0, SpecSections_1.hasRequirementSection)(spec, "REQ-014"), true);
61
+ assert.equal((0, SpecSections_1.hasRequirementSection)(spec, "REQ-01"), false, "REQ-01 must not match REQ-014");
62
+ assert.equal((0, SpecSections_1.hasRequirementSection)(spec, "REQ-999"), false);
63
+ assert.deepEqual((0, SpecSections_1.requirementSections)(spec), ["REQ-001", "REQ-014"]);
64
+ });
65
+ (0, node_test_1.test)("a mention in prose is not a section, but a nested heading is", () => {
66
+ // The shipped template puts requirements under "8. Key requirements", so
67
+ // insisting on top-level `##` would reject documents that are perfectly well
68
+ // organised. A sentence naming the id is still not a section.
69
+ assert.equal((0, SpecSections_1.hasRequirementSection)("# Spec\n\nSee REQ-014 for details.\n", "REQ-014"), false);
70
+ assert.equal((0, SpecSections_1.hasRequirementSection)("# Spec\n\n### REQ-014 — Nested\n\ntext\n", "REQ-014"), true);
71
+ assert.equal((0, SpecSections_1.hasRequirementSection)("# Spec\n\n##### REQ-014 — Too deep\n", "REQ-014"), false);
72
+ });
73
+ (0, node_test_1.test)("a nested section ends at the next heading of its own level", () => {
74
+ const spec = [
75
+ "## 8. Key requirements",
76
+ "",
77
+ "### REQ-001 — First",
78
+ "",
79
+ "The system MUST do the first thing.",
80
+ "",
81
+ "#### Notes",
82
+ "",
83
+ "still part of REQ-001",
84
+ "",
85
+ "### REQ-002 — Second",
86
+ "",
87
+ "not part of REQ-001",
88
+ ].join("\n");
89
+ const section = (0, SpecSections_1.extractRequirementSection)(spec, "REQ-001");
90
+ assert.match(section, /MUST do the first thing/);
91
+ assert.match(section, /still part of REQ-001/, "a deeper sub-heading stays inside");
92
+ assert.doesNotMatch(section, /not part of REQ-001/);
93
+ });
94
+ (0, node_test_1.test)("appending adds the section once and is idempotent", () => {
95
+ const before = "# Spec\n\n## REQ-001 — One\n\ntext\n";
96
+ const first = (0, SpecSections_1.appendRequirementSection)(before, "REQ-002", "Totals are rounded");
97
+ assert.equal(first.added, true);
98
+ assert.equal((0, SpecSections_1.hasRequirementSection)(first.content, "REQ-002"), true);
99
+ const second = (0, SpecSections_1.appendRequirementSection)(first.content, "REQ-002", "Totals are rounded");
100
+ assert.equal(second.added, false);
101
+ assert.equal(second.content, first.content, "running req add twice must not duplicate prose");
102
+ });
103
+ (0, node_test_1.test)("appending does not reorder what a person wrote", () => {
104
+ // spec.md is a document someone writes in. Sorting their prose to satisfy an
105
+ // id order is a worse surprise than a section at the end.
106
+ const before = "# Spec\n\n## REQ-050 — Later\n\ntext\n";
107
+ const after = (0, SpecSections_1.appendRequirementSection)(before, "REQ-002", "Earlier").content;
108
+ assert.ok(after.indexOf("REQ-050") < after.indexOf("REQ-002"));
109
+ });
110
+ (0, node_test_1.test)("an empty spec gets a section without a leading blank run", () => {
111
+ const { content } = (0, SpecSections_1.appendRequirementSection)("", "REQ-001", "First");
112
+ assert.ok(content.startsWith("## REQ-001"));
113
+ });
114
+ // ── The shapes real documents turned out to use ──────────────────────────────
115
+ (0, node_test_1.test)("a labelled heading declares its requirement", () => {
116
+ // `### Requirement: REQ-100 — title` is what capability specs use. Four
117
+ // shapes turned up before this stopped enumerating them.
118
+ const spec = "## Requirements\n\n### Requirement: REQ-100 — A change is a folder\n\ntext\n";
119
+ assert.equal((0, SpecSections_1.hasRequirementSection)(spec, "REQ-100"), true);
120
+ assert.match((0, SpecSections_1.extractRequirementSection)(spec, "REQ-100"), /A change is a folder/);
121
+ });
122
+ (0, node_test_1.test)("the §8 table counts as the requirement's text", () => {
123
+ // The original `init` template, and this repository's own spec.md. Outlawing
124
+ // it would have turned every project built from that template red on
125
+ // upgrade, over text that is right there in the document.
126
+ const spec = [
127
+ "## 8. Key requirements",
128
+ "",
129
+ "| ID | Requirement | Priority | Status |",
130
+ "|---|---|---|---|",
131
+ "| REQ-001 | `init` generates a valid project skeleton. | Must | Verified |",
132
+ "| REQ-002 | - | Must | Draft |",
133
+ ].join("\n");
134
+ assert.equal((0, SpecSections_1.hasRequirementSection)(spec, "REQ-001"), true);
135
+ assert.match((0, SpecSections_1.extractRequirementSection)(spec, "REQ-001"), /generates a valid project skeleton/);
136
+ // A row with no prose is an id with nothing behind it — the case worth catching.
137
+ assert.equal((0, SpecSections_1.hasRequirementSection)(spec, "REQ-002"), false);
138
+ assert.deepEqual((0, SpecSections_1.requirementSections)(spec), ["REQ-001"]);
139
+ });
140
+ (0, node_test_1.test)("a TBD or empty requirement cell is not text", () => {
141
+ for (const cell of ["TBD", "", " ", "-"]) {
142
+ const spec = `| ID | Requirement |\n|---|---|\n| REQ-005 | ${cell} |\n`;
143
+ assert.equal((0, SpecSections_1.hasRequirementSection)(spec, "REQ-005"), false, `'${cell}' should not count`);
144
+ }
145
+ });
146
+ //# sourceMappingURL=spec-sections.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-sections.test.js","sourceRoot":"","sources":["../../../../../packages/core/test/unit/spec-sections.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ /**
3
+ * Fase 2.3: two detectors disagreed on the same pom.xml.
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const node_test_1 = require("node:test");
40
+ const assert = __importStar(require("node:assert/strict"));
41
+ const TestCommand_1 = require("../../src/domain/TestCommand");
42
+ function files(map) {
43
+ return {
44
+ exists: (p) => Object.prototype.hasOwnProperty.call(map, p),
45
+ read: (p) => (typeof map[p] === "string" ? map[p] : null),
46
+ };
47
+ }
48
+ (0, node_test_1.test)("the wrapper wins, because the harness runs in a bare worktree", () => {
49
+ // `./mvnw` is checked in and works there; `mvn` depends on the machine.
50
+ // Getting this wrong burns one of the agent's three attempts on
51
+ // "command not found".
52
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "pom.xml": true, mvnw: true })), "./mvnw -B test");
53
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "build.gradle": true, gradlew: true })), "./gradlew test");
54
+ });
55
+ (0, node_test_1.test)("without a wrapper, the system tool is the only option", () => {
56
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "pom.xml": true })), "mvn -B test");
57
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "build.gradle.kts": true })), "gradle test");
58
+ });
59
+ (0, node_test_1.test)("a declared npm script is the project's own answer", () => {
60
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "package.json": '{"scripts":{"test":"vitest"}}' })), "npm test");
61
+ });
62
+ (0, node_test_1.test)("a malformed package.json does not crash the caller", () => {
63
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "package.json": "{ not json" })), "npm test");
64
+ });
65
+ (0, node_test_1.test)("the other ecosystems", () => {
66
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "Cargo.toml": true })), "cargo test");
67
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "go.mod": true })), "go test ./...");
68
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "pyproject.toml": true })), "pytest");
69
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "setup.py": true })), "pytest");
70
+ });
71
+ (0, node_test_1.test)("nothing recognisable returns null, and null is a real answer", () => {
72
+ // Guessing `npm test` into a Python repository is how AI_RULES.md ended up
73
+ // reading `Testing: unknown` two lines above `Test command: python -m pytest`.
74
+ assert.equal((0, TestCommand_1.detectTestCommand)(files({ "README.md": true })), null);
75
+ });
76
+ (0, node_test_1.test)("both callers would now agree on the same project", () => {
77
+ const project = files({ "pom.xml": true, mvnw: true });
78
+ assert.equal((0, TestCommand_1.detectTestCommand)(project), (0, TestCommand_1.detectTestCommand)(project));
79
+ assert.equal((0, TestCommand_1.detectTestCommand)(project), "./mvnw -B test");
80
+ });
81
+ //# sourceMappingURL=test-command.test.js.map