@codyswann/lisa 4.84.10 → 4.85.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 (92) hide show
  1. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  2. package/dist/core/lisa-owned-hash-ledger.js +34 -0
  3. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  4. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  5. package/dist/core/nightly-e2e-guard-behavior-certificate.js.map +1 -1
  6. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  7. package/dist/core/upstream-evidence-manifest.js +20 -4
  8. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  9. package/package.json +4 -4
  10. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  12. package/plugins/lisa/.codex-plugin/skills/lisa-parity-code-review/SKILL.md +28 -3
  13. package/plugins/lisa/skills/lisa-parity-code-review/SKILL.md +28 -3
  14. package/plugins/lisa-agy/plugin.json +1 -1
  15. package/plugins/lisa-agy/skills/lisa-parity-code-review/SKILL.md +28 -3
  16. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  19. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  23. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-copilot/skills/lisa-parity-code-review/SKILL.md +28 -3
  25. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-cursor/skills/lisa-parity-code-review/SKILL.md +28 -3
  27. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  29. package/plugins/lisa-devin/skills/lisa-parity-code-review/SKILL.md +28 -3
  30. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa-expo-agy/plugin.json +1 -1
  33. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  37. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  39. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  40. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  44. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  47. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs-devin/.devin-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-openclaw-devin/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  58. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  61. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  65. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-rails-agy/plugin.json +1 -1
  68. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  72. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  74. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  75. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  79. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  82. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  86. package/plugins/src/base/skills/lisa-parity-code-review/SKILL.md +28 -3
  87. package/rails/copy-overwrite/scripts/lisa-rspec-shards.mjs +747 -0
  88. package/rails/copy-overwrite/scripts/lisa_rspec_shard_recorder.rb +143 -0
  89. package/rails/copy-overwrite/scripts/lisa_simplecov_collate.rb +51 -0
  90. package/rails/create-only/.github/workflows/ci.yml +4 -0
  91. package/scripts/two-channel-couplings.json +89 -7
  92. package/typescript/copy-overwrite/.prettierignore +1 -0
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
4
+ # Do not edit directly — durable changes belong upstream in Lisa.
5
+ # -----------------------------------------------------------------------------
6
+ # RSpec shard recorder for `quality-rails.yml` `test_shards`.
7
+ #
8
+ # Loaded into one shard leg with `rspec --require`. It does two things, both at
9
+ # `before(:suite)`, when the project's own spec_helper / rails_helper has
10
+ # already required SimpleCov and loaded `.simplecov`:
11
+ #
12
+ # 1. Hands coverage off to the aggregate. `SimpleCov.finalize_merge false`
13
+ # makes this process store its `.resultset.json` and stop there: no
14
+ # report, and no minimum-coverage check against a slice of the suite,
15
+ # which would fail any project whose floor is above what one shard
16
+ # reaches. The aggregate job collates every shard and enforces the
17
+ # project's thresholds once, on the merged result.
18
+ # 2. Writes a run record at the reporter's `close`: the files RSpec itself
19
+ # loaded (`files_to_run`), each example's run time, and where SimpleCov
20
+ # writes. The aggregate proves from these records that every spec file
21
+ # ran exactly once, and folds the timings into the next run's plan.
22
+ #
23
+ # ENVIRONMENT
24
+ # LISA_RSPEC_SHARD "K/N" — this leg's shard. Required.
25
+ # LISA_RSPEC_SHARD_RECORD Path of the run record to write. Required.
26
+ # -----------------------------------------------------------------------------
27
+
28
+ require 'fileutils'
29
+ require 'json'
30
+ require 'pathname'
31
+
32
+ # Hands a shard's coverage to the aggregate and records what RSpec ran.
33
+ module LisaRspecShardRecorder
34
+ SCHEMA_VERSION = 1
35
+
36
+ # Collects example timings and writes the record when the reporter closes.
37
+ class Listener
38
+ def initialize(path:, shard:, configuration:)
39
+ @path = path
40
+ @shard = shard
41
+ @configuration = configuration
42
+ @examples = []
43
+ end
44
+
45
+ def example_finished(notification)
46
+ example = notification.example
47
+ @examples << {
48
+ 'file' => LisaRspecShardRecorder.relative(example.metadata[:rerun_file_path] || example.file_path),
49
+ 'run_time' => example.execution_result.run_time.to_f,
50
+ 'status' => example.execution_result.status.to_s
51
+ }
52
+ end
53
+
54
+ def close(_notification)
55
+ FileUtils.mkdir_p(File.dirname(@path))
56
+ File.write(@path, "#{JSON.pretty_generate(record)}\n")
57
+ end
58
+
59
+ def record
60
+ {
61
+ 'version' => SCHEMA_VERSION,
62
+ 'shard' => @shard,
63
+ 'files_to_run' => @configuration.files_to_run.map { |file| LisaRspecShardRecorder.relative(file) }.sort,
64
+ 'examples' => @examples,
65
+ 'simplecov' => LisaRspecShardRecorder.simplecov_state
66
+ }
67
+ end
68
+ end
69
+
70
+ module_function
71
+
72
+ def fetch_env(name)
73
+ value = ENV.fetch(name, '')
74
+ raise ArgumentError, "lisa-rspec-shard-recorder: #{name} is not set" if value.empty?
75
+
76
+ value
77
+ end
78
+
79
+ # Repo-relative, forward-slashed, with no leading "./" and no ":line" suffix.
80
+ def relative(path)
81
+ text = path.to_s.sub(/:\d+(?::\d+)*\z/, '')
82
+ absolute = Pathname.new(File.expand_path(text))
83
+ absolute.relative_path_from(Pathname.new(Dir.pwd)).to_s
84
+ end
85
+
86
+ def measuring?
87
+ defined?(::SimpleCov) && defined?(::Coverage) && ::Coverage.running?
88
+ end
89
+
90
+ def simplecov_state
91
+ return { 'active' => false } unless measuring?
92
+
93
+ { 'active' => true, 'coverage_path' => ::SimpleCov.coverage_path }
94
+ end
95
+
96
+ # A shard that enforced the project's floor against its own slice would fail
97
+ # every project whose floor is above one shard's reach, and a shard that does
98
+ # not store its resultset leaves the aggregate nothing to merge. Both are
99
+ # refused loudly rather than worked around.
100
+ def hand_off_coverage(shard)
101
+ return unless measuring?
102
+
103
+ refuse_unsupported_simplecov
104
+ refuse_measured_recorder
105
+ ::SimpleCov.finalize_merge(false)
106
+ ::SimpleCov.command_name("RSpec shard #{shard}")
107
+ end
108
+
109
+ def refuse_unsupported_simplecov
110
+ unless ::SimpleCov.respond_to?(:finalize_merge)
111
+ raise 'lisa-rspec-shard-recorder: this SimpleCov has no `finalize_merge`, so a shard cannot store its ' \
112
+ 'coverage without also checking the minimum against its own slice. test_shards above 1 needs ' \
113
+ "simplecov >= 1.0 (Lisa's Gemfile.lisa pins it); upgrade it, or leave test_shards at 1."
114
+ end
115
+ return if ::SimpleCov.merging
116
+
117
+ raise 'lisa-rspec-shard-recorder: SimpleCov merging is off (`merging false`), so this shard would write no ' \
118
+ '.resultset.json for the aggregate to collate. Remove that setting, or leave test_shards at 1.'
119
+ end
120
+
121
+ # Loaded from inside the project, this file is itself measured and lands in
122
+ # the shard's resultset as application code the unsharded run never had,
123
+ # moving the merged percentage. The workflow copies it out first.
124
+ def refuse_measured_recorder
125
+ return unless File.expand_path(__FILE__).start_with?("#{File.expand_path(::SimpleCov.root)}/")
126
+
127
+ raise 'lisa-rspec-shard-recorder: loaded from inside the project root, so SimpleCov would measure it as ' \
128
+ 'application code. Copy it outside the project (the workflow uses $RUNNER_TEMP) and --require that copy.'
129
+ end
130
+
131
+ def arm!(configuration)
132
+ shard = fetch_env('LISA_RSPEC_SHARD')
133
+ path = File.expand_path(fetch_env('LISA_RSPEC_SHARD_RECORD'))
134
+ hand_off_coverage(shard)
135
+ listener = Listener.new(path: path, shard: shard, configuration: configuration)
136
+ configuration.reporter.register_listener(listener, :example_finished, :close)
137
+ listener
138
+ end
139
+ end
140
+
141
+ RSpec.configure do |config|
142
+ config.before(:suite) { LisaRspecShardRecorder.arm!(config) }
143
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
4
+ # Do not edit directly — durable changes belong upstream in Lisa.
5
+ # -----------------------------------------------------------------------------
6
+ # Merge the RSpec shards' SimpleCov resultsets and enforce the project's
7
+ # coverage thresholds once, on the whole suite.
8
+ #
9
+ # bundle exec ruby scripts/lisa_simplecov_collate.rb <resultset.json>...
10
+ #
11
+ # `require 'simplecov'` loads the project's own `.simplecov`, so the merged
12
+ # result is filtered, grouped and checked exactly as an unsharded run would
13
+ # be: the same `minimum_coverage` / `coverage ..., minimum:` and every other
14
+ # limit it sets. This file sets no threshold of its own. `SimpleCov.collate`
15
+ # exits non-zero when a threshold fails.
16
+ # -----------------------------------------------------------------------------
17
+
18
+ require 'simplecov'
19
+
20
+ # Collates shard resultsets and lets SimpleCov's threshold verdict decide.
21
+ module LisaSimplecovCollate
22
+ module_function
23
+
24
+ def resultsets(argv)
25
+ abort 'lisa-simplecov-collate: no resultsets given; nothing to collate.' if argv.empty?
26
+
27
+ missing = argv.reject { |path| File.file?(path) }
28
+ abort "lisa-simplecov-collate: resultset(s) not found: #{missing.join(', ')}" unless missing.empty?
29
+
30
+ argv.map { |path| File.expand_path(path) }
31
+ end
32
+
33
+ # A `.simplecov` that calls `SimpleCov.start` itself has also started
34
+ # measuring THIS process. Left running, SimpleCov's own at_exit would format
35
+ # and check a second time. Stopping it and dropping the result leaves
36
+ # `collate` the only verdict.
37
+ def stand_down_own_measurement
38
+ ::Coverage.result(stop: true, clear: true) if defined?(::Coverage) && ::Coverage.running?
39
+ ::SimpleCov.clear_result if ::SimpleCov.respond_to?(:clear_result)
40
+ end
41
+
42
+ def run(argv)
43
+ files = resultsets(argv)
44
+ puts "Collating #{files.length} SimpleCov resultset(s)."
45
+ ::SimpleCov.collate(files)
46
+ ensure
47
+ stand_down_own_measurement
48
+ end
49
+ end
50
+
51
+ LisaSimplecovCollate.run(ARGV) if $PROGRAM_NAME == __FILE__
@@ -36,6 +36,10 @@ jobs:
36
36
  # Rails projects default to PostgreSQL. For a MySQL app, uncomment:
