@codyswann/lisa 3.11.9 → 3.13.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 (145) hide show
  1. package/all/copy-overwrite/scripts/lisa-command-envelope.mjs +10 -0
  2. package/all/copy-overwrite/scripts/lisa-destructive-guard.mjs +267 -0
  3. package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +15 -2
  4. package/all/create-only/scripts/remote-agent-aws-setup.sh +3 -0
  5. package/cdk/create-only/.github/workflows/deploy.yml +2 -1
  6. package/cdk/create-only/vitest.config.local.ts +3 -0
  7. package/dist/cli/doctor-lisa-owned-artifacts.d.ts +11 -2
  8. package/dist/cli/doctor-lisa-owned-artifacts.d.ts.map +1 -1
  9. package/dist/cli/doctor-lisa-owned-artifacts.js +60 -15
  10. package/dist/cli/doctor-lisa-owned-artifacts.js.map +1 -1
  11. package/dist/core/config.d.ts +21 -1
  12. package/dist/core/config.d.ts.map +1 -1
  13. package/dist/core/config.js +1 -0
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/guard-capabilities.d.ts +60 -0
  16. package/dist/core/guard-capabilities.d.ts.map +1 -0
  17. package/dist/core/guard-capabilities.js +102 -0
  18. package/dist/core/guard-capabilities.js.map +1 -0
  19. package/dist/core/lisa-owned-hash-ledger.d.ts +11 -0
  20. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -0
  21. package/dist/core/lisa-owned-hash-ledger.js +127 -0
  22. package/dist/core/lisa-owned-hash-ledger.js.map +1 -0
  23. package/dist/core/lisa-owned-provenance.d.ts +75 -0
  24. package/dist/core/lisa-owned-provenance.d.ts.map +1 -0
  25. package/dist/core/lisa-owned-provenance.js +127 -0
  26. package/dist/core/lisa-owned-provenance.js.map +1 -0
  27. package/dist/core/lisa.d.ts +18 -0
  28. package/dist/core/lisa.d.ts.map +1 -1
  29. package/dist/core/lisa.js +38 -0
  30. package/dist/core/lisa.js.map +1 -1
  31. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  32. package/dist/core/upstream-evidence-manifest.js +66 -53
  33. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  34. package/dist/strategies/copy-overwrite.d.ts +28 -1
  35. package/dist/strategies/copy-overwrite.d.ts.map +1 -1
  36. package/dist/strategies/copy-overwrite.js +53 -3
  37. package/dist/strategies/copy-overwrite.js.map +1 -1
  38. package/dist/strategies/strategy.interface.d.ts +13 -0
  39. package/dist/strategies/strategy.interface.d.ts.map +1 -1
  40. package/expo/create-only/.github/workflows/ci.yml +3 -0
  41. package/expo/create-only/.github/workflows/deploy.yml +2 -1
  42. package/expo/create-only/.github/workflows/maestro-e2e.yml +3 -0
  43. package/expo/create-only/.github/workflows/nightly-e2e-bypass-reaper.yml +3 -0
  44. package/expo/create-only/.github/workflows/nightly-e2e-health.yml +3 -0
  45. package/expo/create-only/.github/workflows/nightly-e2e-report.yml +3 -0
  46. package/expo/create-only/babel.config.js +3 -0
  47. package/expo/create-only/jest.config.local.ts +3 -0
  48. package/expo/create-only/jest.config.react-native-mock.js +3 -0
  49. package/expo/create-only/jest.setup.local.ts +3 -0
  50. package/expo/create-only/jest.setup.pre.local.js +3 -0
  51. package/expo/create-only/scripts/zap-baseline.sh +3 -0
  52. package/harper-fabric/create-only/.github/workflows/deploy.yml +3 -0
  53. package/harper-fabric/create-only/.github/workflows/zap-baseline.yml +3 -0
  54. package/harper-fabric/create-only/scripts/zap-baseline.sh +3 -0
  55. package/nestjs/create-only/.github/workflows/deploy.yml +3 -0
  56. package/nestjs/create-only/scripts/zap-baseline.sh +3 -0
  57. package/nestjs/create-only/vitest.config.local.ts +3 -0
  58. package/npm-package/create-only/.github/workflows/publish-to-npm.yml +2 -1
  59. package/package.json +3 -1
  60. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa/hooks/block-no-verify.sh +15 -2
  63. package/plugins/lisa/rules/eager/reset-seed-coverage.md +2 -0
  64. package/plugins/lisa-agy/plugin.json +1 -1
  65. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  68. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-copilot/hooks/block-no-verify.sh +15 -2
  72. package/plugins/lisa-copilot/rules/eager/reset-seed-coverage.md +2 -0
  73. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-cursor/hooks/block-no-verify.sh +15 -2
  75. package/plugins/lisa-cursor/rules/reset-seed-coverage.mdc +2 -0
  76. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  78. package/plugins/lisa-expo-agy/plugin.json +1 -1
  79. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  83. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  84. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  87. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  88. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  89. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  91. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  93. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  94. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  96. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  98. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  99. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  103. package/plugins/lisa-rails-agy/plugin.json +1 -1
  104. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  108. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  109. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  110. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  113. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  114. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  115. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  116. package/plugins/src/base/hooks/block-no-verify.sh +15 -2
  117. package/plugins/src/base/rules/eager/reset-seed-coverage.md +2 -0
  118. package/rails/create-only/.github/workflows/claude-code-review-response.yml +2 -1
  119. package/rails/create-only/.github/workflows/claude-nightly-code-complexity.yml +2 -1
  120. package/rails/create-only/.github/workflows/claude-nightly-test-coverage.yml +2 -1
  121. package/rails/create-only/.github/workflows/claude-nightly-test-improvement.yml +2 -1
  122. package/rails/create-only/.github/workflows/claude-sync-down-branches.yml +2 -1
  123. package/rails/create-only/.github/workflows/deploy.yml +2 -1
  124. package/rails/create-only/.mutant.yml +3 -0
  125. package/rails/create-only/.reek.yml +3 -0
  126. package/rails/create-only/.rubocop_todo.yml +3 -0
  127. package/rails/create-only/mutation.gate.yml +3 -0
  128. package/rails/create-only/rubocop.thresholds.yml +3 -0
  129. package/rails/create-only/spec/rails_helper.rb +2 -0
  130. package/rails/create-only/spec/spec_helper.rb +2 -0
  131. package/scripts/generate-lisa-owned-hash-ledger.mjs +226 -0
  132. package/typescript/create-only/.github/workflows/auto-update-pr-branches-dispatch.yml +2 -1
  133. package/typescript/create-only/.github/workflows/auto-update-pr-branches.yml +2 -1
  134. package/typescript/create-only/.github/workflows/claude-ci-auto-fix.yml +2 -1
  135. package/typescript/create-only/.github/workflows/claude-code-review-response.yml +2 -1
  136. package/typescript/create-only/.github/workflows/claude-deploy-auto-fix.yml +2 -1
  137. package/typescript/create-only/.github/workflows/claude-nightly-code-complexity.yml +2 -1
  138. package/typescript/create-only/.github/workflows/claude-nightly-test-coverage.yml +2 -1
  139. package/typescript/create-only/.github/workflows/claude-nightly-test-improvement.yml +2 -1
  140. package/typescript/create-only/.github/workflows/claude-sync-down-branches.yml +2 -1
  141. package/typescript/create-only/.github/workflows/claude.yml +2 -1
  142. package/typescript/create-only/.github/workflows/required-checks-drift.yml +2 -1
  143. package/typescript/create-only/eslint.config.local.ts +3 -0
  144. package/typescript/create-only/jest.config.local.ts +3 -0
  145. package/typescript/create-only/vitest.config.local.ts +3 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-capabilities.d.ts","sourceRoot":"","sources":["../../src/core/guard-capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAsBH,oEAAoE;AACpE,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAQlE;AAgBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE,aAAa,GAClB,SAAS,MAAM,EAAE,CAInB"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The capability marker a Lisa-owned guard uses to declare what it defends
