@codyswann/lisa 4.34.20 → 4.35.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 (64) hide show
  1. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  2. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  3. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  4. package/dist/core/upstream-evidence-manifest.js +11 -0
  5. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  6. package/package.json +4 -4
  7. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  8. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  9. package/plugins/lisa/rules/eager/measurement-integrity.md +18 -0
  10. package/plugins/lisa/rules/reference/measurement-integrity.md +109 -0
  11. package/plugins/lisa-agy/plugin.json +1 -1
  12. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  14. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  15. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  16. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-copilot/rules/eager/measurement-integrity.md +18 -0
  19. package/plugins/lisa-copilot/rules/reference/measurement-integrity.md +109 -0
  20. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-cursor/rules/measurement-integrity-reference.mdc +114 -0
  22. package/plugins/lisa-cursor/rules/measurement-integrity.mdc +23 -0
  23. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa-expo-agy/plugin.json +1 -1
  26. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  31. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  35. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  36. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  41. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  45. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  46. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-rails-agy/plugin.json +1 -1
  51. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  55. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  56. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  61. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/src/base/rules/eager/measurement-integrity.md +18 -0
  64. package/plugins/src/base/rules/reference/measurement-integrity.md +109 -0
package/package.json CHANGED
@@ -166,7 +166,7 @@
166
166
  "zod-validation-error": "^4.0.0"
167
167
  },
168
168
  "name": "@codyswann/lisa",
169
- "version": "4.34.20",
169
+ "version": "4.35.0",
170
170
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
171
171
  "main": "dist/index.js",
172
172
  "exports": {
@@ -314,7 +314,7 @@
314
314
  "test": "tests"
315
315
  },
316
316
  "types": "./dist/index.d.ts",