37
37
  # with:
38
38
  # database_type: 'mysql'
39
+ # To split a slow RSpec suite across parallel runners, add
40
+ # `test_shards: 4` (1-20) to the `with:` block. The legs' SimpleCov
41
+ # coverage is merged and checked once, and the required `Test (...)`
42
+ # check keeps its name.
39
43
  secrets:
40
44
  GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
41
45
  FOSSA_API_KEY: ${{ secrets.FOSSA_API_KEY }}
@@ -63,6 +63,18 @@
63
63
  "decision": "not-needed",
64
64
  "reason": "A runner, not a judge. The gate's verdict is mutant's exit code, and what it runs is read from the project's own `mutation.gate.yml` and `.mutant.yml`; this script's own logic decides whether the gate is enabled and what ref to diff against, both of which come from configuration it does not own. A superseded copy runs the wrong subjects loudly rather than returning a wrong verdict quietly."
65
65
  },
66
+ "quality-rails.yml::scripts/lisa_rspec_shard_recorder.rb": {
67
+ "decision": "not-needed",
68
+ "reason": "A reporter, not a judge. It hands SimpleCov's merge finalization to the aggregate and writes a run record; the verdict on that record is `lisa-rspec-shards.mjs verify`, which refuses any record not at the schema version it speaks. A superseded recorder either leaves a record verify rejects or leaves the shard's own minimum check armed, and both are loud reds, never a believed green."
69
+ },
70
+ "quality-rails.yml::scripts/lisa-rspec-shards.mjs": {
71
+ "decision": "warranted",
72
+ "reason": "Verdict-bearing when test_shards is above 1: its `verify` subcommand is what turns a dropped or duplicated spec file into a red `Test (MySQL)` / `Test (PostgreSQL)`, and its `plan` decides which files each leg runs. Plan and record documents carry a schema version that `verify` refuses on mismatch, and an unknown subcommand or flag fails loudly, so a copy too old to speak this workflow's interface is red rather than green. What that does not cover is a copy at the same schema whose partition check is weaker than the current one; the script exports no contract version the workflow could compare, so the obligation is recorded rather than claimed closed."
73
+ },
74
+ "quality-rails.yml::scripts/lisa_simplecov_collate.rb": {
75
+ "decision": "not-needed",
76
+ "reason": "A runner, not a judge. It forwards the shards' resultsets to `SimpleCov.collate`, and the threshold verdict is SimpleCov's own, read from the project's `.simplecov` and `simplecov.thresholds.json`; the script sets no threshold. A superseded copy can fail to collate, which is red, but cannot change what the thresholds are."
77
+ },
66
78
  "quality-rails.yml::scripts/lisa-scratch-run.sh": {
67
79
  "decision": "not-needed",
68
80
  "reason": "A supervisor that hosts a verdict rather than computing one: it owns run-scoped scratch and cleanup around whatever it wraps. Nothing it does can turn a passing suite red or a failing one green — a superseded copy misplaces or fails to reap a scratch directory, which surfaces as its own visible failure rather than as a believed verdict."
@@ -108,13 +120,13 @@
108
120
  },
