@codyswann/lisa 3.13.0 → 3.13.1
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.
- package/dist/core/lisa-owned-provenance.d.ts +1 -5
- package/dist/core/lisa-owned-provenance.d.ts.map +1 -1
- package/dist/core/lisa-owned-provenance.js +27 -10
- package/dist/core/lisa-owned-provenance.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/scripts/generate-lisa-owned-hash-ledger.mjs +67 -14
|
@@ -11,14 +11,10 @@ export type ProvenanceVerdict =
|
|
|
11
11
|
readonly kind: "host-ahead";
|
|
12
12
|
readonly extraCapabilities: readonly string[];
|
|
13
13
|
}
|
|
14
|
-
/** Content Lisa never shipped
|
|
14
|
+
/** Content Lisa never shipped. Preserve and report. */
|
|
15
15
|
| {
|
|
16
16
|
readonly kind: "host-modified";
|
|
17
17
|
}
|
|
18
|
-
/** Declared hardening that upstream now also has. Safe to refresh. */
|
|
19
|
-
| {
|
|
20
|
-
readonly kind: "absorbed-upstream";
|
|
21
|
-
}
|
|
22
18
|
/** Hash matches a Lisa release. Genuinely stale, so refresh it. */
|
|
23
19
|
| {
|
|
24
20
|
readonly kind: "provably-stale";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lisa-owned-provenance.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-provenance.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lisa-owned-provenance.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-provenance.ts"],"names":[],"mappings":"AAyEA,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,uDAAuD;GACrD;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE;AACpC,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,CAiBnB;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"}
|
|
@@ -33,15 +33,34 @@
|
|
|
33
33
|
* apply after the change is the one that clobbers — it fails exactly once, on the
|
|
34
34
|
* case that matters most.
|
|
35
35
|
*
|
|
36
|
+
* ## Why the ledger is the *only* proof of staleness
|
|
37
|
+
*
|
|
38
|
+
* An earlier draft let capability markers authorise an overwrite too: if the
|
|
39
|
+
* host declared capabilities and Lisa declared all of them, upstream had
|
|
40
|
+
* "absorbed" the hardening, so refreshing was assumed safe. Running it against
|
|
41
|
+
* the real ledger and a real guard showed that this reintroduces the original
|
|
42
|
+
* bug outright.
|
|
43
|
+
*
|
|
44
|
+
* A host hardening a Lisa guard edits a *copy of Lisa's file*, so it keeps
|
|
45
|
+
* Lisa's marker line. Its declared set therefore equals Lisa's while its bytes
|
|
46
|
+
* carry undeclared changes — and the shortcut overwrote them without ever
|
|
47
|
+
* consulting the ledger. That is precisely the propswap case, silently reverted
|
|
48
|
+
* again by the mechanism meant to prevent it.
|
|
49
|
+
*
|
|
50
|
+
* So capabilities may only ever justify *keeping* the host's copy. Declaring
|
|
51
|
+
* nothing is not evidence of having changed nothing, and no marker comparison
|
|
52
|
+
* can account for bytes nobody declared. Overwriting requires positive proof
|
|
53
|
+
* from the ledger, and nothing else.
|
|
54
|
+
*
|
|
36
55
|
* ## How the standoff ends
|
|
37
56
|
*
|
|
38
|
-
* Refusing forever
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
57
|
+
* Refusing forever would be its own failure, so the exits are explicit rather
|
|
58
|
+
* than automatic. A `provably-stale` copy always refreshes, which is the common
|
|
59
|
+
* case and keeps upgrades flowing. A `host-modified` copy is reported on every
|
|
60
|
+
* apply and by `lisa doctor` until someone resolves it — by landing the change
|
|
61
|
+
* upstream, or by taking Lisa's copy via `--refresh-templates`. Lisa does not
|
|
62
|
+
* end that standoff on its own, because every way of doing so amounts to
|
|
63
|
+
* deleting work it cannot account for.
|
|
45
64
|
* @module core/lisa-owned-provenance
|
|
46
65
|
*/
|
|
47
66
|
import { createHash } from "node:crypto";
|
|
@@ -79,8 +98,6 @@ export function classifyHostCopy(relativePath, hostBytes, lisaBytes, ledger = LI
|
|
|
79
98
|
if (extraCapabilities.length > 0) {
|
|
80
99
|
return { kind: HOST_AHEAD, extraCapabilities };
|
|
81
100
|
}
|
|
82
|
-
if (hostCapabilities.size > 0)
|
|
83
|
-
return { kind: "absorbed-upstream" };
|
|
84
101
|
const known = ledger[normalise(relativePath)];
|
|
85
102
|
if (known === undefined || known.length === 0)
|
|
86
103
|
return { kind: "unenrolled" };
|
|
@@ -121,7 +138,7 @@ export function mayRefreshLisaOwned(verdict) {
|
|
|
121
138
|
*/
|
|
122
139
|
export function describePreserved(relativePath, verdict) {
|
|
123
140
|
return verdict.kind === HOST_AHEAD
|
|
124
|
-
? `${relativePath}: your copy guards against ${verdict.extraCapabilities.join(", ")}, which Lisa's copy does not. Kept yours.
|
|
141
|
+
? `${relativePath}: your copy guards against ${verdict.extraCapabilities.join(", ")}, which Lisa's copy does not. Kept yours. Nothing to do unless you want Lisa's version instead — contributing your hardening upstream is the way to get both, or run \`lisa apply\` with \`--refresh-templates\` to take Lisa's copy and drop yours.`
|
|
125
142
|
: `${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
143
|
}
|
|
127
144
|
//# sourceMappingURL=lisa-owned-provenance.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lisa-owned-provenance.js","sourceRoot":"","sources":["../../src/core/lisa-owned-provenance.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"lisa-owned-provenance.js","sourceRoot":"","sources":["../../src/core/lisa-owned-provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;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;AA8BzC;;;;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;IAED,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,sPAAsP;QACzU,CAAC,CAAC,GAAG,YAAY,uSAAuS,CAAC;AAC7T,CAAC"}
|
|
@@ -1058,7 +1058,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
1058
1058
|
"scripts/generate-codex-plugin-artifacts.mjs": "1d1a362afb8dba408741a3eed9fe583296f6bcb048608979f73f359cd609c03d",
|
|
1059
1059
|
"scripts/generate-copilot-plugin-artifacts.mjs": "70d283eed7cb7c1351d13cf0497f06dec175562c5918921c404393a76f11278f",
|
|
1060
1060
|
"scripts/generate-cursor-plugin-artifacts.mjs": "d8806197a723f0c2b50a8155ffc5b6f0abc30c68dc2385cb80b0dd066d061231",
|
|
1061
|
-
"scripts/generate-lisa-owned-hash-ledger.mjs": "
|
|
1061
|
+
"scripts/generate-lisa-owned-hash-ledger.mjs": "977d447ff5bf1b5254a66a7386e4fd8e7bf9c8f3e7a5b8d61cedb771b97002f2",
|
|
1062
1062
|
"scripts/generate-upstream-evidence-manifest.mjs": "de1eb5adaa6252b4abe5859933bff0ab2a52e19cc5a3fc5a41018fbb57b766d1",
|
|
1063
1063
|
"scripts/github-status-check.sh": "c6a4a13ff5cf689fcbd7a6aca718f9e7d46d54ccb6687f662b055c1ca20e792f",
|
|
1064
1064
|
"scripts/install-claude-plugins.sh": "0e96ebbae1e59304bb432286eccba38900a6008b80f2c53a1ec2c95801d4087c",
|
package/package.json
CHANGED
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
}
|
|
125
125
|
},
|
|
126
126
|
"name": "@codyswann/lisa",
|
|
127
|
-
"version": "3.13.
|
|
127
|
+
"version": "3.13.1",
|
|
128
128
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
129
129
|
"main": "dist/index.js",
|
|
130
130
|
"exports": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.13.
|
|
3
|
+
"version": "3.13.1",
|
|
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": "3.13.
|
|
3
|
+
"version": "3.13.1",
|
|
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": "3.13.
|
|
3
|
+
"version": "3.13.1",
|
|
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": "3.13.
|
|
3
|
+
"version": "3.13.1",
|
|
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": "3.13.
|
|
3
|
+
"version": "3.13.1",
|
|
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"
|
|
@@ -93,6 +93,25 @@ function lisaOwnedSources() {
|
|
|
93
93
|
return byDestination;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* The bytes currently on disk for a tracked source.
|
|
98
|
+
*
|
|
99
|
+
* Read from the working tree rather than `HEAD:` so an author who edits a guard
|
|
100
|
+
* and regenerates gets *their* new hash recorded. Reading HEAD would record the
|
|
101
|
+
* pre-edit bytes and force a commit-then-regenerate-then-amend dance, and the
|
|
102
|
+
* `--check` gate would be asserting against a version nobody is shipping. In CI
|
|
103
|
+
* the working tree is the checked-out commit, so the two agree there.
|
|
104
|
+
* @param {string} source - Repo-relative source path
|
|
105
|
+
* @returns {Buffer|undefined} File contents, or undefined when unreadable
|
|
106
|
+
*/
|
|
107
|
+
function workingCopy(source) {
|
|
108
|
+
try {
|
|
109
|
+
return readFileSync(path.join(repoRoot, source));
|
|
110
|
+
} catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
96
115
|
/**
|
|
97
116
|
* Hex sha256 of a blob's bytes.
|
|
98
117
|
* @param {Buffer} bytes - Blob contents
|
|
@@ -112,8 +131,8 @@ function digest(bytes) {
|
|
|
112
131
|
*/
|
|
113
132
|
function historicalHashes(source) {
|
|
114
133
|
const hashes = [];
|
|
115
|
-
const current =
|
|
116
|
-
if (current !==
|
|
134
|
+
const current = workingCopy(source);
|
|
135
|
+
if (current !== undefined) hashes.push(digest(current));
|
|
117
136
|
|
|
118
137
|
// `--name-only` is what makes rename-following actually work. Asking for the
|
|
119
138
|
// commit list alone and then reading `<rev>:<source>` fails for every revision
|
|
@@ -205,22 +224,56 @@ for (const [destination, sources] of lisaOwnedSources()) {
|
|
|
205
224
|
ledger.set(destination, [...known].sort());
|
|
206
225
|
}
|
|
207
226
|
|
|
208
|
-
const rendered = render(ledger);
|
|
209
227
|
if (process.argv.includes("--check")) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
228
|
+
checkCurrentBytesAreRecorded();
|
|
229
|
+
} else {
|
|
230
|
+
writeFileSync(outputPath, render(ledger));
|
|
231
|
+
process.stdout.write(`Wrote ${ledger.size} ledger entries.\n`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Assert the one property that actually protects refresh: every Lisa-owned file
|
|
236
|
+
* Lisa ships *right now* has its current hash recorded.
|
|
237
|
+
*
|
|
238
|
+
* Deliberately narrower than "the file equals a fresh regeneration". That
|
|
239
|
+
* stricter form looks safer and is not: the history walk depends on clone depth
|
|
240
|
+
* and on merge topology, so a byte-exact check fails whenever CI's view of
|
|
241
|
+
* history differs from the author's. It did exactly that here — `autoupdate`
|
|
242
|
+
* merged `main` into the PR branch, main carried other merged template changes,
|
|
243
|
+
* the walk found hashes the author's run never saw, and a correct ledger was
|
|
244
|
+
* reported out of date. With many PRs in flight it would mean every merge
|
|
245
|
+
* reddens every other open PR.
|
|
246
|
+
*
|
|
247
|
+
* Historical hashes appearing later are purely additive, and being additive they
|
|
248
|
+
* cannot cause harm: an extra known-good hash can only let refresh replace a
|
|
249
|
+
* copy that genuinely came from an older Lisa. What must never happen is the
|
|
250
|
+
* *current* bytes going unrecorded — a guard edited without regenerating would
|
|
251
|
+
* stop being recognised as Lisa's own, and refresh would silently stop
|
|
252
|
+
* delivering it. That is the failure this gate exists to catch, and it is
|
|
253
|
+
* deterministic everywhere.
|
|
254
|
+
*/
|
|
255
|
+
function checkCurrentBytesAreRecorded() {
|
|
256
|
+
const recorded = existingLedger();
|
|
257
|
+
const missing = [];
|
|
258
|
+
for (const [destination, sources] of lisaOwnedSources()) {
|
|
259
|
+
const known = new Set(recorded.get(destination) ?? []);
|
|
260
|
+
for (const source of sources) {
|
|
261
|
+
const current = workingCopy(source);
|
|
262
|
+
if (current === undefined) continue;
|
|
263
|
+
if (!known.has(digest(current))) missing.push(source);
|
|
264
|
+
}
|
|
215
265
|
}
|
|
216
|
-
if (
|
|
266
|
+
if (missing.length > 0) {
|
|
217
267
|
process.stderr.write(
|
|
218
|
-
|
|
268
|
+
`Lisa-owned hash ledger does not record the bytes currently shipped for:\n${missing
|
|
269
|
+
.map(source => ` ${source}`)
|
|
270
|
+
.join(
|
|
271
|
+
"\n"
|
|
272
|
+
)}\nRun \`bun run build:lisa-owned-hash-ledger\` and commit the result.\n`
|
|
219
273
|
);
|
|
220
274
|
process.exit(1);
|
|
221
275
|
}
|
|
222
|
-
process.stdout.write(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
process.stdout.write(`Wrote ${ledger.size} ledger entries.\n`);
|
|
276
|
+
process.stdout.write(
|
|
277
|
+
`Lisa-owned hash ledger records every shipped artifact (${recorded.size} entries).\n`
|
|
278
|
+
);
|
|
226
279
|
}
|