@codyswann/lisa 4.4.15 → 4.4.16

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 (99) hide show
  1. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  2. package/dist/core/upstream-evidence-manifest.js +12 -8
  3. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  4. package/package.json +2 -1
  5. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  6. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  7. package/plugins/lisa/.codex-plugin/skills/lisa-improve-test-coverage/SKILL.md +4 -1
  8. package/plugins/lisa/.codex-plugin/skills/lisa-improve-tests/SKILL.md +4 -1
  9. package/plugins/lisa/.codex-plugin/skills/lisa-nightly-lower-code-complexity/SKILL.md +10 -2
  10. package/plugins/lisa/rules/eager/falsifiable-checks.md +16 -1
  11. package/plugins/lisa/rules/reference/falsifiable-checks.md +74 -1
  12. package/plugins/lisa/skills/lisa-improve-test-coverage/SKILL.md +4 -1
  13. package/plugins/lisa/skills/lisa-improve-tests/SKILL.md +4 -1
  14. package/plugins/lisa/skills/lisa-nightly-lower-code-complexity/SKILL.md +10 -2
  15. package/plugins/lisa-agy/plugin.json +1 -1
  16. package/plugins/lisa-agy/skills/lisa-improve-test-coverage/SKILL.md +4 -1
  17. package/plugins/lisa-agy/skills/lisa-improve-tests/SKILL.md +4 -1
  18. package/plugins/lisa-agy/skills/lisa-nightly-lower-code-complexity/SKILL.md +10 -2
  19. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  21. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  22. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-copilot/rules/eager/falsifiable-checks.md +16 -1
  26. package/plugins/lisa-copilot/rules/reference/falsifiable-checks.md +74 -1
  27. package/plugins/lisa-copilot/skills/lisa-improve-test-coverage/SKILL.md +4 -1
  28. package/plugins/lisa-copilot/skills/lisa-improve-tests/SKILL.md +4 -1
  29. package/plugins/lisa-copilot/skills/lisa-nightly-lower-code-complexity/SKILL.md +10 -2
  30. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-cursor/rules/falsifiable-checks-reference.mdc +74 -1
  32. package/plugins/lisa-cursor/rules/falsifiable-checks.mdc +16 -1
  33. package/plugins/lisa-cursor/skills/lisa-improve-test-coverage/SKILL.md +4 -1
  34. package/plugins/lisa-cursor/skills/lisa-improve-tests/SKILL.md +4 -1
  35. package/plugins/lisa-cursor/skills/lisa-nightly-lower-code-complexity/SKILL.md +10 -2
  36. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  38. package/plugins/lisa-expo-agy/plugin.json +1 -1
  39. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  43. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  48. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  49. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  53. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  54. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  59. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  63. package/plugins/lisa-rails/.codex-plugin/skills/improve-code-complexity/SKILL.md +4 -1
  64. package/plugins/lisa-rails/.codex-plugin/skills/improve-test-coverage/SKILL.md +4 -1
  65. package/plugins/lisa-rails/.codex-plugin/skills/ops-verify-telemetry/SKILL.md +30 -1
  66. package/plugins/lisa-rails/skills/improve-code-complexity/SKILL.md +4 -1
  67. package/plugins/lisa-rails/skills/improve-test-coverage/SKILL.md +4 -1
  68. package/plugins/lisa-rails/skills/ops-verify-telemetry/SKILL.md +30 -1
  69. package/plugins/lisa-rails-agy/plugin.json +1 -1
  70. package/plugins/lisa-rails-agy/skills/improve-code-complexity/SKILL.md +4 -1
  71. package/plugins/lisa-rails-agy/skills/improve-test-coverage/SKILL.md +4 -1
  72. package/plugins/lisa-rails-agy/skills/ops-verify-telemetry/SKILL.md +30 -1
  73. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-rails-copilot/skills/improve-code-complexity/SKILL.md +4 -1
  75. package/plugins/lisa-rails-copilot/skills/improve-test-coverage/SKILL.md +4 -1
  76. package/plugins/lisa-rails-copilot/skills/ops-verify-telemetry/SKILL.md +30 -1
  77. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-rails-cursor/skills/improve-code-complexity/SKILL.md +4 -1
  79. package/plugins/lisa-rails-cursor/skills/improve-test-coverage/SKILL.md +4 -1
  80. package/plugins/lisa-rails-cursor/skills/ops-verify-telemetry/SKILL.md +30 -1
  81. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  83. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  84. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  88. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  89. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  91. package/plugins/src/base/rules/eager/falsifiable-checks.md +16 -1
  92. package/plugins/src/base/rules/reference/falsifiable-checks.md +74 -1
  93. package/plugins/src/base/skills/lisa-improve-test-coverage/SKILL.md +4 -1
  94. package/plugins/src/base/skills/lisa-improve-tests/SKILL.md +4 -1
  95. package/plugins/src/base/skills/lisa-nightly-lower-code-complexity/SKILL.md +10 -2
  96. package/plugins/src/rails/skills/improve-code-complexity/SKILL.md +4 -1
  97. package/plugins/src/rails/skills/improve-test-coverage/SKILL.md +4 -1
  98. package/plugins/src/rails/skills/ops-verify-telemetry/SKILL.md +30 -1
  99. package/scripts/check-pipeline-status-reads.mjs +684 -0