109
121
  "inspected": {
110
122
  "workflows": 26,
111
- "steps": 788,
112
- "couplings": 37,
113
- "inventory": 204
123
+ "steps": 823,
124
+ "couplings": 40,
125
+ "inventory": 207
114
126
  },
115
127
  "counts": {
116
128
  "package-backed": 11,
117
- "apply-lagged": 23,
129
+ "apply-lagged": 26,
118
130
  "never-delivered": 0,
119
131
  "undelivered": 3
120
132
  },
@@ -122,13 +134,13 @@
122
134
  "version-handshake": 4,
123
135
  "capability-probe": 0,
124
136
  "existence-only": 5,
125
- "no-probe": 14,
137
+ "no-probe": 17,
126
138
  "unchecked": 3
127
139
  },
128
140
  "decisionCounts": {
129
141
  "handshake": 4,
130
- "not-needed": 12,
131
- "warranted": 10
142
+ "not-needed": 14,
143
+ "warranted": 11
132
144
  },
133
145
  "couplings": [
134
146
  {
@@ -411,6 +423,53 @@
411
423
  ],
412
424
  "detail": "`quality-rails.yml` is delivered at `@main`, so this step is live in every consumer on their next run. It reads `scripts/check-threshold-ratchet.mjs` from the CALLER's tree with no package-relative candidate, and Lisa delivers that path on the `lisa apply` channel (all/copy-overwrite, rails/copy-overwrite, typescript/copy-overwrite). The two halves land in the wrong order: this step is live everywhere immediately, the file it needs is live only where somebody applied. No existence test guards the read, which is ALL this scan examined of the step's control flow: it did not read anything that could absorb the failure. An absent path most likely fails the job loudly — the better of the two failures, because it is visible — but that is inferred from the missing guard, NOT MEASURED. The step's text does carry `else-branch`, `exit-zero`, `exit-nonzero`, `error-annotation` — located in the step, deliberately NOT related to this read, and enough to make \"it skips silently\" a claim to check rather than assume. STALENESS (a separate dimension from the delivery verdict above): `no-probe`. Neither an existence test nor a version probe bears on this read, so nothing here distinguishes a current artifact from an absent or a superseded one. Tokens found: `workflow-requests-version`. `workflow-requests-version` is workflow-scoped — it does not say THIS step asks."
413
425
  },
