@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
@@ -25,13 +25,32 @@ class AgentPrompt {
25
25
  `- Production artifact: ${field("technicalArtifact", "technical_artifact") || "(none declared)"}`,
26
26
  `- Current status: ${req.status || "(none)"}`,
27
27
  ].join("\n");
28
+ // Before the facts, because it is the thing being implemented and the facts
29
+ // are only pointers to where it goes.
30
+ if (opts.requirementText && String(opts.requirementText).trim()) {
31
+ parts.push(AgentPrompt.section("The requirement (spec.md)", String(opts.requirementText).trimEnd()));
32
+ }
33
+ else {
34
+ parts.push(AgentPrompt.section("The requirement (spec.md)", `\`spec.md\` has no \`## ${req.requirement}\` section, so this prompt cannot ` +
35
+ `tell you what ${req.requirement} requires.\n\n` +
36
+ `Do not guess. Stop and report that the requirement has no text — an ` +
37
+ `implementation invented from a table row is worse than no implementation, ` +
38
+ `because the matrix will then claim it is covered.`));
39
+ }
28
40
  parts.push(AgentPrompt.section("Requirement facts", facts));
29
41
  if (opts.hint) {
30
42
  parts.push(AgentPrompt.section("Suggested approach", opts.hint));
31
43
  }
32
- const featureRel = String(field("featureFile", "feature_file") || "")
44
+ // `req add` writes `-` in the Feature file column by design, and this used
45
+ // to render it as a filename: "The feature file `-` does not exist yet.
46
+ // Create it." Every requirement made the documented way hit that, and an
47
+ // evaluator stopped rather than pay an agent to find out what a file named
48
+ // `-` was supposed to be.
49
+ const rawFeature = String(field("featureFile", "feature_file") || "")
33
50
  .replace(/^`|`$/g, "")
34
51
  .trim();
52
+ const featureRel = rawFeature === "-" ? "" : rawFeature;
53
+ const featureUndeclared = rawFeature === "" || rawFeature === "-";
35
54
  if (featureRel) {
36
55
  if (opts.featureContent) {
37
56
  parts.push(AgentPrompt.section(`Gherkin scenario (${featureRel})`, "```gherkin\n" + opts.featureContent.trimEnd() + "\n```"));
@@ -41,9 +60,36 @@ class AgentPrompt {
41
60
  `requirement before writing code.`));
42
61
  }
43
62
  }
63
+ else if (featureUndeclared) {
64
+ parts.push(AgentPrompt.section("Gherkin scenario", `This requirement declares no feature file — the matrix cell is empty.\n\n` +
65
+ `Write the scenario first, as \`features/<area>/<behaviour>.feature\`, tagged ` +
66
+ `\`@${req.requirement}\` and \`@${field("scenarioId", "scenario_id") || "SCN-NNN"}\` above its \`Scenario:\` line. Then record it:\n\n` +
67
+ ` specgate req link ${req.requirement} --feature <path>\n\n` +
68
+ `Creating a file that does not exist is not editing the contract — the ` +
69
+ `rule below is about changing scenarios that are already there.`));
70
+ }
44
71
  if (opts.aiRulesContent) {
45
72
  parts.push(AgentPrompt.section("Project rules (AI_RULES.md — non-negotiable)", opts.aiRulesContent.trimEnd()));
46
73
  }