@@ -15,7 +15,7 @@ It is the more dangerous failure of the two, because the first leaves you uncert
15
15
 
16
16
  The empirical origin: a single defect-sweep run produced four false-passing checks and zero false fixes. Every fix was correct; every one of the four *instruments* was broken. Reviewers then found three real defects the checks had cleared. The error concentrated entirely in verification, which is why the countermeasure belongs at the check level rather than the code level.
17
17
 
18
- ## The four failure modes, in detail
18
+ ## The five failure modes, in detail
19
19
 
20
20
  ### 1. Self-matching guard
21
21
 
@@ -56,6 +56,79 @@ Observed: an uncalled-method detector returned 0 hits across 4,656 files, run on
56
56
 
57
57
  Countermeasure: every detector reporting a zero must first be shown to find known instances on a ref where they exist (`origin/<base>`, the pre-fix commit, or a synthetic fixture).
58
58
 
59
+ ### 5. Pager-shadowed status
60
+
61
+ The check is correct, it ran, and it failed. The *reading* of it is what reported green.
62
+
63
+ A shell pipeline's exit status is the status of its **last** stage. So this reports on `tail`, not on the gate:
64
+
65
+ ```text
66
+ $ node scripts/plugin-parity-drift.mjs 2>&1 | tail -4; echo "exit=$?"
67
+ | ... | safety-net@cc-marketplace | 1.0.6 | 2.0.4 | stale |
68
+ 1 of 5 synced skills drifted
69
+ exit=0 <-- tail's exit code
70
+
71
+ $ node scripts/plugin-parity-drift.mjs > drift.log 2>&1; echo "REAL exit code: $?"
72
+ REAL exit code: 1
73
+ ```
74
+
75
+ This one is worth stating separately from the other four because of *where* it occurs. The other four are defects in a check. This is a defect in **how a correct check gets read**, and the reading happens at exactly the moment someone is deciding whether they are blocked. It is also self-similar in an unhelpful way: it defeats verification while looking like verification.
76
+
77
+ The pipe is there to keep the output short — which means the failure line is the line most likely to fall outside the window. In the transcript above the tool happened to print its verdict inside `tail -4`, so a careful reader could catch it. That is luck about one chatty tool. A quiet gate that fails with no output produces `exit=0` and an empty, plausible-looking tail, and gets recorded as green.
78
+
79
+ #### The one spelling to use
80
+
81
+ Capture the status first; shorten afterwards.
82
+
83
+ ```sh
84
+ status=0
85
+ gate >gate.log 2>&1 || status=$?
86
+ tail -n 20 gate.log
87
+ [ "$status" -eq 0 ] || echo "FAILED ($status) — full output in gate.log"
88
+ ```
89
+
90
+ The status capture must be the very next thing after the command: `$?` holds only the most recent status, and even `echo` overwrites it.
91
+
92
+ ##### `|| status=$?`, never `; status=$?`
93
+
94
+ The `;` spelling is the same class of defect one level down — a status read that cannot report a failure — reached by a different route. Under `set -e`, errexit terminates the shell on the failing command, so the assignment never runs and neither does the branch that would print `FAILED`. The bug is invisible while the gate passes and appears only in the case the code exists to handle. Measured:
95
+
96
+ ```text
97
+ sh -e -c 'status=0; false >/dev/null 2>&1; status=$?; echo "reported=$status"' -> prints nothing, exit 1
98
+ sh -e -c 'status=0; false >/dev/null 2>&1 || status=$?; echo "reported=$status"' -> prints reported=1, exit 0
99
+ ```
100
+
101
+ The left-hand side of `||` is exempt from errexit, which is why the `||` form behaves identically with and without `set -e`. `status=0` is required for the same reason the branch is: on success nothing assigns it, and an unset `status` under `set -u` aborts at the comparison.
102
+
103
+ ##### Return the status when the block's own status is read
104
+
105
+ Inside a hook, a CI step, or anything invoked with `&&`, add `exit "$status"` (`return "$status"` in a function). Without it the last command executed is the `echo`, so the block exits 0 immediately after printing `FAILED` — pager-shadowing displaced by one level, with the trailing `echo` in the role of `tail`.
106
+
107
+ #### Why this one and not the other two
108
+
109
+ | Spelling | Works in | Does not work in | Keeps full output |
110
+ |---|---|---|---|
111
+ | redirect, then read `$?` | every POSIX shell, `sh` included | — | yes |
112
+ | `set -o pipefail` | bash, zsh, ksh, POSIX Issue 8 shells | **not POSIX `sh`** — dash has no such option, and GitHub Actions' default `run:` shell is `bash -e {0}`, with `-e` but no pipefail | no |
113
+ | `${PIPESTATUS[0]}` | bash | zsh spells it `$pipestatus[1]` and indexes from 1; absent from `sh`. Overwritten by the next command, so it must be read immediately | no |
114
+
115
+ The redirect form wins on both columns that matter here. It is the only one that survives `sh` — this repository runs hooks under `sh` — and it is the only one that still has the untruncated output at the moment the status tells you to go look at it. `pipefail` is the right thing to add to a *script* you control; it is not a substitute for this when you are reading a gate at a prompt.
116
+
117
+ #### It is not hypothetical, and not only about `tail`
118
+
119
+ `security-floors.yml` ran `node scripts/check-security-floors.mjs --strict | tee -a "$GITHUB_STEP_SUMMARY"` with no `pipefail`. Every failure `--strict` exists to raise — a dependency floor below a live advisory, a rate-limited inconclusive run, an unresolved `$name` — was discarded, and the job reported green. Measured:
120
+
121
+ ```text
122
+ bash -e -c 'node -e "process.exit(1)" | tee /dev/null' -> 0
123
+ bash -e -o pipefail -c 'node -e "process.exit(1)" | tee /dev/null' -> 1
124
+ ```
125
+
126
+ `tee` is the same mechanism as `head`/`tail`: a last stage that reports on its own writing rather than on the command upstream.
127
+
128
+ #### The executable half
129
+
130
+ `scripts/check-pipeline-status-reads.mjs` sweeps shipped shell scripts and workflow `run:` blocks for the pattern. It reports **how many pipelines it inspected** and exits 2 — not 0 — when that count is zero, because an empty inspection and a clean tree otherwise print the same tick.
131
+
59
132
  ## What a negative result is scoped to
