@codyswann/lisa 4.32.3 → 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.
- package/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +94 -6
- package/all/copy-overwrite/scripts/lisa-gates.mjs +259 -5
- package/all/copy-overwrite/scripts/lisa-run-gates.mjs +20 -39
- package/all/merge/.claude/settings.json +1 -3
- package/cdk/merge/.claude/settings.json +1 -1
- package/dist/cli/doctor-declared-contexts.d.ts +1 -1
- package/dist/cli/doctor-declared-contexts.d.ts.map +1 -1
- package/dist/cli/doctor-declared-contexts.js +48 -7
- package/dist/cli/doctor-declared-contexts.js.map +1 -1
- package/dist/cli/gate-report-drift-section.d.ts.map +1 -1
- package/dist/cli/gate-report-drift-section.js +2 -0
- package/dist/cli/gate-report-drift-section.js.map +1 -1
- package/dist/configs/vitest/base.d.ts +81 -0
- package/dist/configs/vitest/base.d.ts.map +1 -1
- package/dist/configs/vitest/base.js +106 -0
- package/dist/configs/vitest/base.js.map +1 -1
- package/dist/configs/vitest/cdk.d.ts +2 -2
- package/dist/configs/vitest/cdk.d.ts.map +1 -1
- package/dist/configs/vitest/cdk.js +4 -2
- package/dist/configs/vitest/cdk.js.map +1 -1
- package/dist/configs/vitest/harper-fabric.d.ts +2 -2
- package/dist/configs/vitest/harper-fabric.d.ts.map +1 -1
- package/dist/configs/vitest/harper-fabric.js +4 -2
- package/dist/configs/vitest/harper-fabric.js.map +1 -1
- package/dist/configs/vitest/nestjs.d.ts +2 -2
- package/dist/configs/vitest/nestjs.d.ts.map +1 -1
- package/dist/configs/vitest/nestjs.js +4 -2
- package/dist/configs/vitest/nestjs.js.map +1 -1
- package/dist/configs/vitest/phaser.d.ts +2 -2
- package/dist/configs/vitest/phaser.d.ts.map +1 -1
- package/dist/configs/vitest/phaser.js +4 -2
- package/dist/configs/vitest/phaser.js.map +1 -1
- package/dist/configs/vitest/typescript.d.ts +2 -2
- package/dist/configs/vitest/typescript.d.ts.map +1 -1
- package/dist/configs/vitest/typescript.js +4 -2
- package/dist/configs/vitest/typescript.js.map +1 -1
- package/dist/core/gate-context-owners.d.ts +18 -0
- package/dist/core/gate-context-owners.d.ts.map +1 -1
- package/dist/core/gate-context-owners.js +7 -0
- package/dist/core/gate-context-owners.js.map +1 -1
- package/dist/core/gate-declaration-drift.d.ts +37 -0
- package/dist/core/gate-declaration-drift.d.ts.map +1 -1
- package/dist/core/gate-declaration-drift.js +100 -26
- package/dist/core/gate-declaration-drift.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +6 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +47 -29
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/health/declared-checks-inspection.d.ts.map +1 -1
- package/dist/health/declared-checks-inspection.js +23 -7
- package/dist/health/declared-checks-inspection.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +3 -0
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/prune-retired-claude-deny-rules.d.ts +39 -0
- package/dist/migrations/prune-retired-claude-deny-rules.d.ts.map +1 -0
- package/dist/migrations/prune-retired-claude-deny-rules.js +152 -0
- package/dist/migrations/prune-retired-claude-deny-rules.js.map +1 -0
- package/expo/merge/.claude/settings.json +1 -1
- package/harper-fabric/merge/.claude/settings.json +1 -1
- package/nestjs/merge/.claude/settings.json +1 -1
- package/package.json +4 -4
- package/phaser/merge/.claude/settings.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-add-journey/SKILL.md +1 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +28 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +1 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-add-journey/SKILL.md +1 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-add-journey/SKILL.md +1 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +27 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-reproduce-bug/SKILL.md +8 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-tdd-implementation/SKILL.md +1 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-ticket-triage/SKILL.md +8 -0
- package/plugins/lisa/hooks/inject-resolved-config.mjs +12 -4
- package/plugins/lisa/hooks/inject-resolved-config.sh +39 -8
- package/plugins/lisa/rules/eager/control-reachability.md +39 -0
- package/plugins/lisa/rules/eager/falsifiable-checks.md +2 -0
- package/plugins/lisa/rules/eager/work-item-definition-of-ready.md +1 -1
- package/plugins/lisa/rules/reference/control-reachability.md +122 -0
- package/plugins/lisa/rules/reference/falsifiable-checks.md +2 -0
- package/plugins/lisa/skills/lisa-github-add-journey/SKILL.md +1 -0
- package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +28 -1
- package/plugins/lisa/skills/lisa-implement/SKILL.md +1 -0
- package/plugins/lisa/skills/lisa-jira-add-journey/SKILL.md +1 -0
- package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
- package/plugins/lisa/skills/lisa-linear-add-journey/SKILL.md +1 -0
- package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +27 -0
- package/plugins/lisa/skills/lisa-reproduce-bug/SKILL.md +8 -0
- package/plugins/lisa/skills/lisa-tdd-implementation/SKILL.md +1 -0
- package/plugins/lisa/skills/lisa-ticket-triage/SKILL.md +8 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-github-add-journey/SKILL.md +1 -0
- package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +28 -1
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +1 -0
- package/plugins/lisa-agy/skills/lisa-jira-add-journey/SKILL.md +1 -0
- package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
- package/plugins/lisa-agy/skills/lisa-linear-add-journey/SKILL.md +1 -0
- package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +27 -0
- package/plugins/lisa-agy/skills/lisa-reproduce-bug/SKILL.md +8 -0
- package/plugins/lisa-agy/skills/lisa-tdd-implementation/SKILL.md +1 -0
- package/plugins/lisa-agy/skills/lisa-ticket-triage/SKILL.md +8 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/inject-resolved-config.mjs +12 -4
- package/plugins/lisa-copilot/hooks/inject-resolved-config.sh +39 -8
- package/plugins/lisa-copilot/rules/eager/control-reachability.md +39 -0
- package/plugins/lisa-copilot/rules/eager/falsifiable-checks.md +2 -0
- package/plugins/lisa-copilot/rules/eager/work-item-definition-of-ready.md +1 -1
- package/plugins/lisa-copilot/rules/reference/control-reachability.md +122 -0
- package/plugins/lisa-copilot/rules/reference/falsifiable-checks.md +2 -0
- package/plugins/lisa-copilot/skills/lisa-github-add-journey/SKILL.md +1 -0
- package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +28 -1
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +1 -0
- package/plugins/lisa-copilot/skills/lisa-jira-add-journey/SKILL.md +1 -0
- package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
- package/plugins/lisa-copilot/skills/lisa-linear-add-journey/SKILL.md +1 -0
- package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +27 -0
- package/plugins/lisa-copilot/skills/lisa-reproduce-bug/SKILL.md +8 -0
- package/plugins/lisa-copilot/skills/lisa-tdd-implementation/SKILL.md +1 -0
- package/plugins/lisa-copilot/skills/lisa-ticket-triage/SKILL.md +8 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/inject-resolved-config.mjs +12 -4
- package/plugins/lisa-cursor/hooks/inject-resolved-config.sh +39 -8
- package/plugins/lisa-cursor/rules/control-reachability-reference.mdc +127 -0
- package/plugins/lisa-cursor/rules/control-reachability.mdc +44 -0
- package/plugins/lisa-cursor/rules/falsifiable-checks-reference.mdc +2 -0
- package/plugins/lisa-cursor/rules/falsifiable-checks.mdc +2 -0
- package/plugins/lisa-cursor/rules/work-item-definition-of-ready.mdc +1 -1
- package/plugins/lisa-cursor/skills/lisa-github-add-journey/SKILL.md +1 -0
- package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +28 -1
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +1 -0
- package/plugins/lisa-cursor/skills/lisa-jira-add-journey/SKILL.md +1 -0
- package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
- package/plugins/lisa-cursor/skills/lisa-linear-add-journey/SKILL.md +1 -0
- package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +27 -0
- package/plugins/lisa-cursor/skills/lisa-reproduce-bug/SKILL.md +8 -0
- package/plugins/lisa-cursor/skills/lisa-tdd-implementation/SKILL.md +1 -0
- package/plugins/lisa-cursor/skills/lisa-ticket-triage/SKILL.md +8 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/inject-resolved-config.mjs +12 -4
- package/plugins/src/base/hooks/inject-resolved-config.sh +39 -8
- package/plugins/src/base/rules/eager/control-reachability.md +39 -0
- package/plugins/src/base/rules/eager/falsifiable-checks.md +2 -0
- package/plugins/src/base/rules/eager/work-item-definition-of-ready.md +1 -1
- package/plugins/src/base/rules/reference/control-reachability.md +122 -0
- package/plugins/src/base/rules/reference/falsifiable-checks.md +2 -0
- package/plugins/src/base/skills/lisa-github-add-journey/SKILL.md +1 -0
- package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +28 -1
- package/plugins/src/base/skills/lisa-implement/SKILL.md +1 -0
- package/plugins/src/base/skills/lisa-jira-add-journey/SKILL.md +1 -0
- package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +28 -1
- package/plugins/src/base/skills/lisa-linear-add-journey/SKILL.md +1 -0
- package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +27 -0
- package/plugins/src/base/skills/lisa-reproduce-bug/SKILL.md +8 -0
- package/plugins/src/base/skills/lisa-tdd-implementation/SKILL.md +1 -0
- package/plugins/src/base/skills/lisa-ticket-triage/SKILL.md +8 -0
- package/rails/merge/.claude/settings.json +1 -1
- package/scripts/two-channel-couplings.json +1 -1
- package/typescript/merge/.claude/settings.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Control Reachability — reference"
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Control Reachability — reference
|
|
7
|
+
|
|
8
|
+
The full body behind the `control-reachability` eager head. It defines one vendor-neutral contract consumed by the three tracker writers (`lisa-jira-write-ticket`, `lisa-github-write-issue`, `lisa-linear-write-issue`), the three validators (`lisa-jira-validate-ticket`, `lisa-github-validate-issue`, `lisa-linear-validate-issue`) via gate **S20**, and the implementer surfaces `lisa-ticket-triage`, `lisa-tdd-implementation`, `lisa-reproduce-bug` and `lisa-implement`. Each surface cites this slug rather than growing its own prose, exactly as the vendor arms cite `leaf-only-lifecycle`, `repo-scope-split` and `derived-branch-plan`. One slug is what keeps a control rejected on JIRA from being accepted on Linear.
|
|
9
|
+
|
|
10
|
+
## The measured instance
|
|
11
|
+
|
|
12
|
+
A work item in a consumer lane named a real, existing test, stated a real expectation about it, and prescribed a stopping rule in substance:
|
|
13
|
+
|
|
14
|
+
> Make the change; that pinned test must go red. If it still passes, the fix did nothing.
|
|
15
|
+
|
|
16
|
+
On its face this is exemplary. It hands the implementer a falsifiable stopping condition instead of asking it to judge the result by eye, and it is the shape Lisa's own `falsifiable-checks` rule asks for everywhere else.
|
|
17
|
+
|
|
18
|
+
**The test still passed, and the fix had worked.** The pinned test's fixture omitted the field the fix reads, so execution fell through to a default branch. The test stayed green **for an entirely new reason** — not because behaviour was unchanged, but because the fixture never reached the changed path. Following the item's own stopping rule would have reverted a correct fix. A human noticed; an unattended agent following the written rule would not have.
|
|
19
|
+
|
|
20
|
+
## Why this failure mode is distinct
|
|
21
|
+
|
|
22
|
+
Lisa already carries a family of rules about controls that do not bite. This one is the mirror image, and the difference is what makes it worth its own slug.
|
|
23
|
+
|
|
24
|
+
| | The familiar family (`falsifiable-checks`) | This rule |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| Where the defect lives | The check the author wrote | The **specification** that prescribes the check |
|
|
27
|
+
| Failure mode | The control **fails to bite** — passes something it should catch | The control **bites in the wrong direction** |
|
|
28
|
+
| Cost | A defect ships | A **correct fix is reverted** |
|
|
29
|
+
| How it looks | A green check nobody questions | Diligence — the agent followed the written stopping rule |
|
|
30
|
+
| Who it hits hardest | Careless implementers | **Disciplined** implementers |
|
|
31
|
+
|
|
32
|
+
It sits in the layer everyone treats as the thing that catches implementation error, which is exactly why nothing was watching it.
|
|
33
|
+
|
|
34
|
+
Related, at other layers: a gate that blocks a slow reviewer but only reports a hollow one is a gate mis-weighting two real signals; a gate that declares a merge condition nothing verifies is an unverified assertion in config. Here the signal itself is uninterpretable, and it is verified by an agent — incorrectly.
|
|
35
|
+
|
|
36
|
+
## Why the checkable form, and why a marker
|
|
37
|
+
|
|
38
|
+
The obvious fix is a sentence of guidance: *"make sure the fixture reaches the path."* That is the wrong instrument, and it is worth being explicit about why, because it will look like the simple answer every time someone revisits this.
|
|
39
|
+
|
|
40
|
+
**This failure is itself an instance of advisory guidance being insufficient.** A thoughtful author wrote a thoughtful stopping rule and it was still wrong. Prose asking for more thoughtfulness is the same class of artifact that failed. So the obligation is expressed as a **parseable declaration** a validator either finds or does not, in the same family as the `[EVIDENCE: <type>: <name>]` manifest markers — which exist for the same reason, and which turned "attach proof" from a discipline into a property.
|
|
41
|
+
|
|
42
|
+
The marker also moves the check **earlier**. A prose instruction can only be evaluated at the moment the stopping rule misfires, by the one agent least able to see past it. A marker is checked before work begins, by a validator that has no stake in the outcome.
|
|
43
|
+
|
|
44
|
+
## The marker
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
[CONTROL: <test-identifier> | reaches: <input-or-field>]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Placed in `## Validation Journey`, one per named control. Parse by the exact `[CONTROL:` prefix and split on the single `|`.
|
|
51
|
+
|
|
52
|
+
| Field | Requirement |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `<test-identifier>` | Non-empty. The test file path, the test name, or both. It must be enough to run the test. |
|
|
55
|
+
| `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. |
|
|
56
|
+
|
|
57
|
+
Worked examples:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
[CONTROL: tests/unit/pricing/discount.test.ts "applies tier discount" | reaches: fixture sets tier="gold", which is the field the resolver newly reads]
|
|
61
|
+
[CONTROL: spec/models/invoice_spec.rb#late_fee | reaches: seeded invoice has due_on in the past, entering the overdue branch]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Rejected, with the reason:
|
|
65
|
+
|
|
66
|
+
| Marker | Why it fails |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `[CONTROL: discount.test.ts]` | No `reaches:` half — names the control without saying what makes it observe the change |
|
|
69
|
+
| `[CONTROL: | reaches: tier field]` | Empty identifier — nobody can run it |
|
|
70
|
+
| `[CONTROL: discount.test.ts \| reaches: the fix]` | Restates the assertion instead of naming an input |
|
|
71
|
+
| `[CONTROL: discount.test.ts \| reaches: the changed code path]` | Same — names the code, not what gets execution there |
|
|
72
|
+
|
|
73
|
+
**The marker answers one question: what in this fixture gets execution to the changed lines?** Anything that does not answer it is a control whose reachability cannot be stated, and a control whose reachability cannot be stated is not a control.
|
|
74
|
+
|
|
75
|
+
## Gate S20 — when it applies, and when it must stay silent
|
|
76
|
+
|
|
77
|
+
S20 applies 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.
|
|
78
|
+
|
|
79
|
+
It is `N/A` otherwise, and that half of the contract is load-bearing. **An item 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. A gate that fires on every work item is a gate teams learn to route around; the value of S20 is that a FAIL means something specific.
|
|
80
|
+
|
|
81
|
+
The remediation names the input, not the discipline:
|
|
82
|
+
|
|
83
|
+
> 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.
|
|
84
|
+
|
|
85
|
+
`product_relevant: true`. An unfalsifiable stopping rule is a specification defect, and the person who wrote the item is the person who can fix it.
|
|
86
|
+
|
|
87
|
+
## The implementer arm — a green control is two facts
|
|
88
|
+
|
|
89
|
+
The authoring gate catches what it can see before work starts. It cannot see the actual test outcome, and only the implementer can. So the contract has a second arm, bound by `lisa-tdd-implementation` (RED phase) and `lisa-reproduce-bug` (when an existing test is reused as the reproduction):
|
|
90
|
+
|
|
91
|
+
**When a prescribed control does not change state as the item predicted, stop. Establish which of the two causes holds before acting on the stopping rule.**
|
|
92
|
+
|
|
93
|
+
| Cause | Correct action |
|
|
94
|
+
|---|---|
|
|
95
|
+
| The change had no effect on behaviour | Revisit the change |
|
|
96
|
+
| The fixture never reaches the changed code path | Fix or extend the control — do **not** revert |
|
|
97
|
+
|
|
98
|
+
Two properties of this make it worth writing down rather than trusting to judgement:
|
|
99
|
+
|
|
100
|
+
1. **The actions are opposite.** Getting the cause wrong does not degrade the outcome, it inverts it.
|
|
101
|
+
2. **One reading is cheaper than the other.** "The fix did nothing" requires no further work and closes the question; "the fixture never got there" requires an investigation. Left to default, the cheap reading wins — and it is the one that reverts working code.
|
|
102
|
+
|
|
103
|
+
### How to establish it
|
|
104
|
+
|
|
105
|
+
**Prove reachability by execution, never by reading the fixture.** Reasoning that the fixture *looks like* it reaches the path is exactly the "mentally reverting" move `falsifiable-checks` rejects, and for the same reason: the person doing the reasoning already believes the answer.
|
|
106
|
+
|
|
107
|
+
Two sound methods, in order of preference:
|
|
108
|
+
|
|
109
|
+
- **Instrument the changed code.** Put a temporary `throw` — not a log line, which can be swallowed by a reporter — at the top of the changed block, run that one test, and read whether it errors. Then remove it. A throw is preferred because its absence is unambiguous.
|
|
110
|
+
- **Coverage, scoped to the single test.** Run the project's coverage tool over that test alone and read whether the changed lines were executed. Whole-suite coverage does not answer this: some *other* test reaching those lines tells you nothing about this control.
|
|
111
|
+
|
|
112
|
+
Then report which method was used and what it showed. "The control stayed green because the fixture never set `tier`, confirmed by a throw at the resolver that the test did not hit" is a finding. "The control stayed green" is not.
|
|
113
|
+
|
|
114
|
+
### Never revert on an unexplained green
|
|
115
|
+
|
|
116
|
+
An unexplained control is a **blocked observation**, not a verdict. The only state that licenses revisiting the change is a control proven to reach the changed path and still not move. If reachability cannot be established at all — the harness will not run, the code path is unreachable from any fixture — that is a finding to report, and the work item's stopping rule is void until someone repairs the control.
|
|
117
|
+
|
|
118
|
+
Fixing the control is ordinary work, not scope creep: extend the fixture with the field that carries execution to the change, or write the test the item should have named. Either way the control ends the work able to observe the change, which is what the item asked for in the first place.
|
|
119
|
+
|
|
120
|
+
## What this rule does not claim
|
|
121
|
+
|
|
122
|
+
Stated plainly so nobody reads more into it than the evidence supports.
|
|
123
|
+
|
|
124
|
+
- **Frequency.** One measured instance. No count exists of how many work items name an existing test as a red-before-green control, so the rate could be rare or routine. The gate is cheap and `N/A` by default, which is the right shape for an unknown denominator.
|
|
125
|
+
- **Detectability by tooling.** Whether any existing gate could *automatically* detect a fixture that does not reach the changed code path is unexamined. Coverage-diff and per-test path instrumentation are plausible and would strengthen this rule into a machine check rather than a declaration; so is the possibility that nothing available does it cheaply. This contract deliberately requires a **declaration** an author writes and a validator parses, which is checkable today.
|
|
126
|
+
- **Blast radius of the observed instance.** The fix was not reverted, because a human noticed. That an unattended agent would have reverted it is inferred from the written stopping rule, not observed.
|
|
127
|
+
- **That an advisory rule would not have helped.** Untested. It is a reason to prefer the checkable form, not a measured finding.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Control Reachability — A Prescribed Control Must Reach the Changed Path (load-bearing)"
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Control Reachability — A Prescribed Control Must Reach the Changed Path (load-bearing)
|
|
7
|
+
|
|
8
|
+
A work item may hand the implementer a falsifiable stopping rule: *"make the change; `<named existing test>` must go red. If it still passes, the fix did nothing."* That is good practice — it replaces eyeballing with an observation. It is also **only valid if that test's fixture exercises the path the change touches.**
|
|
9
|
+
|
|
10
|
+
When it does not, the test stays green **for a reason unrelated to the change**: execution never reached the changed code. The stopping rule then instructs the implementer to revert a correct fix, and the more faithfully the implementer follows the item, the more reliably it happens. Measured once in the portfolio: a pinned test's fixture omitted the field the fix read, execution fell through to a default branch, the test stayed green, and the fix had worked.
|
|
11
|
+
|
|
12
|
+
This is the specification-side sibling of `falsifiable-checks`, which governs a check *you authored*. Here the check is **prescribed by the work item** and the defect is in the prose, not the code.
|
|
13
|
+
|
|
14
|
+
## Authoring obligation — checkable, not advisory
|
|
15
|
+
|
|
16
|
+
**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.** Naming the code is not enough; the item names the *input*.
|
|
17
|
+
|
|
18
|
+
Declare it in `## Validation Journey`, one marker per named control:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
[CONTROL: <test-identifier> | reaches: <input-or-field>]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `<test-identifier>` — non-empty: the test file path, the test name, or both.
|
|
25
|
+
- `reaches:` — the literal key, then the **input, field, fixture key, argument, or state** that carries execution into the changed code. `reaches: the fix` and `reaches: the changed code` restate the assertion and do not satisfy it.
|
|
26
|
+
|
|
27
|
+
Enforced by gate **S20** in `lisa-jira-validate-ticket`, `lisa-github-validate-issue`, and `lisa-linear-validate-issue`; rendered by `lisa-jira-write-ticket`, `lisa-github-write-issue`, and `lisa-linear-write-issue`; read at claim time by `lisa-ticket-triage` and `lisa-implement`.
|
|
28
|
+
|
|
29
|
+
**An item that prescribes no existing-test control is exempt.** Introducing a *new* test carries no reachability obligation — the gate is `N/A` and the item is never reported as incomplete on this basis.
|
|
30
|
+
|
|
31
|
+
## Implementer obligation — a green control is two facts, not one
|
|
32
|
+
|
|
33
|
+
`lisa-tdd-implementation` and `lisa-reproduce-bug` bind this arm: **when a prescribed control does not change state as the item predicted, stop. Establish which cause holds before acting on the stopping rule.**
|
|
34
|
+
|
|
35
|
+
| Cause | Correct action |
|
|
36
|
+
|---|---|
|
|
37
|
+
| The change had no effect on behaviour | Revisit the change |
|
|
38
|
+
| The fixture never reaches the changed code path | Fix or extend the control — do **not** revert |
|
|
39
|
+
|
|
40
|
+
They demand opposite actions, and the cheaper reading ("the fix did nothing") wins by default when nothing forces the distinction. So force it: **prove reachability by execution, never by reading the fixture.** Put a temporary throw, log line, or counter in the changed code and run that one test; or run coverage scoped to it and read whether the changed lines were hit. Reasoning that the fixture *looks like* it reaches the path is the same move `falsifiable-checks` rejects as "mentally reverting".
|
|
41
|
+
|
|
42
|
+
**Never revert on an unexplained green.** An unexplained control is a blocked observation, not a verdict — and a reachable-but-still-green control is the only form of it that licenses revisiting the change.
|
|
43
|
+
|
|
44
|
+
Full prose, the worked instance, and the reporting template: [reference/control-reachability.md](control-reachability-reference.mdc).
|
|
@@ -7,6 +7,8 @@ alwaysApply: false
|
|
|
7
7
|
|
|
8
8
|
Eager head: [eager/falsifiable-checks.md](falsifiable-checks.mdc).
|
|
9
9
|
|
|
10
|
+
Sibling rule, opposite direction: [`control-reachability`](control-reachability.mdc) covers a check a **work item prescribed** rather than one you authored. There the control does not fail to bite — it bites in the wrong direction, and the cost is a correct fix reverted rather than a defect shipped.
|
|
11
|
+
|
|
10
12
|
## Why this rule exists
|
|
11
13
|
|
|
12
14
|
The `verification` rule prevents the failure "I claimed it works without using it." This rule prevents a subtler one, one layer down: **I used a check, the check said clean, and the check was incapable of saying anything else.**
|
|
@@ -11,6 +11,8 @@ A passing test, a clean lint run, a zero-hit sweep, a green ratchet: each is evi
|
|
|
11
11
|
|
|
12
12
|
This is the instrument-validity counterpart to the `verification` rule (which proves the *software* behaves) and `empirical-inquiry` (which proves a *fact*). All three reject "it looks correct." This one rejects "the check said so."
|
|
13
13
|
|
|
14
|
+
When the check was **prescribed by a work item** rather than authored here — "that named test must go red, or the fix did nothing" — `control-reachability` governs instead, and it inverts the cost: a control whose fixture never reaches the changed path stays green for an unrelated reason, and the item's own stopping rule then reverts a correct fix.
|
|
15
|
+
|
|
14
16
|
## The five ways a check silently measures nothing
|
|
15
17
|
|
|
16
18
|
Each has been observed in real runs; each reported success while asserting nothing:
|
|
@@ -9,7 +9,7 @@ alwaysApply: true
|
|
|
9
9
|
|
|
10
10
|
## Shared core (every leaf, enforced by `*-validate-*` S1–S16)
|
|
11
11
|
|
|
12
|
-
Three-audience description · Gherkin acceptance criteria (except Spike) · Out of Scope · single-repo scope · relationship search · typed evidence manifest on runtime changes · Source Requirement traceability when PRD-sourced · provable read access to every named external surface.
|
|
12
|
+
Three-audience description · Gherkin acceptance criteria (except Spike) · Out of Scope · single-repo scope · relationship search · typed evidence manifest on runtime changes · Source Requirement traceability when PRD-sourced · provable read access to every named external surface · declared reachability for any **existing** test the item pins as a red-before-green control (`control-reachability`, gate S20 — `N/A` when the item introduces a new test instead).
|
|
13
13
|
|
|
14
14
|
## Per-type additions
|
|
15
15
|
|
|
@@ -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 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 issue's evidence manifest: validation gate S14 requires at least one, and the issue 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 issue. Never paste, quote, or code-format the sibling's `[EVIDENCE: ...]` marker: that exact prefix creates a local obligation. `EVIDENCE-REF` never satisfies this issue'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 issue 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
|
|
|
@@ -100,6 +100,7 @@ Per-type content requirements are defined once in the vendor-neutral `work-item-
|
|
|
100
100
|
| S17 Improvement measurability | `acceptance-criteria` | true |
|
|
101
101
|
| S18 Stateless-pickup dry-run | `product-clarity` | true |
|
|
102
102
|
| S19 Branch Plan derivation | `technical` | false |
|
|
103
|
+
| S20 Named-control reachability | `acceptance-criteria` | true |
|
|
103
104
|
| F1 Issue type label exists in repo | `structural` | false |
|
|
104
105
|
| F2 Parent sub-issue exists and is the right type | `structural` | false |
|
|
105
106
|
| F3 Linked issues exist | `structural` | false |
|
|
@@ -326,6 +327,31 @@ Failing a proposed spec is free; failing every existing issue would turn a legac
|
|
|
326
327
|
|
|
327
328
|
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.
|
|
328
329
|
|
|
330
|
+
#### S20 — Named-control reachability
|
|
331
|
+
|
|
332
|
+
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.
|
|
333
|
+
|
|
334
|
+
**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.
|
|
335
|
+
|
|
336
|
+
**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.
|
|
337
|
+
|
|
338
|
+
**What must be present.** One marker per named control, inside `## Validation Journey`:
|
|
339
|
+
|
|
340
|
+
```text
|
|
341
|
+
[CONTROL: <test-identifier> | reaches: <input-or-field>]
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Parse by the exact `[CONTROL:` prefix and split on the single `|`.
|
|
345
|
+
|
|
346
|
+
- `<test-identifier>` — non-empty; the test file path, the test name, or both. It must be enough to run the test.
|
|
347
|
+
- `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.
|
|
348
|
+
|
|
349
|
+
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".
|
|
350
|
+
|
|
351
|
+
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."`
|
|
352
|
+
|
|
353
|
+
`product_relevant: true` — an unfalsifiable stopping rule is a specification defect, and whoever wrote the issue is who can repair it. `lisa-github-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.
|
|
354
|
+
|
|
329
355
|
### Feasibility Gates (require GitHub lookups; skip in `--spec-only`)
|
|
330
356
|
|
|
331
357
|
#### F1 — Issue type label exists in repo
|
|
@@ -487,6 +513,7 @@ Output is a single fenced text block. Callers parse it; do not add free-form pro
|
|
|
487
513
|
- [PASS|FAIL|N/A] S17 Improvement measurability — <one-line reason>
|
|
488
514
|
- [PASS|FAIL|N/A] S18 Stateless-pickup dry-run — <one-line reason>
|
|
489
515
|
- [PASS|FAIL|N/A] S19 Branch Plan derivation — <one-line reason>
|
|
516
|
+
- [PASS|FAIL|N/A] S20 Named-control reachability — <one-line reason>
|
|
490
517
|
|
|
491
518
|
### Feasibility Gates (omit this section when --spec-only)
|
|
492
519
|
- [PASS|FAIL|N/A] F1 Issue type label exists in repo — <one-line reason>
|
|
@@ -513,7 +540,7 @@ The verdict is `PASS` if every applicable gate is `PASS`. Any `FAIL` makes the v
|
|
|
513
540
|
|
|
514
541
|
Same shape and meaning as `lisa-jira-validate-ticket` so downstream PRD-intake skills (Notion, Confluence, Linear, GitHub) can format comments uniformly:
|
|
515
542
|
|
|
516
|
-
- **gate**: the gate ID (`S1`–`
|
|
543
|
+
- **gate**: the gate ID (`S1`–`S20`, `F1`–`F5`).
|
|
517
544
|
- **category**: the gate's fixed category from the table.
|
|
518
545
|
- **product_relevant**: matches the gate's table entry. `false` means the failure is an internal data-quality problem the caller should fix without bothering product.
|
|
519
546
|
- **what**: plain-language, product-readable.
|
|
@@ -100,6 +100,7 @@ When deciding the agents to use, consider:
|
|
|
100
100
|
* Before any task is implemented, the agent team must explore the codebase for relevant research (documentation, code, git history, etc) and update each task's `metadata.relevant_documentation` with the findings.
|
|
101
101
|
* Each task must be reviewed by the team to make sure their verification passes.
|
|
102
102
|
* Each task must have their learnings captured to the ledger by the learner subagent.
|
|
103
|
+
* When the work item prescribes an **existing** test as a red-before-green control ("that test must go red once this lands"), the `control-reachability` rule binds the team. Read the item's `[CONTROL: <test-identifier> | reaches: <input-or-field>]` declaration (gate S20) and confirm the named input is in that test's fixture. If the control does not move when the change lands, **do not act on the stopping rule until the cause is established**: the change had no effect (revisit the change) or the fixture never reached the changed path (fix or extend the control — never revert). Prove reachability by execution — a temporary `throw` in the changed block under that one test, or coverage scoped to it — never by reading the fixture. A named control with no reachability declaration is an unvalidated control; treat it as a triage finding, not a pass.
|
|
103
104
|
|
|
104
105
|
Using the general-purpose agent in Team Lead session, Determine the name of this plan
|
|
105
106
|
|
|
@@ -111,6 +111,7 @@ h3. Assertions
|
|
|
111
111
|
6. **Cover happy path and error path** — At minimum, one success and one failure evidence marker
|
|
112
112
|
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 ticket's evidence manifest: validation gate S14 requires at least one, and the ticket 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.
|
|
113
113
|
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 ticket. Never paste, quote, or code-format the sibling's `[EVIDENCE: ...]` marker: that exact prefix creates a local obligation. `EVIDENCE-REF` never satisfies this ticket's S14 minimum, uniqueness check, capture list, or completion gate; a runtime-changing leaf still needs at least one local `[EVIDENCE: ...]` marker.
|
|
114
|
+
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 ticket 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.
|
|
114
115
|
|
|
115
116
|
### Step 6: Present to User for Approval
|
|
116
117
|
|
|
@@ -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`–`
|
|
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:
|