@codyswann/lisa 2.325.4 → 2.325.5

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 (53) hide show
  1. package/dist/core/upstream-evidence-manifest.js +1 -1
  2. package/package.json +1 -1
  3. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  4. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  5. package/plugins/lisa-agy/plugin.json +1 -1
  6. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  7. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  8. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  9. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  10. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  14. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  15. package/plugins/lisa-expo-agy/plugin.json +1 -1
  16. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  20. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  21. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  26. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  31. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  35. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  36. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-rails-agy/plugin.json +1 -1
  41. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  45. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  46. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  51. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  53. package/scripts/check-security-floors.mjs +98 -13
@@ -961,7 +961,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
961
961
  "scripts/check-learnings-budget.ts": "007430c2d6938efcc79ccbfd5717ebe2c7cf66b4de9a21a3e67b8db7f3600349",
962
962
  "scripts/check-plugins-sync.sh": "3a99cbff0e8ec5e7c944690eae003a6d51b51c85436d77bd37faea4d553728af",
963
963
  "scripts/check-rules-pairing.sh": "4d4a0d9e8d36794a22020f419c879d7336b1c5bfe883acdcc826d26764560c7a",
964
- "scripts/check-security-floors.mjs": "3d3c0d0ddff2d810bf3470ff88926566210a3373954761727bd4281c38fe8d8c",
964
+ "scripts/check-security-floors.mjs": "0060a0f65885b54fc93bd3885ca249cbb1ac822c52813817aae640bc1303827b",
965
965
  "scripts/claude-remote-setup.sh": "0e33accf8aa057c70497f01c38bef9f9f3801d649272f583578d89201b242655",
966
966
  "scripts/clean-dist.mjs": "04970d768464acbf96ebeb77c16a6a3b3da56c744eef0da64c573c6152ee0dc7",
967
967
  "scripts/cleanup-amplify-branches.sh": "eb9db90b07818dd156dd48d04b6350a10b39be8ffe0267d72cc13705c8eb5350",
package/package.json CHANGED
@@ -115,7 +115,7 @@
115
115
  "brace-expansion": ">=5.0.9"
116
116
  },
117
117
  "name": "@codyswann/lisa",
118
- "version": "2.325.4",
118
+ "version": "2.325.5",
119
119
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
120
120
  "main": "dist/index.js",
121
121
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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": "2.325.4",
3
+ "version": "2.325.5",
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"
@@ -43,12 +43,45 @@ const GATING_SEVERITIES = new Set(["high", "critical"]);
43
43
 
44
44
  const ADVISORY_ENDPOINT = "https://api.github.com/advisories";
45
45
 
46
+ /**
47
+ * Resolve a `$name` self-reference to the range the manifest pins it to.
48
+ *
49
+ * Searches the dependency sections of every governance group, because a
50
+ * template may declare the target in `force`, `defaults` or `merge` and the
51
+ * reference means the same thing in each. Constraint sections that are
52
+ * themselves override maps are skipped, so a `$name` cannot resolve to another
53
+ * `$name` and loop.
54
+ * @param {object} manifest A parsed package.lisa.json.
55
+ * @param {string} name The referenced package.
56
+ * @returns {string|null} The pinned range, or null when nothing declares it.
57
+ */
58
+ export function resolveSelfReference(manifest, name) {
59
+ const DEPENDENCY_SECTIONS = [
60
+ "dependencies",
61
+ "devDependencies",
62
+ "peerDependencies",
63
+ ];
64
+ for (const group of GOVERNANCE_GROUPS) {
65
+ const block = manifest[group];
66
+ if (!block || typeof block !== "object") continue;
67
+ for (const section of DEPENDENCY_SECTIONS) {
68
+ const spec = block[section]?.[name];
69
+ if (typeof spec === "string" && !spec.startsWith("$")) return spec;
70
+ }
71
+ }
72
+ return null;
73
+ }
74
+
46
75
  /**
47
76
  * Every version constraint declared across the template manifests.
48
- * @returns {Map<string, Array<{file: string, path: string, spec: string}>>} By package name.
77
+ * @returns {{found: Map<string, Array<{file: string, path: string, spec: string}>>,
78
+ * unresolved: Array<{file: string, path: string, name: string, spec: string}>}}
79
+ * Constraints by package name, plus self-references pointing at nothing.
49
80
  */