60
133
 
61
134
  A clean result is a statement about what the check can perceive, not about the code. State the boundary:
@@ -11,7 +11,7 @@ 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
- ## The four ways a check silently measures nothing
14
+ ## The five ways a check silently measures nothing
15
15
 
16
16
  Each has been observed in real runs; each reported success while asserting nothing:
17
17
 
@@ -19,9 +19,24 @@ Each has been observed in real runs; each reported success while asserting nothi
19
19
  2. **Fixture-validated assertion** — the assertion reads the test's own fixture rather than the artifact under test. Common when the production path consumes a *raw* input the fixture supplies directly.
20
20
  3. **Stale-artifact pass** — the revert-to-verify step silently failed (generator errored, cache served old output, build skipped), so the check re-read unchanged input and "passed".
21
21
  4. **Wrong-baseline sweep** — the detector ran against already-fixed state, so its zero is uninformative. Validate detectors against a ref where the defect still exists.
22
+ 5. **Pager-shadowed status** — the check ran and FAILED, but its result was read through a pipe. A shell pipeline's exit status is its LAST stage's, so `gate | tail -4; echo $?` reports `tail`'s success. Shortening output is exactly what one does while checking whether a gate passed, and the failure line is exactly what the shortening drops.
22
23
 
23
24
  ## Mandatory
24
25
 
