@codyswann/lisa 4.57.2 → 4.57.4

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 (66) hide show
  1. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  2. package/dist/core/lisa-owned-hash-ledger.js +4 -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/upstream-evidence-manifest.d.ts.map +1 -1
  6. package/dist/core/upstream-evidence-manifest.js +5 -3
  7. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  8. package/expo/copy-overwrite/scripts/classify-maestro-failures.mjs +66 -14
  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-setup-remote-env/scripts/preflight-tools.mjs +44 -19
  13. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +44 -19
  14. package/plugins/lisa-agy/plugin.json +1 -1
  15. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +44 -19
  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-copilot/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +44 -19
  23. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +44 -19
  25. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-expo-agy/plugin.json +1 -1
  28. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  33. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  38. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  43. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  48. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails-agy/plugin.json +1 -1
  53. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  58. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  63. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/preflight-tools.mjs +44 -19
  66. package/scripts/two-channel-couplings.json +7 -5
@@ -232,8 +232,22 @@ export function preflightTools(
232
232
  };
233
233
  }
234
234
 
235
+ /**
236
+ * Format a count with the appropriate noun phrase.
237
+ * @param {number} n How many.
238
+ * @param {string} one Singular noun phrase.
239
+ * @param {string} many Plural noun phrase.
240
+ * @returns {string} A counted phrase such as "1 tool" or "3 tools".
241
+ */
242
+ function count(n, one, many) {
243
+ return `${n} ${n === 1 ? one : many}`;
244
+ }
245
+
235
246
  /**
236
247
  * Render a verdict for whoever has to act on it.
248
+ *
249
+ * Put blockers first and scope the stop instruction to that section. The
250
+ * separate installable section must not send usable work to the blocked lane.
237
251
  * @param {object} result A {@link preflightTools} result.
238
252
  * @returns {string} Operator-readable report, empty when nothing needs saying.
239
253
  */