426
+ {
427
+ "key": "quality-rails.yml::scripts/lisa_rspec_shard_recorder.rb",
428
+ "workflow": "quality-rails.yml",
429
+ "path": "scripts/lisa_rspec_shard_recorder.rb",
430
+ "channel": "apply",
431
+ "verdict": "apply-lagged",
432
+ "remedy": "run-lisa-apply",
433
+ "guarded": false,
434
+ "handling": [
435
+ "exit-nonzero",
436
+ "error-annotation"
437
+ ],
438
+ "detection": "no-probe",
439
+ "stalenessSignals": [
440
+ "workflow-requests-version"
441
+ ],
442
+ "decision": "not-needed",
443
+ "reason": "A reporter, not a judge. It hands SimpleCov's merge finalization to the aggregate and writes a run record; the verdict on that record is `lisa-rspec-shards.mjs verify`, which refuses any record not at the schema version it speaks. A superseded recorder either leaves a record verify rejects or leaves the shard's own minimum check armed, and both are loud reds, never a believed green.",
444
+ "lanes": [
445
+ "rails/copy-overwrite"
446
+ ],
447
+ "detail": "`quality-rails.yml` is delivered at `@main`, so this step is live in every consumer on their next run. It reads `scripts/lisa_rspec_shard_recorder.rb` from the CALLER's tree with no package-relative candidate, and Lisa delivers that path on the `lisa apply` channel (rails/copy-overwrite). The two halves land in the wrong order: this step is live everywhere immediately, the file it needs is live only where somebody applied. No existence test guards the read, which is ALL this scan examined of the step's control flow: it did not read anything that could absorb the failure. An absent path most likely fails the job loudly — the better of the two failures, because it is visible — but that is inferred from the missing guard, NOT MEASURED. The step's text does carry `exit-nonzero`, `error-annotation` — located in the step, deliberately NOT related to this read, and enough to make \"it skips silently\" a claim to check rather than assume. STALENESS (a separate dimension from the delivery verdict above): `no-probe`. Neither an existence test nor a version probe bears on this read, so nothing here distinguishes a current artifact from an absent or a superseded one. Tokens found: `workflow-requests-version`. `workflow-requests-version` is workflow-scoped — it does not say THIS step asks."
448
+ },
449
+ {
450
+ "key": "quality-rails.yml::scripts/lisa_simplecov_collate.rb",
451
+ "workflow": "quality-rails.yml",
452
+ "path": "scripts/lisa_simplecov_collate.rb",
453
+ "channel": "apply",
454
+ "verdict": "apply-lagged",
455
+ "remedy": "run-lisa-apply",
456
+ "guarded": false,
457
+ "handling": [
458
+ "exit-zero",
459
+ "exit-nonzero",
460
+ "error-annotation"
461
+ ],
462
+ "detection": "no-probe",
463
+ "stalenessSignals": [
464
+ "workflow-requests-version"
465
+ ],
466
+ "decision": "not-needed",
467
+ "reason": "A runner, not a judge. It forwards the shards' resultsets to `SimpleCov.collate`, and the threshold verdict is SimpleCov's own, read from the project's `.simplecov` and `simplecov.thresholds.json`; the script sets no threshold. A superseded copy can fail to collate, which is red, but cannot change what the thresholds are.",
468
+ "lanes": [
469
+ "rails/copy-overwrite"
470
+ ],
471
+ "detail": "`quality-rails.yml` is delivered at `@main`, so this step is live in every consumer on their next run. It reads `scripts/lisa_simplecov_collate.rb` from the CALLER's tree with no package-relative candidate, and Lisa delivers that path on the `lisa apply` channel (rails/copy-overwrite). The two halves land in the wrong order: this step is live everywhere immediately, the file it needs is live only where somebody applied. No existence test guards the read, which is ALL this scan examined of the step's control flow: it did not read anything that could absorb the failure. An absent path most likely fails the job loudly — the better of the two failures, because it is visible — but that is inferred from the missing guard, NOT MEASURED. The step's text does carry `exit-zero`, `exit-nonzero`, `error-annotation` — located in the step, deliberately NOT related to this read, and enough to make \"it skips silently\" a claim to check rather than assume. STALENESS (a separate dimension from the delivery verdict above): `no-probe`. Neither an existence test nor a version probe bears on this read, so nothing here distinguishes a current artifact from an absent or a superseded one. Tokens found: `workflow-requests-version`. `workflow-requests-version` is workflow-scoped — it does not say THIS step asks."
472
+ },
414
473
  {
415
474
  "key": "quality-rails.yml::scripts/lisa-mutation.sh",
416
475
  "workflow": "quality-rails.yml",
@@ -435,6 +494,29 @@
435
494
  ],