26
+ - **Read the command's own status, never a pipeline's.** When the answer decides whether you are blocked, redirect first and read the status before shortening anything — one spelling, portable to every shell including POSIX `sh`:
27
+
28
+ ```sh
29
+ status=0
30
+ gate >gate.log 2>&1 || status=$? # `||`, not `;` — see below
31
+ tail -n 20 gate.log # shorten for reading, not for deciding
32
+ [ "$status" -eq 0 ] || echo "FAILED ($status) — full output in gate.log"
33
+ ```
34
+
35
+ `|| status=$?` rather than `; status=$?`, because the `;` form is itself a silent-measurement bug under `set -e`: errexit kills the shell on the failing command and the assignment never runs, so the branch that reports the failure is unreachable in exactly the case it exists for. The left side of `||` is exempt from errexit, which is why this spelling survives both `set -e` and its absence — and why `status` is initialized, since on success nothing assigns it.
36
+
37
+ When the block's **own** status is what gets read — a hook, a CI step, a script someone runs with `&&` — end it with `exit "$status"` (or `return "$status"` in a function). Without that, the last command is the `echo` and the block exits 0 after printing `FAILED`: the same pager-shadowing one line further out.
38
+
39
+ This form is preferred over the alternatives because it is the only one that both survives `sh` and keeps the full output, which is what you need the moment the status says the gate failed. `set -o pipefail` is correct where it exists but is **not POSIX `sh`** — dash has no such option, and this repository runs hooks under `sh`. `${PIPESTATUS[0]}` is **bash-only** (zsh spells it `$pipestatus[1]`, 1-indexed) and is overwritten by the very next command, so it must be read immediately. Never report a gate as green from a status read through `| tail`, `| head`, or `| tee`.
25
40
  - **Falsify before reporting.** No clean result is reportable until the check has been shown to fail on a deliberate break. **"Mentally reverting" does not count** — reasoning that the assertion *would* fail is precisely the step that lets a non-functional guard ship, because the author already believes it is load-bearing. Run the break.
26
41
  - **Mutation-prove the guard, then read the cardinality.** Neuter the protection **in production code**, run the **whole suite**, count the failures and **read their names**, then revert. **Zero** ⇒ the guard is inert (one pinning a single field shipped with all 50 tests green while the regression walked through). **Many, unrelated** ⇒ over-broad; its next failure will not name the cause. **Exactly one, or several all named for the same regression** ⇒ load-bearing and correctly scoped. A **zero is robust to contention** — load only ever adds failures — so only the non-zero counts need a quiet machine.
27
42
  - **Never scope the probe by filename, and never infer absent coverage from a grep.** Suites split at the line cap, so a protection's tests often sit in a sibling file: a filename-scoped probe reported cardinality 0 on a live security fix that a whole-suite run showed failing 4 tests, every one named for it.
@@ -16,8 +16,11 @@ If no argument provided, prompt the user for a target.
16
16
  1. **Find coverage config** (jest.config.js, vitest.config.ts, .nycrc, etc.)
17
17
  2. **Run coverage report** to get current state:
18
18
  ```bash
19
- bun run test:cov 2>&1 | head -100
19
+ status=0
20
+ bun run test:cov >coverage.log 2>&1 || status=$?
21
+ head -n 100 coverage.log; echo "exit=$status"
20
22
  ```
23
+ The status is captured before the pipe, because a pipeline reports its LAST stage's exit code — `head` always succeeds, so a failing run reads as `exit=0` (`falsifiable-checks`, pager-shadowed status). `|| status=$?` rather than `; status=$?`: under `set -e` the `;` form exits before the assignment, so the failure is never reported at all.
21
24
  3. **Identify the 20 files with lowest coverage**, noting:
22
25
  - File path
23
26
  - Current coverage % (lines, branches, functions)
@@ -14,8 +14,11 @@ If no argument provided, scan the full test suite.
14
14
 
15
15
  1. **Run test suite** to establish baseline:
16
16
  ```bash
17
- bun run test 2>&1 | tail -20
17
+ status=0
18
+ bun run test >test.log 2>&1 || status=$?
19
+ tail -n 20 test.log; echo "exit=$status"
18
20
  ```
21
+ The status is captured before the pipe, because a pipeline reports its LAST stage's exit code — `tail` always succeeds, so a failing run reads as `exit=0` (`falsifiable-checks`, pager-shadowed status). `|| status=$?` rather than `; status=$?`: under `set -e` the `;` form exits before the assignment, so the failure is never reported at all.
19
22
  2. **Scan test files** for quality issues:
