@rsaglobaltech/specgate 0.8.1 → 0.10.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 (144) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/README.md +17 -25
  3. package/bin/specgate.ts +23 -5
  4. package/dist/bin/specgate.js +19 -4
  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 +89 -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/GherkinQuality.js +19 -3
  21. package/dist/packages/core/src/domain/GherkinQuality.js.map +1 -1
  22. package/dist/packages/core/src/domain/HarnessConfig.js +6 -0
  23. package/dist/packages/core/src/domain/HarnessConfig.js.map +1 -1
  24. package/dist/packages/core/src/domain/MultiStack.js +96 -0
  25. package/dist/packages/core/src/domain/MultiStack.js.map +1 -0
  26. package/dist/packages/core/src/domain/NewRequirement.js +46 -0
  27. package/dist/packages/core/src/domain/NewRequirement.js.map +1 -0
  28. package/dist/packages/core/src/domain/Precedents.js +96 -0
  29. package/dist/packages/core/src/domain/Precedents.js.map +1 -0
  30. package/dist/packages/core/src/domain/RequirementPlan.js +1 -0
  31. package/dist/packages/core/src/domain/RequirementPlan.js.map +1 -1
  32. package/dist/packages/core/src/domain/RequirementRemoval.js +161 -0
  33. package/dist/packages/core/src/domain/RequirementRemoval.js.map +1 -0
  34. package/dist/packages/core/src/domain/ScenarioCoverage.js +178 -0
  35. package/dist/packages/core/src/domain/ScenarioCoverage.js.map +1 -0
  36. package/dist/packages/core/src/domain/SpecSections.js +206 -0
  37. package/dist/packages/core/src/domain/SpecSections.js.map +1 -0
  38. package/dist/packages/core/src/domain/TestCommand.js +82 -0
  39. package/dist/packages/core/src/domain/TestCommand.js.map +1 -0
  40. package/dist/packages/core/src/domain/TraceabilityFormat.js +45 -2
  41. package/dist/packages/core/src/domain/TraceabilityFormat.js.map +1 -1
  42. package/dist/packages/core/src/infrastructure/HarnessConfigFile.js +31 -0
  43. package/dist/packages/core/src/infrastructure/HarnessConfigFile.js.map +1 -1
  44. package/dist/packages/core/test/unit/adversarial-probe.test.js +81 -0
  45. package/dist/packages/core/test/unit/adversarial-probe.test.js.map +1 -0
  46. package/dist/packages/core/test/unit/done-verification.test.js +84 -0
  47. package/dist/packages/core/test/unit/done-verification.test.js.map +1 -0
  48. package/dist/packages/core/test/unit/gate-command-integrity.test.js +114 -0
  49. package/dist/packages/core/test/unit/gate-command-integrity.test.js.map +1 -0
  50. package/dist/packages/core/test/unit/gherkin-quality.test.js +28 -0
  51. package/dist/packages/core/test/unit/gherkin-quality.test.js.map +1 -1
  52. package/dist/packages/core/test/unit/multi-stack.test.js +86 -0
  53. package/dist/packages/core/test/unit/multi-stack.test.js.map +1 -0
  54. package/dist/packages/core/test/unit/new-requirement.test.js +67 -0
  55. package/dist/packages/core/test/unit/new-requirement.test.js.map +1 -0
  56. package/dist/packages/core/test/unit/precedents.test.js +99 -0
  57. package/dist/packages/core/test/unit/precedents.test.js.map +1 -0
  58. package/dist/packages/core/test/unit/requirement-removal.test.js +124 -0
  59. package/dist/packages/core/test/unit/requirement-removal.test.js.map +1 -0
  60. package/dist/packages/core/test/unit/scenario-coverage.test.js +146 -0
  61. package/dist/packages/core/test/unit/scenario-coverage.test.js.map +1 -0
  62. package/dist/packages/core/test/unit/spec-sections.test.js +146 -0
  63. package/dist/packages/core/test/unit/spec-sections.test.js.map +1 -0
  64. package/dist/packages/core/test/unit/test-command.test.js +81 -0
  65. package/dist/packages/core/test/unit/test-command.test.js.map +1 -0
  66. package/dist/packages/core/test/unit/validate-project.test.js +21 -0
  67. package/dist/packages/core/test/unit/validate-project.test.js.map +1 -1
  68. package/dist/scripts/agents/commands.js +1 -1
  69. package/dist/scripts/agents/commands.js.map +1 -1
  70. package/dist/scripts/agents/init.js +59 -15
  71. package/dist/scripts/agents/init.js.map +1 -1
  72. package/dist/scripts/change/instructions.js +2 -2
  73. package/dist/scripts/change/instructions.js.map +1 -1
  74. package/dist/scripts/check.js +10 -0
  75. package/dist/scripts/check.js.map +1 -0
  76. package/dist/scripts/cli/commands/change/ChangeCommand.js +2 -2
  77. package/dist/scripts/cli/commands/change/ChangeCommand.js.map +1 -1
  78. package/dist/scripts/cli/commands/config/CiInitCommand.js +1 -1
  79. package/dist/scripts/cli/commands/config/CiInitCommand.js.map +1 -1
  80. package/dist/scripts/cli/commands/harness/RunCommand.js +214 -9
  81. package/dist/scripts/cli/commands/harness/RunCommand.js.map +1 -1
  82. package/dist/scripts/cli/commands/pack/InferPackCommand.js +331 -31
  83. package/dist/scripts/cli/commands/pack/InferPackCommand.js.map +1 -1
  84. package/dist/scripts/cli/commands/project/AdoptCommand.js +86 -9
  85. package/dist/scripts/cli/commands/project/AdoptCommand.js.map +1 -1
  86. package/dist/scripts/cli/commands/project/InitProjectCommand.js +286 -45
  87. package/dist/scripts/cli/commands/project/InitProjectCommand.js.map +1 -1
  88. package/dist/scripts/cli/commands/project/UpdateCommand.js +118 -8
  89. package/dist/scripts/cli/commands/project/UpdateCommand.js.map +1 -1
  90. package/dist/scripts/cli/commands/quality/CheckCommand.js +184 -0
  91. package/dist/scripts/cli/commands/quality/CheckCommand.js.map +1 -0
  92. package/dist/scripts/cli/commands/quality/DoctorCommand.js +20 -2
  93. package/dist/scripts/cli/commands/quality/DoctorCommand.js.map +1 -1
  94. package/dist/scripts/cli/commands/quality/FixCommand.js +1 -1
  95. package/dist/scripts/cli/commands/quality/FixCommand.js.map +1 -1
  96. package/dist/scripts/cli/commands/quality/ReportCommand.js +1 -1
  97. package/dist/scripts/cli/commands/quality/ReportCommand.js.map +1 -1
  98. package/dist/scripts/cli/commands/quality/ValidateSpecsCommand.js +406 -24
  99. package/dist/scripts/cli/commands/quality/ValidateSpecsCommand.js.map +1 -1
  100. package/dist/scripts/cli/commands/spec/DoneCommand.js +108 -0
  101. package/dist/scripts/cli/commands/spec/DoneCommand.js.map +1 -1
  102. package/dist/scripts/cli/commands/spec/NewCommand.js +178 -0
  103. package/dist/scripts/cli/commands/spec/NewCommand.js.map +1 -0
  104. package/dist/scripts/cli/commands/spec/PlanCommand.js +8 -2
  105. package/dist/scripts/cli/commands/spec/PlanCommand.js.map +1 -1
  106. package/dist/scripts/cli/commands/spec/ReqCommand.js +124 -4
  107. package/dist/scripts/cli/commands/spec/ReqCommand.js.map +1 -1
  108. package/dist/scripts/cli/commands/spec/StatusCommand.js +84 -32
  109. package/dist/scripts/cli/commands/spec/StatusCommand.js.map +1 -1
  110. package/dist/scripts/docs/build-site.js +145 -35
  111. package/dist/scripts/docs/build-site.js.map +1 -1
  112. package/dist/scripts/docs/components.js +208 -0
  113. package/dist/scripts/docs/components.js.map +1 -0
  114. package/dist/scripts/docs/highlight.js +180 -0
  115. package/dist/scripts/docs/highlight.js.map +1 -0
  116. package/dist/scripts/docs/icons.js +63 -0
  117. package/dist/scripts/docs/icons.js.map +1 -0
  118. package/dist/scripts/docs/nav.js +9 -3
  119. package/dist/scripts/docs/nav.js.map +1 -1
  120. package/dist/scripts/gen_mcp_tools.js +22 -2
  121. package/dist/scripts/gen_mcp_tools.js.map +1 -1
  122. package/dist/scripts/harness/init.js +101 -40
  123. package/dist/scripts/harness/init.js.map +1 -1
  124. package/dist/scripts/harness/worktree-lock.js +161 -0
  125. package/dist/scripts/harness/worktree-lock.js.map +1 -0
  126. package/dist/scripts/infer_pack.js +4 -1
  127. package/dist/scripts/infer_pack.js.map +1 -1
  128. package/dist/scripts/lib/surface.js +88 -38
  129. package/dist/scripts/lib/surface.js.map +1 -1
  130. package/dist/scripts/new.js +10 -0
  131. package/dist/scripts/new.js.map +1 -0
  132. package/dist/scripts/plugin/gate-hook.js +2 -2
  133. package/dist/scripts/plugin/gate-hook.js.map +1 -1
  134. package/package.json +5 -2
  135. package/templates/adopt/AI_RULES.md.tpl +1 -1
  136. package/templates/adopt/spec.md.tpl +4 -4
  137. package/templates/base/spec.md.tpl +12 -0
  138. package/templates/ci/azure.yml.tpl +2 -2
  139. package/templates/ci/github.yml.tpl +2 -2
  140. package/templates/ci/gitlab.yml.tpl +1 -1
  141. package/templates/ci/jenkins.tpl +1 -1
  142. package/templates/contracts/test-strategy.md.tpl +2 -2
  143. package/templates/harness/harness.config.yaml.tpl +3 -3
  144. package/templates/harness/prompt-prefix.md.tpl +2 -5
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ /**
3
+ * What `init --multi-stack` decides, with no filesystem underneath it.
4
+ *
5
+ * The shape this produces exists because of one constraint that is easy to get
6
+ * wrong: three stacks implementing the same product share their *requirements*
7
+ * and their *scenarios*, and share nothing else. A requirement is the same
8
+ * sentence whether Spring or Quarkus implements it, and a Gherkin scenario
9
+ * describes behaviour, which is the point of writing it in Gherkin. But the
10
+ * traceability matrix maps a requirement to the file that implements it and the
11
+ * file that proves it, and those are different files in every stack — so the
12
+ * matrix cannot be shared, and neither can AI_RULES.md, which is the rulebook
13
+ * for one toolchain.
14
+ *
15
+ * Sharing the first set and splitting the second is the whole design. Copying
16
+ * everything would be simpler and would be a lie: editing the spec in one
17
+ * project would leave the other two describing a different product.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.InvalidStackList = exports.PER_STACK_PATHS = exports.SHARED_PATHS = void 0;
21
+ exports.planStacks = planStacks;
22
+ exports.renderSpecopsConfig = renderSpecopsConfig;
23
+ exports.relativeToRoot = relativeToRoot;
24
+ /** Paths that are one artifact for the whole tree, held at the root. */
25
+ exports.SHARED_PATHS = ["spec.md", "features", "docs/specs/adr"];
26
+ /** Paths each stack owns outright — they name files that differ per toolchain. */
27
+ exports.PER_STACK_PATHS = ["AI_RULES.md", "README.md", "docs/specs/traceability.md"];
28
+ class InvalidStackList extends Error {
29
+ }
30
+ exports.InvalidStackList = InvalidStackList;
31
+ /**
32
+ * Turn `--multi-stack spring,quarkus,micronaut` into the plan.
33
+ *
34
+ * Each name becomes a directory, so it has to survive being one. A stack called
35
+ * `../etc` would write outside the root the user named, and `projects:` already
36
+ * accepts relative paths that nobody checks stay inside the repository (#103) —
37
+ * no reason to open a second door to the same place.
38
+ */
39
+ function planStacks(raw) {
40
+ if (typeof raw !== "string" || raw.trim() === "") {
41
+ throw new InvalidStackList("--multi-stack needs a comma-separated list, e.g. spring,quarkus");
42
+ }
43
+ const names = raw
44
+ .split(",")
45
+ .map((s) => s.trim())
46
+ .filter((s) => s !== "");
47
+ if (names.length < 2) {
48
+ throw new InvalidStackList(`--multi-stack needs at least two stacks (got ${names.length === 1 ? `'${names[0]}'` : "none"}) — ` +
49
+ "for one stack, plain `specgate init` is the command");
50
+ }
51
+ const seen = new Set();
52
+ for (const name of names) {
53
+ if (!/^[a-z0-9][a-z0-9._-]*$/i.test(name)) {
54
+ throw new InvalidStackList(`'${name}' is not usable as a directory name — use letters, digits, dot, dash or underscore`);
55
+ }
56
+ if (name === "." || name === ".." || name.includes("..")) {
57
+ throw new InvalidStackList(`'${name}' would write outside the project root`);
58
+ }
59
+ const key = name.toLowerCase();
60
+ if (seen.has(key)) {
61
+ throw new InvalidStackList(`'${name}' is listed twice — each stack is one directory`);
62
+ }
63
+ seen.add(key);
64
+ }
65
+ return { stacks: names.map((name) => ({ name, entry: `./${name}` })) };
66
+ }
67
+ /**
68
+ * The `projects:` list `validate`, `plan`, `status` and `report` already fan out
69
+ * over. Written by hand rather than through a YAML library because the rest of
70
+ * this project reads it with `parseYamlLite`, and a writer that can emit
71
+ * something its own reader rejects is worse than no writer.
72
+ */
73
+ function renderSpecopsConfig(plan, projectName) {
74
+ return (`# Generated by \`specgate init --multi-stack\`.\n` +
75
+ `#\n` +
76
+ `# Every entry is validated as a project in its own right. The spec and the\n` +
77
+ `# scenarios live once, at this root; each stack below carries its own\n` +
78
+ `# traceability matrix, because the files that implement and prove a\n` +
79
+ `# requirement are different files in every stack.\n` +
80
+ `name: ${projectName}\n` +
81
+ `projects:\n` +
82
+ plan.stacks.map((s) => ` - ${s.entry}\n`).join(""));
83
+ }
84
+ /**
85
+ * Where a shared path sits, seen from inside a stack directory.
86
+ *
87
+ * `docs/specs/adr` is two levels down, so the link back is not the same depth
88
+ * as the one for `spec.md`. Getting this wrong produces a link that resolves to
89
+ * nothing and a validate run that reports a missing required file, which is a
90
+ * confusing way to learn about an off-by-one.
91
+ */
92
+ function relativeToRoot(sharedPath) {
93
+ const depth = sharedPath.split("/").length;
94
+ return "../".repeat(depth) + sharedPath;
95
+ }
96
+ //# sourceMappingURL=MultiStack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MultiStack.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/MultiStack.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * What `specgate new` writes for a requirement: a slug for its feature file
4
+ * and a tagged scenario of placeholders for a person to rewrite.
5
+ *
6
+ * The placeholders are deliberate. A scenario the tool invents would read as
7
+ * a decision nobody made; `<the observable outcome>` reads as a question. The
8
+ * gate leaves them alone while the requirement is Draft and refuses them once
9
+ * it is delivered (`scenario_placeholder_step`).
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.slugFor = slugFor;
13
+ exports.scenarioBlock = scenarioBlock;
14
+ exports.featureWithScenario = featureWithScenario;
15
+ const MAX_SLUG_WORDS = 6;
16
+ /** `Totals are rounded half-up` → `totals-are-rounded-half-up`. */
17
+ function slugFor(title) {
18
+ const words = title
19
+ .normalize("NFKD")
20
+ .replace(/[̀-ͯ]/g, "")
21
+ .toLowerCase()
22
+ .replace(/[^a-z0-9]+/g, " ")
23
+ .trim()
24
+ .split(/\s+/)
25
+ .filter(Boolean)
26
+ .slice(0, MAX_SLUG_WORDS);
27
+ return words.join("-") || "requirement";
28
+ }
29
+ /** The scenario, tagged so `validate` can tie it to its matrix row. */
30
+ function scenarioBlock(reqId, scenarioId, title) {
31
+ return [
32
+ ` @${reqId} @${scenarioId}`,
33
+ ` Scenario: ${title}`,
34
+ " Given <the state before the action>",
35
+ " When <the action under test>",
36
+ " Then <the observable outcome>",
37
+ "",
38
+ ].join("\n");
39
+ }
40
+ /** A new feature file, or the scenario appended to an existing one. */
41
+ function featureWithScenario(existing, title, block) {
42
+ if (existing === null)
43
+ return `Feature: ${title}\n\n${block}`;
44
+ return `${existing.replace(/\s*$/, "")}\n\n${block}`;
45
+ }
46
+ //# sourceMappingURL=NewRequirement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NewRequirement.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/NewRequirement.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAKH,0BAWC;AAGD,sCASC;AAGD,kDAGC;AAhCD,MAAM,cAAc,GAAG,CAAC,CAAC;AAEzB,mEAAmE;AACnE,SAAgB,OAAO,CAAC,KAAa;IACnC,MAAM,KAAK,GAAG,KAAK;SAChB,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,OAAO,CAAC;SACf,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IAC5B,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,aAAa,CAAC;AAC1C,CAAC;AAED,uEAAuE;AACvE,SAAgB,aAAa,CAAC,KAAa,EAAE,UAAkB,EAAE,KAAa;IAC5E,OAAO;QACL,MAAM,KAAK,KAAK,UAAU,EAAE;QAC5B,eAAe,KAAK,EAAE;QACtB,yCAAyC;QACzC,kCAAkC;QAClC,mCAAmC;QACnC,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,uEAAuE;AACvE,SAAgB,mBAAmB,CAAC,QAAuB,EAAE,KAAa,EAAE,KAAa;IACvF,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,YAAY,KAAK,OAAO,KAAK,EAAE,CAAC;IAC9D,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,CAAC;AACvD,CAAC"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ /**
3
+ * Which already-accepted requirement an agent should be shown as an example (D2).
4
+ *
5
+ * ## What the prompt is missing
6
+ *
7
+ * `buildPrompt` injects the requirement's facts, its Gherkin, `AI_RULES.md` and
8
+ * the definition of done. Every one of those is **normative** — it says what
9
+ * must be true. None of them is an *example*: an agent with no conversation
10
+ * history has never seen what an accepted implementation looks like in this
11
+ * repository, so it invents a house style and the reviewer spends the next
12
+ * attempt correcting it.
13
+ *
14
+ * A precedent is the cheapest possible answer: here is a requirement that was
15
+ * accepted, here is its test, here is its production code.
16
+ *
17
+ * ## Why the same bounded context
18
+ *
19
+ * A precedent from a different part of the system is worse than none — it
20
+ * teaches conventions that do not apply here, with the authority of "this was
21
+ * accepted". D1 already derives each requirement's bounded context and records
22
+ * it on the trace line, so "the same context" is a question the tool can answer
23
+ * rather than a guess from a path.
24
+ *
25
+ * When a project has no contexts at all, every requirement shares the empty
26
+ * one, which is the right answer for a codebase that has not been divided yet.
27
+ *
28
+ * ## Why only Verified
29
+ *
30
+ * `Implemented` means the code exists. `Verified` means somebody checked it.
31
+ * Showing an agent an unverified implementation as a precedent would propagate
32
+ * whatever is wrong with it, and do so as an example to copy.
33
+ *
34
+ * ## Why this module reads nothing
35
+ *
36
+ * Choosing the precedent is a rule; reading the files is I/O. Keeping them
37
+ * apart is what lets every case below be tested without a repository.
38
+ */
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.choosePrecedent = choosePrecedent;
41
+ exports.excerpt = excerpt;
42
+ /** A path cell that names nothing. The matrix uses several spellings of "no". */
43
+ function isPlaceholder(cell) {
44
+ const value = String(cell || "")
45
+ .replace(/`/g, "")
46
+ .trim();
47
+ return value === "" || value === "-" || value === "—" || /^tbd$/i.test(value);
48
+ }
49
+ function clean(cell) {
50
+ return String(cell || "")
51
+ .replace(/`/g, "")
52
+ .trim();
53
+ }
54
+ /**
55
+ * The most recent accepted requirement in `forRequirement`'s bounded context.
56
+ *
57
+ * "Most recent" is the highest requirement id below the current one, which is
58
+ * the order they were written in. Returns `null` when there is nothing worth
59
+ * showing — no verified sibling, or one with no artifacts to point at.
60
+ */
61
+ function choosePrecedent(rows, contexts, forRequirement) {
62
+ const own = contexts[forRequirement] || "";
63
+ const candidates = rows
64
+ .filter((row) => row.requirementId && row.requirementId !== forRequirement)
65
+ .filter((row) => /^verified$/i.test(String(row.status || "").trim()))
66
+ .filter((row) => (contexts[row.requirementId] || "") === own)
67
+ // A precedent with neither a test nor an implementation to look at is not a
68
+ // precedent; it is a row.
69
+ .filter((row) => !isPlaceholder(row.testArtifact) || !isPlaceholder(row.technicalArtifact))
70
+ // Ids sort lexically the way they were written: REQ-002 before REQ-010
71
+ // requires the padding the format already guarantees.
72
+ .filter((row) => row.requirementId < forRequirement)
73
+ .sort((a, b) => b.requirementId.localeCompare(a.requirementId));
74
+ const best = candidates[0];
75
+ if (!best)
76
+ return null;
77
+ return {
78
+ requirementId: best.requirementId,
79
+ testArtifact: isPlaceholder(best.testArtifact) ? "" : clean(best.testArtifact),
80
+ technicalArtifact: isPlaceholder(best.technicalArtifact) ? "" : clean(best.technicalArtifact),
81
+ };
82
+ }
83
+ /**
84
+ * The first `lines` lines of a file's content, marked when it was cut short.
85
+ *
86
+ * A precedent is an example of shape, not a file to read: a thousand-line class
87
+ * in the prompt costs tokens the agent needs for its own work, and the part
88
+ * that teaches the convention is the top.
89
+ */
90
+ function excerpt(content, lines = 40) {
91
+ const all = String(content || "").split("\n");
92
+ if (all.length <= lines)
93
+ return all.join("\n").trimEnd();
94
+ return `${all.slice(0, lines).join("\n").trimEnd()}\n… (${all.length - lines} more lines)`;
95
+ }
96
+ //# sourceMappingURL=Precedents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Precedents.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/Precedents.ts"],"names":[],"mappings":"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"}
@@ -48,6 +48,7 @@ class RequirementPlan {
48
48
  technical_exists: techExists,
49
49
  test_exists: testExists,
50
50
  category,
51
+ title: row.useCase || "",
51
52
  };
