@codyswann/lisa 4.32.2 → 4.33.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 (208) hide show
  1. package/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +94 -6
  2. package/all/copy-overwrite/scripts/lisa-gates.mjs +259 -5
  3. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +20 -39
  4. package/all/merge/.claude/settings.json +1 -3
  5. package/cdk/merge/.claude/settings.json +1 -1
  6. package/dist/cli/doctor-declared-contexts.d.ts +1 -1
  7. package/dist/cli/doctor-declared-contexts.d.ts.map +1 -1
  8. package/dist/cli/doctor-declared-contexts.js +48 -7
  9. package/dist/cli/doctor-declared-contexts.js.map +1 -1
  10. package/dist/cli/gate-report-drift-section.d.ts.map +1 -1
  11. package/dist/cli/gate-report-drift-section.js +2 -0
  12. package/dist/cli/gate-report-drift-section.js.map +1 -1
  13. package/dist/configs/vitest/base.d.ts +81 -0
  14. package/dist/configs/vitest/base.d.ts.map +1 -1
  15. package/dist/configs/vitest/base.js +106 -0
  16. package/dist/configs/vitest/base.js.map +1 -1
  17. package/dist/configs/vitest/cdk.d.ts +2 -2
  18. package/dist/configs/vitest/cdk.d.ts.map +1 -1
  19. package/dist/configs/vitest/cdk.js +4 -2
  20. package/dist/configs/vitest/cdk.js.map +1 -1
  21. package/dist/configs/vitest/harper-fabric.d.ts +2 -2
  22. package/dist/configs/vitest/harper-fabric.d.ts.map +1 -1
  23. package/dist/configs/vitest/harper-fabric.js +4 -2
  24. package/dist/configs/vitest/harper-fabric.js.map +1 -1
  25. package/dist/configs/vitest/nestjs.d.ts +2 -2
  26. package/dist/configs/vitest/nestjs.d.ts.map +1 -1
  27. package/dist/configs/vitest/nestjs.js +4 -2
  28. package/dist/configs/vitest/nestjs.js.map +1 -1
  29. package/dist/configs/vitest/phaser.d.ts +2 -2
  30. package/dist/configs/vitest/phaser.d.ts.map +1 -1
  31. package/dist/configs/vitest/phaser.js +4 -2
  32. package/dist/configs/vitest/phaser.js.map +1 -1
  33. package/dist/configs/vitest/typescript.d.ts +2 -2
  34. package/dist/configs/vitest/typescript.d.ts.map +1 -1
  35. package/dist/configs/vitest/typescript.js +4 -2
  36. package/dist/configs/vitest/typescript.js.map +1 -1
  37. package/dist/core/gate-context-owners.d.ts +18 -0
  38. package/dist/core/gate-context-owners.d.ts.map +1 -1
  39. package/dist/core/gate-context-owners.js +7 -0
  40. package/dist/core/gate-context-owners.js.map +1 -1
  41. package/dist/core/gate-declaration-drift.d.ts +37 -0
  42. package/dist/core/gate-declaration-drift.d.ts.map +1 -1
  43. package/dist/core/gate-declaration-drift.js +100 -26
  44. package/dist/core/gate-declaration-drift.js.map +1 -1
  45. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  46. package/dist/core/lisa-owned-hash-ledger.js +6 -0
  47. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  48. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  49. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  50. package/dist/core/upstream-evidence-manifest.js +47 -29
  51. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  52. package/dist/health/declared-checks-inspection.d.ts.map +1 -1
  53. package/dist/health/declared-checks-inspection.js +23 -7
  54. package/dist/health/declared-checks-inspection.js.map +1 -1
  55. package/dist/migrations/index.d.ts +1 -0
  56. package/dist/migrations/index.d.ts.map +1 -1
  57. package/dist/migrations/index.js +3 -0
  58. package/dist/migrations/index.js.map +1 -1
  59. package/dist/migrations/prune-retired-claude-deny-rules.d.ts +39 -0
  60. package/dist/migrations/prune-retired-claude-deny-rules.d.ts.map +1 -0
  61. package/dist/migrations/prune-retired-claude-deny-rules.js +152 -0
  62. package/dist/migrations/prune-retired-claude-deny-rules.js.map +1 -0
  63. package/expo/merge/.claude/settings.json +1 -1
  64. package/harper-fabric/merge/.claude/settings.json +1 -1
  65. package/nestjs/merge/.claude/settings.json +1 -1
  66. package/package.json +4 -4
  67. package/phaser/merge/.claude/settings.json +1 -1
  68. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa/.codex-plugin/skills/lisa-github-add-journey/SKILL.md +1 -0
  71. package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +28 -1
  72. package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +1 -0
  73. package/plugins/lisa/.codex-plugin/skills/lisa-jira-add-journey/SKILL.md +1 -0
  74. package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
  75. package/plugins/lisa/.codex-plugin/skills/lisa-linear-add-journey/SKILL.md +1 -0
  76. package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +27 -0
  77. package/plugins/lisa/.codex-plugin/skills/lisa-reproduce-bug/SKILL.md +8 -0
  78. package/plugins/lisa/.codex-plugin/skills/lisa-tdd-implementation/SKILL.md +1 -0
  79. package/plugins/lisa/.codex-plugin/skills/lisa-ticket-triage/SKILL.md +8 -0
  80. package/plugins/lisa/hooks/inject-resolved-config.mjs +12 -4
  81. package/plugins/lisa/hooks/inject-resolved-config.sh +39 -8
  82. package/plugins/lisa/rules/eager/control-reachability.md +39 -0
  83. package/plugins/lisa/rules/eager/falsifiable-checks.md +2 -0
  84. package/plugins/lisa/rules/eager/work-item-definition-of-ready.md +1 -1
  85. package/plugins/lisa/rules/reference/control-reachability.md +122 -0
  86. package/plugins/lisa/rules/reference/falsifiable-checks.md +2 -0
  87. package/plugins/lisa/skills/lisa-github-add-journey/SKILL.md +1 -0
  88. package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +28 -1
  89. package/plugins/lisa/skills/lisa-implement/SKILL.md +1 -0
  90. package/plugins/lisa/skills/lisa-jira-add-journey/SKILL.md +1 -0
  91. package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
  92. package/plugins/lisa/skills/lisa-linear-add-journey/SKILL.md +1 -0
  93. package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +27 -0
  94. package/plugins/lisa/skills/lisa-reproduce-bug/SKILL.md +8 -0
  95. package/plugins/lisa/skills/lisa-tdd-implementation/SKILL.md +1 -0
  96. package/plugins/lisa/skills/lisa-ticket-triage/SKILL.md +8 -0
  97. package/plugins/lisa-agy/plugin.json +1 -1
  98. package/plugins/lisa-agy/skills/lisa-github-add-journey/SKILL.md +1 -0
  99. package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +28 -1
  100. package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +1 -0
  101. package/plugins/lisa-agy/skills/lisa-jira-add-journey/SKILL.md +1 -0
  102. package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
  103. package/plugins/lisa-agy/skills/lisa-linear-add-journey/SKILL.md +1 -0
  104. package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +27 -0
  105. package/plugins/lisa-agy/skills/lisa-reproduce-bug/SKILL.md +8 -0
  106. package/plugins/lisa-agy/skills/lisa-tdd-implementation/SKILL.md +1 -0
  107. package/plugins/lisa-agy/skills/lisa-ticket-triage/SKILL.md +8 -0
  108. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  109. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  110. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  111. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  113. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  114. package/plugins/lisa-copilot/hooks/inject-resolved-config.mjs +12 -4
  115. package/plugins/lisa-copilot/hooks/inject-resolved-config.sh +39 -8
  116. package/plugins/lisa-copilot/rules/eager/control-reachability.md +39 -0
  117. package/plugins/lisa-copilot/rules/eager/falsifiable-checks.md +2 -0
  118. package/plugins/lisa-copilot/rules/eager/work-item-definition-of-ready.md +1 -1
  119. package/plugins/lisa-copilot/rules/reference/control-reachability.md +122 -0
  120. package/plugins/lisa-copilot/rules/reference/falsifiable-checks.md +2 -0
  121. package/plugins/lisa-copilot/skills/lisa-github-add-journey/SKILL.md +1 -0
  122. package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +28 -1
  123. package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +1 -0
  124. package/plugins/lisa-copilot/skills/lisa-jira-add-journey/SKILL.md +1 -0
  125. package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
  126. package/plugins/lisa-copilot/skills/lisa-linear-add-journey/SKILL.md +1 -0
  127. package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +27 -0
  128. package/plugins/lisa-copilot/skills/lisa-reproduce-bug/SKILL.md +8 -0
  129. package/plugins/lisa-copilot/skills/lisa-tdd-implementation/SKILL.md +1 -0
  130. package/plugins/lisa-copilot/skills/lisa-ticket-triage/SKILL.md +8 -0
  131. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  132. package/plugins/lisa-cursor/hooks/inject-resolved-config.mjs +12 -4
  133. package/plugins/lisa-cursor/hooks/inject-resolved-config.sh +39 -8
  134. package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +127 -0
  135. package/plugins/lisa-cursor/rules/control-reachability.mdc +44 -0
  136. package/plugins/lisa-cursor/rules/falsifiable-checks-reference.mdc +2 -0
  137. package/plugins/lisa-cursor/rules/falsifiable-checks.mdc +2 -0
  138. package/plugins/lisa-cursor/rules/work-item-definition-of-ready.mdc +1 -1
  139. package/plugins/lisa-cursor/skills/lisa-github-add-journey/SKILL.md +1 -0
  140. package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +28 -1
  141. package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +1 -0
  142. package/plugins/lisa-cursor/skills/lisa-jira-add-journey/SKILL.md +1 -0
  143. package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
  144. package/plugins/lisa-cursor/skills/lisa-linear-add-journey/SKILL.md +1 -0
  145. package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +27 -0
  146. package/plugins/lisa-cursor/skills/lisa-reproduce-bug/SKILL.md +8 -0
  147. package/plugins/lisa-cursor/skills/lisa-tdd-implementation/SKILL.md +1 -0
  148. package/plugins/lisa-cursor/skills/lisa-ticket-triage/SKILL.md +8 -0
  149. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  150. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  151. package/plugins/lisa-expo-agy/plugin.json +1 -1
  152. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  153. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  154. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  155. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  156. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  157. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  158. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  159. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  160. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  161. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  162. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  163. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  164. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  165. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  166. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  167. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  168. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  169. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  170. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  171. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  172. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  173. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  174. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  175. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  176. package/plugins/lisa-rails-agy/plugin.json +1 -1
  177. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  178. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  179. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  180. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  181. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  182. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  183. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  184. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  185. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  186. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  187. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  188. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  189. package/plugins/src/base/hooks/inject-resolved-config.mjs +12 -4
  190. package/plugins/src/base/hooks/inject-resolved-config.sh +39 -8
  191. package/plugins/src/base/rules/eager/control-reachability.md +39 -0
  192. package/plugins/src/base/rules/eager/falsifiable-checks.md +2 -0
  193. package/plugins/src/base/rules/eager/work-item-definition-of-ready.md +1 -1
  194. package/plugins/src/base/rules/reference/control-reachability.md +122 -0
  195. package/plugins/src/base/rules/reference/falsifiable-checks.md +2 -0
  196. package/plugins/src/base/skills/lisa-github-add-journey/SKILL.md +1 -0
  197. package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +28 -1
  198. package/plugins/src/base/skills/lisa-implement/SKILL.md +1 -0
  199. package/plugins/src/base/skills/lisa-jira-add-journey/SKILL.md +1 -0
  200. package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
  201. package/plugins/src/base/skills/lisa-linear-add-journey/SKILL.md +1 -0
  202. package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +27 -0
  203. package/plugins/src/base/skills/lisa-reproduce-bug/SKILL.md +8 -0
  204. package/plugins/src/base/skills/lisa-tdd-implementation/SKILL.md +1 -0
  205. package/plugins/src/base/skills/lisa-ticket-triage/SKILL.md +8 -0
  206. package/rails/merge/.claude/settings.json +1 -1
  207. package/scripts/two-channel-couplings.json +1 -1
  208. package/typescript/merge/.claude/settings.json +1 -1