20
23
  - Weak assertions (`toBeTruthy`, `toBeDefined` instead of specific values)
21
24
  - Missing edge cases (no boundary values, no error paths)
@@ -20,9 +20,17 @@ The caller provides pre-computed context:
20
20
  4. Fix violations one file at a time. Read only the specific function that violates — do not pre-read all files upfront. Fix it, then move to the next.
21
21
  5. For cognitive complexity violations: use early returns, extract helper functions, replace conditionals with lookup tables
22
22
  6. For max-lines-per-function violations: split large functions, extract helper functions, separate concerns
23
- 7. After each file edit, run the project's formatter (e.g., `bun run format` or `npx prettier --write <file>`) to ensure line counts reflect the final formatted state before moving on
23
+ 7. After each file edit, run the project's formatter **with the provided package manager** (e.g., `npm run format`, `yarn format`, or `bun run format`) to ensure line counts reflect the final formatted state before moving on. Do not reach for a bare `npx prettier`: when the binary is absent `npx` silently installs and executes whatever the registry currently publishes under that name, which is an unpinned dependency introduced by a formatting step. If the project has no format script, run the lockfile-pinned binary directly (`./node_modules/.bin/prettier --write <file>`)
24
24
  8. Re-run the lint script with the provided package manager to verify all violations are resolved (both the target metric AND max-lines)
25
- 9. Run the TypeScript compiler to catch type errors early: `npx tsc --noEmit 2>&1 | head -30`. If there are type errors, fix them now do NOT wait until the commit step. Pre-commit hooks run type checking, and discovering errors at commit time wastes turns.
25
+ 9. Run the project's typecheck script with the provided package manager to catch type errors early same reasoning as step 7, so `npm run typecheck` / `yarn typecheck` / `bun run typecheck`, falling back to `./node_modules/.bin/tsc --noEmit` rather than `npx tsc`:
26
+
27
+ ```sh
28
+ status=0
29
+ bun run typecheck >tsc.log 2>&1 || status=$?
30
+ head -n 30 tsc.log; echo "exit=$status"
31
+ ```
32
+
33
+ Capture the status before the pipe — a pipeline reports its LAST stage's exit code, and `head` always succeeds, so `tsc --noEmit | head -30` reads as clean however many errors it printed (`falsifiable-checks`, pager-shadowed status). `|| status=$?` rather than `; status=$?`: under `set -e` the `;` form exits before the assignment, so the failure is never reported at all. If there are type errors, fix them now — do NOT wait until the commit step. Pre-commit hooks run type checking, and discovering errors at commit time wastes turns.
26
34
  10. Run the project's test script with the provided package manager (e.g., `npm run test`, `yarn test`, or `bun run test`) to verify no tests are broken by the refactoring
27
35
  11. Commit all changes (refactored code + updated eslint.thresholds.json) with conventional commit messages
28
36
  12. Create a PR with `gh pr create` with a title like "refactor: reduce code complexity: [metrics being reduced]" summarizing the changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
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.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -17,8 +17,11 @@ Reduces the CyclomaticComplexity threshold by 2 and fixes all violations.
17
17
  bundle exec rubocop --only Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity --format json 2>&1
18
18
  ```
19
19
  ```bash
20
- bundle exec flog --all --group app/ 2>&1 | head -50
20
+ status=0
21
+ bundle exec flog --all --group app/ >flog.log 2>&1 || status=$?
22
+ head -n 50 flog.log; echo "exit=$status"
21
23
  ```
24
+ The status is captured before the pipe, because a pipeline reports its LAST stage's exit code — `head` always succeeds, so a failing run reads as `exit=0` (`falsifiable-checks`, pager-shadowed status). `|| status=$?` rather than `; status=$?`: under `set -e` the `;` form exits before the assignment, so the failure is never reported at all.
22
25
  4. **Note for each violation**:
23
26
  - File path and line number
24
27
  - Method name
@@ -16,8 +16,11 @@ If no argument provided, prompt the user for a target.
16
16
  1. **Find coverage config** (`.simplecov` or `spec/spec_helper.rb`)
17
17
  2. **Run test suite with coverage** to get current state:
18
18
  ```bash