317
- "lisaReleaseCommit": "10494a527b6448b5c7c37ca29fc6722367f5214b",
318
- "gitHead": "10494a527b6448b5c7c37ca29fc6722367f5214b",
319
- "lisaReleaseTag": "v4.34.20"
317
+ "lisaReleaseCommit": "6ba4777d77e74e8cf75fcc77de82d20c95147160",
318
+ "gitHead": "6ba4777d77e74e8cf75fcc77de82d20c95147160",
319
+ "lisaReleaseTag": "v4.35.0"
320
320
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -0,0 +1,18 @@
1
+ # Measurement Integrity (load-bearing)
2
+
3
+ **Before you report a count, a population, or "N items affected", run the checks — because every way this goes wrong produces output that looks exactly like an answer.** No exception, no warning, no partial flag: the wrong number is indistinguishable from the right one by reading it.
4
+
5
+ That is why "be careful about scope" is not a remedy. **Every check below is performed or not performed**, never noticed:
6
+
7
+ 1. **State the population boundary** before enumerating — what is in, and what is adjacent and out.
8
+ 2. **Prove the enumeration complete** — paginate, and *assert* the end-of-pages signal rather than trusting it. A silent cap looks exactly like a population.
9
+ 3. **Validate the detector against known-true items.** If it cannot find the examples you already have, its total means nothing.
10
+ 4. **Treat search results as candidates, never members** — read each one directly and confirm the literal.
11
+ 5. **Control the instrument in both directions** — it must report a real absence, and must not invent one.
12
+ 6. **Report pattern-derived totals as floors**, and **state the unit** beside any count whose argument rests on how many *independent* observations you have.
13
+
14
+ No single check covers the others: validating against known items is blind to truncation, and reading every candidate can only ever see what enumeration handed it.
15
+
16
+ **And one that fires while reading rather than measuring:** a filter built from the failure shapes you already expect cannot show you a shape you did not. Read gate and tool output **unfiltered** on a refusal — the diagnosis is usually already in it.
17
+
18
+ Full method (six modes in two families, the membership test for adding a seventh, and which check is blind to what): [reference/measurement-integrity.md](../reference/measurement-integrity.md).
@@ -0,0 +1,109 @@
1
+ # Measurement Integrity
2
+
3
+ How an audit, sweep, or population count arrives at a confident wrong number, and the mechanical steps that catch each way.
4
+
5
+ ## The unifying property
6
+
7
+ **Every failure here produces output that looks exactly like an answer.** None throws, none warns, none reports partial. The wrong number is indistinguishable from the right one by inspecting the result, which is why it survives review and gets cited.
8
+
9
+ That has a direct consequence for remedies: **a step that requires the auditor to notice something has already failed.** Every recommendation below is shaped as an action that is either performed or not — validate the detector against known-true items, assert the end-of-pages signal, pair a negative with a positive control. "Be careful about scope" is not one of these.
10
+
11
+ ## The membership test
12
+
13
+ This is a method, not a bestiary. A failure belongs here when **all three** hold:
14
+
15
+ 1. **The output is well formed.** No exception, no warning, no truncation marker, no partial flag.
16
+ 2. **The error is invisible from the artifact alone.** Reading the result — the number, the list, the report — cannot distinguish it from a correct one.
17
+ 3. **A mechanical step would have caught it.** Something performed-or-not, not a judgement. If no such step exists, it is a hazard worth documenting but it is not a mode here, because the entry earns its place by supplying the check.
18
+
19
+ **The list is open.** It is currently six modes in two families plus one adjacent family. An earlier four-item version of a related list felt complete at four and was not; a taxonomy is the artifact most likely to be believed finished, and its own characteristic failure is a reader who stops looking once their case appears covered. Condition 3 is what lets you add to it rather than merely consult it.
20
+
21
+ ## Family A — the instrument mis-measures
22
+
23
+ Six modes fall out of two axes: **direction** (does it under- or over-report?) and **stage** (does it fail while *enumerating* candidates, or while *matching* them?). All four cells are occupied, which is a reason to believe the family is close to complete and not a reason to stop looking.
24
+
25
+ | | under-reports | over-reports |
26
+ |---|---|---|
27
+ | **enumeration** | class exclusion · truncation | scope inflation |
28
+ | **matching** | pattern blindness | tokenization · alternation order |
29
+
30
+ ### A1. Class exclusion — under, enumeration
31
+
32
+ An entire corpus sits outside the query's scope and nothing says so. `gh search issues` does not return pull requests; run alone against a mostly-PR population it reported **10 items where the answer was 45**, and omitted the two items already known to be members. A list that misses the examples you started from still reads as authoritative.
33
+
34
+ **Catch it:** validate the detector against known-true items before believing any count.
35
+
36
+ ### A2. Truncation — under, enumeration
37
+
38
+ Real members past the first page are dropped with no truncation signal. `gh issue list` and `gh pr list` default to 30 and cap silently at whatever `--limit` is passed: one audit reported **37 items where the answer was 513**, a 14× undercount. A paging API returning **249 of 845 rows with `hasNextPage: false`** is the same mode on another substrate.
39
+
40
+ **Catch it:** paginate to exhaustion and assert the end-of-pages signal rather than trusting it.
41
+
42
+ ### A3. Pattern blindness — under, matching
43
+
44
+ Members the pattern cannot express are never matched. A prose form of an organisation's name appeared in three bodies and was matched by no pattern in the audit that was looking for exactly that; it was caught only because a person added the spelling by hand.
45
+
46
+ **Catch it:** nothing automatic. Report every pattern-derived total as a **floor**, never a total — saying so is the honest form, and it is a step that is either performed or not.
47
+
48
+ ### A4. Scope inflation — over, enumeration
49
+
50
+ Candidates are enumerated from a root that reaches beyond the population. A tree walk asserting a repository ships exactly one copy of a script found **21 additional copies**, one per agent worktree nested inside the checkout, and failed. The walk was correct; its root was wrong.
51
+
52
+ Note the trap this mode sets for A1's remedy: **path reachability is not membership.** "The search reached it" and "it belongs to the population" are different claims, and conflating them is how an over-report is defended as thoroughness.
53
+
54
+ **Catch it:** state the population boundary before enumerating, and assert that a known non-member is excluded — the positive control's mirror.
55
+
56
+ ### A5. Tokenization — over, matching
57
+
58
+ The query is split into terms, so unrelated items match. Searching for a URL-shaped needle matched issues containing only its bare final word in ordinary prose — one read *"There is no account, token, or session."* Two of 47 candidates were phantoms.
59
+
60
+ **Catch it:** treat search results as **candidates, never members**; read each one directly and confirm the literal.
61
+
62
+ ### A6. Alternation order — over, matching, and it fabricates
63
+
64
+ A regex alternative matches early and produces a value with no referent. Extracting paths with `\.(mjs|js|sh|json)` matched `js` inside `package.json` before reaching `json`, producing a phantom `package.js` and reporting a live exposure that did not exist.
65
+
66
+ This is the most expensive mode. A4 and A5 return a *real item for a wrong reason* and die on a direct read; this returns an **artifact that does not exist**, so a direct read cannot disconfirm it — there is nothing to read — and it sends someone chasing a break that was never there.
67
+
68
+ **Catch it:** order alternations longest-first, or anchor the match end. Control it with an input crafted to trip the partial match; asserting the regex matches what it should is satisfied by the broken version.
69
+
70
+ ## Family B — the instrument is right and the report is wrong
71
+
72
+ ### B1. Unit error
73
+
74
+ The measurement is correct and the **reported unit** is not. "Three separate branches" versus *three events across two branches* is not a count error — the count was three either way. It inflates the **independence** of the sample, which is the property the argument rested on: repeated observations on one branch are weaker evidence of a general flake than the same number spread across branches.
75
+
76
+ No amount of instrument validation catches this. The detector was never involved.
77
+
78
+ **Catch it:** state the unit next to the number, and ask whether the claim rests on *how many* or on *how many independent*. Where it rests on independence, report both.
79
+
80
+ ## Family C — adjacent, and deliberately not a mode here
81
+
82
+ ### C1. Filter blindness
83
+
84
+ **A filter built from the failure shapes you already expect cannot show you a shape you did not.** An agent grep-filtered push output for failure markers and discarded the gate's own line naming the cause; the gate had diagnosed itself perfectly and the filter threw the diagnosis away.
85
+
86
+ This is listed and **excluded from the taxonomy on purpose.** It fails condition 2 in a different place: the artifact was complete and correct, and the loss happened while *reading* it. Forcing it into Family A would dilute the membership test, which is the part of this document that does work. It is here because it is the same shape one layer out, and because the remedy is one line: **read gate and tool output unfiltered on a refusal.**
87
+
88
+ ## Composition: no single check catches all of them
89
+
90
+ This is the payload. Each check is blind to modes the others catch.
91
+
92
+ | check | catches | blind to |
93
+ |---|---|---|
94
+ | validate the detector against known-true items | A1, A3 | A2 when the knowns sit on page one · A4 · A5 · A6 · B1 |
95
+ | read every candidate directly | A5, A6 | A1 · A2 · A3 — it only ever sees what enumeration handed it |
96
+ | paginate and assert end-of-pages | A2 | everything else |
97
+ | assert a known non-member is excluded | A4 | A1 · A2 · A3 |
98
+ | state the unit beside the number | B1 | all of Family A |
99
+
100
+ **Validating against known-true items** loses to truncation, because your known examples are usually recent and therefore on the first page. **Reading every candidate** kills both over-reports and cannot see what was never enumerated. They are complements, not alternatives.
101
+
102
+ ## The procedure
103
+
104
+ 1. **State the population boundary** before enumerating — what is in, what is adjacent and out.
105
+ 2. **Enumerate candidates and prove the enumeration complete** — paginate, assert the end-of-pages signal, and check the query's scope covers every corpus (issues *and* pull requests, open *and* closed, bodies *and* comments).
106
+ 3. **Validate the detector against known-true items.** If it cannot find the examples you already have, its total means nothing.
107
+ 4. **Read every candidate directly** and confirm the literal. Search results are candidates, never members.
108
+ 5. **Control the instrument in both directions** — it must report a real absence, and must not invent one.
109
+ 6. **Report pattern-derived totals as floors**, and **state the unit** beside any count whose argument rests on independence.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -0,0 +1,18 @@
1
+ # Measurement Integrity (load-bearing)
2
+
3
+ **Before you report a count, a population, or "N items affected", run the checks — because every way this goes wrong produces output that looks exactly like an answer.** No exception, no warning, no partial flag: the wrong number is indistinguishable from the right one by reading it.
4
+
5
+ That is why "be careful about scope" is not a remedy. **Every check below is performed or not performed**, never noticed:
6
+
7
+ 1. **State the population boundary** before enumerating — what is in, and what is adjacent and out.
8
+ 2. **Prove the enumeration complete** — paginate, and *assert* the end-of-pages signal rather than trusting it. A silent cap looks exactly like a population.
9
+ 3. **Validate the detector against known-true items.** If it cannot find the examples you already have, its total means nothing.
10
+ 4. **Treat search results as candidates, never members** — read each one directly and confirm the literal.
11
+ 5. **Control the instrument in both directions** — it must report a real absence, and must not invent one.
12
+ 6. **Report pattern-derived totals as floors**, and **state the unit** beside any count whose argument rests on how many *independent* observations you have.
13
+
14
+ No single check covers the others: validating against known items is blind to truncation, and reading every candidate can only ever see what enumeration handed it.
15
+
16
+ **And one that fires while reading rather than measuring:** a filter built from the failure shapes you already expect cannot show you a shape you did not. Read gate and tool output **unfiltered** on a refusal — the diagnosis is usually already in it.
17
+
18
+ Full method (six modes in two families, the membership test for adding a seventh, and which check is blind to what): [reference/measurement-integrity.md](../reference/measurement-integrity.md).
@@ -0,0 +1,109 @@
1
+ # Measurement Integrity
2
+
3
+ How an audit, sweep, or population count arrives at a confident wrong number, and the mechanical steps that catch each way.
4
+
5
+ ## The unifying property
6
+
7
+ **Every failure here produces output that looks exactly like an answer.** None throws, none warns, none reports partial. The wrong number is indistinguishable from the right one by inspecting the result, which is why it survives review and gets cited.
8
+
9
+ That has a direct consequence for remedies: **a step that requires the auditor to notice something has already failed.** Every recommendation below is shaped as an action that is either performed or not — validate the detector against known-true items, assert the end-of-pages signal, pair a negative with a positive control. "Be careful about scope" is not one of these.
10
+
11
+ ## The membership test
12
+
13
+ This is a method, not a bestiary. A failure belongs here when **all three** hold:
14
+
15
+ 1. **The output is well formed.** No exception, no warning, no truncation marker, no partial flag.
16
+ 2. **The error is invisible from the artifact alone.** Reading the result — the number, the list, the report — cannot distinguish it from a correct one.
17
+ 3. **A mechanical step would have caught it.** Something performed-or-not, not a judgement. If no such step exists, it is a hazard worth documenting but it is not a mode here, because the entry earns its place by supplying the check.
18
+
19
+ **The list is open.** It is currently six modes in two families plus one adjacent family. An earlier four-item version of a related list felt complete at four and was not; a taxonomy is the artifact most likely to be believed finished, and its own characteristic failure is a reader who stops looking once their case appears covered. Condition 3 is what lets you add to it rather than merely consult it.
20
+
21
+ ## Family A — the instrument mis-measures
22
+
23
+ Six modes fall out of two axes: **direction** (does it under- or over-report?) and **stage** (does it fail while *enumerating* candidates, or while *matching* them?). All four cells are occupied, which is a reason to believe the family is close to complete and not a reason to stop looking.
24
+
25
+ | | under-reports | over-reports |
26
+ |---|---|---|
27
+ | **enumeration** | class exclusion · truncation | scope inflation |
28
+ | **matching** | pattern blindness | tokenization · alternation order |
29
+
30
+ ### A1. Class exclusion — under, enumeration
31
+
32
+ An entire corpus sits outside the query's scope and nothing says so. `gh search issues` does not return pull requests; run alone against a mostly-PR population it reported **10 items where the answer was 45**, and omitted the two items already known to be members. A list that misses the examples you started from still reads as authoritative.
33
+
34
+ **Catch it:** validate the detector against known-true items before believing any count.
35
+
36
+ ### A2. Truncation — under, enumeration
37
+
38
+ Real members past the first page are dropped with no truncation signal. `gh issue list` and `gh pr list` default to 30 and cap silently at whatever `--limit` is passed: one audit reported **37 items where the answer was 513**, a 14× undercount. A paging API returning **249 of 845 rows with `hasNextPage: false`** is the same mode on another substrate.
39
+
40
+ **Catch it:** paginate to exhaustion and assert the end-of-pages signal rather than trusting it.
41
+
42
+ ### A3. Pattern blindness — under, matching
43
+
44
+ Members the pattern cannot express are never matched. A prose form of an organisation's name appeared in three bodies and was matched by no pattern in the audit that was looking for exactly that; it was caught only because a person added the spelling by hand.
45
+
46
+ **Catch it:** nothing automatic. Report every pattern-derived total as a **floor**, never a total — saying so is the honest form, and it is a step that is either performed or not.
47
+
48
+ ### A4. Scope inflation — over, enumeration
49
+
50
+ Candidates are enumerated from a root that reaches beyond the population. A tree walk asserting a repository ships exactly one copy of a script found **21 additional copies**, one per agent worktree nested inside the checkout, and failed. The walk was correct; its root was wrong.
51
+
52
+ Note the trap this mode sets for A1's remedy: **path reachability is not membership.** "The search reached it" and "it belongs to the population" are different claims, and conflating them is how an over-report is defended as thoroughness.
53
+
54
+ **Catch it:** state the population boundary before enumerating, and assert that a known non-member is excluded — the positive control's mirror.
55
+
56
+ ### A5. Tokenization — over, matching
57
+
58
+ The query is split into terms, so unrelated items match. Searching for a URL-shaped needle matched issues containing only its bare final word in ordinary prose — one read *"There is no account, token, or session."* Two of 47 candidates were phantoms.
59
+
60
+ **Catch it:** treat search results as **candidates, never members**; read each one directly and confirm the literal.
61
+
62
+ ### A6. Alternation order — over, matching, and it fabricates
63
+
64
+ A regex alternative matches early and produces a value with no referent. Extracting paths with `\.(mjs|js|sh|json)` matched `js` inside `package.json` before reaching `json`, producing a phantom `package.js` and reporting a live exposure that did not exist.
65
+
66
+ This is the most expensive mode. A4 and A5 return a *real item for a wrong reason* and die on a direct read; this returns an **artifact that does not exist**, so a direct read cannot disconfirm it — there is nothing to read — and it sends someone chasing a break that was never there.
67
+
68
+ **Catch it:** order alternations longest-first, or anchor the match end. Control it with an input crafted to trip the partial match; asserting the regex matches what it should is satisfied by the broken version.
69
+
70
+ ## Family B — the instrument is right and the report is wrong
71
+
72
+ ### B1. Unit error
73
+
74
+ The measurement is correct and the **reported unit** is not. "Three separate branches" versus *three events across two branches* is not a count error — the count was three either way. It inflates the **independence** of the sample, which is the property the argument rested on: repeated observations on one branch are weaker evidence of a general flake than the same number spread across branches.
75
+
76
+ No amount of instrument validation catches this. The detector was never involved.
77
+
78
+ **Catch it:** state the unit next to the number, and ask whether the claim rests on *how many* or on *how many independent*. Where it rests on independence, report both.
79
+
80
+ ## Family C — adjacent, and deliberately not a mode here
81
+
82
+ ### C1. Filter blindness
83
+
84
+ **A filter built from the failure shapes you already expect cannot show you a shape you did not.** An agent grep-filtered push output for failure markers and discarded the gate's own line naming the cause; the gate had diagnosed itself perfectly and the filter threw the diagnosis away.
85
+
86
+ This is listed and **excluded from the taxonomy on purpose.** It fails condition 2 in a different place: the artifact was complete and correct, and the loss happened while *reading* it. Forcing it into Family A would dilute the membership test, which is the part of this document that does work. It is here because it is the same shape one layer out, and because the remedy is one line: **read gate and tool output unfiltered on a refusal.**
87
+
88
+ ## Composition: no single check catches all of them
89
+
90
+ This is the payload. Each check is blind to modes the others catch.
91
+
92
+ | check | catches | blind to |
93
+ |---|---|---|
94
+ | validate the detector against known-true items | A1, A3 | A2 when the knowns sit on page one · A4 · A5 · A6 · B1 |
95
+ | read every candidate directly | A5, A6 | A1 · A2 · A3 — it only ever sees what enumeration handed it |
96
+ | paginate and assert end-of-pages | A2 | everything else |
97
+ | assert a known non-member is excluded | A4 | A1 · A2 · A3 |
98
+ | state the unit beside the number | B1 | all of Family A |
99
+
100
+ **Validating against known-true items** loses to truncation, because your known examples are usually recent and therefore on the first page. **Reading every candidate** kills both over-reports and cannot see what was never enumerated. They are complements, not alternatives.
101
+
102
+ ## The procedure
103
+
104
+ 1. **State the population boundary** before enumerating — what is in, what is adjacent and out.
105
+ 2. **Enumerate candidates and prove the enumeration complete** — paginate, assert the end-of-pages signal, and check the query's scope covers every corpus (issues *and* pull requests, open *and* closed, bodies *and* comments).
106
+ 3. **Validate the detector against known-true items.** If it cannot find the examples you already have, its total means nothing.
107
+ 4. **Read every candidate directly** and confirm the literal. Search results are candidates, never members.
108
+ 5. **Control the instrument in both directions** — it must report a real absence, and must not invent one.
109
+ 6. **Report pattern-derived totals as floors**, and **state the unit** beside any count whose argument rests on independence.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -0,0 +1,114 @@
1
+ ---
2
+ description: "Measurement Integrity"
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # Measurement Integrity
7
+
8
+ How an audit, sweep, or population count arrives at a confident wrong number, and the mechanical steps that catch each way.
9
+
10
+ ## The unifying property
11
+
12
+ **Every failure here produces output that looks exactly like an answer.** None throws, none warns, none reports partial. The wrong number is indistinguishable from the right one by inspecting the result, which is why it survives review and gets cited.
13
+
14
+ That has a direct consequence for remedies: **a step that requires the auditor to notice something has already failed.** Every recommendation below is shaped as an action that is either performed or not — validate the detector against known-true items, assert the end-of-pages signal, pair a negative with a positive control. "Be careful about scope" is not one of these.
15
+
16
+ ## The membership test
17
+
18
+ This is a method, not a bestiary. A failure belongs here when **all three** hold:
19
+
20
+ 1. **The output is well formed.** No exception, no warning, no truncation marker, no partial flag.
21
+ 2. **The error is invisible from the artifact alone.** Reading the result — the number, the list, the report — cannot distinguish it from a correct one.
22
+ 3. **A mechanical step would have caught it.** Something performed-or-not, not a judgement. If no such step exists, it is a hazard worth documenting but it is not a mode here, because the entry earns its place by supplying the check.
23
+
24
+ **The list is open.** It is currently six modes in two families plus one adjacent family. An earlier four-item version of a related list felt complete at four and was not; a taxonomy is the artifact most likely to be believed finished, and its own characteristic failure is a reader who stops looking once their case appears covered. Condition 3 is what lets you add to it rather than merely consult it.
25
+
26
+ ## Family A — the instrument mis-measures
27
+
28
+ Six modes fall out of two axes: **direction** (does it under- or over-report?) and **stage** (does it fail while *enumerating* candidates, or while *matching* them?). All four cells are occupied, which is a reason to believe the family is close to complete and not a reason to stop looking.
29
+
30
+ | | under-reports | over-reports |
31
+ |---|---|---|
32
+ | **enumeration** | class exclusion · truncation | scope inflation |
33
+ | **matching** | pattern blindness | tokenization · alternation order |
34
+
35
+ ### A1. Class exclusion — under, enumeration
36
+
37
+ An entire corpus sits outside the query's scope and nothing says so. `gh search issues` does not return pull requests; run alone against a mostly-PR population it reported **10 items where the answer was 45**, and omitted the two items already known to be members. A list that misses the examples you started from still reads as authoritative.
38
+
39
+ **Catch it:** validate the detector against known-true items before believing any count.
40
+
41
+ ### A2. Truncation — under, enumeration
42
+
43
+ Real members past the first page are dropped with no truncation signal. `gh issue list` and `gh pr list` default to 30 and cap silently at whatever `--limit` is passed: one audit reported **37 items where the answer was 513**, a 14× undercount. A paging API returning **249 of 845 rows with `hasNextPage: false`** is the same mode on another substrate.
44
+
45
+ **Catch it:** paginate to exhaustion and assert the end-of-pages signal rather than trusting it.
46
+
47
+ ### A3. Pattern blindness — under, matching
48
+
49
+ Members the pattern cannot express are never matched. A prose form of an organisation's name appeared in three bodies and was matched by no pattern in the audit that was looking for exactly that; it was caught only because a person added the spelling by hand.
50
+
51
+ **Catch it:** nothing automatic. Report every pattern-derived total as a **floor**, never a total — saying so is the honest form, and it is a step that is either performed or not.
52
+
53
+ ### A4. Scope inflation — over, enumeration
54
+
55
+ Candidates are enumerated from a root that reaches beyond the population. A tree walk asserting a repository ships exactly one copy of a script found **21 additional copies**, one per agent worktree nested inside the checkout, and failed. The walk was correct; its root was wrong.
56
+
57
+ Note the trap this mode sets for A1's remedy: **path reachability is not membership.** "The search reached it" and "it belongs to the population" are different claims, and conflating them is how an over-report is defended as thoroughness.
58
+
59
+ **Catch it:** state the population boundary before enumerating, and assert that a known non-member is excluded — the positive control's mirror.
60
+
61
+ ### A5. Tokenization — over, matching
62
+
63
+ The query is split into terms, so unrelated items match. Searching for a URL-shaped needle matched issues containing only its bare final word in ordinary prose — one read *"There is no account, token, or session."* Two of 47 candidates were phantoms.
64
+
65
+ **Catch it:** treat search results as **candidates, never members**; read each one directly and confirm the literal.
66
+
67
+ ### A6. Alternation order — over, matching, and it fabricates
68
+
69
+ A regex alternative matches early and produces a value with no referent. Extracting paths with `\.(mjs|js|sh|json)` matched `js` inside `package.json` before reaching `json`, producing a phantom `package.js` and reporting a live exposure that did not exist.
70
+
71
+ This is the most expensive mode. A4 and A5 return a *real item for a wrong reason* and die on a direct read; this returns an **artifact that does not exist**, so a direct read cannot disconfirm it — there is nothing to read — and it sends someone chasing a break that was never there.
72
+
73
+ **Catch it:** order alternations longest-first, or anchor the match end. Control it with an input crafted to trip the partial match; asserting the regex matches what it should is satisfied by the broken version.
74
+
75
+ ## Family B — the instrument is right and the report is wrong
76
+
77
+ ### B1. Unit error
78
+
79
+ The measurement is correct and the **reported unit** is not. "Three separate branches" versus *three events across two branches* is not a count error — the count was three either way. It inflates the **independence** of the sample, which is the property the argument rested on: repeated observations on one branch are weaker evidence of a general flake than the same number spread across branches.
80
+
81
+ No amount of instrument validation catches this. The detector was never involved.
82
+
83
+ **Catch it:** state the unit next to the number, and ask whether the claim rests on *how many* or on *how many independent*. Where it rests on independence, report both.
84
+
85
+ ## Family C — adjacent, and deliberately not a mode here
86
+
87
+ ### C1. Filter blindness
88
+
89
+ **A filter built from the failure shapes you already expect cannot show you a shape you did not.** An agent grep-filtered push output for failure markers and discarded the gate's own line naming the cause; the gate had diagnosed itself perfectly and the filter threw the diagnosis away.
90
+
91
+ This is listed and **excluded from the taxonomy on purpose.** It fails condition 2 in a different place: the artifact was complete and correct, and the loss happened while *reading* it. Forcing it into Family A would dilute the membership test, which is the part of this document that does work. It is here because it is the same shape one layer out, and because the remedy is one line: **read gate and tool output unfiltered on a refusal.**
92
+
93
+ ## Composition: no single check catches all of them
94
+
95
+ This is the payload. Each check is blind to modes the others catch.
96
+
97
+ | check | catches | blind to |
98
+ |---|---|---|
99
+ | validate the detector against known-true items | A1, A3 | A2 when the knowns sit on page one · A4 · A5 · A6 · B1 |
100
+ | read every candidate directly | A5, A6 | A1 · A2 · A3 — it only ever sees what enumeration handed it |
101
+ | paginate and assert end-of-pages | A2 | everything else |
102
+ | assert a known non-member is excluded | A4 | A1 · A2 · A3 |
103
+ | state the unit beside the number | B1 | all of Family A |
104
+
105
+ **Validating against known-true items** loses to truncation, because your known examples are usually recent and therefore on the first page. **Reading every candidate** kills both over-reports and cannot see what was never enumerated. They are complements, not alternatives.
106
+
107
+ ## The procedure
108
+
109
+ 1. **State the population boundary** before enumerating — what is in, what is adjacent and out.
110
+ 2. **Enumerate candidates and prove the enumeration complete** — paginate, assert the end-of-pages signal, and check the query's scope covers every corpus (issues *and* pull requests, open *and* closed, bodies *and* comments).
111
+ 3. **Validate the detector against known-true items.** If it cannot find the examples you already have, its total means nothing.
112
+ 4. **Read every candidate directly** and confirm the literal. Search results are candidates, never members.
113
+ 5. **Control the instrument in both directions** — it must report a real absence, and must not invent one.
114
+ 6. **Report pattern-derived totals as floors**, and **state the unit** beside any count whose argument rests on independence.
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: "Measurement Integrity (load-bearing)"
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Measurement Integrity (load-bearing)
7
+
8
+ **Before you report a count, a population, or "N items affected", run the checks — because every way this goes wrong produces output that looks exactly like an answer.** No exception, no warning, no partial flag: the wrong number is indistinguishable from the right one by reading it.
9
+
10
+ That is why "be careful about scope" is not a remedy. **Every check below is performed or not performed**, never noticed:
11
+
12
+ 1. **State the population boundary** before enumerating — what is in, and what is adjacent and out.
13
+ 2. **Prove the enumeration complete** — paginate, and *assert* the end-of-pages signal rather than trusting it. A silent cap looks exactly like a population.
14
+ 3. **Validate the detector against known-true items.** If it cannot find the examples you already have, its total means nothing.
15
+ 4. **Treat search results as candidates, never members** — read each one directly and confirm the literal.
16
+ 5. **Control the instrument in both directions** — it must report a real absence, and must not invent one.
17
+ 6. **Report pattern-derived totals as floors**, and **state the unit** beside any count whose argument rests on how many *independent* observations you have.
18
+
19
+ No single check covers the others: validating against known items is blind to truncation, and reading every candidate can only ever see what enumeration handed it.
20
+
21
+ **And one that fires while reading rather than measuring:** a filter built from the failure shapes you already expect cannot show you a shape you did not. Read gate and tool output **unfiltered** on a refusal — the diagnosis is usually already in it.
22
+
23
+ Full method (six modes in two families, the membership test for adding a seventh, and which check is blind to what): [reference/measurement-integrity.md](measurement-integrity-reference.mdc).
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.34.20",
3
+ "version": "4.35.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"