@@ -97,6 +97,7 @@ Per-type content requirements are defined once in the vendor-neutral `work-item-
97
97
  | S17 Improvement measurability | `acceptance-criteria` | true |
98
98
  | S18 Stateless-pickup dry-run | `product-clarity` | true |
99
99
  | S19 Branch Plan derivation | `technical` | false |
100
+ | S20 Named-control reachability | `acceptance-criteria` | true |
100
101
  | F1 Issue type valid in project | `structural` | false |
101
102
  | F2 Epic parent exists and is an Epic | `structural` | false |
102
103
  | F3 Linked tickets exist | `structural` | false |
@@ -328,6 +329,31 @@ Failing a proposed spec is free; failing every existing ticket would turn a lega
328
329
 
329
330
  FAIL names both plans (rendered and recomputed) and points the remediation at **the environment, never the branch** — e.g. `"Branch Plan conflicts with the environment mapping. Rendered 'Branch from: release/staging'; recomputed from Target Backend Environment 'production' via deploy.branches → 'main'. Correct the environment, not the branch — the branches are derived. Then re-render."` Never silently choose between the two plans.
330
331
 
332
+ #### S20 — Named-control reachability
333
+
334
+ Enforces the vendor-neutral `control-reachability` rule: **a work item that names an existing test as a red-before-green control must say what makes that test reach the code the change touches.** A control whose reachability cannot be stated is not a control — its green tells the implementer nothing, and the ticket's own stopping rule then instructs a revert of a correct fix.
335
+
336
+ **When the gate applies.** Run S20 when the body — `Acceptance Criteria`, `Technical Approach`, or `Validation Journey` — names an **existing** test AND predicts a state change for it when the work lands. Signals: "must go red", "currently passes and must fail", "fails before the fix and passes after", "if it still passes the fix did nothing", or any equivalent stopping rule.
337
+
338
+ **When it must stay silent.** `N/A` otherwise. A ticket that introduces a **new** test rather than pinning an existing one carries no reachability obligation and must never be reported as incomplete on this basis. This half is load-bearing: a gate that fires on every work item is one callers learn to route around.
339
+
340
+ **What must be present.** One marker per named control, inside `h2. Validation Journey`:
341
+
342
+ ```text
343
+ [CONTROL: <test-identifier> | reaches: <input-or-field>]
344
+ ```
345
+
346
+ Parse by the exact `[CONTROL:` prefix and split on the single `|`.
347
+
348
+ - `<test-identifier>` — non-empty; the test file path, the test name, or both. It must be enough to run the test.
349
+ - `reaches:` — the literal key, then a non-empty description of the **input, field, fixture key, argument, or state** that carries execution into the changed code.
350
+
351
+ FAIL when a named existing-test control has no marker, when a marker is malformed (missing the `|`, an empty identifier, a missing or empty `reaches:` half), or when the `reaches:` half restates the assertion instead of naming an input (`reaches: the fix`, `reaches: the changed code path`). Naming the code is not enough; the marker answers "what in this fixture gets execution there".
352
+
353
+ Remediation: `"Name the input or field that makes <test> reach the code this work changes, as [CONTROL: <test> | reaches: <field>]. If the fixture does not reach it, the test cannot observe this change — extend the fixture or specify a new test instead."`
354
+
355
+ `product_relevant: true` — an unfalsifiable stopping rule is a specification defect, and whoever wrote the ticket is who can repair it. `lisa-jira-write-ticket` renders the marker; `control-reachability` carries the implementer-side counterpart, which forbids acting on the stopping rule before the cause of an unmoved control is established.
356
+
331
357
  ### Feasibility Gates (require JIRA lookups; skip in dry-run if requested)
332
358
 
333
359
  #### F1 — Issue type valid in project
@@ -416,6 +442,7 @@ Output is a single fenced text block. Callers parse it; do not add free-form pro
416
442
  - [PASS|FAIL|N/A] S17 Improvement measurability — <one-line reason>
417
443
  - [PASS|FAIL|N/A] S18 Stateless-pickup dry-run — <one-line reason>
418
444
  - [PASS|FAIL|N/A] S19 Branch Plan derivation — <one-line reason>
445
+ - [PASS|FAIL|N/A] S20 Named-control reachability — <one-line reason>
419
446
 
420
447
  ### Feasibility Gates (omit this section when --spec-only)
421
448
  - [PASS|FAIL|N/A] F1 Issue type valid in project — <one-line reason>
@@ -441,7 +468,7 @@ The verdict is `PASS` if and only if every applicable gate is `PASS`. Any `FAIL`
441
468
 
442
469
  ### Failure-detail fields
443
470
 
444
- - **gate**: the gate ID (`S1`–`S18`, `F1`–`F5`).
471
+ - **gate**: the gate ID (`S1`–`S20`, `F1`–`F5`).
445
472
  - **category**: the gate's fixed category from the table above. Callers use this to label or filter comments — `product-clarity`, `acceptance-criteria`, `design-ux`, `scope`, `dependency`, `data`, `technical`, or `structural`.
446
473
  - **product_relevant**: matches the gate's table entry. `false` means the failure is an internal data-quality problem (e.g., the agent built a malformed spec, an issue type is invalid in the project) and the caller should fix it without bothering the product team. `true` means the PRD needs product input to resolve.
447
474
  - **what**: plain-language description of the issue. No gate IDs, no JIRA jargon, no engineering shorthand. A product owner reading this on a Notion comment should understand what is unclear and why.
@@ -87,6 +87,7 @@ Compose the journey with typed `[EVIDENCE: <artifact-type>: <name>]` markers at
87
87
  6. **Cover happy path and error path** — At minimum, one success and one failure evidence marker.
88
88
  7. **On a leaf work unit, the markers are binding** — For a Bug / Task / Sub-task / Improvement, every typed `[EVIDENCE: <artifact-type>: <name>]` here is the item's evidence manifest: validation gate S14 requires at least one, and the item cannot be closed until each named artifact is captured **in its declared type** and attached (see the "Per-Work-Unit Evidence Contract" in the `verification` rule). Name only evidence you intend to capture — and name all of it.
89
89
  8. **Reference sibling evidence without claiming it** — Use only `[EVIDENCE-REF: <work-item-ref> | <artifact-type>: <kebab-case-name>]` when prose points to an artifact declared by another item. Never paste, quote, or code-format the sibling's `[EVIDENCE: ...]` marker: that exact prefix creates a local obligation. `EVIDENCE-REF` never satisfies this item's S14 minimum, uniqueness check, capture list, or completion gate; a runtime-changing leaf still needs at least one local `[EVIDENCE: ...]` marker.
90
+ 9. **A named existing test is a control, and a control declares its reachability** — When the journey pins an *existing* test as a red-before-green control ("must go red", "fails before the fix and passes after"), the `control-reachability` rule requires the item to say what makes that test reach the changed code: `[CONTROL: <test-identifier> | reaches: <input-or-field>]`, one per named control, validated by gate S20. Name the fixture key, field, argument, or state that carries execution into the change — not the code itself. A test whose fixture never reaches the changed path stays green for a reason unrelated to the change, and the stopping rule then reads as "revert a correct fix". Introducing a **new** test instead carries no such obligation; S20 is `N/A` and no marker is written.
90
91
 
91
92
  ### Step 6: Present to User for Approval
92
93
 
@@ -98,6 +98,7 @@ Per-type content requirements are defined once in the vendor-neutral `work-item-
98
98
  | S17 Improvement measurability | `acceptance-criteria` | true |
99
99
  | S18 Stateless-pickup dry-run | `product-clarity` | true |
100
100
  | S19 Branch Plan derivation | `technical` | false |
101
+ | S20 Named-control reachability | `acceptance-criteria` | true |
101
102
  | F1 Issue type valid in team | `structural` | false |
102
103
  | F2 Project parent exists and is in same team | `structural` | false |
103
104
  | F3 Linked items exist | `structural` | false |
@@ -330,6 +331,31 @@ Failing a proposed spec is free; failing every existing Issue would turn a legac
330
331
 
331
332
  FAIL names both plans (rendered and recomputed) and points the remediation at **the environment, never the branch** — e.g. `"Branch Plan conflicts with the environment mapping. Rendered 'Branch from: release/staging'; recomputed from Target Backend Environment 'production' via deploy.branches → 'main'. Correct the environment, not the branch — the branches are derived. Then re-render."` Never silently choose between the two plans.
332
333
 
334
+ #### S20 — Named-control reachability
335
+
336
+ Enforces the vendor-neutral `control-reachability` rule: **a work item that names an existing test as a red-before-green control must say what makes that test reach the code the change touches.** A control whose reachability cannot be stated is not a control — its green tells the implementer nothing, and the Issue's own stopping rule then instructs a revert of a correct fix.
337
+
338
+ **When the gate applies.** Run S20 when the body — `Acceptance Criteria`, `Technical Approach`, or `Validation Journey` — names an **existing** test AND predicts a state change for it when the work lands. Signals: "must go red", "currently passes and must fail", "fails before the fix and passes after", "if it still passes the fix did nothing", or any equivalent stopping rule.
339
+
340
+ **When it must stay silent.** `N/A` otherwise. A Issue that introduces a **new** test rather than pinning an existing one carries no reachability obligation and must never be reported as incomplete on this basis. This half is load-bearing: a gate that fires on every work item is one callers learn to route around.
341
+
342
+ **What must be present.** One marker per named control, inside `## Validation Journey`:
343
+
344
+ ```text
345
+ [CONTROL: <test-identifier> | reaches: <input-or-field>]
346
+ ```
347
+
348
+ Parse by the exact `[CONTROL:` prefix and split on the single `|`.
349
+
350
+ - `<test-identifier>` — non-empty; the test file path, the test name, or both. It must be enough to run the test.
351
+ - `reaches:` — the literal key, then a non-empty description of the **input, field, fixture key, argument, or state** that carries execution into the changed code.
352
+
353
+ FAIL when a named existing-test control has no marker, when a marker is malformed (missing the `|`, an empty identifier, a missing or empty `reaches:` half), or when the `reaches:` half restates the assertion instead of naming an input (`reaches: the fix`, `reaches: the changed code path`). Naming the code is not enough; the marker answers "what in this fixture gets execution there".
354
+
355
+ Remediation: `"Name the input or field that makes <test> reach the code this work changes, as [CONTROL: <test> | reaches: <field>]. If the fixture does not reach it, the test cannot observe this change — extend the fixture or specify a new test instead."`
356
+
357
+ `product_relevant: true` — an unfalsifiable stopping rule is a specification defect, and whoever wrote the Issue is who can repair it. `lisa-linear-write-issue` renders the marker; `control-reachability` carries the implementer-side counterpart, which forbids acting on the stopping rule before the cause of an unmoved control is established.
358
+
333
359
  ### Feasibility Gates (require Linear lookups; skip in dry-run if requested)
334
360
 
335
361
  #### F1 — Issue type valid in team
@@ -422,6 +448,7 @@ Output is a single fenced text block. Callers parse it; do not add free-form pro
422
448
  - [PASS|FAIL|N/A] S17 Improvement measurability — <one-line reason>
423
449
  - [PASS|FAIL|N/A] S18 Stateless-pickup dry-run — <one-line reason>
424
450
  - [PASS|FAIL|N/A] S19 Branch Plan derivation — <one-line reason>
451
+ - [PASS|FAIL|N/A] S20 Named-control reachability — <one-line reason>
425
452
 
426
453
  ### Feasibility Gates (omit when --spec-only)
427
454
  - [PASS|FAIL|N/A] F1 Issue type valid in team — <one-line reason>
@@ -38,6 +38,14 @@ If the real path is unreachable — no credentials, no environment, no data —
38
38
 
39
39
  **A failing test is the preferred form** wherever it can cross the real path: it runs in CI, it becomes the regression guard, and `codify-verification` expects it. A script is the fallback. Manual steps are the last resort and must carry their prerequisites.
40
40
 
41
+ ## When the work item names an existing test as the reproduction
42
+
43
+ A work item may hand you the control instead of asking you to build one: *"`<existing test>` pins today's wrong behaviour; it must go red once this is fixed."* Treat that as a reproduction only once it is **reachable** — the `control-reachability` rule holds that a test whose fixture never exercises the changed path stays green for a reason unrelated to the change, so its green is uninterpretable and the item's stopping rule reads as "revert a correct fix".
44
+
45
+ - **Before trusting it**, check the item for the `[CONTROL: <test-identifier> | reaches: <input-or-field>]` marker gate S20 requires, and confirm the named input is actually in the fixture. A named test with no stated reachability is an unvalidated control, not a reproduction.
46
+ - **If it does not move when the change lands**, stop and establish why: the change had no effect (revisit the change), or the fixture never reached the changed path (fix or extend the control). Prove it by execution — a temporary `throw` in the changed block run under that one test, or coverage scoped to that test alone — never by reading the fixture and concluding it looks right.
47
+ - **Never revert on an unexplained green.** Report the finding — which method you used and what it showed — the way any other reproduction result is reported.
48
+
41
49
  ## Report the failure rate, do not round it
42
50
 
43
51
  Run the reproduction enough times to state a rate. A reproduction that fails half the time cannot prove a fix — one passing run afterwards means nothing at that rate. If the rate is too low to distinguish a fix from luck, say what would raise it: more iterations, a forced schedule, a narrowed trigger, a seeded clock.
@@ -66,6 +66,7 @@ TDD Cycle:
66
66
  - The team lead may not waive, defer, or mark that user-visible regression spec as optional, "if cheap", or equivalent. The only exits are a recorded absence of an end-to-end harness for the affected platform, or a genuine technical blocker with a linked build-ready follow-up ticket created before merge and referenced from the PR and source work item.
67
67
  - For frontend work, the `bdd-e2e-coverage` rule governs what that spec is sealing: before writing it, add or update the Gherkin scenario with its stable ID in the project's behavior contract, then write aligned automation in the project's configured runner for each platform the scenario requires. The scenario is the specification the RED test encodes — write the scenario first, in the same PR.
68
68
  - A regression spec is not complete merely because it exists. Completion evidence must prove the spec actually ran and passed in PR CI with a named log line, reporter output, or equivalent execution record. Guard against `test.skip`, suite-level environment gates, shard filters, and "0 tests" passes.
69
+ - When the work item **prescribes an existing test as the control** — "that test must go red; if it still passes the fix did nothing" — the `control-reachability` rule governs what a green control means. It is two facts, not one: the change had no effect (revisit the change), or the fixture never reached the changed path (fix or extend the control — do **not** revert). They demand opposite actions, and the cheaper reading wins by default when nothing forces the distinction. **Establish which holds before acting on the stopping rule, and prove reachability by execution, never by reading the fixture** — a temporary `throw` at the top of the changed block run under that one test, or coverage scoped to that test alone. Never revert on an unexplained green; an unexplained control is a blocked observation, not a verdict. If the item carries a `[CONTROL: <test> | reaches: <input>]` marker, that marker is the claim you are checking.
69
70
 
70
71
  ### GREEN Phase
71
72
 
@@ -156,6 +156,14 @@ Produce a table:
156
156
 
157
157
  Every verification method must be specific enough that an automated agent could execute it.
158
158
 
159
+ ### Prescribed controls must be reachable
160
+
161
+ When the ticket pins an **existing** test as a red-before-green control — "that test must go red", "fails before the fix and passes after", "if it still passes the fix did nothing" — apply the `control-reachability` rule before accepting it. The stopping rule is only valid if the test's fixture exercises the path the change touches; when it does not, the test stays green for an unrelated reason and the ticket instructs the implementer to revert a correct fix.
162
+
163
+ - Confirm the ticket carries the `[CONTROL: <test-identifier> | reaches: <input-or-field>]` marker gate S20 requires, and that the named input is actually present in that test's fixture. Verify it in the codebase — this is a triage step precisely because triage is the last point before an implementer starts trusting it.
164
+ - A named control with no reachability declaration, or one whose declared input is absent from the fixture, is an **Ambiguity** finding under Phase 3, not a pass. Report it with the specific input the fixture would need, or the recommendation to specify a new test instead.
165
+ - A ticket that introduces a **new** test rather than pinning an existing one carries no such obligation. Do not raise a finding on this basis.
166
+
159
167
  ## Phase 6 -- Verdict
160
168
 
161
169
  Evaluate the findings and produce exactly one verdict:
@@ -82,7 +82,7 @@
82
82
  ]
83
83
  },
84
84
  "permissions": {
85
- "deny": ["Read(./.entire/metadata/**)"]
85
+ "deny": []
86
86
  },
87
87
  "plansDirectory": "./plans"
88
88
  }
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "inspected": {
7
7
  "workflows": 22,
8
- "steps": 670,
8
+ "steps": 677,
9
9
  "couplings": 33,
10
10
  "inventory": 69
11
11
  },
@@ -28,7 +28,7 @@
28
28
  "ENABLE_LSP_TOOL": "1"
29
29
  },
30
30
  "permissions": {
31
- "deny": ["Read(./.entire/metadata/**)"]
31
+ "deny": []
32
32
  },
33
33
  "hooks": {
34
34
  "WorktreeCreate": [