19
- bundle exec rspec 2>&1 | tail -50
19
+ status=0
20
+ bundle exec rspec >rspec.log 2>&1 || status=$?
21
+ tail -n 50 rspec.log; echo "exit=$status"
20
22
  ```
23
+ The status is captured before the pipe, because a pipeline reports its LAST stage's exit code — `tail` always succeeds, so a failing run reads as `exit=0` (`falsifiable-checks`, pager-shadowed status). `|| status=$?` rather than `; status=$?`: under `set -e` the `;` form exits before the assignment, so the failure is never reported at all.
21
24
  3. **Check SimpleCov output** in `coverage/index.html` or console output
22
25
  4. **Identify the 20 files with lowest coverage**, noting:
23
26
  - File path
@@ -58,7 +58,36 @@ grep -i "otel\|opentelemetry\|trace_id" log/development.log | tail -10
58
58
  ### Check Collector Sidecar (if using Docker Compose)
59
59
 
60
60
  ```bash
61
- docker compose logs otel-collector 2>/dev/null | tail -20 || echo "No otel-collector service in Docker Compose"
61
+ # Capture the status BEFORE the pipe. A pipeline reports its LAST stage's exit
62
+ # code, so `docker compose logs ... | tail -20 || echo "No otel-collector"`
63
+ # never prints the fallback: `tail` succeeds even when there is no such
64
+ # service, and the absent sidecar reads as a silent pass.
65
+ #
66
+ # `|| status=$?`, not `; status=$?`: under `set -e` the `;` form exits before
67
+ # the assignment, so the branch that reports the failure never runs.
68
+ #
69
+ # Ask whether the service EXISTS separately from whether its logs could be
70
+ # read. `docker compose logs` also exits non-zero for a stopped daemon, a
71
+ # permission failure, and an unparseable compose file; answering all of those
72
+ # with "No otel-collector service" turns a broken telemetry stack into a
73
+ # reassuring sentence — the same silent-measurement failure one level up.
74
+ services_status=0
75
+ services=$(docker compose config --services 2>&1) || services_status=$?
76
+ if [ "$services_status" -ne 0 ]; then
77
+ echo "FAILED ($services_status) — docker compose config could not be read:"
78
+ printf '%s\n' "$services"
79
+ elif ! printf '%s\n' "$services" | grep -qx otel-collector; then
80
+ echo "No otel-collector service in Docker Compose"
81
+ else
82
+ logs_status=0
83
+ docker compose logs otel-collector >otel.log 2>&1 || logs_status=$?
84
+ if [ "$logs_status" -eq 0 ]; then
85
+ tail -n 20 otel.log
86
+ else
87
+ echo "FAILED ($logs_status) — otel-collector is configured but its logs could not be read:"
88
+ cat otel.log
89
+ fi
90
+ fi
62
91
  ```
63
92
 
64
93
  ## Remote Verification (AWS X-Ray)
@@ -17,8 +17,11 @@ Reduces the CyclomaticComplexity threshold by 2 and fixes all violations.
17
17
  bundle exec rubocop --only Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity --format json 2>&1
18
18
  ```
19
19
  ```bash
20
- bundle exec flog --all --group app/ 2>&1 | head -50
20
+ status=0
21
+ bundle exec flog --all --group app/ >flog.log 2>&1 || status=$?
22
+ head -n 50 flog.log; echo "exit=$status"
21
23
  ```
24
+ The status is captured before the pipe, because a pipeline reports its LAST stage's exit code — `head` always succeeds, so a failing run reads as `exit=0` (`falsifiable-checks`, pager-shadowed status). `|| status=$?` rather than `; status=$?`: under `set -e` the `;` form exits before the assignment, so the failure is never reported at all.
22
25
  4. **Note for each violation**:
23
26
  - File path and line number
24
27
  - Method name
@@ -16,8 +16,11 @@ If no argument provided, prompt the user for a target.
16
16
  1. **Find coverage config** (`.simplecov` or `spec/spec_helper.rb`)
17
17
  2. **Run test suite with coverage** to get current state:
18
18
  ```bash
19
- bundle exec rspec 2>&1 | tail -50
19
+ status=0
20
+ bundle exec rspec >rspec.log 2>&1 || status=$?
21
+ tail -n 50 rspec.log; echo "exit=$status"
20
22
  ```
23
+ The status is captured before the pipe, because a pipeline reports its LAST stage's exit code — `tail` always succeeds, so a failing run reads as `exit=0` (`falsifiable-checks`, pager-shadowed status). `|| status=$?` rather than `; status=$?`: under `set -e` the `;` form exits before the assignment, so the failure is never reported at all.
21
24
  3. **Check SimpleCov output** in `coverage/index.html` or console output