436
495
  "detail": "`quality-rails.yml` is delivered at `@main`, so this step is live in every consumer on their next run. It reads `scripts/lisa-mutation.sh` from the CALLER's tree with no package-relative candidate, and Lisa delivers that path on the `lisa apply` channel (all/copy-contents). The two halves land in the wrong order: this step is live everywhere immediately, the file it needs is live only where somebody applied. The read sits behind an existence test, which is ALL this scan examined of the step's control flow: it did not read the test's other branch, any fallback, any explicit `exit`, or any verification of the step's output. Whether an absent path skips quietly, falls back, or fails loudly is therefore NOT MEASURED — read the step before concluding it proves nothing. The shape worth ruling out is the silent one, where a skip posts no context and an absent required context is not a red one. The step's text does carry `exit-zero`, `exit-nonzero`, `error-annotation` — located in the step, deliberately NOT related to this read, and enough to make \"it skips silently\" a claim to check rather than assume. STALENESS (a separate dimension from the delivery verdict above): `existence-only`. An existence test and no version probe: this coupling can see that the artifact is MISSING and cannot see that it is OLD. A present copy of any vintage runs, and its verdict is believed. Tokens found: `workflow-requests-version`. `workflow-requests-version` is workflow-scoped — it does not say THIS step asks."