50
81
  function collectFloors() {
51
82
  const found = new Map();
83
+ /** `$name` references pointing at nothing this manifest declares. */
84
+ const unresolved = [];
52
85
  const files = globSync("*/package-lisa/package.lisa.json").filter(
53
86
  file => !file.includes("node_modules") && !file.includes(".worktrees")
54
87
  );
@@ -66,17 +99,45 @@ function collectFloors() {
66
99
  const entries = block[section];
67
100
  if (!entries || typeof entries !== "object") continue;
68
101
  for (const [name, spec] of Object.entries(entries)) {
69
- // `$name` self-references defer to the project's own pin and carry
70
- // no floor of their own; a literal like "workspace:*" likewise.
71
- if (typeof spec !== "string" || !/\d/.test(spec)) continue;
72
- if (spec.startsWith("$")) continue;
102
+ if (typeof spec !== "string") continue;
103
+ // A `$name` self-reference carries no floor of its own it adopts
104
+ // whatever the manifest pins that package to elsewhere. Resolve it
105
+ // and check THAT, rather than skipping.
106
+ //
107
+ // Skipping was safe only by coincidence: every `$name` in the
108
+ // templates today happens to target a package also declared as a
109
+ // literal in a scanned group, so the range got checked under its own
110
+ // entry. Nothing enforces that. The first `$name` whose target is
111
+ // declared only downstream — or not at all — would be a floor
112
+ // nothing checks, reported as clean, which is worse than no check.
113
+ if (spec.startsWith("$")) {
114
+ const target = resolveSelfReference(manifest, spec.slice(1));
115
+ if (target === null) {
116
+ unresolved.push({
117
+ file,
118
+ path: `${group}.${section}`,
119
+ name,
120
+ spec,
121
+ });
122
+ continue;
123
+ }
124
+ if (!/\d/.test(target)) continue;
125
+ if (!found.has(name)) found.set(name, []);
126
+ found
127
+ .get(name)
128
+ .push({ file, path: `${group}.${section}`, spec: target });
129
+ continue;
130
+ }
131
+ // A literal carrying no digits — "workspace:*", "latest" — is not a
132
+ // floor and there is nothing to compare.
133
+ if (!/\d/.test(spec)) continue;
73
134
  if (!found.has(name)) found.set(name, []);
74
135
  found.get(name).push({ file, path: `${group}.${section}`, spec });
75
136
  }
76
137
  }
77
138
  }
78
139
  }
79
- return found;
140
+ return { found, unresolved };
80
141
  }
81
142
 
82
143
  /**
@@ -185,10 +246,10 @@ async function advisoriesFor(name) {
185
246
 
186
247
  /**
187
248
  * Audit every collected floor.
188
- * @returns {Promise<{problems: Array, unreachable: string[], checked: number}>} Findings.
249
+ * @returns {Promise<{problems: Array, unreachable: Array, unresolved: Array, checked: number}>} Findings.
189
250
  */
190
251
  async function audit() {
191
- const floors = collectFloors();
252
+ const { found: floors, unresolved } = collectFloors();
192
253
  const problems = [];
193
254
  const unreachable = [];
194
255
  for (const [name, sites] of floors) {
@@ -220,16 +281,18 @@ async function audit() {
220
281
  }
221
282
  }
222
283
  }
223
- return { problems, unreachable, checked: floors.size };
284
+ return { problems, unreachable, unresolved, checked: floors.size };
224
285
  }
225
286
 
226
287
  async function main() {
227
288
  const strict = process.argv.includes("--strict");
228
289
  const asJson = process.argv.includes("--json");
229
- const { problems, unreachable, checked } = await audit();
290
+ const { problems, unreachable, unresolved, checked } = await audit();
230
291
 
231
292
  if (asJson) {
232
- console.log(JSON.stringify({ problems, unreachable, checked }, null, 2));
293
+ console.log(
294
+ JSON.stringify({ problems, unreachable, unresolved, checked }, null, 2)
295
+ );
233
296
  } else if (problems.length === 0) {
234
297
  console.log(
235
298
  `## Security floors\n\nNo force-pinned floor permits a high or critical vulnerable release. ${checked} package(s) checked.`
@@ -251,7 +314,24 @@ async function main() {
251
314
  );
252
315
  }
253
316
 
254
- if (unreachable.length > 0) {
317
+ // Markdown notes belong to the human report only. Printed in --json mode
318
+ // they follow the document and break every parser reading it; the same
319
+ // facts are already fields in the JSON.
320
+ if (!asJson && unresolved.length > 0) {
321
+ console.log(
322
+ `\n> **${unresolved.length} self-reference(s) resolve to nothing.** A \`$name\` adopts whatever the manifest pins that package to; when nothing pins it, there is no floor to check and no floor to enforce. Reported rather than skipped, because silence here is indistinguishable from a clean result.`
323
+ );
324
+ for (const entry of unresolved) {
325
+ console.log(
326
+ `> - \`${entry.name}: ${entry.spec}\` in ${entry.file} ${entry.path}`
327
+ );
328
+ }
329
+ console.log(
330
+ "> Fix by declaring the target in a governance dependency section, or by replacing the reference with an explicit floor."
331
+ );
332
+ }
333
+
334
+ if (!asJson && unreachable.length > 0) {
255
335
  const limited = unreachable.filter(entry =>
256
336
  entry.reason.includes("rate limited")
257
337
  );
@@ -275,7 +355,12 @@ async function main() {
275
355
  // Under --strict an inconclusive run also fails: it proves nothing, and
276
356
  // treating "could not check" as "checked and clean" is exactly the silent
277
357
  // degradation this script exists to prevent.
278
- if (strict && (problems.length > 0 || unreachable.length > 0)) {
358
+ // An unresolved self-reference gates for the same reason an inconclusive one
359
+ // does: it is a floor nobody checked, and letting it pass reports it clean.
360
+ if (
361
+ strict &&
362
+ (problems.length > 0 || unreachable.length > 0 || unresolved.length > 0)
363
+ ) {
279
364
  process.exitCode = 1;
280
365
  }
281
366
  }