@codyswann/lisa 2.281.0 → 2.282.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.
- package/all/copy-contents/gitignore +2 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +20 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/standards-proof-cmd.d.ts +7 -0
- package/dist/cli/standards-proof-cmd.d.ts.map +1 -0
- package/dist/cli/standards-proof-cmd.js +14 -0
- package/dist/cli/standards-proof-cmd.js.map +1 -0
- package/dist/cli/ui-setup-readiness-local.d.ts +2 -1
- package/dist/cli/ui-setup-readiness-local.d.ts.map +1 -1
- package/dist/cli/ui-setup-readiness-local.js +4 -2
- package/dist/cli/ui-setup-readiness-local.js.map +1 -1
- package/dist/cli/ui-setup-readiness.d.ts.map +1 -1
- package/dist/cli/ui-setup-readiness.js +3 -2
- package/dist/cli/ui-setup-readiness.js.map +1 -1
- package/dist/cli/update-check-hook.d.ts +19 -0
- package/dist/cli/update-check-hook.d.ts.map +1 -0
- package/dist/cli/update-check-hook.js +36 -0
- package/dist/cli/update-check-hook.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +37 -6
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/standards/capture.d.ts +41 -0
- package/dist/standards/capture.d.ts.map +1 -0
- package/dist/standards/capture.js +196 -0
- package/dist/standards/capture.js.map +1 -0
- package/dist/standards/contract.d.ts +45 -0
- package/dist/standards/contract.d.ts.map +1 -0
- package/dist/standards/contract.js +175 -0
- package/dist/standards/contract.js.map +1 -0
- package/dist/standards/git-state.d.ts +30 -0
- package/dist/standards/git-state.d.ts.map +1 -0
- package/dist/standards/git-state.js +179 -0
- package/dist/standards/git-state.js.map +1 -0
- package/dist/standards/index.d.ts +8 -0
- package/dist/standards/index.d.ts.map +1 -0
- package/dist/standards/index.js +8 -0
- package/dist/standards/index.js.map +1 -0
- package/dist/standards/readiness.d.ts +11 -0
- package/dist/standards/readiness.d.ts.map +1 -0
- package/dist/standards/readiness.js +170 -0
- package/dist/standards/readiness.js.map +1 -0
- package/dist/standards/registry.d.ts +42 -0
- package/dist/standards/registry.d.ts.map +1 -0
- package/dist/standards/registry.js +330 -0
- package/dist/standards/registry.js.map +1 -0
- package/dist/standards/storage-reasons.d.ts +15 -0
- package/dist/standards/storage-reasons.d.ts.map +1 -0
- package/dist/standards/storage-reasons.js +48 -0
- package/dist/standards/storage-reasons.js.map +1 -0
- package/dist/standards/storage.d.ts +36 -0
- package/dist/standards/storage.d.ts.map +1 -0
- package/dist/standards/storage.js +330 -0
- package/dist/standards/storage.js.map +1 -0
- package/dist/standards/test-evidence.d.ts +19 -0
- package/dist/standards/test-evidence.d.ts.map +1 -0
- package/dist/standards/test-evidence.js +195 -0
- package/dist/standards/test-evidence.js.map +1 -0
- 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/.codex-plugin/skills/lisa-doctor/SKILL.md +21 -3
- package/plugins/lisa/scripts/doctor-report.mjs +266 -22
- package/plugins/lisa/skills/lisa-doctor/SKILL.md +21 -3
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/doctor-report.mjs +266 -22
- package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +21 -3
- 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/scripts/doctor-report.mjs +266 -22
- package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +21 -3
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/scripts/doctor-report.mjs +266 -22
- package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +21 -3
- 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/scripts/doctor-report.mjs +266 -22
- package/plugins/src/base/skills/lisa-doctor/SKILL.md +21 -3
- package/rails/copy-overwrite/lefthook.yml +9 -11
- package/rails/copy-overwrite/scripts/lisa-clean-git-env.sh +13 -0
- package/typescript/copy-contents/.husky/pre-push +9 -0
- package/ui/index.html +3 -2
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/** Confined, bounded, atomic storage for standards-conformance proof. */
|
|
2
|
+
import * as fse from "fs-extra";
|
|
3
|
+
import { constants as fsConstants } from "node:fs";
|
|
4
|
+
import { lstat, open, realpath, rename, rm } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { withFileTargetLock } from "../core/learnings-lock.js";
|
|
7
|
+
import { syncContainingDirectory } from "../health/directory-sync.js";
|
|
8
|
+
import { STANDARDS_PROOF_PATH, validateStandardsProof, } from "./contract.js";
|
|
9
|
+
import { MALFORMED_STANDARDS_PROOF_REASON, safeStandardsStorageReason, safeStandardsValidationReason, } from "./storage-reasons.js";
|
|
10
|
+
export const MAX_STANDARDS_PROOF_BYTES = 128 * 1024;
|
|
11
|
+
const PROOF_TOO_LARGE = "Standards proof exceeds 128 KiB";
|
|
12
|
+
const UNSAFE_PROOF_TARGET = "Unsafe standards proof target";
|
|
13
|
+
/**
|
|
14
|
+
* Read strict proof state without throwing for runtime-state faults.
|
|
15
|
+
* @param projectRoot - Project directory containing the proof
|
|
16
|
+
* @param now - Observation time for future-timestamp rejection
|
|
17
|
+
* @param dependencies - Test-only timing dependencies
|
|
18
|
+
* @returns Closed proof read result
|
|
19
|
+
*/
|
|
20
|
+
export async function readStandardsProof(projectRoot, now = new Date(), dependencies = {}) {
|
|
21
|
+
const payload = await readProofPayload(projectRoot, dependencies);
|
|
22
|
+
if (typeof payload !== "string")
|
|
23
|
+
return payload;
|
|
24
|
+
const parsed = parseProofPayload(payload);
|
|
25
|
+
if (parsed.status === "unreadable")
|
|
26
|
+
return parsed;
|
|
27
|
+
try {
|
|
28
|
+
return Object.freeze({
|
|
29
|
+
status: "available",
|
|
30
|
+
proof: validateStandardsProof(parsed.candidate, now),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return unreadable(safeStandardsValidationReason(error));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Parse a bounded proof payload into untrusted data.
|
|
39
|
+
* @param payload - UTF-8 JSON payload
|
|
40
|
+
* @returns Parsed candidate or a safe unreadable result
|
|
41
|
+
*/
|
|
42
|
+
function parseProofPayload(payload) {
|
|
43
|
+
try {
|
|
44
|
+
return Object.freeze({
|
|
45
|
+
status: "parsed",
|
|
46
|
+
candidate: JSON.parse(payload),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return unreadable(MALFORMED_STANDARDS_PROOF_REASON);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Read a proof payload while converting storage faults to closed results.
|
|
55
|
+
* @param projectRoot - Project directory containing the proof
|
|
56
|
+
* @param dependencies - Test-only timing dependencies
|
|
57
|
+
* @returns Raw payload or a closed proof read result
|
|
58
|
+
*/
|
|
59
|
+
async function readProofPayload(projectRoot, dependencies) {
|
|
60
|
+
try {
|
|
61
|
+
const target = await resolveReadTarget(projectRoot);
|
|
62
|
+
const payload = await readBoundedRegularFile(target, dependencies);
|
|
63
|
+
return payload === undefined
|
|
64
|
+
? Object.freeze({ status: "missing" })
|
|
65
|
+
: payload;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
return unreadable(safeStandardsStorageReason(error));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Construct an immutable unreadable result.
|
|
73
|
+
* @param reason - Sanitized operator-facing reason
|
|
74
|
+
* @returns Closed unreadable result
|
|
75
|
+
*/
|
|
76
|
+
function unreadable(reason) {
|
|
77
|
+
return Object.freeze({ status: "unreadable", reason });
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate and atomically replace the proof only after capture succeeds.
|
|
81
|
+
* @param projectRoot - Project directory that owns the proof
|
|
82
|
+
* @param candidate - Untrusted proof candidate
|
|
83
|
+
* @param now - Validation time for future-timestamp rejection
|
|
84
|
+
* @returns Stored path and immutable proof
|
|
85
|
+
*/
|
|
86
|
+
export async function writeStandardsProof(projectRoot, candidate, now = new Date()) {
|
|
87
|
+
const proof = validateStandardsProof(candidate, now);
|
|
88
|
+
const serialized = serialize(proof);
|
|
89
|
+
const { directory, target } = await resolveWriteTarget(projectRoot);
|
|
90
|
+
return await withFileTargetLock(target, async () => {
|
|
91
|
+
await assertSafeTarget(target);
|
|
92
|
+
const temporary = path.join(directory, `.${path.basename(target)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
93
|
+
try {
|
|
94
|
+
const handle = await open(temporary, "wx", 0o600);
|
|
95
|
+
try {
|
|
96
|
+
await handle.writeFile(serialized, "utf8");
|
|
97
|
+
await handle.sync();
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
await handle.close();
|
|
101
|
+
}
|
|
102
|
+
await assertSafeTarget(target);
|
|
103
|
+
await rename(temporary, target);
|
|
104
|
+
await syncContainingDirectory(directory);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
await rm(temporary, { force: true });
|
|
108
|
+
}
|
|
109
|
+
return Object.freeze({ path: target, proof });
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Serialize a proof within its storage budget.
|
|
114
|
+
* @param proof - Validated proof
|
|
115
|
+
* @returns Canonical JSON file payload
|
|
116
|
+
*/
|
|
117
|
+
function serialize(proof) {
|
|
118
|
+
const payload = `${JSON.stringify(proof, null, 2)}\n`;
|
|
119
|
+
if (Buffer.byteLength(payload, "utf8") > MAX_STANDARDS_PROOF_BYTES) {
|
|
120
|
+
throw new Error(PROOF_TOO_LARGE);
|
|
121
|
+
}
|
|
122
|
+
return payload;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Resolve and validate a real project directory.
|
|
126
|
+
* @param projectRoot - Candidate project directory
|
|
127
|
+
* @returns Canonical project directory
|
|
128
|
+
*/
|
|
129
|
+
async function requireProjectRoot(projectRoot) {
|
|
130
|
+
const root = await realpath(path.resolve(projectRoot));
|
|
131
|
+
const metadata = await lstat(root);
|
|
132
|
+
if (!metadata.isDirectory())
|
|
133
|
+
throw new Error("Project root is not a directory");
|
|
134
|
+
return root;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Resolve a confined proof read target without creating directories.
|
|
138
|
+
* @param projectRoot - Project directory containing the proof
|
|
139
|
+
* @returns Confined proof path
|
|
140
|
+
*/
|
|
141
|
+
async function resolveReadTarget(projectRoot) {
|
|
142
|
+
const root = await requireProjectRoot(projectRoot);
|
|
143
|
+
for (const directory of [
|
|
144
|
+
path.join(root, ".lisa"),
|
|
145
|
+
path.join(root, ".lisa", "standards"),
|
|
146
|
+
]) {
|
|
147
|
+
if ((await statPath(directory)) !== undefined) {
|
|
148
|
+
await assertConfinedDirectory(root, directory);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return path.join(root, STANDARDS_PROOF_PATH);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Resolve and prepare a confined proof write target.
|
|
155
|
+
* @param projectRoot - Project directory that owns the proof
|
|
156
|
+
* @returns Confined storage directory and proof path
|
|
157
|
+
*/
|
|
158
|
+
async function resolveWriteTarget(projectRoot) {
|
|
159
|
+
const root = await requireProjectRoot(projectRoot);
|
|
160
|
+
const lisa = path.join(root, ".lisa");
|
|
161
|
+
const directory = path.join(lisa, "standards");
|
|
162
|
+
await ensureSafeDirectory(root, lisa);
|
|
163
|
+
await ensureSafeDirectory(root, directory);
|
|
164
|
+
return { directory, target: path.join(directory, "latest.json") };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Create a directory only through a confined real parent.
|
|
168
|
+
* @param root - Canonical project directory
|
|
169
|
+
* @param directory - Directory to validate or create
|
|
170
|
+
*/
|
|
171
|
+
async function ensureSafeDirectory(root, directory) {
|
|
172
|
+
if ((await statPath(directory)) !== undefined) {
|
|
173
|
+
await assertConfinedDirectory(root, directory);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
await assertConfinedDirectory(root, path.dirname(directory));
|
|
177
|
+
await fse.ensureDir(directory);
|
|
178
|
+
await assertConfinedDirectory(root, directory);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Require a real directory confined beneath the project root.
|
|
182
|
+
* @param root - Canonical project directory
|
|
183
|
+
* @param directory - Directory to validate
|
|
184
|
+
*/
|
|
185
|
+
async function assertConfinedDirectory(root, directory) {
|
|
186
|
+
const metadata = await lstat(directory);
|
|
187
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
188
|
+
throw new Error("Unsafe standards storage parent");
|
|
189
|
+
}
|
|
190
|
+
const actual = await realpath(directory);
|
|
191
|
+
if (actual !== root && !actual.startsWith(`${root}${path.sep}`)) {
|
|
192
|
+
throw new Error("Unsafe standards storage parent escapes project root");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Reject an existing proof target unless it is a regular file.
|
|
197
|
+
* @param target - Proof path to validate
|
|
198
|
+
*/
|
|
199
|
+
async function assertSafeTarget(target) {
|
|
200
|
+
try {
|
|
201
|
+
const metadata = await lstat(target);
|
|
202
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
203
|
+
throw new Error(UNSAFE_PROOF_TARGET);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (error.code !== "ENOENT")
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Open and read one bounded regular file without following symlinks.
|
|
213
|
+
* @param target - Proof path to read
|
|
214
|
+
* @param dependencies - Test-only timing dependencies
|
|
215
|
+
* @returns UTF-8 payload, or undefined when the path is absent
|
|
216
|
+
*/
|
|
217
|
+
async function readBoundedRegularFile(target, dependencies) {
|
|
218
|
+
try {
|
|
219
|
+
const before = await lstat(target);
|
|
220
|
+
assertReadableMetadata(before);
|
|
221
|
+
const handle = await open(target, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK);
|
|
222
|
+
try {
|
|
223
|
+
return await readOpenedProof(handle, target, before, dependencies);
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
await handle.close();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
if (error.code === "ENOENT")
|
|
231
|
+
return undefined;
|
|
232
|
+
if (["ELOOP", "ENXIO", "EAGAIN"].includes(error.code ?? "")) {
|
|
233
|
+
throw new Error(UNSAFE_PROOF_TARGET);
|
|
234
|
+
}
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Require regular-file metadata within the proof size budget.
|
|
240
|
+
* @param metadata - Filesystem metadata to validate
|
|
241
|
+
*/
|
|
242
|
+
function assertReadableMetadata(metadata) {
|
|
243
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
244
|
+
throw new Error(UNSAFE_PROOF_TARGET);
|
|
245
|
+
}
|
|
246
|
+
if (metadata.size > MAX_STANDARDS_PROOF_BYTES) {
|
|
247
|
+
throw new Error(PROOF_TOO_LARGE);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Pin an opened proof descriptor before reading its contents.
|
|
252
|
+
* @param handle - Open no-follow file handle
|
|
253
|
+
* @param target - Proof path used for identity checks
|
|
254
|
+
* @param before - Metadata observed before opening
|
|
255
|
+
* @param dependencies - Test-only timing dependencies
|
|
256
|
+
* @returns Stable UTF-8 payload
|
|
257
|
+
*/
|
|
258
|
+
async function readOpenedProof(handle, target, before, dependencies) {
|
|
259
|
+
const opened = await handle.stat();
|
|
260
|
+
const bytes = Buffer.alloc(MAX_STANDARDS_PROOF_BYTES + 1);
|
|
261
|
+
assertSameFile(before, opened);
|
|
262
|
+
await dependencies.afterOpen?.(target);
|
|
263
|
+
return await finishOpenedProofRead(handle, target, opened, bytes);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Read an opened proof and verify its descriptor and path remain stable.
|
|
267
|
+
* @param handle - Open proof file handle
|
|
268
|
+
* @param target - Proof path used for identity checks
|
|
269
|
+
* @param opened - Metadata pinned immediately after opening
|
|
270
|
+
* @param bytes - Fixed-size destination buffer
|
|
271
|
+
* @returns Stable UTF-8 payload
|
|
272
|
+
*/
|
|
273
|
+
async function finishOpenedProofRead(handle, target, opened, bytes) {
|
|
274
|
+
const total = await readDescriptorBytes(handle, bytes);
|
|
275
|
+
if (total > MAX_STANDARDS_PROOF_BYTES)
|
|
276
|
+
throw new Error(PROOF_TOO_LARGE);
|
|
277
|
+
const [afterOpened, afterPath] = await Promise.all([
|
|
278
|
+
handle.stat(),
|
|
279
|
+
lstat(target),
|
|
280
|
+
]);
|
|
281
|
+
assertSameFile(opened, afterOpened);
|
|
282
|
+
assertSameFile(opened, afterPath);
|
|
283
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, total));
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Require two metadata snapshots to identify the same unchanged file.
|
|
287
|
+
* @param expected - Pinned metadata snapshot
|
|
288
|
+
* @param actual - Later metadata snapshot
|
|
289
|
+
*/
|
|
290
|
+
function assertSameFile(expected, actual) {
|
|
291
|
+
if (!actual.isFile() ||
|
|
292
|
+
actual.dev !== expected.dev ||
|
|
293
|
+
actual.ino !== expected.ino ||
|
|
294
|
+
actual.size !== expected.size) {
|
|
295
|
+
throw new Error("Standards proof changed during read");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Fill a fixed-size buffer from an opened descriptor.
|
|
300
|
+
* @param handle - Open proof file handle
|
|
301
|
+
* @param bytes - Destination buffer
|
|
302
|
+
* @returns Number of bytes read
|
|
303
|
+
*/
|
|
304
|
+
async function readDescriptorBytes(handle, bytes) {
|
|
305
|
+
// eslint-disable-next-line functional/no-let -- fixed-size descriptor read advances one bounded offset
|
|
306
|
+
let total = 0;
|
|
307
|
+
while (total < bytes.byteLength) {
|
|
308
|
+
const chunk = await handle.read(bytes, total, bytes.byteLength - total, total);
|
|
309
|
+
if (chunk.bytesRead === 0)
|
|
310
|
+
break;
|
|
311
|
+
total += chunk.bytesRead;
|
|
312
|
+
}
|
|
313
|
+
return total;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Read path metadata while treating absence as undefined.
|
|
317
|
+
* @param filePath - Filesystem path to inspect
|
|
318
|
+
* @returns Metadata, or undefined when absent
|
|
319
|
+
*/
|
|
320
|
+
async function statPath(filePath) {
|
|
321
|
+
try {
|
|
322
|
+
return await lstat(filePath);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
if (error.code === "ENOENT")
|
|
326
|
+
return undefined;
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/standards/storage.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EACL,oBAAoB,EAEpB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gCAAgC,EAChC,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,GAAG,IAAI,CAAC;AACpD,MAAM,eAAe,GAAG,iCAAiC,CAAC;AAC1D,MAAM,mBAAmB,GAAG,+BAA+B,CAAC;AAe5D;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,MAAY,IAAI,IAAI,EAAE,EACtB,eAA+C,EAAE;IAEjD,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAClE,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,MAAM,CAAC,MAAM,KAAK,YAAY;QAAE,OAAO,MAAM,CAAC;IAClD,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,sBAAsB,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC;SACrD,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,UAAU,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,iBAAiB,CACxB,OAAe;IAIf,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,MAAM,EAAE,QAAQ;YAChB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY;SAC1C,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,UAAU,CAAC,gCAAgC,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,gBAAgB,CAC7B,WAAmB,EACnB,YAA4C;IAE5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACnE,OAAO,OAAO,KAAK,SAAS;YAC1B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;YACtC,CAAC,CAAC,OAAO,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,UAAU,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CACjB,MAAc;IAEd,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,WAAmB,EACnB,SAAkB,EAClB,MAAY,IAAI,IAAI,EAAE;IAEtB,MAAM,KAAK,GAAG,sBAAsB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACpE,OAAO,MAAM,kBAAkB,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CACzB,SAAS,EACT,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,MAAM,CACtE,CAAC;QACF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YAClD,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;gBAC3C,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACtB,CAAC;oBAAS,CAAC;gBACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACvB,CAAC;YACD,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;YAC/B,MAAM,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAChC,MAAM,uBAAuB,CAAC,SAAS,CAAC,CAAC;QAC3C,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,KAAqB;IACtC,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IACtD,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,yBAAyB,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,kBAAkB,CAAC,WAAmB;IACnD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE;QACzB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,iBAAiB,CAAC,WAAmB;IAClD,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACnD,KAAK,MAAM,SAAS,IAAI;QACtB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC;KACtC,EAAE,CAAC;QACF,IAAI,CAAC,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;YAC9C,MAAM,uBAAuB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,kBAAkB,CAC/B,WAAmB;IAEnB,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAC/C,MAAM,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,MAAM,mBAAmB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,CAAC;AACpE,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,mBAAmB,CAChC,IAAY,EACZ,SAAiB;IAEjB,IAAI,CAAC,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QAC9C,MAAM,uBAAuB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC/C,OAAO;IACT,CAAC;IACD,MAAM,uBAAuB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7D,MAAM,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,MAAM,uBAAuB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AACjD,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,uBAAuB,CACpC,IAAY,EACZ,SAAiB;IAEjB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,gBAAgB,CAAC,MAAc;IAC5C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IACtE,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,sBAAsB,CACnC,MAAc,EACd,YAA4C;IAE5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QACnC,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,IAAI,CACvB,MAAM,EACN,WAAW,CAAC,QAAQ,GAAG,WAAW,CAAC,UAAU,GAAG,WAAW,CAAC,UAAU,CACvE,CAAC;QACF,IAAI,CAAC;YACH,OAAO,MAAM,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;QACrE,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACzE,IACE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAClC,KAA+B,CAAC,IAAI,IAAI,EAAE,CAC5C,EACD,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,sBAAsB,CAAC,QAAsB;IACpD,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,GAAG,yBAAyB,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,eAAe,CAC5B,MAAwC,EACxC,MAAc,EACd,MAAoB,EACpB,YAA4C;IAE5C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,yBAAyB,GAAG,CAAC,CAAC,CAAC;IAC1D,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,YAAY,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,MAAM,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AACpE,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,qBAAqB,CAClC,MAAwC,EACxC,MAAc,EACd,MAAoB,EACpB,KAAa;IAEb,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACvD,IAAI,KAAK,GAAG,yBAAyB;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACxE,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACjD,MAAM,CAAC,IAAI,EAAE;QACb,KAAK,CAAC,MAAM,CAAC;KACd,CAAC,CAAC;IACH,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CACrD,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CACzB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,QAAsB,EAAE,MAAoB;IAClE,IACE,CAAC,MAAM,CAAC,MAAM,EAAE;QAChB,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG;QAC3B,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG;QAC3B,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,EAC7B,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,mBAAmB,CAChC,MAAwC,EACxC,KAAa;IAEb,uGAAuG;IACvG,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,KAAK,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAC7B,KAAK,EACL,KAAK,EACL,KAAK,CAAC,UAAU,GAAG,KAAK,EACxB,KAAK,CACN,CAAC;QACF,IAAI,KAAK,CAAC,SAAS,KAAK,CAAC;YAAE,MAAM;QACjC,KAAK,IAAI,KAAK,CAAC,SAAS,CAAC;IAC3B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,QAAQ,CACrB,QAAgB;IAEhB,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACzE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Positive, private-output evidence that a managed test runner executed tests. */
|
|
2
|
+
export declare const TEST_EVIDENCE_FORMATS: readonly ["vitest", "jest", "playwright", "rspec", "managed"];
|
|
3
|
+
/** Runner grammar used to prove that at least one test executed. */
|
|
4
|
+
export type TestEvidenceFormat = (typeof TEST_EVIDENCE_FORMATS)[number];
|
|
5
|
+
/**
|
|
6
|
+
* Require affirmative runner output for at least one executed test. A clean
|
|
7
|
+
* exit, an empty stream, or an all-skipped summary is never sufficient.
|
|
8
|
+
* @param format - Runner-specific or managed-union evidence grammar
|
|
9
|
+
* @param output - Bounded private stdout/stderr captured by the explicit CLI
|
|
10
|
+
* @returns Whether at least one test was positively reported as executed
|
|
11
|
+
*/
|
|
12
|
+
export declare function hasPositiveTestEvidence(format: TestEvidenceFormat, output: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Select the narrowest known parser from a managed package script.
|
|
15
|
+
* @param script - Package-script text read from the confined manifest
|
|
16
|
+
* @returns Positive-evidence grammar for its runner
|
|
17
|
+
*/
|
|
18
|
+
export declare function inferTestEvidenceFormat(script: string): TestEvidenceFormat;
|
|
19
|
+
//# sourceMappingURL=test-evidence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-evidence.d.ts","sourceRoot":"","sources":["../../src/standards/test-evidence.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAEnF,eAAO,MAAM,qBAAqB,+DAMxB,CAAC;AACX,oEAAoE;AACpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAMxE;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAgBT;AA6KD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAO1E"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/** Positive, private-output evidence that a managed test runner executed tests. */
|
|
2
|
+
export const TEST_EVIDENCE_FORMATS = [
|
|
3
|
+
"vitest",
|
|
4
|
+
"jest",
|
|
5
|
+
"playwright",
|
|
6
|
+
"rspec",
|
|
7
|
+
"managed",
|
|
8
|
+
];
|
|
9
|
+
const MAX_EVIDENCE_OUTPUT_CHARACTERS = 1024 * 1024;
|
|
10
|
+
const MAX_EVIDENCE_LINES = 4096;
|
|
11
|
+
const MAX_EVIDENCE_LINE_CHARACTERS = 4096;
|
|
12
|
+
/**
|
|
13
|
+
* Require affirmative runner output for at least one executed test. A clean
|
|
14
|
+
* exit, an empty stream, or an all-skipped summary is never sufficient.
|
|
15
|
+
* @param format - Runner-specific or managed-union evidence grammar
|
|
16
|
+
* @param output - Bounded private stdout/stderr captured by the explicit CLI
|
|
17
|
+
* @returns Whether at least one test was positively reported as executed
|
|
18
|
+
*/
|
|
19
|
+
export function hasPositiveTestEvidence(format, output) {
|
|
20
|
+
const lines = boundedTokenLines(output);
|
|
21
|
+
const runnerEvidence = lines.some(tokens => {
|
|
22
|
+
if (format === "vitest" || format === "jest") {
|
|
23
|
+
return isTestsSummary(tokens);
|
|
24
|
+
}
|
|
25
|
+
if (format === "playwright")
|
|
26
|
+
return isLeadingStatusSummary(tokens);
|
|
27
|
+
if (format === "rspec")
|
|
28
|
+
return isExamplesSummary(tokens);
|
|
29
|
+
return (isTestsSummary(tokens) ||
|
|
30
|
+
isLeadingStatusSummary(tokens) ||
|
|
31
|
+
isExamplesSummary(tokens) ||
|
|
32
|
+
isManagedTestSummary(tokens));
|
|
33
|
+
});
|
|
34
|
+
return runnerEvidence || (format === "managed" && isTapSummary(lines));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Split bounded output into bounded, punctuation-neutral token lines.
|
|
38
|
+
* @param output - Private runner output
|
|
39
|
+
* @returns Bounded token lines
|
|
40
|
+
*/
|
|
41
|
+
function boundedTokenLines(output) {
|
|
42
|
+
return output
|
|
43
|
+
.slice(0, MAX_EVIDENCE_OUTPUT_CHARACTERS)
|
|
44
|
+
.split("\n", MAX_EVIDENCE_LINES)
|
|
45
|
+
.map(line => tokenizeLine(line.slice(0, MAX_EVIDENCE_LINE_CHARACTERS)));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Convert one bounded ASCII runner line to lowercase alphanumeric tokens.
|
|
49
|
+
* @param line - One bounded runner line
|
|
50
|
+
* @returns Lowercase alphanumeric tokens
|
|
51
|
+
*/
|
|
52
|
+
function tokenizeLine(line) {
|
|
53
|
+
return Array.from(line, character => isAsciiAlphanumeric(character) ? character.toLowerCase() : " ")
|
|
54
|
+
.join("")
|
|
55
|
+
.split(" ")
|
|
56
|
+
.filter(Boolean);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Return whether one character is an ASCII letter or digit.
|
|
60
|
+
* @param character - Single input character
|
|
61
|
+
* @returns Whether the character is token-safe
|
|
62
|
+
*/
|
|
63
|
+
function isAsciiAlphanumeric(character) {
|
|
64
|
+
const code = character.charCodeAt(0);
|
|
65
|
+
return ((code >= 48 && code <= 57) ||
|
|
66
|
+
(code >= 65 && code <= 90) ||
|
|
67
|
+
(code >= 97 && code <= 122));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Return whether a token is a nonzero base-10 integer.
|
|
71
|
+
* @param token - Candidate numeric token
|
|
72
|
+
* @returns Whether the token is positive
|
|
73
|
+
*/
|
|
74
|
+
function isPositiveInteger(token) {
|
|
75
|
+
const value = nonnegativeIntegerValue(token);
|
|
76
|
+
return value !== undefined && value > 0;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse one bounded decimal token without accepting signs or fractions.
|
|
80
|
+
* @param token - Candidate count token
|
|
81
|
+
* @returns Safe nonnegative integer, when valid
|
|
82
|
+
*/
|
|
83
|
+
function nonnegativeIntegerValue(token) {
|
|
84
|
+
if (token === undefined || token.length === 0)
|
|
85
|
+
return undefined;
|
|
86
|
+
const digitsOnly = Array.from(token).every(character => {
|
|
87
|
+
const code = character.charCodeAt(0);
|
|
88
|
+
return code >= 48 && code <= 57;
|
|
89
|
+
});
|
|
90
|
+
if (!digitsOnly)
|
|
91
|
+
return undefined;
|
|
92
|
+
const value = Number(token);
|
|
93
|
+
return Number.isSafeInteger(value) ? value : undefined;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Recognize Vitest/Jest `Tests: N passed|failed` summary fragments.
|
|
97
|
+
* @param tokens - One bounded token line
|
|
98
|
+
* @returns Whether the line proves test execution
|
|
99
|
+
*/
|
|
100
|
+
function isTestsSummary(tokens) {
|
|
101
|
+
if (tokens[0] !== "tests")
|
|
102
|
+
return false;
|
|
103
|
+
return tokens.some((token, index) => isOutcome(token) && isPositiveInteger(tokens[index - 1]));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Recognize Playwright `N passed|failed` summary lines.
|
|
107
|
+
* @param tokens - One bounded token line
|
|
108
|
+
* @returns Whether the line proves test execution
|
|
109
|
+
*/
|
|
110
|
+
function isLeadingStatusSummary(tokens) {
|
|
111
|
+
return isPositiveInteger(tokens[0]) && isOutcome(tokens[1]);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Recognize RSpec `N examples` summary fragments.
|
|
115
|
+
* @param tokens - One bounded token line
|
|
116
|
+
* @returns Whether the line proves example execution
|
|
117
|
+
*/
|
|
118
|
+
function isExamplesSummary(tokens) {
|
|
119
|
+
const examples = countBeforeLabel(tokens, ["example", "examples"]);
|
|
120
|
+
const failures = countBeforeLabel(tokens, ["failure", "failures"]) ?? 0;
|
|
121
|
+
const pending = countBeforeLabel(tokens, ["pending"]) ?? 0;
|
|
122
|
+
return (examples !== undefined &&
|
|
123
|
+
examples > 0 &&
|
|
124
|
+
(failures > 0 || examples > pending));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Read the count immediately before a known summary label.
|
|
128
|
+
* @param tokens - One bounded token line
|
|
129
|
+
* @param labels - Accepted singular/plural labels
|
|
130
|
+
* @returns Parsed count, when present
|
|
131
|
+
*/
|
|
132
|
+
function countBeforeLabel(tokens, labels) {
|
|
133
|
+
const index = tokens.findIndex(token => labels.includes(token));
|
|
134
|
+
return index < 1 ? undefined : nonnegativeIntegerValue(tokens[index - 1]);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Correlate bounded TAP aggregate lines and reject explicitly all-skipped runs.
|
|
138
|
+
* @param lines - Bounded token lines
|
|
139
|
+
* @returns Whether TAP proves at least one actual execution
|
|
140
|
+
*/
|
|
141
|
+
function isTapSummary(lines) {
|
|
142
|
+
const tests = tapAggregate(lines, ["tests"]);
|
|
143
|
+
if (tests === undefined || tests === 0)
|
|
144
|
+
return false;
|
|
145
|
+
const passed = tapAggregate(lines, ["pass", "passed"]);
|
|
146
|
+
const failed = tapAggregate(lines, ["fail", "failed"]);
|
|
147
|
+
const executed = (passed ?? 0) + (failed ?? 0);
|
|
148
|
+
return executed > 0 && executed <= tests;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Read one exact two-token TAP aggregate line.
|
|
152
|
+
* @param lines - Bounded token lines
|
|
153
|
+
* @param labels - Accepted aggregate labels
|
|
154
|
+
* @returns Parsed aggregate count, when present
|
|
155
|
+
*/
|
|
156
|
+
function tapAggregate(lines, labels) {
|
|
157
|
+
const line = lines.find(tokens => tokens.length === 2 && labels.includes(tokens[0] ?? ""));
|
|
158
|
+
return line === undefined ? undefined : nonnegativeIntegerValue(line[1]);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Recognize generic managed-runner `N test(s) passed|failed` summaries.
|
|
162
|
+
* @param tokens - One bounded token line
|
|
163
|
+
* @returns Whether the line proves test execution
|
|
164
|
+
*/
|
|
165
|
+
function isManagedTestSummary(tokens) {
|
|
166
|
+
return (isPositiveInteger(tokens[0]) &&
|
|
167
|
+
(tokens[1] === "test" || tokens[1] === "tests") &&
|
|
168
|
+
isOutcome(tokens[2]));
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Return whether a summary token represents an executed test outcome.
|
|
172
|
+
* @param token - Candidate outcome token
|
|
173
|
+
* @returns Whether it is a pass/fail outcome
|
|
174
|
+
*/
|
|
175
|
+
function isOutcome(token) {
|
|
176
|
+
return token === "passed" || token === "failed";
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Select the narrowest known parser from a managed package script.
|
|
180
|
+
* @param script - Package-script text read from the confined manifest
|
|
181
|
+
* @returns Positive-evidence grammar for its runner
|
|
182
|
+
*/
|
|
183
|
+
export function inferTestEvidenceFormat(script) {
|
|
184
|
+
const normalized = script.toLowerCase();
|
|
185
|
+
if (normalized.includes("vitest"))
|
|
186
|
+
return "vitest";
|
|
187
|
+
if (normalized.includes("playwright"))
|
|
188
|
+
return "playwright";
|
|
189
|
+
if (normalized.includes("jest"))
|
|
190
|
+
return "jest";
|
|
191
|
+
if (normalized.includes("rspec"))
|
|
192
|
+
return "rspec";
|
|
193
|
+
return "managed";
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=test-evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-evidence.js","sourceRoot":"","sources":["../../src/standards/test-evidence.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"brace-expansion": ">=5.0.6"
|
|
113
113
|
},
|
|
114
114
|
"name": "@codyswann/lisa",
|
|
115
|
-
"version": "2.
|
|
115
|
+
"version": "2.282.0",
|
|
116
116
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
117
117
|
"main": "dist/index.js",
|
|
118
118
|
"exports": {
|
|
@@ -319,9 +319,27 @@ default doctor path never renders it and stays byte-identical.
|
|
|
319
319
|
contract; do not restate or fork that vocabulary here.
|
|
320
320
|
3. **`SKIP` carries a reason and is never blank.** A dimension with no applicable evidence renders
|
|
321
321
|
`SKIP` with a stated reason ("no deployment target configured, so delivery/authority was not
|
|
322
|
-
assessed"). An unassessed dimension is a known unknown, and the report says so.
|
|
323
|
-
|
|
324
|
-
|
|
322
|
+
assessed"). An unassessed dimension is a known unknown, and the report says so.
|
|
323
|
+
|
|
324
|
+
**This surface reflects the CLI; it does not re-score.** The evidence producers and the blocker
|
|
325
|
+
engine live in the Lisa CLI, which is the single source of truth. When `.lisa/readiness.json` is
|
|
326
|
+
present and readable (parses, matching `schema_version`, carries a `dimensions` array), this group
|
|
327
|
+
**projects** each recorded dimension's status and its operator-facing evidence/reason text, so an
|
|
328
|
+
operator running `/lisa:doctor` through any coding agent sees the same readiness answer the CLI
|
|
329
|
+
gives. When the report is absent, unparseable, or stamped with an unknown `schema_version` — or
|
|
330
|
+
records nothing for a given dimension — that dimension falls back to `SKIP` with the reason it was
|
|
331
|
+
not assessed. Absence means the readiness pass has not run, never that the repository is clean: a
|
|
332
|
+
pass or a fail is never manufactured from a missing report. Run
|
|
333
|
+
`lisa doctor --offline --readiness` to produce the report first.
|
|
334
|
+
|
|
335
|
+
**A standing blocker outranks the recorded status.** Some CLI producers record `WARN` while
|
|
336
|
+
standing a ship blocker — the blocker engine never reads the per-dimension status, so the finding
|
|
337
|
+
flips the repository to `NOT_READY` exactly as a `FAIL` would. A dimension that owns an entry in
|
|
338
|
+
the report's `blockers[]` therefore projects as `FAIL` regardless of its recorded label, and its
|
|
339
|
+
remediation names the blocker ids and repeats the report's `narrowed_claim` (the "IS ready for
|
|
340
|
+
supervised, single-ticket agent work" fallback `readiness-rubric` requires whenever a blocker
|
|
341
|
+
stands). Projected checks also carry the report's `generated_at`/`lisa_version` so a stale report
|
|
342
|
+
cannot read as current truth, and unassessed dimensions carry the CLI's not-established caveat.
|
|
325
343
|
4. **Reuse the shipped verdict ladder and consequence ordering.** No new verdict value and no new
|
|
326
344
|
severity: reuse `READY` / `READY_WITH_WARNINGS` / `NOT_READY`. `READY` requires *positive*
|
|
327
345
|
evidence — every readiness dimension assessed and clean, with no blocker standing. An unassessed
|