3
+ * against, so refresh can compare two copies by *what they stop* rather than by
4
+ * bytes.
5
+ *
6
+ * A byte comparison answers "are these the same?" and nothing else. It cannot
7
+ * answer the only question refresh actually needs answered — "is the host's copy
8
+ * behind mine, or ahead of it?" — and guessing "behind" is how a host's stronger
9
+ * guard gets silently replaced by a weaker upstream one.
10
+ *
11
+ * The marker is one comment line in the guard's header:
12
+ *
13
+ * ```
14
+ * # lisa-guard-capabilities: no-verify-abbrev, hookspath-allowlist, config-env
15
+ * ```
16
+ *
17
+ * Deliberately a *set*, not a version number. A monotonic `lisa-guard-version: N`
18
+ * is easier to compare but lies the moment two hosts harden different vectors
19
+ * independently — which is exactly what happened: `propswapllc/frontend` closed
20
+ * the `GIT_CONFIG_KEY_<n>` bypass while `tunnl-infrastructure` closed a different
21
+ * one. Both would have bumped to the same number and one would have silently
22
+ * won. Two sets stay two sets, and set difference names precisely what each copy
23
+ * has that the other does not.
24
+ *
25
+ * Only the header window is scanned. A guard whose *body* greps for the marker
26
+ * string, or a test fixture that quotes one, must not be read as declaring it.
27
+ * @module core/guard-capabilities
28
+ */
29
+ /**
30
+ * How much of a file counts as its header.
31
+ *
32
+ * Generous enough for the long rationale comments Lisa's guards carry, small
33
+ * enough that the marker cannot be smuggled in from a file's body.
34
+ */
35
+ const HEADER_BYTES = 4096;
36
+ /** The token that introduces a declaration. */
37
+ const MARKER = "lisa-guard-capabilities:";
38
+ /**
39
+ * Characters allowed between the start of a line and the marker.
40
+ *
41
+ * Covers every comment syntax Lisa's owned artifacts use: `#` for shell, `//`
42
+ * and `*` for JS/TS. Requiring the marker to be preceded by nothing else is what
43
+ * makes it a declaration rather than a mention inside prose or a quoted string.
44
+ */
45
+ const COMMENT_PREFIX = new Set([" ", "\t", "#", "/", "*"]);
46
+ /**
47
+ * Read the capabilities a guard's header declares.
48
+ *
49
+ * Absence is not an error and not a failure signal — it means the artifact has
50
+ * not been enrolled, and every caller treats that as "no declaration made"
51
+ * rather than "declares nothing dangerous". That is what lets the marker roll
52
+ * out one guard at a time with no flag day.
53
+ * Scanned line by line with plain string operations rather than a regular
54
+ * expression. A header is untrusted input — it arrives from whatever repository
55
+ * the "host" copy came from — and a linear scan cannot be made to backtrack.
56
+ * @param bytes - Full contents of the guard file
57
+ * @returns The declared capabilities, lower-cased and de-duplicated
58
+ */
59
+ export function readGuardCapabilities(bytes) {
60
+ const header = bytes.subarray(0, HEADER_BYTES).toString("utf8");
61
+ const declared = header
62
+ .split("\n")
63
+ .flatMap(line => declarationOn(line))
64
+ .map(entry => entry.trim().toLowerCase())
65
+ .filter(entry => entry.length > 0);
66
+ return new Set(declared);
67
+ }
68
+ /**
69
+ * The capabilities one line declares, if it is a declaration at all.
70
+ * @param line - A single line from the header
71
+ * @returns Raw comma-separated entries, or nothing when the line declares none
72
+ */
73
+ function declarationOn(line) {
74
+ const at = line.indexOf(MARKER);
75
+ if (at === -1)
76
+ return [];
77
+ for (const character of line.slice(0, at)) {
78
+ if (!COMMENT_PREFIX.has(character))
79
+ return [];
80
+ }
81
+ return line.slice(at + MARKER.length).split(",");
82
+ }
83
+ /**
84
+ * The capabilities the host declares that Lisa's copy does not.
85
+ *
86
+ * A non-empty result is the whole signal: the host copy defends something
87
+ * upstream does not, so overwriting it removes protection. This is set
88
+ * *difference*, not a strict-superset test, on purpose — a host that hardened
89
+ * vector A while upstream hardened vector B is not a superset of upstream, yet
90
+ * overwriting it still deletes A. Asking only "is the host a superset?" would
91
+ * clobber precisely the divergent-hardening case the set representation exists
92
+ * to handle.
93
+ * @param host - Capabilities declared by the installed copy
94
+ * @param lisa - Capabilities declared by the packaged copy
95
+ * @returns Capabilities present on the host and absent upstream, sorted
96
+ */
97
+ export function capabilitiesOnlyOnHost(host, lisa) {
98
+ return [...host]
99
+ .filter(capability => !lisa.has(capability))
100
+ .sort((left, right) => left.localeCompare(right));
101
+ }
102
+ //# sourceMappingURL=guard-capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard-capabilities.js","sourceRoot":"","sources":["../../src/core/guard-capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;GAKG;AACH,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,+CAA+C;AAC/C,MAAM,MAAM,GAAG,0BAA0B,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAK3D;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,MAAM;SACpB,KAAK,CAAC,IAAI,CAAC;SACX,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;SACpC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;SACxC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrC,OAAO,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IACzB,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAO,EAAE,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAmB,EACnB,IAAmB;IAEnB,OAAO,CAAC,GAAG,IAAI,CAAC;SACb,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;SAC3C,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** Generated by scripts/generate-lisa-owned-hash-ledger.mjs. Do not edit. */
2
+ /**
3
+ * Every content hash Lisa has published at each Lisa-owned destination path.
4
+ *
5
+ * A host copy whose hash appears here is provably an older Lisa artifact and may
6
+ * be refreshed. One that appears nowhere here was edited downstream, and refresh
7
+ * refuses rather than risk deleting a stronger guard. See
8
+ * `src/core/lisa-owned-provenance.ts`.
9
+ */
10
+ export declare const LISA_OWNED_HASH_LEDGER: Readonly<Record<string, readonly string[]>>;
11
+ //# sourceMappingURL=lisa-owned-hash-ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAqHjC,CAAC"}
@@ -0,0 +1,127 @@
1
+ /** Generated by scripts/generate-lisa-owned-hash-ledger.mjs. Do not edit. */
2
+ /**
3
+ * Every content hash Lisa has published at each Lisa-owned destination path.
4
+ *
5
+ * A host copy whose hash appears here is provably an older Lisa artifact and may
6
+ * be refreshed. One that appears nowhere here was edited downstream, and refresh
7
+ * refuses rather than risk deleting a stronger guard. See
8
+ * `src/core/lisa-owned-provenance.ts`.
9
+ */
10
+ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
11
+ "scripts/lisa-clean-git-env.sh": Object.freeze([
12
+ "d15af6f13eedbca41046070972380e69fc0af8f7d903aac12b8644389b9a0c91",
13
+ "e7121a0ee9e1bf7c01cd2ab55f563dd6d9ab75990739bb6ddf571a513efa10e9",
14
+ ]),
15
+ "scripts/lisa-command-envelope.mjs": Object.freeze([
16
+ "2011331056b1c86bd181649713a395dbfd2768f9fcd7e37b521d6ad2a9318ea9",
17
+ "4f76bb7d466eb153a4e1414d06f12856a1242de2cfa4cb4b1b942cf29d8b7f37",
18
+ "6b198f088cd3cd862d4357fbb069b1ad5d3afeb7be594f2bf1ff603b092a8354",
19
+ "e06cb11f1418fe94564cea527a22b039f53b7ab10c1cd182264a31e52bba9fa8",
20
+ ]),
21
+ "scripts/lisa-destructive-guard.mjs": Object.freeze([
22
+ "4fb431a3c58f443e303d613934d023d9e95ddae5c42710bb7009f7c080c82a64",
23
+ ]),
24
+ "scripts/lisa-enforcement-fallback.sh": Object.freeze([
25
+ "1bec5f3c284b3febdc471487ee6a23ffb72bd4e7b293f9e26b0188f32318c722",
26
+ "3049c2f1051c7c02dd60b62355964651e1565ebe6bdcb5e37634cd686cf1deb1",
27
+ "8dc942ac4edc2017d6b8277296c8f80542e7c024eef1bb65de609979d0ca9a0e",
28
+ "9cab71562d01421d57c6719318a4d03a6249a45c1aea434db3ff26e3065eeb66",
29
+ "d7d88e44763694bed5eae998cd3663922c957e49f9f9621d9201f0417a4128ad",
30
+ "e17c45f7cadf0fb55dd07270776caf211af99b7d4771a4fde75aafe5919e8dbe",
31
+ ]),
32
+ "scripts/lisa-floor-collisions.mjs": Object.freeze([
33
+ "2d11968f6852ab745cba939de03c6d6cb5f413975d2cfc8fc447bdd0b218e91a",
34
+ "3c7a0234b4f5609fce49e44711d2ec86af8cf7fb82b0831e4450e3347ecefe45",
35
+ "a612f172282d13ba9318fe5e03689d7c6ff42a0122d0c9d5d39051e1967b93dc",
36
+ ]),
37
+ "scripts/lisa-hooks/block-direct-issue-create.sh": Object.freeze([
38
+ "22184f1dd1b96e818741bd4ae659446299535d7ce594817155edebac4172a7ba",
39
+ "60a4f94ec671b2372f7492680ab327d97aff686588ce85791f54e0fbd098b0ca",
40
+ "73e94b73830d8961026947d99bf4ac5f959b5bca025203b913d8a6363cbe2b9f",
41
+ "847dfca48823a495fc3de47dfdb4b26e49d6808c6570c46d024a99793df1d370",
42
+ ]),
43
+ "scripts/lisa-hooks/block-instruction-file-edits.sh": Object.freeze([
44
+ "6f52076e3488821d638b6dd45b5faca527d92b181cc12e6e5033859e0786c8a7",
45
+ "850cfc23852eb752114ae1e938a1c7bcf34bc50c85d3aaca0c02311cc8c0ef70",
46
+ "8996e0bcbf1db085a5d99734e797c91f5025997bd967bc374efff8348dac14c2",
47
+ "be1e77fb0ddb246eddae71ebac25e972c3c9e8079514e0dc78d0b73df919464c",
48
+ "d47314b66d6ce85f77d6e058f861eede4462b2b0a33d54e82ff1c931167ec3f7",
49
+ ]),
50
+ "scripts/lisa-hooks/block-no-verify.sh": Object.freeze([
51
+ "031ef7a53fc49cb18665105b834b7b50ad6a43317e0821949809e877e0562ce4",
52
+ "18b7bb8867c5e6f719a9c503a38525f4c05e3d611717c3e1a503cf58dfb2832f",
53
+ "2df476aacebcb2544a12f459a0092e88eb28c0d00e45fd7ed909c75dfe441027",
54
+ "33e2bd341eb992036059b0c4606db35a218adcfeca7e44d174fedf8ebdff29d2",
55
+ "3b93a0affab6413e3c14a6f43de5acb586286ed1819ece0a66b30429b4d00490",
56
+ "3ff7255f395a9552366faaac34d383f4c95fecfb39882cd1d57854356422fe61",
57
+ "7790a560850c9d38709dbaccdaa10733e632c1bd4d975cbec7e3cc5a8650b36a",
58
+ "a6ed91576efebb6ba40cfa4e9d8a3e8566314909210ae8d5860be62a3feb4cff",
59
+ "aa95a84f9f9224914947a09b858535aa8bd052199ff64cb53768cf78fb69b687",
60
+ "b9f299cb9d3ad75b71296376ae2c40eb207a3b2706dafdaa41fc586050a53aad",
61
+ "dd1aea1b9fdd0da023bf704b331b350bc14620f4364afa5001c6690fcbc940cc",
62
+ "e3407d63eae16662923d46b75b6a41e7ca7ab206b428ea2cbb0b4fee226f6516",
63
+ ]),
64
+ "scripts/lisa-hooks/block-shell-json-parsing.sh": Object.freeze([
65
+ "1934a15e154fda3c2a40979a5cd6225661b14fe7bc77328a69ce499bea85dba7",
66
+ "3b5f074f3ab5708030af507eea962389f3b067c5dbdc76580bd9e44d3ac6c603",
67
+ "90601e31603c440d8f19d213d63bfef8d18596c60f88cb6e28c93d12453c5c0c",
68
+ ]),
69
+ "scripts/lisa-hooks/parity-safety-net.sh": Object.freeze([
70
+ "062c69eea85157f1e941ec3626d8912aa9f182af6ccdbe19687588a6074db5ce",
71
+ "07002535de10d89f6d73e8922517550a02a8974549aaf200c9c314e6f4f83c70",
72
+ "1162ffd16cef3b23282d2f5207abcb88429462a74a9df1035f0df4968f6dd456",
73
+ "14fc5e292aebf83dd24fec12fb32e2beb509a9324f127a24a64d5c5d4d05c576",
74
+ "1bf3512bb94923a6cb25b1e7a619a3fc8e1336bffa16f499b459aaee3eaed66d",
75
+ "388cf8847581d96a1ff8ea98290a33a5f96d67cdd502ea807a0b8d88efe90e19",
76
+ "64546813f168fb446f39e34bc674fd3af85b15bd5e7f4f6bf4503463e701a7b9",
77
+ "772202ddd42d626625ff719666afbbe83445d8bae1bc7c0fb5ffdcb402be83f8",
78
+ "7f55986eb29f8ace8616595da4bbe60d80a96f9f29b24fb72efd66bb96b22464",
79
+ "8e19d5f1ba7abe3d22fbee47ff8e0fad76b965ccca20b8849e3c43ab07d9a4ef",
80
+ "a22fd1b7c483f1a538202353ee85c82be17a8910e03c0a930635ce8cb3595c64",
81
+ "b83c5043a0f29b2d8851258acfe6906aab7922cd80f486e25220e55f26a8c0a5",
82
+ "caea02a07027fc1c74d47e08b36299d369c5c9f7c4043da8828fbca3c64eb2aa",
83
+ "d12583803e8ff5e4b64e68eafc8d3c377af4c400d8b7ecd1c7fe699d8f92784a",
84
+ "d9ea58150aa27e7f0aa9a4ef155ed446d30bdfc932d01c64e2b8ca211aa0de7b",
85
+ "f740f600b255742a50f28e5c9d09427c5783c4084e84052dca46bc1f0c94a187",
86
+ "fa8f4a6517eacd9266feb7afd62398cdf2075d4824cac24b45bb64ad0cd18c86",
87
+ ]),
88
+ "scripts/lisa-hooks/sonar-secrets.sh": Object.freeze([
89
+ "7a408788975a6351d53e14aa32bb9a5d161f33df9dfea59ba32784ae33a62e67",
90
+ "945453fb275a58aa7185e150e6b05e5810739750928ab5bd6edfbf87d1c8aa0d",
91
+ "bf4132e49ba18e2f7e941520c299c15aeeacfd220e489f3d2eb8397f2048157b",
92
+ ]),
93
+ "scripts/lisa-mutation.mjs": Object.freeze([
94
+ "7d8d24dea151ef2178807046e97aea7ebfadd9c88eb50347feb7bb4bb190227d",
95
+ "8e0bdb491dfaea04e88be1c1e2c6cc7cd34fa4ca18adddf5fecc43d7de8ffded",
96
+ "a630bde6cddae283776b8d34763a3642edbea8096d583a90ec6d43a9588ca8a6",
97
+ ]),
98
+ "scripts/lisa-schema-validate.mjs": Object.freeze([
99
+ "2ace82daacdebbbb00a7eceb09fbe82cabec04330a083ca10417b2ee4e0a63eb",
100
+ "4a05a0ed70a274827fd2f0fd0c98890c66bee2fde70785b8379943ebe6650473",
101
+ "9dd145c0f9dac6ed252b13d32e130dbab964da6cd357981b58541780ecb2f981",
102
+ ]),
103
+ "scripts/lisa-work-item.mjs": Object.freeze([
104
+ "238dc2da29938edd92518c2b08192d14c6f2751ed175c07d6e882814829b7b7e",
105
+ "2d9ca0841db9e75ddfd95e3a4d485fd01aa639a512ef648996f565b7991d8e2b",
106
+ "4c263fef48c03b3b6c623ea8a6cd55505bfaf28197e5072226ea35f9f3d26885",
107
+ "5eb31c284e820a51312d4484f0669128007326af55ee515923fec33700d003a1",
108
+ "6a8be8577242588f14ab52b25b2a22b6def53aefa9513b4d3d7c49f4d8027079",
109
+ "9223856be4618fb1e3e731b259ae5bf5328decc322f0b4a0d0fd18d6b12cd45a",
110
+ "96a87c131606b3edd43e52485e68f8ffbf5c528cb4beb7a1185263519a9632ce",
111
+ "ab0dede91c5c8a07984ae13aa48ec70cdcd2fa691b59617bc4b57998dc79a51c",
112
+ "c78df9d5f0f3fc388ff60a7db5898478a2a4d6d7325833f10384b5a8c62bb34a",
113
+ "e30016a49deff1c3cd3ae7951262aeb2ef3d472bce76bbbe2a177db6017c7cad",
114
+ "e41e9534988ce69a836664437a78f3a3f3dc7ebbe396c86cdab0ea63fb7c19da",
115
+ "f12006df4ed9974f65d955718af7dcc8a079799d02c58bd38b2d9c49f271db72",
116
+ "f88f69d44693084428f44e3f2b80afecda08c7ee8747588f40277fdc9396fd8f",
117
+ "fc0d92739e620d8fd70f7aa4a12d3e0b3a8f790d47c4a8322040f32c30b9445a",
118
+ "fe064a2bd11200ed256fb36e203978f858fccbcaf40708496c066383345900ef",
119
+ ]),
120
+ "scripts/schemas/lisa-command-envelope.v1.schema.json": Object.freeze([
121
+ "d153b7c2953a30f180e38f09e98240c63327f5196eeba9bdf545e5a1f125a879",
122
+ ]),
123
+ "scripts/schemas/lisa-state-contract.v1.schema.json": Object.freeze([
124
+ "6b7afe4ffeba8aaa53871d8855172f7eacde05953b1321145218ec638ff5c1c4",
125
+ ]),
126
+ });
127
+ //# sourceMappingURL=lisa-owned-hash-ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lisa-owned-hash-ledger.js","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAE/B,MAAM,CAAC,MAAM,CAAC;IAChB,+BAA+B,EAAE,MAAM,CAAC,MAAM,CAAC;QAC7C,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,mCAAmC,EAAE,MAAM,CAAC,MAAM,CAAC;QACjD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,oCAAoC,EAAE,MAAM,CAAC,MAAM,CAAC;QAClD,kEAAkE;KACnE,CAAC;IACF,sCAAsC,EAAE,MAAM,CAAC,MAAM,CAAC;QACpD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,mCAAmC,EAAE,MAAM,CAAC,MAAM,CAAC;QACjD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,iDAAiD,EAAE,MAAM,CAAC,MAAM,CAAC;QAC/D,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,oDAAoD,EAAE,MAAM,CAAC,MAAM,CAAC;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,uCAAuC,EAAE,MAAM,CAAC,MAAM,CAAC;QACrD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,gDAAgD,EAAE,MAAM,CAAC,MAAM,CAAC;QAC9D,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,yCAAyC,EAAE,MAAM,CAAC,MAAM,CAAC;QACvD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,qCAAqC,EAAE,MAAM,CAAC,MAAM,CAAC;QACnD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,2BAA2B,EAAE,MAAM,CAAC,MAAM,CAAC;QACzC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,kCAAkC,EAAE,MAAM,CAAC,MAAM,CAAC;QAChD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,4BAA4B,EAAE,MAAM,CAAC,MAAM,CAAC;QAC1C,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,sDAAsD,EAAE,MAAM,CAAC,MAAM,CAAC;QACpE,kEAAkE;KACnE,CAAC;IACF,oDAAoD,EAAE,MAAM,CAAC,MAAM,CAAC;QAClE,kEAAkE;KACnE,CAAC;CACH,CAAC,CAAC"}
@@ -0,0 +1,75 @@
1
+ /** Known-good hashes per Lisa-owned destination path. */
2
+ export type HashLedger = Readonly<Record<string, readonly string[]>>;
3
+ /** What refresh concluded about a host's copy of a Lisa-owned artifact. */
4
+ export type ProvenanceVerdict =
5
+ /** Byte-identical to Lisa's copy; there is nothing to decide. */
6
+ {
7
+ readonly kind: "identical";
8
+ }
9
+ /** Declares hardening Lisa lacks. Preserve, and name what would be lost. */
10
+ | {
11
+ readonly kind: "host-ahead";
12
+ readonly extraCapabilities: readonly string[];
13
+ }
14
+ /** Content Lisa never shipped, with nothing declared. Preserve and report. */
15
+ | {
16
+ readonly kind: "host-modified";
17
+ }
18
+ /** Declared hardening that upstream now also has. Safe to refresh. */
19
+ | {
20
+ readonly kind: "absorbed-upstream";
21
+ }
22
+ /** Hash matches a Lisa release. Genuinely stale, so refresh it. */
23
+ | {
24
+ readonly kind: "provably-stale";
25
+ }
26
+ /** Artifact carries no provenance yet; behave exactly as before this change. */
27
+ | {
28
+ readonly kind: "unenrolled";
29
+ };
30
+ /**
31
+ * The verdicts under which the host's copy is kept.
32
+ *
33
+ * Derived from `ProvenanceVerdict` rather than restated, so a verdict added
34
+ * later cannot quietly go unhandled: `mayRefreshLisaOwned` must classify it, and
35
+ * anything it declines to refresh has to be describable here.
36
+ */
37
+ export type PreservedVerdict = Extract<ProvenanceVerdict, {
38
+ readonly kind: "host-ahead" | "host-modified";
39
+ }>;
40
+ /**
41
+ * Decide whether a host's copy of a Lisa-owned artifact may be overwritten.
42
+ *
43
+ * Order matters. Capabilities are consulted before the ledger because a
44
+ * declaration is an explicit statement of intent by whoever edited the file, and
45
+ * an explicit statement outranks an inference drawn from a hash table.
46
+ * @param relativePath - Repo-relative destination path of the artifact
47
+ * @param hostBytes - Contents currently installed in the host project
48
+ * @param lisaBytes - Contents Lisa ships for that path
49
+ * @param ledger - Known-good hash ledger (injected by tests)
50
+ * @returns The verdict describing which side is ahead
51
+ */
52
+ export declare function classifyHostCopy(relativePath: string, hostBytes: Buffer, lisaBytes: Buffer, ledger?: HashLedger): ProvenanceVerdict;
53
+ /**
54
+ * Whether refresh is allowed to replace the host's copy.
55
+ *
56
+ * The default is deliberately asymmetric. Failing closed leaves a stale file in
57
+ * place and tells somebody about it; failing open deletes a security control and
58
+ * tells nobody. Those costs are not comparable, so anything short of proof that
59
+ * the host is behind means the host's copy stays.
60
+ * @param verdict - Result of `classifyHostCopy`
61
+ * @returns True when the host copy is provably not ahead
62
+ */
63
+ export declare function mayRefreshLisaOwned(verdict: ProvenanceVerdict): verdict is Exclude<ProvenanceVerdict, PreservedVerdict>;
64
+ /**
65
+ * Explain a preserved file to whoever is reading the apply output.
66
+ *
67
+ * Written for an operator who did not make the change and may not be an
68
+ * engineer: it names the file, says plainly that theirs is the stronger copy,
69
+ * and gives the two actions that end the standoff.
70
+ * @param relativePath - Repo-relative destination path of the artifact
71
+ * @param verdict - Result of `classifyHostCopy`
72
+ * @returns One operator-readable sentence, or undefined when nothing was held back
73
+ */
74
+ export declare function describePreserved(relativePath: string, verdict: PreservedVerdict): string;
75
+ //# sourceMappingURL=lisa-owned-provenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lisa-owned-provenance.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-provenance.ts"],"names":[],"mappings":"AAsDA,yDAAyD;AACzD,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;AAQrE,2EAA2E;AAC3E,MAAM,MAAM,iBAAiB;AAC3B,iEAAiE;AAC/D;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE;AAChC,4EAA4E;GAC1E;IACE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/C;AACH,8EAA8E;GAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE;AACpC,sEAAsE;GACpE;IAAE,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAA;CAAE;AACxC,mEAAmE;GACjE;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE;AACrC,gFAAgF;GAC9E;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,CACpC,iBAAiB,EACjB;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,eAAe,CAAA;CAAE,CAClD,CAAC;AAWF;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,UAAmC,GAC1C,iBAAiB,CAkBnB;AAWD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,iBAAiB,GACzB,OAAO,IAAI,OAAO,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAEzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,gBAAgB,GACxB,MAAM,CAIR"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Whether a host's copy of a Lisa-owned artifact is *behind* Lisa's or *ahead*
3
+ * of it — the question byte comparison cannot answer.
4
+ *
5
+ * Refresh (#2436) decided whether to overwrite a host's copy by comparing bytes.
6
+ * Unequal bytes were read as "the host is out of date", which is one of two
7
+ * possibilities and not the dangerous one. When the host copy is ahead —
8
+ * a guard hardened downstream before upstream caught up — the same comparison
9
+ * produces the same answer, and the overwrite silently deletes a security
10
+ * control. `propswapllc/frontend` closed the `GIT_CONFIG_KEY_<n>` hooks-path
11
+ * bypass before Lisa did; a `bun install` regenerated the file and reverted the
12
+ * hardening with no warning and no failure. Their own tests caught it. Nothing
13
+ * in Lisa would have.
14
+ *
15
+ * ## How "behind" gets proved
16
+ *
17
+ * The primary signal is provenance by content hash. Lisa ships a ledger of every
18
+ * sha256 it has ever published at each Lisa-owned destination path (see
19
+ * `scripts/generate-lisa-owned-hash-ledger.mjs`). A host's copy came from some
20
+ * past Lisa, so if its hash is in the ledger it is *provably* an older Lisa
21
+ * artifact and refreshing it is correct. If the hash appears nowhere in Lisa's
22
+ * shipping history, somebody downstream edited it, and refresh has no basis to
23
+ * call that stale.
24
+ *
25
+ * Hashes are the primary signal rather than capability markers because markers
26
+ * only protect a host that *declared*. propswap did not declare; they edited the
27
+ * guard. A marker-only design would still have clobbered the very incident that
28
+ * motivated this. The ledger protects undeclared hardening, retroactively, with
29
+ * no host-side action required.
30
+ *
31
+ * A ledger beats a receipt file written at apply time for the same reason: a
32
+ * receipt has to be written by a *prior* apply to arm the mechanism, so the first
33
+ * apply after the change is the one that clobbers — it fails exactly once, on the
34
+ * case that matters most.
35
+ *
36
+ * ## How the standoff ends
37
+ *
38
+ * Refusing forever is its own failure: genuine drift would accumulate and never
39
+ * be fixed. Capability markers are the release valve. When the host declares
40
+ * capabilities and Lisa's copy declares all of them, upstream has absorbed the
41
+ * host's hardening, so overwriting restores parity instead of removing
42
+ * protection — and refresh resumes automatically, with no operator action. A
43
+ * host that hardened without declaring gets told to do one of the two things
44
+ * that end the standoff: land it upstream, or declare it.
45
+ * @module core/lisa-owned-provenance
46
+ */
47
+ import { createHash } from "node:crypto";
48
+ import { capabilitiesOnlyOnHost, readGuardCapabilities, } from "./guard-capabilities.js";
49
+ import { LISA_OWNED_HASH_LEDGER } from "./lisa-owned-hash-ledger.js";
50
+ /** The verdict for content Lisa has no record of ever having shipped. */
51
+ const HOST_MODIFIED = "host-modified";
52
+ /** The verdict for a copy declaring hardening Lisa's copy does not. */
53
+ const HOST_AHEAD = "host-ahead";
54
+ /**
55
+ * Hex sha256 of a file's contents.
56
+ * @param bytes - File contents
57
+ * @returns Lower-case hex digest
58
+ */
59
+ function digest(bytes) {
60
+ return createHash("sha256").update(bytes).digest("hex");
61
+ }
62
+ /**
63
+ * Decide whether a host's copy of a Lisa-owned artifact may be overwritten.
64
+ *
65
+ * Order matters. Capabilities are consulted before the ledger because a
66
+ * declaration is an explicit statement of intent by whoever edited the file, and
67
+ * an explicit statement outranks an inference drawn from a hash table.
68
+ * @param relativePath - Repo-relative destination path of the artifact
69
+ * @param hostBytes - Contents currently installed in the host project
70
+ * @param lisaBytes - Contents Lisa ships for that path
71
+ * @param ledger - Known-good hash ledger (injected by tests)
72
+ * @returns The verdict describing which side is ahead
73
+ */
74
+ export function classifyHostCopy(relativePath, hostBytes, lisaBytes, ledger = LISA_OWNED_HASH_LEDGER) {
75
+ if (hostBytes.equals(lisaBytes))
76
+ return { kind: "identical" };
77
+ const hostCapabilities = readGuardCapabilities(hostBytes);
78
+ const extraCapabilities = capabilitiesOnlyOnHost(hostCapabilities, readGuardCapabilities(lisaBytes));
79
+ if (extraCapabilities.length > 0) {
80
+ return { kind: HOST_AHEAD, extraCapabilities };
81
+ }
82
+ if (hostCapabilities.size > 0)
83
+ return { kind: "absorbed-upstream" };
84
+ const known = ledger[normalise(relativePath)];
85
+ if (known === undefined || known.length === 0)
86
+ return { kind: "unenrolled" };
87
+ return known.includes(digest(hostBytes))
88
+ ? { kind: "provably-stale" }
89
+ : { kind: HOST_MODIFIED };
90
+ }
91
+ /**
92
+ * Normalise a destination path to the ledger's key form.
93
+ * @param relativePath - Repo-relative destination path
94
+ * @returns The path with forward slashes
95
+ */
96
+ function normalise(relativePath) {
97
+ return relativePath.replaceAll("\\", "/");
98
+ }
99
+ /**
100
+ * Whether refresh is allowed to replace the host's copy.
101
+ *
102
+ * The default is deliberately asymmetric. Failing closed leaves a stale file in
103
+ * place and tells somebody about it; failing open deletes a security control and
104
+ * tells nobody. Those costs are not comparable, so anything short of proof that
105
+ * the host is behind means the host's copy stays.
106
+ * @param verdict - Result of `classifyHostCopy`
107
+ * @returns True when the host copy is provably not ahead
108
+ */
109
+ export function mayRefreshLisaOwned(verdict) {
110
+ return verdict.kind !== HOST_AHEAD && verdict.kind !== HOST_MODIFIED;
111
+ }
112
+ /**
113
+ * Explain a preserved file to whoever is reading the apply output.
114
+ *
115
+ * Written for an operator who did not make the change and may not be an
116
+ * engineer: it names the file, says plainly that theirs is the stronger copy,
117
+ * and gives the two actions that end the standoff.
118
+ * @param relativePath - Repo-relative destination path of the artifact
119
+ * @param verdict - Result of `classifyHostCopy`
120
+ * @returns One operator-readable sentence, or undefined when nothing was held back
121
+ */
122
+ export function describePreserved(relativePath, verdict) {
123
+ return verdict.kind === HOST_AHEAD
124
+ ? `${relativePath}: your copy guards against ${verdict.extraCapabilities.join(", ")}, which Lisa's copy does not. Kept yours. To take Lisa's copy later, contribute your hardening upstream — refresh resumes on its own once Lisa declares it too.`
125
+ : `${relativePath}: this file was edited in your project — its contents match no Lisa release, so Lisa cannot tell whether it is out of date or deliberately stronger. Kept yours. Review the difference, then either contribute the change upstream or add a \`lisa-guard-capabilities:\` line naming what it defends.`;
126
+ }
127
+ //# sourceMappingURL=lisa-owned-provenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lisa-owned-provenance.js","sourceRoot":"","sources":["../../src/core/lisa-owned-provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAKrE,yEAAyE;AACzE,MAAM,aAAa,GAAG,eAAwB,CAAC;AAE/C,uEAAuE;AACvE,MAAM,UAAU,GAAG,YAAqB,CAAC;AAgCzC;;;;GAIG;AACH,SAAS,MAAM,CAAC,KAAa;IAC3B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC9B,YAAoB,EACpB,SAAiB,EACjB,SAAiB,EACjB,SAAqB,sBAAsB;IAE3C,IAAI,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAE9D,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC1D,MAAM,iBAAiB,GAAG,sBAAsB,CAC9C,gBAAgB,EAChB,qBAAqB,CAAC,SAAS,CAAC,CACjC,CAAC;IACF,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC;IACjD,CAAC;IACD,IAAI,gBAAgB,CAAC,IAAI,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;IAEpE,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;IAC9C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IAC7E,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACtC,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE;QAC5B,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,YAAoB;IACrC,OAAO,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAA0B;IAE1B,OAAO,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,IAAI,KAAK,aAAa,CAAC;AACvE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,YAAoB,EACpB,OAAyB;IAEzB,OAAO,OAAO,CAAC,IAAI,KAAK,UAAU;QAChC,CAAC,CAAC,GAAG,YAAY,8BAA8B,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,iKAAiK;QACpP,CAAC,CAAC,GAAG,YAAY,uSAAuS,CAAC;AAC7T,CAAC"}
@@ -33,6 +33,15 @@ export declare class Lisa {
33
33
  * back to the silence this exists to end.
34
34
  */
35
35
  private readonly stalePaths;
36
+ /**
37
+ * Lisa-owned artifacts this apply deliberately did not replace, each with the
38
+ * reason spelled out for whoever reads the output.
39
+ *
40
+ * Full sentences rather than paths, because this is the one outcome an
41
+ * operator cannot act on from the filename: "we left yours alone because it
42
+ * defends something ours does not" is the entire message.
43
+ */
44
+ private readonly hostAheadNotes;
36
45
  private detectedTypes;
37
46
  private ignorePatterns;
38
47
  /**
@@ -663,6 +672,15 @@ export declare class Lisa {
663
672
  * that stayed on its old version while the release notes said otherwise.
664
673
  */
665
674
  private printStaleDetail;
675
+ /**
676
+ * Report Lisa-owned artifacts this apply chose not to replace.
677
+ *
678
+ * Reported as good news, not as a warning about the project: keeping a guard
679
+ * that is stronger than Lisa's is the correct outcome, and phrasing it as a
680
+ * problem would push an operator to "fix" it by discarding their own
681
+ * hardening — the exact loss the check exists to prevent.
682
+ */
683
+ private printHostAheadDetail;
666
684
  /**
667
685
  * Print summary statistics
668
686
  */
@@ -1 +1 @@
1
- {"version":3,"file":"lisa.d.ts","sourceRoot":"","sources":["../../src/core/lisa.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lisa.d.ts","sourceRoot":"","sources":["../../src/core/lisa.ts"],"names":[],"mappings":"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"}
package/dist/core/lisa.js CHANGED
@@ -53,6 +53,15 @@ export class Lisa {
53
53
  * back to the silence this exists to end.
54
54
  */
55
55
  stalePaths = [];
56
+ /**
57
+ * Lisa-owned artifacts this apply deliberately did not replace, each with the
58
+ * reason spelled out for whoever reads the output.
59
+ *
60
+ * Full sentences rather than paths, because this is the one outcome an
61
+ * operator cannot act on from the filename: "we left yours alone because it
62
+ * defends something ours does not" is the entire message.
63
+ */
64
+ hostAheadNotes = [];
56
65
  detectedTypes = [];
57
66
  ignorePatterns = {
58
67
  patterns: [],
@@ -1489,6 +1498,10 @@ export class Lisa {
1489
1498
  this.counters.stale++;
1490
1499
  this.stalePaths.push(result.relativePath);
1491
1500
  break;
1501
+ case "host-ahead":
1502
+ this.counters.hostAhead++;
1503
+ this.hostAheadNotes.push(result.note ?? `${result.relativePath}: kept your copy.`);
1504
+ break;
1492
1505
  case "overwritten":
1493
1506
  this.counters.overwritten++;
1494
1507
  break;
@@ -1529,6 +1542,11 @@ export class Lisa {
1529
1542
  ? `Out of date, would not be updated: ${result.relativePath}`
1530
1543
  : `Out of date, not updated: ${result.relativePath}`);
1531
1544
  break;
1545
+ case "host-ahead":
1546
+ this.deps.logger.warn(this.config.dryRun
1547
+ ? `Would keep your copy: ${result.relativePath}`
1548
+ : `Kept your copy: ${result.relativePath}`);
1549
+ break;
1532
1550
  case "overwritten":
1533
1551
  this.logMessage(`Would prompt to overwrite: ${result.relativePath}`, `Overwritten: ${result.relativePath}`);
1534
1552
  break;
@@ -1669,6 +1687,7 @@ export class Lisa {
1669
1687
  this.printStatLine("Would ignore: ", ignored, pc.magenta, this.lisaignoreSuffix);
1670
1688
  }
1671
1689
  this.printStaleDetail();
1690
+ this.printHostAheadDetail();
1672
1691
  }
1673
1692
  /**
1674
1693
  * Print summary statistics for apply mode
@@ -1690,6 +1709,7 @@ export class Lisa {
1690
1709
  this.printStatLine("Ignored: ", ignored, pc.magenta, this.lisaignoreSuffix);
1691
1710
  }
1692
1711
  this.printStaleDetail();
1712
+ this.printHostAheadDetail();
1693
1713
  }
1694
1714
  /**
1695
1715
  * Name the out-of-date managed files and say how to update them.
@@ -1712,6 +1732,24 @@ export class Lisa {
1712
1732
  logger.info("A non-interactive apply will not replace a file your project may have customised.");
1713
1733
  logger.info("Review the differences and run `lisa apply .` interactively to take them, or add paths to .lisaignore to keep yours.");
1714
1734
  }
1735
+ /**
1736
+ * Report Lisa-owned artifacts this apply chose not to replace.
1737
+ *
1738
+ * Reported as good news, not as a warning about the project: keeping a guard
1739
+ * that is stronger than Lisa's is the correct outcome, and phrasing it as a
1740
+ * problem would push an operator to "fix" it by discarding their own
1741
+ * hardening — the exact loss the check exists to prevent.
1742
+ */
1743
+ printHostAheadDetail() {
1744
+ if (this.counters.hostAhead === 0)
1745
+ return;
1746
+ const { logger } = this.deps;
1747
+ logger.info(`${this.counters.hostAhead} Lisa-owned file(s) look stronger in your project than in Lisa, so your version was kept:`);
1748
+ for (const note of this.hostAheadNotes) {
1749
+ logger.info(` ${note}`);
1750
+ }
1751
+ logger.info("Nothing is broken. Lisa only replaces one of its own files when it can prove your copy came from an older Lisa.");
1752
+ }
1715
1753
  /**
1716
1754
  * Print summary statistics
1717
1755
  */