@codyswann/lisa 4.27.5 → 4.27.6
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/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +27 -4
- package/dist/cli/doctor-enforcement-coverage.d.ts +14 -0
- package/dist/cli/doctor-enforcement-coverage.d.ts.map +1 -0
- package/dist/cli/doctor-enforcement-coverage.js +103 -0
- package/dist/cli/doctor-enforcement-coverage.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +9 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/core/enforcement-census-report.d.ts +19 -0
- package/dist/core/enforcement-census-report.d.ts.map +1 -0
- package/dist/core/enforcement-census-report.js +198 -0
- package/dist/core/enforcement-census-report.js.map +1 -0
- package/dist/core/enforcement-census.d.ts +177 -0
- package/dist/core/enforcement-census.d.ts.map +1 -0
- package/dist/core/enforcement-census.js +252 -0
- package/dist/core/enforcement-census.js.map +1 -0
- package/dist/core/enforcement-coverage.d.ts +168 -0
- package/dist/core/enforcement-coverage.d.ts.map +1 -0
- package/dist/core/enforcement-coverage.js +342 -0
- package/dist/core/enforcement-coverage.js.map +1 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +4 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +17 -3
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +4 -3
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-automations/SKILL.md +23 -0
- package/plugins/lisa/skills/lisa-setup-automations/SKILL.md +23 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-setup-automations/SKILL.md +23 -0
- 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-copilot/skills/lisa-setup-automations/SKILL.md +23 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/lisa-setup-automations/SKILL.md +23 -0
- 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/plugins/src/base/skills/lisa-setup-automations/SKILL.md +23 -0
- package/scripts/lisa-enforcement-census.mjs +168 -0
- package/scripts/lisa-enforcement-fallback.sh +27 -4
|
@@ -207,6 +207,29 @@ project: the per-check marker dedupe converges under a single scheduled runner,
|
|
|
207
207
|
and two concurrent runs can both observe an empty open-ticket set and both
|
|
208
208
|
file. Tear-down removes it with the rest of the `lisa-auto-<project>-*` set.
|
|
209
209
|
|
|
210
|
+
**Optional automation — the fleet enforcement census.** Register
|
|
211
|
+
`lisa-auto-<project>-enforcement-census` running `/lisa:enforcement-census`
|
|
212
|
+
once a **day** only when BOTH are true of the repository being set up: it
|
|
213
|
+
maintains a fleet roster (`.lisa.workspaces.json`, the machine-local list of
|
|
214
|
+
host checkouts), and it provides the `/lisa:enforcement-census` command. Both
|
|
215
|
+
hold in the Lisa monorepo and in no ordinary host project, which is the point —
|
|
216
|
+
the census measures OTHER checkouts on the same machine, so only the workstation
|
|
217
|
+
holding the roster has a fleet to measure. Where either is absent, register
|
|
218
|
+
nothing — a host project has no fleet, and scheduling a command it does not
|
|
219
|
+
have would be a loop that fails every night — and report the census as SKIPPED
|
|
220
|
+
naming which prerequisite was missing, per the report contract. Registering
|
|
221
|
+
nothing is correct; omitting it from the report is not, because a silently
|
|
222
|
+
absent loop is indistinguishable from one that was never asked for.
|
|
223
|
+
|
|
224
|
+
It **reports and never gates**: no finding about the fleet changes an exit
|
|
225
|
+
status, and no run files a ticket. Enforcement resolves from the checkout an
|
|
226
|
+
agent is working in, so a checkout resolving no guard is protected by nothing
|
|
227
|
+
while looking identical, from inside a session, to one protected by an old
|
|
228
|
+
policy — and nothing was re-measuring that (CodySwannGT/lisa#3490). The
|
|
229
|
+
per-checkout half of the same finding is carried by `lisa doctor`, which every
|
|
230
|
+
host does get. Tear-down removes it with the rest of the `lisa-auto-<project>-*`
|
|
231
|
+
set.
|
|
232
|
+
|
|
210
233
|
**Exploratory PRD pressure gate.** `auto-start-prds=true` means "create PRDs in the ready PRD
|
|
211
234
|
lifecycle when the PRD queue has capacity," not "always create a new ready PRD." The
|
|
212
235
|
`exploratory-prds` automation uses the same PRD source queue and pressure roles reported by
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Fleet enforcement census — re-measures how many host checkouts resolve a
|
|
4
|
+
* Lisa enforcement guard, and reports it.
|
|
5
|
+
*
|
|
6
|
+
* The per-session staleness notice in `scripts/lisa-enforcement-fallback.sh`
|
|
7
|
+
* tells THIS session about THIS checkout, which is right and is not the gap.
|
|
8
|
+
* The gap is that nobody was looking at the fleet, so the only fleet-wide
|
|
9
|
+
* number that ever existed was taken by hand and frozen in a comment
|
|
10
|
+
* (CodySwannGT/lisa#3490). This runs the measurement again, every time.
|
|
11
|
+
*
|
|
12
|
+
* Two properties it must keep:
|
|
13
|
+
*
|
|
14
|
+
* - It REPORTS. Its exit status is 0 for every finding about the fleet,
|
|
15
|
+
* including a fleet where every checkout is stale. A census that could
|
|
16
|
+
* redden someone's build would be routed around.
|
|
17
|
+
* - It keeps "resolves nothing" apart from "resolves something old". They are
|
|
18
|
+
* different failures with different remedies, and folding them loses the
|
|
19
|
+
* serious half.
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* node scripts/lisa-enforcement-census.mjs [options]
|
|
23
|
+
*
|
|
24
|
+
* Options:
|
|
25
|
+
* --roster <path> Roster file (default: .lisa.workspaces.json beside this repo)
|
|
26
|
+
* --scan <dir> Also discover checkouts under <dir>. Repeatable.
|
|
27
|
+
* --depth <n> How deep --scan descends (default 2)
|
|
28
|
+
* --reference <ver> Compare against this version instead of the newest found
|
|
29
|
+
* --redact Replace real paths with stable anonymous labels
|
|
30
|
+
* --json Emit the measured fleet as JSON
|
|
31
|
+
*
|
|
32
|
+
* Exit status: 0 for every fleet finding. 1 only when an explicitly named
|
|
33
|
+
* roster cannot be read, and 2 for a usage error — both are failures of the
|
|
34
|
+
* census's own inputs, never findings about the fleet.
|
|
35
|
+
*/
|
|
36
|
+
import path from "node:path";
|
|
37
|
+
import { fileURLToPath } from "node:url";
|
|
38
|
+
|
|
39
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
40
|
+
const repoRoot = path.resolve(here, "..");
|
|
41
|
+
|
|
42
|
+
const census = await import(
|
|
43
|
+
path.join(repoRoot, "dist", "core", "enforcement-census.js")
|
|
44
|
+
).catch(error => {
|
|
45
|
+
process.stderr.write(
|
|
46
|
+
`Could not load the census from dist/. Run \`bun run build:dist\` first.\n${error.message}\n`
|
|
47
|
+
);
|
|
48
|
+
process.exit(2);
|
|
49
|
+
});
|
|
50
|
+
const report = await import(
|
|
51
|
+
path.join(repoRoot, "dist", "core", "enforcement-census-report.js")
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const options = {
|
|
55
|
+
roster: null,
|
|
56
|
+
scans: [],
|
|
57
|
+
depth: 2,
|
|
58
|
+
reference: null,
|
|
59
|
+
redact: false,
|
|
60
|
+
json: false,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const argv = process.argv.slice(2);
|
|
64
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
65
|
+
const flag = argv[index];
|
|
66
|
+
const value = argv[index + 1];
|
|
67
|
+
// A value-taking flag left at the end of the line has no value. Assigning
|
|
68
|
+
// `undefined` there silently falls back to the default roster or drops the
|
|
69
|
+
// requested setting, so the run measures something other than what was
|
|
70
|
+
// asked for and says nothing about it — a census reporting the wrong fleet
|
|
71
|
+
// is worse than one that refuses. This is the census's own input being
|
|
72
|
+
// wrong, so it exits 2 like any other usage error and never as a finding.
|
|
73
|
+
const VALUE_FLAGS = ["--roster", "--scan", "--depth", "--reference"];
|
|
74
|
+
if (VALUE_FLAGS.includes(flag) && value === undefined) {
|
|
75
|
+
process.stderr.write(`${flag} requires a value\n`);
|
|
76
|
+
process.exit(2);
|
|
77
|
+
}
|
|
78
|
+
if (flag === "--roster") {
|
|
79
|
+
options.roster = value;
|
|
80
|
+
index += 1;
|
|
81
|
+
} else if (flag === "--scan") {
|
|
82
|
+
options.scans.push(value);
|
|
83
|
+
index += 1;
|
|
84
|
+
} else if (flag === "--depth") {
|
|
85
|
+
options.depth = Number.parseInt(value ?? "", 10);
|
|
86
|
+
index += 1;
|
|
87
|
+
} else if (flag === "--reference") {
|
|
88
|
+
options.reference = value;
|
|
89
|
+
index += 1;
|
|
90
|
+
} else if (flag === "--redact") {
|
|
91
|
+
options.redact = true;
|
|
92
|
+
} else if (flag === "--json") {
|
|
93
|
+
options.json = true;
|
|
94
|
+
} else if (flag === "--help" || flag === "-h") {
|
|
95
|
+
process.stdout.write(
|
|
96
|
+
"Usage: node scripts/lisa-enforcement-census.mjs [--roster <path>] [--scan <dir>] [--depth <n>] [--reference <ver>] [--redact] [--json]\n"
|
|
97
|
+
);
|
|
98
|
+
process.exit(0);
|
|
99
|
+
} else {
|
|
100
|
+
process.stderr.write(`Unknown option: ${flag}\n`);
|
|
101
|
+
process.exit(2);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!Number.isInteger(options.depth) || options.depth < 0) {
|
|
106
|
+
process.stderr.write("--depth must be a non-negative integer\n");
|
|
107
|
+
process.exit(2);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const rosterPath =
|
|
111
|
+
options.roster ?? path.join(repoRoot, census.DEFAULT_ROSTER_FILE);
|
|
112
|
+
const rosterEntries = await census.readFleetRoster(rosterPath);
|
|
113
|
+
|
|
114
|
+
// An explicitly named roster that cannot be read is an input error, not a
|
|
115
|
+
// finding: the operator asked for a specific file and did not get it. A missing
|
|
116
|
+
// DEFAULT roster is neither — this repository is simply not a fleet hub — so it
|
|
117
|
+
// reports an empty fleet and still exits 0.
|
|
118
|
+
if (rosterEntries === null && options.roster !== null) {
|
|
119
|
+
process.stderr.write(`Could not read the roster: ${rosterPath}\n`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const scanned = (
|
|
124
|
+
await Promise.all(
|
|
125
|
+
options.scans.map(directory =>
|
|
126
|
+
census.scanForCheckouts(directory, options.depth)
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
).flat();
|
|
130
|
+
|
|
131
|
+
const roster = await census.dedupeRoster([
|
|
132
|
+
...(rosterEntries ?? []),
|
|
133
|
+
...scanned,
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
const origins = [
|
|
137
|
+
rosterEntries === null ? null : rosterPath,
|
|
138
|
+
...options.scans.map(directory => `scan of ${directory}`),
|
|
139
|
+
].filter(Boolean);
|
|
140
|
+
|
|
141
|
+
const seedVersion = await import(path.join(repoRoot, "package.json"), {
|
|
142
|
+
with: { type: "json" },
|
|
143
|
+
})
|
|
144
|
+
.then(module => module.default.version ?? null)
|
|
145
|
+
.catch(() => null);
|
|
146
|
+
|
|
147
|
+
const measured = await census.runFleetCensus({
|
|
148
|
+
roster,
|
|
149
|
+
rosterOrigin: origins.length === 0 ? "no roster found" : origins.join(" + "),
|
|
150
|
+
seedVersion,
|
|
151
|
+
reference: options.reference,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const output = options.redact ? report.redactCensus(measured) : measured;
|
|
155
|
+
|
|
156
|
+
process.stdout.write(
|
|
157
|
+
options.json
|
|
158
|
+
? `${JSON.stringify(output, null, 2)}\n`
|
|
159
|
+
: report.renderFleetCensusReport(output)
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Unconditional. Nothing above may change it.
|
|
163
|
+
//
|
|
164
|
+
// Set rather than called: `process.exit()` tears the process down synchronously
|
|
165
|
+
// and can drop a pending `process.stdout.write` when stdout is a pipe, which is
|
|
166
|
+
// exactly how this is read — piped to a pager, a file, or a scheduled loop's
|
|
167
|
+
// log. A truncated census reports a smaller fleet than it measured, silently.
|
|
168
|
+
process.exitCode = 0;
|
|
@@ -404,10 +404,33 @@ fi
|
|
|
404
404
|
# - `main` cut 80 releases in 24 hours, median gap 10 minutes. "Behind the
|
|
405
405
|
# newest Lisa on this disk" is the DEFAULT state of a checkout within
|
|
406
406
|
# minutes of being cut, through nothing anyone did wrong.
|
|
407
|
-
# -
|
|
408
|
-
#
|
|
409
|
-
#
|
|
410
|
-
#
|
|
407
|
+
# - Measured once across the host checkouts on one fleet: of those that
|
|
408
|
+
# resolved guards at all, every one was behind or undateable and none was
|
|
409
|
+
# current, several by a whole MAJOR — while a larger number resolved NO
|
|
410
|
+
# guard at all.
|
|
411
|
+
#
|
|
412
|
+
# That second reading used to be written here as a set of counts, and that was
|
|
413
|
+
# the wrong place for it. A number in a comment is a measurement, not a monitor:
|
|
414
|
+
# it was true on the day it was taken and nothing re-took it, so the drift after
|
|
415
|
+
# that date was unobserved — which is the same defect the number documents. The
|
|
416
|
+
# counts are therefore not restated here. Re-derive them:
|
|
417
|
+
#
|
|
418
|
+
# Run these FROM A LISA MONOREPO CHECKOUT — the census and the fleet roster are
|
|
419
|
+
# Lisa-monorepo artifacts, and this comment travels into host projects where
|
|
420
|
+
# neither exists. The script wrapper builds `dist/` first, which a fresh clone
|
|
421
|
+
# does not have:
|
|
422
|
+
#
|
|
423
|
+
# bun run lisa:enforcement-census # roster on this machine
|
|
424
|
+
# bun run lisa:enforcement-census -- --redact # safe to quote publicly
|
|
425
|
+
#
|
|
426
|
+
# In a host project the local half of the same question is `lisa doctor`, which
|
|
427
|
+
# reports what THIS checkout resolves and needs no roster.
|
|
428
|
+
#
|
|
429
|
+
# The census reports and never gates, and it keeps "resolves NO guard" apart
|
|
430
|
+
# from "resolves something old" — different failures, different remedies, and
|
|
431
|
+
# folding them is what made the unenforced checkouts easy to miss
|
|
432
|
+
# (CodySwannGT/lisa#3490). `lisa doctor` carries the same finding for the single
|
|
433
|
+
# checkout it is run in.
|
|
411
434
|
#
|
|
412
435
|
# So no distance threshold rescues it — even the strictest version predicate is
|
|
413
436
|
# permanently true across the fleet. The noise is the REPETITION, not the
|