22
25
  4. **Identify the 20 files with lowest coverage**, noting:
23
26
  - File path
@@ -58,7 +58,36 @@ grep -i "otel\|opentelemetry\|trace_id" log/development.log | tail -10
58
58
  ### Check Collector Sidecar (if using Docker Compose)
59
59
 
60
60
  ```bash
61
- docker compose logs otel-collector 2>/dev/null | tail -20 || echo "No otel-collector service in Docker Compose"
61
+ # Capture the status BEFORE the pipe. A pipeline reports its LAST stage's exit
62
+ # code, so `docker compose logs ... | tail -20 || echo "No otel-collector"`
63
+ # never prints the fallback: `tail` succeeds even when there is no such
64
+ # service, and the absent sidecar reads as a silent pass.
65
+ #
66
+ # `|| status=$?`, not `; status=$?`: under `set -e` the `;` form exits before
67
+ # the assignment, so the branch that reports the failure never runs.
68
+ #
69
+ # Ask whether the service EXISTS separately from whether its logs could be
70
+ # read. `docker compose logs` also exits non-zero for a stopped daemon, a
71
+ # permission failure, and an unparseable compose file; answering all of those
72
+ # with "No otel-collector service" turns a broken telemetry stack into a
73
+ # reassuring sentence — the same silent-measurement failure one level up.
74
+ services_status=0
75
+ services=$(docker compose config --services 2>&1) || services_status=$?
76
+ if [ "$services_status" -ne 0 ]; then
77
+ echo "FAILED ($services_status) — docker compose config could not be read:"
78
+ printf '%s\n' "$services"
79
+ elif ! printf '%s\n' "$services" | grep -qx otel-collector; then
80
+ echo "No otel-collector service in Docker Compose"
81
+ else
82
+ logs_status=0
83
+ docker compose logs otel-collector >otel.log 2>&1 || logs_status=$?
84
+ if [ "$logs_status" -eq 0 ]; then
85
+ tail -n 20 otel.log
86
+ else
87
+ echo "FAILED ($logs_status) — otel-collector is configured but its logs could not be read:"
88
+ cat otel.log
89
+ fi
90
+ fi
62
91
  ```
63
92
 
64
93
  ## Remote Verification (AWS X-Ray)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.4.15",
3
+ "version": "4.4.16",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -17,8 +17,11 @@ Reduces the CyclomaticComplexity threshold by 2 and fixes all violations.
17
17
  bundle exec rubocop --only Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity --format json 2>&1
18
18
  ```
19
19
  ```bash
20
- bundle exec flog --all --group app/ 2>&1 | head -50
20
+ status=0
21
+ bundle exec flog --all --group app/ >flog.log 2>&1 || status=$?
22
+ head -n 50 flog.log; echo "exit=$status"
21
23
  ```
24
+ The status is captured before the pipe, because a pipeline reports its LAST stage's exit code — `head` always succeeds, so a failing run reads as `exit=0` (`falsifiable-checks`, pager-shadowed status). `|| status=$?` rather than `; status=$?`: under `set -e` the `;` form exits before the assignment, so the failure is never reported at all.
22
25
  4. **Note for each violation**:
23
26
  - File path and line number
24
27
  - Method name
@@ -16,8 +16,11 @@ If no argument provided, prompt the user for a target.
16
16
  1. **Find coverage config** (`.simplecov` or `spec/spec_helper.rb`)
17
17
  2. **Run test suite with coverage** to get current state:
18
18
  ```bash
19
- bundle exec rspec 2>&1 | tail -50
19
+ status=0
20
+ bundle exec rspec >rspec.log 2>&1 || status=$?
21
+ tail -n 50 rspec.log; echo "exit=$status"
20
22
  ```
23
+ The status is captured before the pipe, because a pipeline reports its LAST stage's exit code — `tail` always succeeds, so a failing run reads as `exit=0` (`falsifiable-checks`, pager-shadowed status). `|| status=$?` rather than `; status=$?`: under `set -e` the `;` form exits before the assignment, so the failure is never reported at all.
21
24
  3. **Check SimpleCov output** in `coverage/index.html` or console output
22
25
  4. **Identify the 20 files with lowest coverage**, noting:
23
26
  - File path