@@ -243,42 +257,53 @@ export function reportTools(result) {
243
257
  // The header tracks the exit code. Only a blocked tool is a failure — one
244
258
  // Lisa can install is an action with a command attached, and calling that
245
259
  // "FAILED" while exiting zero teaches readers that the word means nothing.
260
+ const blocking = count(result.blocked.length, "tool blocks", "tools block");
261
+ const spare = result.installable.length
262
+ ? ` ${result.installable.length} more Lisa can install for you.`
263
+ : ``;
246
264
  const header = () => {
247
- if (result.blocked.length) return "Tooling preflight FAILED.";
265
+ if (result.blocked.length)
266
+ return `Tooling preflight FAILED — ${blocking}.${spare}`;
248
267
  if (result.installable.length)
249
- return "Tooling preflight — action available.";
268
+ return (
269
+ `Tooling preflight — action available. Nothing blocks;` +
270
+ ` ${count(result.installable.length, "tool", "tools")} Lisa can` +
271
+ ` install for you.`
272
+ );
250
273
  // Nothing to do and nothing to fix; the note below is the whole message.
251
- return "Tooling preflight passed, with a note.";
274
+ return `Tooling preflight passed, with a note.`;
252
275
  };
253
276
  const lines = [header()];
254
- if (result.installable.length) {
255
- lines.push(
256
- ``,
257
- `Lisa can install these itself — they are pinned and checksummed.`,
258
- `Run /lisa:setup:local-env to place them:`,
259
- ``
260
- );
261
- for (const step of result.installable) {
262
- lines.push(` ${step.name} — ${step.reason}`);
263
- }
264
- }
265
277
  if (result.blocked.length) {
266
278
  lines.push(
267
279
  ``,
268
- `These need you. Lisa has no pinned artifact it can place:`,
280
+ `STOP — these block the work. Lisa has no pinned artifact it can place,`,
281
+ `so it cannot fix them for you:`,
269
282
  ``
270
283
  );
271
284
  for (const step of result.blocked) {
272
285
  const why = result.reasons[step.name];
273
- lines.push(` ${step.name} — ${step.reason}`);
274
- if (why) lines.push(` required because ${why}`);
286
+ lines.push(` [BLOCKED] ${step.name} — ${step.reason}`);
287
+ if (why) lines.push(` required because ${why}`);
275
288
  }
276
289
  lines.push(
277
290
  ``,
278
- `Work needing one of these cannot be completed. Route the item to`,
279
- `blocked with this reason rather than claiming it and stopping partway.`
291
+ ` Work needing one of the tools above cannot be completed. Route that`,
292
+ ` item to blocked citing that tool, rather than claiming it and`,
293
+ ` stopping partway. Nothing else in this report is a reason to stop.`
280
294
  );
281
295
  }
296
+ if (result.installable.length) {
297
+ lines.push(
298
+ ``,
299
+ `NOT A BLOCKER — Lisa can install these itself; they are pinned and`,
300
+ `checksummed. Run /lisa:setup:local-env to place them:`,
301
+ ``
302
+ );
303
+ for (const step of result.installable) {
304
+ lines.push(` [INSTALLABLE] ${step.name} — ${step.reason}`);
305
+ }
306
+ }
282
307
  if (result.unverified.length) {
283
308
  lines.push(
284
309
  ``,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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.57.2",
3
+ "version": "4.57.4",
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-typescript",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "4.57.2",
3
+ "version": "4.57.4",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -232,8 +232,22 @@ export function preflightTools(
232
232
  };
233
233
  }
234
234
 
235
+ /**
236
+ * Format a count with the appropriate noun phrase.
237
+ * @param {number} n How many.
238
+ * @param {string} one Singular noun phrase.
239
+ * @param {string} many Plural noun phrase.
240
+ * @returns {string} A counted phrase such as "1 tool" or "3 tools".
241
+ */
242
+ function count(n, one, many) {
243
+ return `${n} ${n === 1 ? one : many}`;
244
+ }
245
+
235
246
  /**
236
247
  * Render a verdict for whoever has to act on it.
248
+ *
249
+ * Put blockers first and scope the stop instruction to that section. The
250
+ * separate installable section must not send usable work to the blocked lane.
237
251
  * @param {object} result A {@link preflightTools} result.
238
252
  * @returns {string} Operator-readable report, empty when nothing needs saying.
239
253
  */
@@ -243,42 +257,53 @@ export function reportTools(result) {
243
257
  // The header tracks the exit code. Only a blocked tool is a failure — one
244
258
  // Lisa can install is an action with a command attached, and calling that
245
259
  // "FAILED" while exiting zero teaches readers that the word means nothing.
260
+ const blocking = count(result.blocked.length, "tool blocks", "tools block");
261
+ const spare = result.installable.length
262
+ ? ` ${result.installable.length} more Lisa can install for you.`
263
+ : ``;
246
264
  const header = () => {
247
- if (result.blocked.length) return "Tooling preflight FAILED.";
265
+ if (result.blocked.length)
266
+ return `Tooling preflight FAILED — ${blocking}.${spare}`;
248
267
  if (result.installable.length)
249
- return "Tooling preflight — action available.";
268
+ return (
269
+ `Tooling preflight — action available. Nothing blocks;` +
270
+ ` ${count(result.installable.length, "tool", "tools")} Lisa can` +
271
+ ` install for you.`
272
+ );
250
273
  // Nothing to do and nothing to fix; the note below is the whole message.
251
- return "Tooling preflight passed, with a note.";
274
+ return `Tooling preflight passed, with a note.`;
252
275
  };
253
276
  const lines = [header()];
254
- if (result.installable.length) {
255
- lines.push(
256
- ``,
257
- `Lisa can install these itself — they are pinned and checksummed.`,
258
- `Run /lisa:setup:local-env to place them:`,
259
- ``
260
- );
261
- for (const step of result.installable) {
262
- lines.push(` ${step.name} — ${step.reason}`);
263
- }
264
- }
265
277
  if (result.blocked.length) {
266
278
  lines.push(
267
279
  ``,
268
- `These need you. Lisa has no pinned artifact it can place:`,
280
+ `STOP — these block the work. Lisa has no pinned artifact it can place,`,
281
+ `so it cannot fix them for you:`,
269
282
  ``
270
283
  );
271
284
  for (const step of result.blocked) {
272
285
  const why = result.reasons[step.name];
273
- lines.push(` ${step.name} — ${step.reason}`);
274
- if (why) lines.push(` required because ${why}`);
286
+ lines.push(` [BLOCKED] ${step.name} — ${step.reason}`);
287
+ if (why) lines.push(` required because ${why}`);
275
288
  }
276
289
  lines.push(
277
290
  ``,
278
- `Work needing one of these cannot be completed. Route the item to`,
279
- `blocked with this reason rather than claiming it and stopping partway.`
291
+ ` Work needing one of the tools above cannot be completed. Route that`,
292
+ ` item to blocked citing that tool, rather than claiming it and`,
293
+ ` stopping partway. Nothing else in this report is a reason to stop.`
280
294
  );
281
295
  }
296
+ if (result.installable.length) {
297
+ lines.push(
298
+ ``,
299
+ `NOT A BLOCKER — Lisa can install these itself; they are pinned and`,
300
+ `checksummed. Run /lisa:setup:local-env to place them:`,
301
+ ``
302
+ );
303
+ for (const step of result.installable) {
304
+ lines.push(` [INSTALLABLE] ${step.name} — ${step.reason}`);
305
+ }
306
+ }
282
307
  if (result.unverified.length) {
283
308
  lines.push(
284
309
  ``,
@@ -108,8 +108,8 @@
108
108
  "detectionCounts": {
109
109
  "version-handshake": 4,
110
110
  "capability-probe": 0,
111
- "existence-only": 4,
112
- "no-probe": 13,
111
+ "existence-only": 5,
112
+ "no-probe": 12,
113
113
  "unchecked": 2
114
114
  },
115
115
  "decisionCounts": {
@@ -197,19 +197,21 @@
197
197
  "channel": "apply",
198
198
  "verdict": "apply-lagged",
199
199
  "remedy": "run-lisa-apply",
200
- "guarded": false,
200
+ "guarded": true,
201
201
  "handling": [
202
+ "else-branch",
202
203
  "exit-zero",
204
+ "notice-annotation",
203
205
  "failure-suppression"
204
206
  ],
205
- "detection": "no-probe",
207
+ "detection": "existence-only",
206
208
  "stalenessSignals": [],
207
209
  "decision": "not-needed",
208
210
  "reason": "Advisory classifier over a failure that has already happened. The step carries `exit-zero` and `failure-suppression`, and the job's verdict comes from the suite it annotates rather than from this script — a superseded copy mislabels a failure, it cannot invent or suppress one. Not verdict-bearing, so a handshake is cost without cover.",
209
211
  "lanes": [
210
212
  "expo/copy-overwrite"
211
213
  ],
212
- "detail": "`maestro-native-e2e.yml` is delivered at `@main`, so this step is live in every consumer on their next run. It reads `scripts/classify-maestro-failures.mjs` from the CALLER's tree with no package-relative candidate, and Lisa delivers that path on the `lisa apply` channel (expo/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`, `failure-suppression` — 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. None of the staleness tokens this scan can see (`artifact-declares-version`, `artifact-answers-version`, `step-requests-version`, `workflow-requests-version`) were found."
214
+ "detail": "`maestro-native-e2e.yml` is delivered at `@main`, so this step is live in every consumer on their next run. It reads `scripts/classify-maestro-failures.mjs` from the CALLER's tree with no package-relative candidate, and Lisa delivers that path on the `lisa apply` channel (expo/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. 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 `else-branch`, `exit-zero`, `notice-annotation`, `failure-suppression` — 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. None of the staleness tokens this scan can see (`artifact-declares-version`, `artifact-answers-version`, `step-requests-version`, `workflow-requests-version`) were found."
213
215
  },
214
216
  {
215
217
  "key": "nightly-e2e-health.yml::scripts/check-nightly-e2e-health.mjs",