52
53
  }
53
54
  static buildSummary(items) {
@@ -1 +1 @@
1
- {"version":3,"file":"RequirementPlan.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/RequirementPlan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RequirementPlan.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/RequirementPlan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ /**
3
+ * Taking a requirement back out (Fase 4.1).
4
+ *
5
+ * `req add` had no counterpart. When the matrix got into a state somebody
6
+ * wanted to undo, two of three cold adoptions repaired it by hand with a
7
+ * script, because nothing supported it: *"There is no `req rm`/renumber, so I
8
+ * repaired it with a script."*
9
+ *
10
+ * The trigger for that was a defect — `req add` reissuing an id that `adopt`
11
+ * had already seeded — and that defect is fixed. The gap is not: a matrix a
12
+ * team cannot delete a row from is not a document a team can maintain.
13
+ *
14
+ * ## Why there is no `req renumber`
15
+ *
16
+ * It was the other half of what they asked for, and it is deliberately not
17
+ * here. A requirement id is not only a cell in this table: it appears in
18
+ * `@REQ-014` Gherkin tags, in test names, in commit messages, in branch names
19
+ * the harness created, and in whatever a team wrote in their own tracker.
20
+ * Renumbering the two files this tool owns while every other mention keeps the
21
+ * old id would leave a project in a worse state than the one it was trying to
22
+ * fix, and a rename that silently covers 60% of the occurrences is exactly the
23
+ * kind of half-promise this repository keeps finding in itself.
24
+ *
25
+ * Deleting is honest because it is complete: the row and the prose go, and the
26
+ * caller is told what it did *not* touch.
27
+ */
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.isDelivered = isDelivered;
30
+ exports.planRemoval = planRemoval;
31
+ exports.removeMatrixRows = removeMatrixRows;
32
+ exports.removeSpecProse = removeSpecProse;
33
+ const DRAFT = "Draft";
34
+ /** Statuses that mean somebody recorded this requirement as delivered. */
35
+ function isDelivered(status) {
36
+ const s = String(status || "").trim();
37
+ return s !== "" && s !== DRAFT;
38
+ }
39
+ function cells(line) {
40
+ return line.split("|").map((c) => c.trim());
41
+ }
42
+ /**
43
+ * What removing `reqId` would take out of the matrix.
44
+ *
45
+ * `otherRowsUseFeature` decides whether a declared feature file becomes an
46
+ * orphan: a feature still referenced by another row is nobody's problem.
47
+ */
48
+ function planRemoval(traceContent, reqId, specContent) {
49
+ const wanted = String(reqId || "").trim();
50
+ const lines = String(traceContent || "")
51
+ .replace(/\r\n/g, "\n")
52
+ .split("\n");
53
+ const rows = [];
54
+ const statuses = [];
55
+ const declaredFeatures = [];
56
+ const featuresElsewhere = new Set();
57
+ for (const line of lines) {
58
+ const trimmed = line.trim();
59
+ if (!trimmed.startsWith("|") || trimmed.includes("---"))
60
+ continue;
61
+ const c = cells(trimmed);
62
+ if (c.length < 4)
63
+ continue;
64
+ const rowId = c[1];
65
+ if (!/^REQ-/.test(rowId))
66
+ continue;
67
+ const feature = (c[3] || "").replace(/^`|`$/g, "").split("#")[0].trim();
68
+ if (rowId === wanted) {
69
+ rows.push(line);
70
+ statuses.push(c[c.length - 2] || "");
71
+ if (feature && feature !== "-")
72
+ declaredFeatures.push(feature);
73
+ }
74
+ else if (feature && feature !== "-") {
75
+ featuresElsewhere.add(feature);
76
+ }
77
+ }
78
+ return {
79
+ rows,
80
+ statuses,
81
+ orphanedFeatures: [...new Set(declaredFeatures)].filter((f) => !featuresElsewhere.has(f)),
82
+ hasProse: hasProseFor(specContent, wanted),
83
+ };
84
+ }
85
+ /** Every requirement id a piece of text names. Static: never built from input. */
86
+ const REQ_ID = /\bREQ-[A-Za-z0-9.]+\b/g;
87
+ const HEADING = /^#{2,4}\s+/;
88
+ function namedIds(text) {
89
+ return text.match(REQ_ID) || [];
90
+ }
91
+ /** `| REQ-014 | …` — the id in a table row's first cell, or null. */
92
+ function firstCellId(trimmedLine) {
93
+ if (!trimmedLine.startsWith("|"))
94
+ return null;
95
+ const first = trimmedLine.split("|")[1];
96
+ return first === undefined ? null : first.trim();
97
+ }
98
+ function hasProseFor(specContent, reqId) {
99
+ if (!reqId)
100
+ return false;
101
+ for (const line of String(specContent || "")
102
+ .replace(/\r\n/g, "\n")
103
+ .split("\n")) {
104
+ const trimmed = line.trim();
105
+ if (HEADING.test(trimmed) && namedIds(trimmed).includes(reqId))
106
+ return true;
107
+ if (firstCellId(trimmed) === reqId)
108
+ return true;
109
+ }
110
+ return false;
111
+ }
112
+ /** The matrix without `reqId`'s rows. */
113
+ function removeMatrixRows(traceContent, reqId) {
114
+ const wanted = String(reqId || "").trim();
115
+ return String(traceContent || "")
116
+ .replace(/\r\n/g, "\n")
117
+ .split("\n")
118
+ .filter((line) => {
119
+ const trimmed = line.trim();
120
+ if (!trimmed.startsWith("|"))
121
+ return true;
122
+ const c = cells(trimmed);
123
+ return c.length < 2 || c[1] !== wanted;
124
+ })
125
+ .join("\n");
126
+ }
127
+ /**
128
+ * `spec.md` without `reqId`'s section or table row.
129
+ *
130
+ * A section runs until the next heading of the same level or higher, matching
131
+ * how `SpecSections` reads it back — the two have to agree or removal leaves
132
+ * half a section behind.
133
+ */
134
+ function removeSpecProse(specContent, reqId) {
135
+ const wanted = String(reqId || "").trim();
136
+ const lines = String(specContent || "")
137
+ .replace(/\r\n/g, "\n")
138
+ .split("\n");
139
+ const kept = [];
140
+ let skipUntilLevel = 0;
141
+ for (const line of lines) {
142
+ const trimmed = line.trim();
143
+ const heading = /^(#{1,6})\s+\S/.exec(trimmed);
144
+ if (skipUntilLevel > 0) {
145
+ if (heading && heading[1].length <= skipUntilLevel)
146
+ skipUntilLevel = 0;
147
+ else
148
+ continue;
149
+ }
150
+ const reqHeading = /^(#{2,4})\s+(.*)$/.exec(trimmed);
151
+ if (reqHeading && namedIds(reqHeading[2]).includes(wanted)) {
152
+ skipUntilLevel = reqHeading[1].length;
153
+ continue;
154
+ }
155
+ if (firstCellId(trimmed) === wanted)
156
+ continue;
157
+ kept.push(line);
158
+ }
159
+ return kept.join("\n").replace(/\n{3,}/g, "\n\n");
160
+ }
161
+ //# sourceMappingURL=RequirementRemoval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RequirementRemoval.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/RequirementRemoval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ /**
3
+ * Does anything actually prove this scenario? (#168)
4
+ *
5
+ * A matrix row names one `Scenario ID` and one feature file. The file may hold
6
+ * five scenarios. Nothing related the other four to anything, so a scenario
7
+ * could be declared and never tested while every gate stayed green — measured,
8
+ * with `--strict-tdd --strict-scenarios --strict-links` all on at once.
9
+ *
10
+ * That is not a theoretical hole. Handed five scenarios where one contradicted
11
+ * another, an agent wrote tests for four and silently skipped the impossible
12
+ * one; the gate approved and the row went to `Implemented`. The scenario it
13
+ * dropped was precisely the one worth knowing about.
14
+ *
15
+ * ## Why matching by name is the right amount of cleverness
16
+ *
17
+ * This tool does not run the test suite and cannot know which assertion
18
+ * corresponds to which scenario. What it can check is that the scenario is
19
+ * *named* in the artifact the matrix says proves it. That works because it is
20
+ * what people and agents already do without being asked — `test('SCN-012 money
21
+ * is rounded half-up')` was written by an agent nobody instructed to do so.
22
+ *
23
+ * It is a heuristic, and it is stated as one: this lives behind
24
+ * `--strict-coverage` rather than in the default gate, because a project that
25
+ * names its tests some other way would fail through no fault of its own.
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.normalise = normalise;
29
+ exports.scenariosIn = scenariosIn;
30
+ exports.uncoveredScenarios = uncoveredScenarios;
31
+ exports.linkIsUnevidenced = linkIsUnevidenced;
32
+ exports.usesNamingConvention = usesNamingConvention;
33
+ exports.skippedScenarios = skippedScenarios;
34
+ const SCENARIO_LINE = /^(\s*)(Scenario Outline|Scenario|Escenario|Example)\s*:\s*(.*)$/;
35
+ const TAG_LINE = /^\s*@/;
36
+ const SCN_ID = /\b(SCN-[A-Za-z0-9.]+)\b/;
37
+ /** Lowercase alphanumerics only, so punctuation and case cannot break a match. */
38
+ function normalise(text) {
39
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "");
40
+ }
41
+ /**
42
+ * Every scenario in a feature file, with what would identify it in a test.
43
+ *
44
+ * Scenario Outlines count: an outline with Examples is still behaviour someone
45
+ * promised, and skipping it would leave the widest scenarios unchecked.
46
+ */
47
+ function scenariosIn(featureSource) {
48
+ const lines = featureSource.replace(/\r\n/g, "\n").split("\n");
49
+ const out = [];
50
+ for (let i = 0; i < lines.length; i += 1) {
51
+ const match = SCENARIO_LINE.exec(lines[i]);
52
+ if (!match)
53
+ continue;
54
+ const title = match[3].trim();
55
+ if (title === "")
56
+ continue;
57
+ const needles = [];
58
+ // A tag on the line above is the strongest identity: it survives a rename,
59
+ // which is the one edit that otherwise defeats every link to a scenario.
60
+ if (i > 0 && TAG_LINE.test(lines[i - 1])) {
61
+ const tagged = SCN_ID.exec(lines[i - 1]);
62
+ if (tagged)
63
+ needles.push(tagged[1]);
64
+ }
65
+ const inTitle = SCN_ID.exec(title);
66
+ if (inTitle && !needles.includes(inTitle[1]))
67
+ needles.push(inTitle[1]);
68
+ // Without an id anywhere, the title is all there is.
69
+ if (needles.length === 0)
70
+ needles.push(title);
71
+ out.push({ title, needles });
72
+ }
73
+ return out;
74
+ }
75
+ /**
76
+ * The scenarios none of `testSources` mentions.
77
+ *
78
+ * `testSources` is every file the matrix declares as proof for this row — one
79
+ * file usually, but a directory expands to several, and a scenario proved in
80
+ * any of them is proved.
81
+ */
82
+ function uncoveredScenarios(featureSource, testSources) {
83
+ const haystack = testSources.map(normalise).join("\n");
84
+ if (haystack === "")
85
+ return scenariosIn(featureSource);
86
+ return scenariosIn(featureSource).filter((scenario) => !scenario.needles.some((needle) => haystack.includes(normalise(needle))));
87
+ }
88
+ /**
89
+ * A declared test artifact that names neither the requirement nor any of its
90
+ * scenarios (Fase 1.2).
91
+ *
92
+ * `--strict-links` is `fs.existsSync` and nothing more, so a link that merely
93
+ * *lies* passes: measured on a real adoption, a "Vet" requirement ended up
94
+ * declaring `PetValidatorTests.java` as its proof and every gate stayed green.
95
+ * `uncoveredScenarios` catches that whenever the row has scenarios to name —
96
+ * but the rows that lie most easily are the ones `adopt` seeds, which have a
97
+ * use case and no scenario at all, so there is nothing to match and the check
98
+ * skips them.
99
+ *
100
+ * For those, the requirement id is the only available evidence: a test file
101
+ * that mentions neither `REQ-014` nor any scenario of REQ-014 is not visibly
102
+ * proving REQ-014.
103
+ *
104
+ * Returns true when the link has no evidence in it.
105
+ *
106
+ * ## The limit, stated rather than hidden
107
+ *
108
+ * This says the artifact does not *mention* the requirement. It cannot say the
109
+ * artifact does not *prove* it — that needs the suite executed and coverage
110
+ * attributed per test, which this tool does not do. Naming is a proxy, and the
111
+ * flag it lives behind is opt-in for exactly that reason.
112
+ */
113
+ function linkIsUnevidenced(requirementId, featureSource, testSources) {
114
+ if (!requirementId)
115
+ return false;
116
+ const haystack = testSources.map(normalise).join("\n");
117
+ if (haystack === "")
118
+ return true;
119
+ if (haystack.includes(normalise(requirementId)))
120
+ return false;
121
+ return !scenariosIn(featureSource).some((scenario) => scenario.needles.some((needle) => haystack.includes(normalise(needle))));
122
+ }
123
+ /**
124
+ * Does this project name requirements or scenarios in its tests at all?
125
+ *
126
+ * ## Why the check has to ask this first
127
+ *
128
+ * `--strict-coverage` matches names, and that works because agents and people
129
+ * writing tests alongside scenarios do it without being asked. It does **not**
130
+ * work for the case this product leads with. Retro-fitting specs onto a
131
+ * brownfield repository means pointing a row at a test that already exists and
132
+ * was written years before anyone had heard of this tool — and the only way to
133
+ * satisfy a name match there is to edit that test.
134
+ *
135
+ * Which is exactly what `adopt` promises it will never make you do. Measured on
136
+ * a real adoption: every retro-fitted row failed `link_without_evidence`, and
137
+ * the only fix was to write `REQ-006` into someone's existing test file. A gate
138
+ * that contradicts the product's headline promise is not a gate, it is a
139
+ * reason to stop using the product.
140
+ *
141
+ * So the check calibrates on the project instead of assuming. If some tests
142
+ * name their requirements and others do not, the silent ones are worth
143
+ * reporting. If none do, the convention is not in use here and the honest
144
+ * answer is to say the check could not run — not to fail every row for
145
+ * following a convention nobody adopted.
146
+ */
147
+ function usesNamingConvention(rows) {
148
+ return rows.some((row) => !linkIsUnevidenced(row.requirement || "", row.feature, row.tests));
149
+ }
150
+ /**
151
+ * Which scenarios in one feature file the tests actually name.
152
+ *
153
+ * ## Why this is per file and not per project
154
+ *
155
+ * The first version calibrated on the whole project: if any test named any
156
+ * requirement, every row had to. A cold evaluator found what that feels like —
157
+ * adding one comment, `// Covers REQ-009`, to one test turned three unrelated
158
+ * rows red at once. They called it a one-comment CI landmine, and they were
159
+ * right: doing the correct thing produced a punishment, which teaches people
160
+ * not to do the correct thing.
161
+ *
162
+ * Per file, the signal is sharper and the blast radius is the file. If four of
163
+ * five scenarios in `totals.feature` are named by its test and the fifth is
164
+ * not, the fifth was skipped — that is exactly #168, an agent quietly dropping
165
+ * the scenario it could not satisfy. If none are named, nobody has adopted the
166
+ * convention here yet and there is nothing to conclude.
167
+ */
168
+ function skippedScenarios(featureSource, testSources) {
169
+ const all = scenariosIn(featureSource);
170
+ if (all.length === 0)
171
+ return [];
172
+ const uncovered = uncoveredScenarios(featureSource, testSources);
173
+ // Nothing in this file is named: the convention is not in use here.
174
+ if (uncovered.length === all.length)
175
+ return [];
176
+ return uncovered;
177
+ }
178
+ //# sourceMappingURL=ScenarioCoverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScenarioCoverage.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/ScenarioCoverage.ts"],"names":[],"mappings":"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"}