437
496
  },
497
+ {
498
+ "key": "quality-rails.yml::scripts/lisa-rspec-shards.mjs",
499
+ "workflow": "quality-rails.yml",
500
+ "path": "scripts/lisa-rspec-shards.mjs",
501
+ "channel": "apply",
502
+ "verdict": "apply-lagged",
503
+ "remedy": "run-lisa-apply",
504
+ "guarded": false,
505
+ "handling": [
506
+ "exit-nonzero",
507
+ "error-annotation"
508
+ ],
509
+ "detection": "no-probe",
510
+ "stalenessSignals": [
511
+ "workflow-requests-version"
512
+ ],
513
+ "decision": "warranted",
514
+ "reason": "Verdict-bearing when test_shards is above 1: its `verify` subcommand is what turns a dropped or duplicated spec file into a red `Test (MySQL)` / `Test (PostgreSQL)`, and its `plan` decides which files each leg runs. Plan and record documents carry a schema version that `verify` refuses on mismatch, and an unknown subcommand or flag fails loudly, so a copy too old to speak this workflow's interface is red rather than green. What that does not cover is a copy at the same schema whose partition check is weaker than the current one; the script exports no contract version the workflow could compare, so the obligation is recorded rather than claimed closed.",
515
+ "lanes": [
516
+ "rails/copy-overwrite"
517
+ ],
518
+ "detail": "`quality-rails.yml` is delivered at `@main`, so this step is live in every consumer on their next run. It reads `scripts/lisa-rspec-shards.mjs` from the CALLER's tree with no package-relative candidate, and Lisa delivers that path on the `lisa apply` channel (rails/copy-overwrite). The two halves land in the wrong order: this step is live everywhere immediately, the file it needs is live only where somebody applied. No existence test guards the read, which is ALL this scan examined of the step's control flow: it did not read anything that could absorb the failure. An absent path most likely fails the job loudly — the better of the two failures, because it is visible — but that is inferred from the missing guard, NOT MEASURED. The step's text does carry `exit-nonzero`, `error-annotation` — located in the step, deliberately NOT related to this read, and enough to make \"it skips silently\" a claim to check rather than assume. STALENESS (a separate dimension from the delivery verdict above): `no-probe`. Neither an existence test nor a version probe bears on this read, so nothing here distinguishes a current artifact from an absent or a superseded one. Tokens found: `workflow-requests-version`. `workflow-requests-version` is workflow-scoped — it does not say THIS step asks."
519
+ },
438
520
  {
439
521
  "key": "quality-rails.yml::scripts/lisa-scratch-run.sh",
440
522
  "workflow": "quality-rails.yml",
@@ -95,6 +95,7 @@ scripts/lisa-npm-updater.mjs
95
95
  scripts/lisa-postinstall.mjs
96
96
  scripts/lisa-rails-prepush.mjs
97
97
  scripts/lisa-reconcile-policy.mjs
98
+ scripts/lisa-rspec-shards.mjs
98
99
  scripts/lisa-run-gates.mjs
99
100
  scripts/lisa-schema-validate.mjs
100
101
  scripts/lisa-test-node.mjs