74
+ if (opts.precedent && (opts.precedent.testExcerpt || opts.precedent.technicalExcerpt)) {
75
+ const p = opts.precedent;
76
+ const body = [
77
+ `${p.requirementId} was accepted in this same bounded context. Match its ` +
78
+ `structure, naming and test style — this is what "done" looks like here.`,
79
+ "",
80
+ ];
81
+ if (p.testExcerpt) {
82
+ body.push(`**Its test — \`${p.testArtifact}\`**`, "", "```", p.testExcerpt, "```", "");
83
+ }
84
+ if (p.technicalExcerpt) {
85
+ body.push(`**Its implementation — \`${p.technicalArtifact}\`**`, "", "```", p.technicalExcerpt, "```", "");
86
+ }
87
+ // Named a precedent, not a rule: the agent must not conclude that copying
88
+ // it satisfies a different requirement.
89
+ body.push("This is an example of *shape*, not of behaviour. Your requirement is the " +
90
+ "one above; do not copy its assertions.");
91
+ parts.push(AgentPrompt.section("Precedent — an accepted requirement nearby", body.join("\n")));
92
+ }
47
93
  const baseRules = opts.stageRules || [
48
94
  "Write the test first against the technical artifact path declared in the requirement.",
49
95
  "Run the project test command; verify the test fails for the expected reason.",
@@ -51,8 +97,11 @@ class AgentPrompt {
51
97
  ];
52
98
  const doneRules = [
53
99
  ...baseRules,
54
- "**Do not modify** `spec.md`, `AI_RULES.md`, or any `features/**/*.feature` — they are the project's source of truth.",
55
- "The harness will run `validate --strict-tdd` and the project test command. Both must pass.",
100
+ "**Do not modify** `spec.md`, `AI_RULES.md`, or any existing `features/**/*.feature` — " +
101
+ "they are the contract you are being judged against. Creating a feature file that " +
102
+ "does not exist yet is not modifying one, and the gate allows it: an agent asked " +
103
+ "to write a missing scenario is not being asked to break this rule.",
104
+ "The harness will run `validate . --strict` and the project test command. Both must pass.",
56
105
  ];
57
106
  parts.push(AgentPrompt.section("Definition of done", doneRules.map((r, i) => `${i + 1}. ${r}`).join("\n")));
58
107
  if (opts.previousFailure) {
@@ -1 +1 @@
1
- {"version":3,"file":"AgentPrompt.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/AgentPrompt.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgentPrompt.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/AgentPrompt.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
34
34
  exports.ARTIFACT_CODES = void 0;
35
35
  exports.looksLikePath = looksLikePath;
36
36
  exports.declaredPaths = declaredPaths;
37
+ exports.artifactFile = artifactFile;
37
38
  exports.checkDeclaredArtifacts = checkDeclaredArtifacts;
38
39
  const Diagnostic_1 = require("./Diagnostic");
39
40
  exports.ARTIFACT_CODES = Object.freeze({
@@ -75,6 +76,30 @@ function declaredPaths(cell) {
75
76
  .filter((part) => looksLikePath(part))
76
77
  .map((part) => part.replace(/\\/g, "/").replace(/^\.\//, ""));
77
78
  }
79
+ /**
80
+ * The file a declared artifact names, with any selector removed.
81
+ *
82
+ * A matrix cell can point *into* a file as well as at it. Two conventions turn
83
+ * up: an anchor (`src/auth/login.ts#L15-L89`) and a test selector
84
+ * (`tests/test_config.py::test_request_dispatching`, which is pytest's, and the
85
+ * shape Jest and Go equivalents are usually written in too).
86
+ *
87
+ * The anchor was already stripped before checking the path exists; the selector
88
+ * was not. So `req link --test "tests/x.py::test_y"` was accepted and then
89
+ * `validate` rejected the same string as `declared_artifact_missing` — two parts
90
+ * of the tool disagreeing about a value one of them had just written. A cold
91
+ * evaluator hit it while trying to do the more precise thing, and concluded a
92
+ * requirement can only point at a 1500-line file.
93
+ *
94
+ * The selector stays in the cell. It is information for whoever reads the row
95
+ * and for the command that runs the test; it is just not part of the filename.
96
+ */
97
+ function artifactFile(cell) {
98
+ return String(cell || "")
99
+ .split("#")[0]
100
+ .split("::")[0]
101
+ .trim();
102
+ }
78
103
  /**
79
104
  * A declared path counts as touched when the diff contains it, or contains
80
105
  * something inside it — a row may name a directory (`src/health/`) and the work
@@ -1 +1 @@
1
- {"version":3,"file":"DeclaredArtifacts.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/DeclaredArtifacts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DeclaredArtifacts.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/DeclaredArtifacts.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;;;AAkBH,sCAMC;AAUD,sCAOC;AAoBD,oCAKC;AA6BD,wDAmCC;AA/HD,6CAA8C;AAEjC,QAAA,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1C,SAAS,EAAE,6BAA6B;CACzC,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,SAAgB,aAAa,CAAC,QAAgB;IAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,IAAI,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACvD,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,aAAa,CAAC,IAA+B;IAC3D,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,OAAO,MAAM,CAAC,IAAI,CAAC;SAChB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;SAC5C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;SACrC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,YAAY,CAAC,IAA+B;IAC1D,OAAO,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;SACtB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SACb,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACd,IAAI,EAAE,CAAC;AACZ,CAAC;AAWD;;;;GAIG;AACH,SAAS,UAAU,CAAC,QAAgB,EAAE,OAA0B;IAC9D,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC3C,OAAO,OAAO,CAAC,IAAI,CACjB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,EAAE,CAAC,CAC1F,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,sBAAsB,CACpC,KAA6B,EAC7B,MAAM,GAAG,KAAK;IAEd,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;IACvC,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,kBAAK,CAAC,CAAC,CAAC,oBAAO,CAAC;IAEvC,MAAM,KAAK,GAA+C;QACxD,CAAC,MAAM,EAAE,KAAK,CAAC,YAAY,CAAC;QAC5B,CAAC,YAAY,EAAE,KAAK,CAAC,iBAAiB,CAAC;KACxC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QACjC,KAAK,MAAM,QAAQ,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,IAAI,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC;gBAAE,SAAS;YAClD,KAAK,CAAC,IAAI,CACR,KAAK,CACH,sBAAc,CAAC,SAAS,EACxB,yBAAyB,QAAQ,4BAA4B,IAAI,aAAa;gBAC5E,gCAAgC,EAClC;gBACE,MAAM;gBACN,mEAAmE;gBACnE,gEAAgE;gBAChE,GAAG,EACD,iDAAiD;oBACjD,uBAAuB,MAAM,IAAI,OAAO,MAAM,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,YAAY;aAC9F,CACF,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * What `done --check` has to prove before it flips a row (Fase 1.1).
4
+ *
5
+ * ## What it was
6
+ *
7
+ * `--check` and `--strict` were parsed into `DoneOptions` and then never read.
8
+ * `done REQ-001 --check` was a no-op that printed a tick — measured on a matrix
9
+ * pointing at files that do not exist, where `validate --strict-links` exits 1:
10
+ *
11
+ * ✔ REQ-001 → Implemented (1 row updated) exit=0
12
+ *
13
+ * Four documentation pages say `--check` "validates first". It validated
14
+ * nothing. That is the shape this repository keeps finding in itself: something
15
+ * that claims more than it does.
16
+ *
17
+ * ## Why the test command matters here specifically
18
+ *
19
+ * A cold evaluator's summary of the product was "nothing ever runs a test", and
20
+ * they were right: only the harness executes a test command, so a team on the
21
+ * documented path never runs one. `validate` is a static checker — it reads
22
+ * markdown and stats files. Marking a requirement Implemented on the strength
23
+ * of a static check is how "specs as executable contracts" becomes a folder of
24
+ * markdown and a badge.
25
+ *
26
+ * ## The rule about not knowing
27
+ *
28
+ * A project with no test command configured is not a project that passed. It is
29
+ * a project nothing ran, and the two have to look different — otherwise the
30
+ * silent case is the flattering one, which is the bias this whole tool exists to
31
+ * remove.
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.NO_TEST_COMMAND_WARNING = void 0;
35
+ exports.planDoneVerification = planDoneVerification;
36
+ /**
37
+ * The checks `done` should run, in the order they should run.
38
+ *
39
+ * `validate` comes first because it is fast and its failures are the ones with
40
+ * a fix line attached; running a whole test suite to then reject the row for a
41
+ * missing matrix header wastes the slowest thing in the loop.
42
+ */
43
+ function planDoneVerification(projectDir, opts) {
44
+ if (!opts.check && !opts.strict)
45
+ return { steps: [], testsUnverified: false };
46
+ const validateArgs = [projectDir];
47
+ if (opts.strict) {
48
+ // `--strict` means "the gate this project can actually give me". Anything
49
+ // weaker recreates the defect a cold evaluator named: following the
50
+ // documentation leaves you with a weaker gate than the tool supports.
51
+ validateArgs.push("--strict-tdd", "--strict-links", "--strict-coverage");
52
+ }
53
+ const steps = [{ stage: "validate", argv: validateArgs }];
54
+ const testCmd = (opts.testCmd || "").trim();
55
+ if (testCmd !== "")
56
+ steps.push({ stage: "tests", argv: [testCmd] });
57
+ return { steps, testsUnverified: testCmd === "" };
58
+ }
59
+ /** The line `done` prints when nothing ran the project's tests. */
60
+ exports.NO_TEST_COMMAND_WARNING = Object.freeze({
61
+ message: "No test command configured — this checked the specification, not the code.",
62
+ fix: [
63
+ 'Pass --test-cmd "<command>", or set test_cmd: in harness.config.yaml so',
64
+ "every `done --check` runs it. A requirement marked Implemented on a static",
65
+ "check alone is a claim nothing executed.",
66
+ ],
67
+ });
68
+ //# sourceMappingURL=DoneVerification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DoneVerification.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/DoneVerification.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;;;AAmCH,oDAoBC;AA3BD;;;;;;GAMG;AACH,SAAgB,oBAAoB,CAClC,UAAkB,EAClB,IAAsB;IAEtB,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;IAE9E,MAAM,YAAY,GAAG,CAAC,UAAU,CAAC,CAAC;IAClC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,0EAA0E;QAC1E,oEAAoE;QACpE,sEAAsE;QACtE,YAAY,CAAC,IAAI,CAAC,cAAc,EAAE,gBAAgB,EAAE,mBAAmB,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,KAAK,GAAuB,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;IAE9E,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5C,IAAI,OAAO,KAAK,EAAE;QAAE,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAEpE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,KAAK,EAAE,EAAE,CAAC;AACpD,CAAC;AAED,mEAAmE;AACtD,QAAA,uBAAuB,GAAG,MAAM,CAAC,MAAM,CAAC;IACnD,OAAO,EAAE,4EAA4E;IACrF,GAAG,EAAE;QACH,yEAAyE;QACzE,4EAA4E;QAC5E,0CAA0C;KAC3C;CACF,CAAC,CAAC"}
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ /**
3
+ * The gate must mean the same thing after the attempt as before it (#167).
4
+ *
5
+ * `WriteScope` protects the contract: spec.md, AI_RULES.md, features/**,
6
+ * docs/specs/**. It does not protect the file that defines the command the gate
7
+ * *runs*, and that turns out to be the same hole one level up. Measured, with a
8
+ * stub agent that writes no implementation at all:
9
+ *
10
+ * "scripts": { "test": "node --test tests/*.test.js" } → "echo 'all good'"
11
+ *
12
+ * over a suite that was red before it ran. The harness reported
13
+ * `✅ REQ-001 pass (1 attempt)`. An agent that cannot pass the test can weaken
14
+ * the test.
15
+ *
16
+ * ## Why this is not "protect package.json"
17
+ *
18
+ * Adding a dependency during implementation is legitimate and common, and a
19
+ * guard that blocks it would be turned off within a day — at which point it
20
+ * protects nothing. So this does not watch the file. It resolves which *script*
21
+ * the gate command actually invokes and watches only that one value.
22
+ *
23
+ * ## Why it declares what it cannot see
24
+ *
25
+ * `npm test` and `make check` name a script this can find. `mvn -B test` and
26
+ * `pytest` do not — their behaviour lives in a plugin graph or a config file
27
+ * this would have to interpret, and a guard that guesses is worse than one that
28
+ * says it did not look. `resolveGateScript` returns `null` there, and the
29
+ * caller reports the check as not applicable rather than as passed.
30
+ */
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.resolveGateScript = resolveGateScript;
33
+ exports.npmScriptValue = npmScriptValue;
34
+ exports.makeTargetRecipe = makeTargetRecipe;
35
+ exports.gateCommandIntegrity = gateCommandIntegrity;
36
+ const NPM_RUNNERS = ["npm", "yarn", "pnpm", "bun"];
37
+ /**
38
+ * Which script definition, if any, a gate command depends on.
39
+ *
40
+ * Returns `null` when the command's meaning does not live in a file this can
41
+ * read — which is a real answer, not a failure.
42
+ */
43
+ function resolveGateScript(testCmd) {
44
+ const tokens = String(testCmd || "")
45
+ .trim()
46
+ .split(/\s+/)
47
+ .filter(Boolean);
48
+ if (tokens.length === 0)
49
+ return null;
50
+ const [runner, ...rest] = tokens;
51
+ if (NPM_RUNNERS.includes(runner)) {
52
+ // `npm test`, `npm run verify`, `yarn test`, `pnpm run check`.
53
+ const args = rest.filter((a) => !a.startsWith("-"));
54
+ if (args.length === 0)
55
+ return null;
56
+ if (args[0] === "run" || args[0] === "run-script") {
57
+ return args[1] ? { manifest: "package.json", script: args[1] } : null;
58
+ }
59
+ if (args[0] === "test" || args[0] === "t")
60
+ return { manifest: "package.json", script: "test" };
61
+ // `npm start`, `npm ci` and friends are not gates; anything else is a
62
+ // script name only if package.json says so, which the caller resolves.
63
+ return { manifest: "package.json", script: args[0] };
64
+ }
65
+ if (runner === "make") {
66
+ const target = rest.find((a) => !a.startsWith("-"));
67
+ return { manifest: "Makefile", script: target || "all" };
68
+ }
69
+ return null;
70
+ }
71
+ /** The value of an npm script, or `null` when the manifest has no such script. */
72
+ function npmScriptValue(manifestSource, script) {
73
+ let parsed;
74
+ try {
75
+ parsed = JSON.parse(manifestSource);
76
+ }
77
+ catch {
78
+ return null;
79
+ }
80
+ const scripts = parsed && parsed.scripts;
81
+ if (!scripts || typeof scripts !== "object")
82
+ return null;
83
+ const value = scripts[script];
84
+ return typeof value === "string" ? value : null;
85
+ }
86
+ /**
87
+ * The recipe lines of one Make target.
88
+ *
89
+ * Deliberately literal: a target's body is every indented line under it, up to
90
+ * the next rule. Reproducing Make's own parser would be a bigger promise than
91
+ * this needs to keep.
92
+ */
93
+ function makeTargetRecipe(manifestSource, target) {
94
+ const lines = String(manifestSource).replace(/\r\n/g, "\n").split("\n");
95
+ const head = new RegExp(`^${target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*:`);
96
+ const body = [];
97
+ let inside = false;
98
+ for (const line of lines) {
99
+ if (head.test(line)) {
100
+ inside = true;
101
+ continue;
102
+ }
103
+ if (!inside)
104
+ continue;
105
+ if (line.trim() === "")
106
+ continue;
107
+ if (!/^\s/.test(line))
108
+ break;
109
+ body.push(line.trim());
110
+ }
111
+ return inside ? body.join("\n") : null;
112
+ }
113
+ /**
114
+ * Did the definition the gate command depends on change during the attempt?
115
+ *
116
+ * A script that did not exist before and does not exist after is unchanged. A
117
+ * script that *appears* is a change: inventing the target the gate names is the
118
+ * same move as rewriting it.
119
+ */
120
+ function gateCommandIntegrity(testCmd, manifestBefore, manifestAfter) {
121
+ const script = resolveGateScript(testCmd);
122
+ if (!script)
123
+ return { checked: false, changed: false };
124
+ const read = (source) => {
125
+ if (source === null)
126
+ return null;
127
+ return script.manifest === "Makefile"
128
+ ? makeTargetRecipe(source, script.script)
129
+ : npmScriptValue(source, script.script);
130
+ };
131
+ const before = read(manifestBefore);
132
+ const after = read(manifestAfter);
133
+ // The manifest carries no such script either side: nothing here defines the
134
+ // gate, so there is nothing to protect and saying "passed" would overstate it.
135
+ if (before === null && after === null)
136
+ return { checked: false, changed: false, script };
137
+ return { checked: true, changed: before !== after, script, before, after };
138
+ }
139
+ //# sourceMappingURL=GateCommandIntegrity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GateCommandIntegrity.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/GateCommandIntegrity.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,8 @@ class HarnessConfig {
15
15
  prCmd: "",
16
16
  attemptProfiles: [],
17
17
  reviewProfile: "",
18
+ adversaryProfile: "",
19
+ promptPrecedents: false,
18
20
  profileAgents: {},
19
21
  protectedPaths: [],
20
22
  allowPaths: [],
@@ -42,6 +44,10 @@ class HarnessConfig {
42
44
  prCmd: cliArgs.prCmd || file.prCmd || HarnessConfig.DEFAULT_SETTINGS.prCmd,
43
45
  attemptProfiles: file.attemptProfiles || HarnessConfig.DEFAULT_SETTINGS.attemptProfiles,
44
46
  reviewProfile: file.reviewProfile || HarnessConfig.DEFAULT_SETTINGS.reviewProfile,
47
+ adversaryProfile: "",
48
+ promptPrecedents: file.promptPrecedents === undefined
49
+ ? HarnessConfig.DEFAULT_SETTINGS.promptPrecedents
50
+ : file.promptPrecedents === true,
45
51
  profileAgents: file.profileAgents || HarnessConfig.DEFAULT_SETTINGS.profileAgents,
46
52
  // Write scope is a repository decision, like the role ladder: it comes
47
53
  // from the file only. A flag that relaxes what the agent may edit is a
@@ -1 +1 @@
1
- {"version":3,"file":"HarnessConfig.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/HarnessConfig.ts"],"names":[],"mappings":";;;AA2CA,MAAa,aAAa;IAqBW;IApB5B,MAAM,CAAU,QAAQ,GAAG,qBAAqB,CAAC;IACjD,MAAM,CAAU,gBAAgB,GAAoB;QACzD,KAAK,EAAE,EAAE;QACT,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,CAAC;QACd,WAAW,EAAE,CAAC;QACd,YAAY,EAAE,EAAE;QAChB,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,EAAE;QACT,eAAe,EAAE,EAAE;QACnB,aAAa,EAAE,EAAE;QACjB,aAAa,EAAE,EAAE;QACjB,cAAc,EAAE,EAAE;QAClB,UAAU,EAAE,EAAE;QACd,aAAa,EAAE,EAAE;QACjB,cAAc,EAAE,EAAE;QAClB,YAAY,EAAE,EAAE;KACjB,CAAC;IAEF,YAAmC,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAEzD,MAAM,CAAC,KAAK,CACjB,UAA2C,EAC3C,OAAiC;QAEjC,MAAM,IAAI,GAAG,UAAU,IAAI,EAAE,CAAC;QAC9B,OAAO,IAAI,aAAa,CAAC;YACvB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,aAAa,CAAC,gBAAgB,CAAC,KAAK;YAC1E,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,IAAI,aAAa,CAAC,gBAAgB,CAAC,OAAO;YAClF,WAAW,EACT,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,IAAI,aAAa,CAAC,gBAAgB,CAAC,WAAW;YACvF,WAAW,EACT,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,IAAI,aAAa,CAAC,gBAAgB,CAAC,WAAW;YACvF,YAAY,EACV,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,IAAI,aAAa,CAAC,gBAAgB,CAAC,YAAY;YAC1F,IAAI,EACF,OAAO,CAAC,IAAI,KAAK,SAAS;gBACxB,CAAC,CAAC,OAAO,CAAC,IAAI;gBACd,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS;oBACvB,CAAC,CAAC,IAAI,CAAC,IAAI;oBACX,CAAC,CAAC,aAAa,CAAC,gBAAgB,CAAC,IAAI;YAC3C,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC,gBAAgB,CAAC,MAAM;YAC9E,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,aAAa,CAAC,gBAAgB,CAAC,KAAK;YAC1E,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,aAAa,CAAC,gBAAgB,CAAC,eAAe;YACvF,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,gBAAgB,CAAC,aAAa;YACjF,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,gBAAgB,CAAC,aAAa;YACjF,uEAAuE;YACvE,uEAAuE;YACvE,6DAA6D;YAC7D,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,aAAa,CAAC,gBAAgB,CAAC,cAAc;YACpF,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,aAAa,CAAC,gBAAgB,CAAC,UAAU;YACxE,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,gBAAgB,CAAC,aAAa;YACjF,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,aAAa,CAAC,gBAAgB,CAAC,cAAc;YACpF,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,aAAa,CAAC,gBAAgB,CAAC,YAAY;SAC/E,CAAC,CAAC;IACL,CAAC;;AAzDH,sCA0DC"}
1
+ {"version":3,"file":"HarnessConfig.js","sourceRoot":"","sources":["../../../../../packages/core/src/domain/HarnessConfig.ts"],"names":[],"mappings":";;;AA2DA,MAAa,aAAa;IAuBW;IAtB5B,MAAM,CAAU,QAAQ,GAAG,qBAAqB,CAAC;IACjD,MAAM,CAAU,gBAAgB,GAAoB;QACzD,KAAK,EAAE,EAAE;QACT,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,CAAC;QACd,WAAW,EAAE,CAAC;QACd,YAAY,EAAE,EAAE;QAChB,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,EAAE;QACT,eAAe,EAAE,EAAE;QACnB,aAAa,EAAE,EAAE;QACjB,gBAAgB,EAAE,EAAE;QACpB,gBAAgB,EAAE,KAAK;QACvB,aAAa,EAAE,EAAE;QACjB,cAAc,EAAE,EAAE;QAClB,UAAU,EAAE,EAAE;QACd,aAAa,EAAE,EAAE;QACjB,cAAc,EAAE,EAAE;QAClB,YAAY,EAAE,EAAE;KACjB,CAAC;IAEF,YAAmC,QAAyB;QAAzB,aAAQ,GAAR,QAAQ,CAAiB;IAAG,CAAC;IAEzD,MAAM,CAAC,KAAK,CACjB,UAA2C,EAC3C,OAAiC;QAEjC,MAAM,IAAI,GAAG,UAAU,IAAI,EAAE,CAAC;QAC9B,OAAO,IAAI,aAAa,CAAC;YACvB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,aAAa,CAAC,gBAAgB,CAAC,KAAK;YAC1E,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,IAAI,aAAa,CAAC,gBAAgB,CAAC,OAAO;YAClF,WAAW,EACT,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,IAAI,aAAa,CAAC,gBAAgB,CAAC,WAAW;YACvF,WAAW,EACT,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,IAAI,aAAa,CAAC,gBAAgB,CAAC,WAAW;YACvF,YAAY,EACV,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,IAAI,aAAa,CAAC,gBAAgB,CAAC,YAAY;YAC1F,IAAI,EACF,OAAO,CAAC,IAAI,KAAK,SAAS;gBACxB,CAAC,CAAC,OAAO,CAAC,IAAI;gBACd,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS;oBACvB,CAAC,CAAC,IAAI,CAAC,IAAI;oBACX,CAAC,CAAC,aAAa,CAAC,gBAAgB,CAAC,IAAI;YAC3C,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC,gBAAgB,CAAC,MAAM;YAC9E,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,aAAa,CAAC,gBAAgB,CAAC,KAAK;YAC1E,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,aAAa,CAAC,gBAAgB,CAAC,eAAe;YACvF,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,gBAAgB,CAAC,aAAa;YACjF,gBAAgB,EAAE,EAAE;YACpB,gBAAgB,EACd,IAAI,CAAC,gBAAgB,KAAK,SAAS;gBACjC,CAAC,CAAC,aAAa,CAAC,gBAAgB,CAAC,gBAAgB;gBACjD,CAAC,CAAC,IAAI,CAAC,gBAAgB,KAAK,IAAI;YACpC,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,gBAAgB,CAAC,aAAa;YACjF,uEAAuE;YACvE,uEAAuE;YACvE,6DAA6D;YAC7D,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,aAAa,CAAC,gBAAgB,CAAC,cAAc;YACpF,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,aAAa,CAAC,gBAAgB,CAAC,UAAU;YACxE,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,gBAAgB,CAAC,aAAa;YACjF,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,aAAa,CAAC,gBAAgB,CAAC,cAAc;YACpF,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,aAAa,CAAC,gBAAgB,CAAC,YAAY;SAC/E,CAAC,CAAC;IACL,CAAC;;AAhEH,sCAiEC"}
@@ -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,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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"}