assertledger 1.3.1 → 1.5.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/README.fr.md +6 -3
- package/README.md +6 -3
- package/conformance/schema-extensions.json +15 -0
- package/dist/build-info.json +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +218 -8
- package/dist/cli.js.map +1 -1
- package/dist/contracts/index.d.ts +467 -4
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +256 -3
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/qualification.d.ts +528 -0
- package/dist/contracts/qualification.d.ts.map +1 -0
- package/dist/contracts/qualification.js +357 -0
- package/dist/contracts/qualification.js.map +1 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +46 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/qualification.d.ts +17 -0
- package/dist/core/qualification.d.ts.map +1 -0
- package/dist/core/qualification.js +571 -0
- package/dist/core/qualification.js.map +1 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +81 -4
- package/dist/diagnostics.js.map +1 -1
- package/dist/engine/adapters/bun-native.d.ts +35 -0
- package/dist/engine/adapters/bun-native.d.ts.map +1 -0
- package/dist/engine/adapters/bun-native.js +179 -0
- package/dist/engine/adapters/bun-native.js.map +1 -0
- package/dist/engine/adapters/bun-test-designated-profile.d.ts +23 -0
- package/dist/engine/adapters/bun-test-designated-profile.d.ts.map +1 -0
- package/dist/engine/adapters/bun-test-designated-profile.js +24 -0
- package/dist/engine/adapters/bun-test-designated-profile.js.map +1 -0
- package/dist/engine/container.d.ts +7 -0
- package/dist/engine/container.d.ts.map +1 -1
- package/dist/engine/container.js +22 -7
- package/dist/engine/container.js.map +1 -1
- package/dist/engine/git-regression.d.ts.map +1 -1
- package/dist/engine/git-regression.js +9 -2
- package/dist/engine/git-regression.js.map +1 -1
- package/dist/engine/index.d.ts +14 -0
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +828 -97
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/qualification.d.ts +154 -0
- package/dist/engine/qualification.d.ts.map +1 -0
- package/dist/engine/qualification.js +725 -0
- package/dist/engine/qualification.js.map +1 -0
- package/dist/engine/test-closure.d.ts +39 -0
- package/dist/engine/test-closure.d.ts.map +1 -0
- package/dist/engine/test-closure.js +513 -0
- package/dist/engine/test-closure.js.map +1 -0
- package/dist/engine/witness-import.d.ts +31 -0
- package/dist/engine/witness-import.d.ts.map +1 -0
- package/dist/engine/witness-import.js +224 -0
- package/dist/engine/witness-import.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +65 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/sdk/index.d.ts +292 -3
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +39 -1
- package/dist/sdk/index.js.map +1 -1
- package/docs/container-isolation.md +6 -0
- package/docs/migration-orchestration-qualification-v1.md +109 -0
- package/docs/migration-verification-v4.md +94 -0
- package/docs/orchestration-qualification.md +167 -0
- package/docs/reference.md +13 -0
- package/docs/witness-import.md +67 -0
- package/examples/orchestration/README.md +68 -0
- package/examples/orchestration/consumer-obligations.json +101 -0
- package/examples/orchestration/workspace/bitbucket-pipelines.yml +10 -0
- package/examples/orchestration/workspace/command-shape.test.mjs +25 -0
- package/examples/orchestration/workspace/commands.mjs +14 -0
- package/examples/orchestration/workspace/completion.mjs +39 -0
- package/examples/orchestration/workspace/faults.json +1 -0
- package/examples/orchestration/workspace/native.test.ts +4 -0
- package/examples/orchestration/workspace/package.json +6 -0
- package/examples/orchestration/workspace/packages/app/package.json +12 -0
- package/examples/orchestration/workspace/packages/app/verdict.txt +1 -0
- package/examples/orchestration/workspace/packages/leaf/package.json +9 -0
- package/examples/orchestration/workspace/packages/leaf/verdict.txt +1 -0
- package/examples/orchestration/workspace/pnpm-lock.yaml +170 -0
- package/examples/orchestration/workspace/pnpm-workspace.yaml +2 -0
- package/examples/orchestration/workspace/task.mjs +23 -0
- package/examples/orchestration/workspace/turbo.json +38 -0
- package/examples/orchestration/workspace/wrapper.mjs +45 -0
- package/integrations/bun/driver.d.mts +30 -1
- package/integrations/bun/driver.mjs +215 -6
- package/integrations/bun/preload.mjs +186 -5
- package/integrations/bun-native/README.md +86 -0
- package/integrations/bun-native/driver.mjs +396 -0
- package/integrations/bun-native/preload.mjs +375 -0
- package/integrations/skill/SKILL.md +25 -5
- package/package.json +1 -1
- package/schemas/evidence-manifest.v4.json +995 -0
- package/schemas/qualification-execution-request.v1.json +571 -0
- package/schemas/qualification-plan.v1.json +528 -0
- package/schemas/qualification-receipt.v1.json +821 -0
- package/schemas/qualification-replay-result.v1.json +37 -0
- package/schemas/verification-request.v4.json +590 -0
- package/schemas/witness-import-request.v1.json +231 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import * as bunTest from "bun:test";
|
|
2
|
+
import nodeAssert from "node:assert/strict";
|
|
3
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
+
import { createHmac, randomUUID } from "node:crypto";
|
|
5
|
+
import { closeSync, readSync, writeSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import * as nodeTest from "node:test";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { types } from "node:util";
|
|
10
|
+
|
|
11
|
+
const root = process.env.ASSERTLEDGER_BUN_ROOT;
|
|
12
|
+
if (!path.isAbsolute(root ?? "")) throw new Error("ASSERTLEDGER_BUN_PRELOAD_CONFIGURATION_INVALID");
|
|
13
|
+
const preloadPath = fileURLToPath(import.meta.url);
|
|
14
|
+
const safeApply = Reflect.apply.bind(Reflect);
|
|
15
|
+
const safeGet = Reflect.get.bind(Reflect);
|
|
16
|
+
const issuedDuringTest = new WeakMap();
|
|
17
|
+
const markIssuingTest = WeakMap.prototype.set.bind(issuedDuringTest);
|
|
18
|
+
const issuingTest = WeakMap.prototype.get.bind(issuedDuringTest);
|
|
19
|
+
const activeTestExecution = new AsyncLocalStorage();
|
|
20
|
+
const currentTestId = AsyncLocalStorage.prototype.getStore.bind(activeTestExecution);
|
|
21
|
+
const runInTest = AsyncLocalStorage.prototype.run.bind(activeTestExecution);
|
|
22
|
+
const supportedMatchers = new Set([
|
|
23
|
+
"toBe",
|
|
24
|
+
"toEqual",
|
|
25
|
+
"toStrictEqual",
|
|
26
|
+
"toHaveProperty",
|
|
27
|
+
"toThrow",
|
|
28
|
+
]);
|
|
29
|
+
const supportedNodeAssertions = new Set(["ok", "equal", "strictEqual"]);
|
|
30
|
+
const isProxy = types.isProxy.bind(types);
|
|
31
|
+
const descriptorsOf = Object.getOwnPropertyDescriptors.bind(Object);
|
|
32
|
+
const prototypeOf = Object.getPrototypeOf.bind(Object);
|
|
33
|
+
function refuse() {
|
|
34
|
+
throw new Error("ASSERTLEDGER_BUN_NATIVE_ACTIVE_OR_UNQUALIFIED_INPUT");
|
|
35
|
+
}
|
|
36
|
+
function passive(value, seen = new WeakSet(), depth = 0) {
|
|
37
|
+
if (typeof value === "function") refuse();
|
|
38
|
+
if (value === null || typeof value !== "object") return;
|
|
39
|
+
if (depth > 64 || isProxy(value)) refuse();
|
|
40
|
+
if (seen.has(value)) return;
|
|
41
|
+
seen.add(value);
|
|
42
|
+
const prototype = prototypeOf(value);
|
|
43
|
+
if (prototype !== Object.prototype && prototype !== Array.prototype && prototype !== null)
|
|
44
|
+
refuse();
|
|
45
|
+
const descriptors = descriptorsOf(value);
|
|
46
|
+
for (const descriptor of Reflect.ownKeys(descriptors).map((key) => descriptors[key])) {
|
|
47
|
+
if (!("value" in descriptor)) refuse();
|
|
48
|
+
passive(descriptor.value, seen, depth + 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function passiveThrown(value) {
|
|
52
|
+
if (value === null || typeof value !== "object") return passive(value);
|
|
53
|
+
if (isProxy(value) || prototypeOf(value) !== Error.prototype) refuse();
|
|
54
|
+
const descriptors = descriptorsOf(value);
|
|
55
|
+
for (const descriptor of Reflect.ownKeys(descriptors).map((key) => descriptors[key])) {
|
|
56
|
+
if (!("value" in descriptor)) refuse();
|
|
57
|
+
passive(descriptor.value);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function validateMatcher(name, arguments_, operand, modifier) {
|
|
61
|
+
if (!supportedMatchers.has(name)) refuse();
|
|
62
|
+
if (name === "toThrow") {
|
|
63
|
+
if (arguments_.length > 1 || (arguments_.length === 1 && typeof arguments_[0] !== "string"))
|
|
64
|
+
refuse();
|
|
65
|
+
if (modifier !== "rejects" && (typeof operand.actual !== "function" || isProxy(operand.actual)))
|
|
66
|
+
refuse();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (name === "toHaveProperty") {
|
|
70
|
+
if (
|
|
71
|
+
arguments_.length < 1 ||
|
|
72
|
+
arguments_.length > 2 ||
|
|
73
|
+
typeof arguments_[0] !== "string" ||
|
|
74
|
+
!/^[A-Za-z_$][A-Za-z0-9_$]*(\.[A-Za-z_$][A-Za-z0-9_$]*)*$/u.test(arguments_[0]) ||
|
|
75
|
+
arguments_[0]
|
|
76
|
+
.split(".")
|
|
77
|
+
.some((key) => ["__proto__", "constructor", "prototype"].includes(key))
|
|
78
|
+
)
|
|
79
|
+
refuse();
|
|
80
|
+
if (arguments_.length === 2) passive(arguments_[1]);
|
|
81
|
+
} else {
|
|
82
|
+
if (arguments_.length !== 1) refuse();
|
|
83
|
+
passive(arguments_[0]);
|
|
84
|
+
}
|
|
85
|
+
if (operand.promise === undefined) passive(operand.actual);
|
|
86
|
+
else if (modifier !== "resolves" && modifier !== "rejects") refuse();
|
|
87
|
+
}
|
|
88
|
+
function wrapExpectation(expectation, operand, modifier) {
|
|
89
|
+
return new Proxy(expectation, {
|
|
90
|
+
get(target, property) {
|
|
91
|
+
if (
|
|
92
|
+
property !== "not" &&
|
|
93
|
+
property !== "resolves" &&
|
|
94
|
+
property !== "rejects" &&
|
|
95
|
+
!supportedMatchers.has(property)
|
|
96
|
+
)
|
|
97
|
+
refuse();
|
|
98
|
+
const value = safeGet(target, property, target);
|
|
99
|
+
if (property === "not" || property === "resolves" || property === "rejects")
|
|
100
|
+
return wrapExpectation(value, operand, property === "not" ? modifier : property);
|
|
101
|
+
return (...arguments_) => {
|
|
102
|
+
validateMatcher(property, arguments_, operand, modifier);
|
|
103
|
+
const id = currentTestId();
|
|
104
|
+
const failed = (error) => {
|
|
105
|
+
if (
|
|
106
|
+
id !== undefined &&
|
|
107
|
+
error !== null &&
|
|
108
|
+
typeof error === "object" &&
|
|
109
|
+
!(error instanceof TypeError) &&
|
|
110
|
+
!(error instanceof RangeError) &&
|
|
111
|
+
!operand.invalidOperand &&
|
|
112
|
+
!operand.userErrors.has(error)
|
|
113
|
+
)
|
|
114
|
+
markIssuingTest(error, id);
|
|
115
|
+
throw error;
|
|
116
|
+
};
|
|
117
|
+
const invoke = () => {
|
|
118
|
+
try {
|
|
119
|
+
const result = safeApply(value, target, arguments_);
|
|
120
|
+
if (operand.invalidOperand) refuse();
|
|
121
|
+
return result && typeof result.then === "function"
|
|
122
|
+
? Promise.resolve(result).catch(failed)
|
|
123
|
+
: result;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return failed(error);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
return operand.promise !== undefined && modifier === "resolves"
|
|
129
|
+
? operand.promise.then((actual) => {
|
|
130
|
+
passive(actual);
|
|
131
|
+
return invoke();
|
|
132
|
+
})
|
|
133
|
+
: operand.promise !== undefined && modifier === "rejects"
|
|
134
|
+
? operand.promise.then(
|
|
135
|
+
() => invoke(),
|
|
136
|
+
(error) => {
|
|
137
|
+
passiveThrown(error);
|
|
138
|
+
return invoke();
|
|
139
|
+
},
|
|
140
|
+
)
|
|
141
|
+
: invoke();
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const nativeExpect = new Proxy(bunTest.expect, {
|
|
147
|
+
get() {
|
|
148
|
+
refuse();
|
|
149
|
+
},
|
|
150
|
+
apply(target, thisArg, arguments_) {
|
|
151
|
+
if (arguments_.length !== 1 || isProxy(arguments_[0])) refuse();
|
|
152
|
+
const operand = {
|
|
153
|
+
actual: arguments_[0],
|
|
154
|
+
promise: undefined,
|
|
155
|
+
userErrors: new WeakSet(),
|
|
156
|
+
invalidOperand: false,
|
|
157
|
+
};
|
|
158
|
+
if (types.isPromise(operand.actual)) {
|
|
159
|
+
if (prototypeOf(operand.actual) !== Promise.prototype) refuse();
|
|
160
|
+
operand.promise = operand.actual;
|
|
161
|
+
}
|
|
162
|
+
const controlledArguments = [...arguments_];
|
|
163
|
+
if (typeof operand.actual === "function") {
|
|
164
|
+
controlledArguments[0] = (...callbackArguments) => {
|
|
165
|
+
try {
|
|
166
|
+
const result = safeApply(operand.actual, undefined, callbackArguments);
|
|
167
|
+
try {
|
|
168
|
+
passive(result);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
operand.invalidOperand = true;
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (error !== null && typeof error === "object") operand.userErrors.add(error);
|
|
176
|
+
try {
|
|
177
|
+
passiveThrown(error);
|
|
178
|
+
} catch (failure) {
|
|
179
|
+
operand.invalidOperand = true;
|
|
180
|
+
throw failure;
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return wrapExpectation(safeApply(target, thisArg, controlledArguments), operand, undefined);
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
const AssertionError = nodeAssert.AssertionError;
|
|
190
|
+
const assertionCache = new WeakMap();
|
|
191
|
+
function wrapNodeAssertion(native, name = "ok") {
|
|
192
|
+
if (assertionCache.has(native)) return assertionCache.get(native);
|
|
193
|
+
const wrapper = new Proxy(native, {
|
|
194
|
+
apply(target, thisArg, arguments_) {
|
|
195
|
+
if (!supportedNodeAssertions.has(name)) refuse();
|
|
196
|
+
if (
|
|
197
|
+
arguments_.length < (name === "ok" ? 1 : 2) ||
|
|
198
|
+
arguments_.length > (name === "ok" ? 2 : 3)
|
|
199
|
+
)
|
|
200
|
+
refuse();
|
|
201
|
+
for (const argument of arguments_) passive(argument);
|
|
202
|
+
const id = currentTestId();
|
|
203
|
+
try {
|
|
204
|
+
return safeApply(target, thisArg, arguments_);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (id !== undefined && error instanceof AssertionError) markIssuingTest(error, id);
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
get(target, property, receiver) {
|
|
211
|
+
const value = safeGet(target, property, receiver);
|
|
212
|
+
return typeof value === "function" &&
|
|
213
|
+
property !== "AssertionError" &&
|
|
214
|
+
property !== "constructor"
|
|
215
|
+
? wrapNodeAssertion(value, property === "strict" ? "ok" : property)
|
|
216
|
+
: value;
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
assertionCache.set(native, wrapper);
|
|
220
|
+
return wrapper;
|
|
221
|
+
}
|
|
222
|
+
const stringify = JSON.stringify.bind(JSON);
|
|
223
|
+
const evidenceKey = Buffer.alloc(32);
|
|
224
|
+
let received = 0;
|
|
225
|
+
while (received < evidenceKey.length) {
|
|
226
|
+
const length = readSync(4, evidenceKey, received, evidenceKey.length - received, null);
|
|
227
|
+
if (length === 0) throw new Error("ASSERTLEDGER_BUN_EVIDENCE_KEY_MISSING");
|
|
228
|
+
received += length;
|
|
229
|
+
}
|
|
230
|
+
closeSync(4);
|
|
231
|
+
function record(event) {
|
|
232
|
+
const body = stringify(event);
|
|
233
|
+
const mac = createHmac("sha256", evidenceKey).update(body).digest("hex");
|
|
234
|
+
writeSync(3, `${stringify({ event, mac })}\n`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function registrationFile() {
|
|
238
|
+
const lines = new Error().stack?.split(/\r?\n/u).slice(1) ?? [];
|
|
239
|
+
for (const line of lines) {
|
|
240
|
+
let location = line.trim().replace(/^at\s+/u, "");
|
|
241
|
+
if (location.endsWith(")") && location.includes("(")) {
|
|
242
|
+
location = location.slice(location.lastIndexOf("(") + 1, -1);
|
|
243
|
+
}
|
|
244
|
+
const match = location.match(/^(.+?):\d+(?::\d+)?$/u);
|
|
245
|
+
if (match === null) continue;
|
|
246
|
+
let file = match[1];
|
|
247
|
+
if (file.startsWith("file:")) {
|
|
248
|
+
try {
|
|
249
|
+
file = fileURLToPath(file);
|
|
250
|
+
} catch {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (!path.isAbsolute(file)) continue;
|
|
255
|
+
const absolute = path.resolve(file);
|
|
256
|
+
if (absolute === preloadPath) continue;
|
|
257
|
+
return absolute;
|
|
258
|
+
}
|
|
259
|
+
throw new Error("ASSERTLEDGER_BUN_REGISTRATION_FILE_UNAVAILABLE");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function wrapRegistration(native, cache) {
|
|
263
|
+
if (cache.has(native)) return cache.get(native);
|
|
264
|
+
const wrapper = new Proxy(native, {
|
|
265
|
+
apply(target, thisArg, arguments_) {
|
|
266
|
+
const callbackIndex = arguments_.findIndex(
|
|
267
|
+
(value, index) => typeof value === "function" && (index > 0 || arguments_.length === 1),
|
|
268
|
+
);
|
|
269
|
+
if (callbackIndex < 0) {
|
|
270
|
+
const result = safeApply(target, thisArg, arguments_);
|
|
271
|
+
return typeof result === "function" ? wrapRegistration(result, cache) : result;
|
|
272
|
+
}
|
|
273
|
+
const callback = arguments_[callbackIndex];
|
|
274
|
+
const file = registrationFile();
|
|
275
|
+
const wrappedArguments = [...arguments_];
|
|
276
|
+
wrappedArguments[callbackIndex] = function (...callbackArguments) {
|
|
277
|
+
const id = randomUUID();
|
|
278
|
+
record({ kind: "found", id, file });
|
|
279
|
+
return runInTest(id, () => {
|
|
280
|
+
const passed = (value) => {
|
|
281
|
+
record({ kind: "end", id, status: "pass" });
|
|
282
|
+
return value;
|
|
283
|
+
};
|
|
284
|
+
const failed = (error) => {
|
|
285
|
+
record({
|
|
286
|
+
kind: "end",
|
|
287
|
+
id,
|
|
288
|
+
status: "fail",
|
|
289
|
+
owned: issuingTest(error) === id,
|
|
290
|
+
});
|
|
291
|
+
throw error;
|
|
292
|
+
};
|
|
293
|
+
try {
|
|
294
|
+
const result = safeApply(callback, this, callbackArguments);
|
|
295
|
+
return result && typeof result.then === "function"
|
|
296
|
+
? Promise.resolve(result).then(passed, failed)
|
|
297
|
+
: passed(result);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return failed(error);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
};
|
|
303
|
+
return safeApply(target, thisArg, wrappedArguments);
|
|
304
|
+
},
|
|
305
|
+
get(target, property, receiver) {
|
|
306
|
+
const value = safeGet(target, property, receiver);
|
|
307
|
+
return typeof value === "function" && property !== "constructor"
|
|
308
|
+
? wrapRegistration(value.bind(target), cache)
|
|
309
|
+
: value;
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
cache.set(native, wrapper);
|
|
313
|
+
return wrapper;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function wrapHook(nativeHook) {
|
|
317
|
+
return (callback, ...options) => {
|
|
318
|
+
if (typeof callback !== "function")
|
|
319
|
+
return safeApply(nativeHook, bunTest, [callback, ...options]);
|
|
320
|
+
const wrappedCallback = function (...arguments_) {
|
|
321
|
+
const failed = (error) => {
|
|
322
|
+
record({ kind: "hook-error" });
|
|
323
|
+
throw error;
|
|
324
|
+
};
|
|
325
|
+
try {
|
|
326
|
+
const result = safeApply(callback, this, arguments_);
|
|
327
|
+
return result && typeof result.then === "function"
|
|
328
|
+
? Promise.resolve(result).catch(failed)
|
|
329
|
+
: result;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
return failed(error);
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
return safeApply(nativeHook, bunTest, [wrappedCallback, ...options]);
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const cache = new WeakMap();
|
|
339
|
+
const wrappedTest = wrapRegistration(bunTest.test, cache);
|
|
340
|
+
const wrappedIt = wrapRegistration(bunTest.it, cache);
|
|
341
|
+
bunTest.mock.module("bun:test", () => ({
|
|
342
|
+
...bunTest,
|
|
343
|
+
expect: nativeExpect,
|
|
344
|
+
test: wrappedTest,
|
|
345
|
+
it: wrappedIt,
|
|
346
|
+
beforeAll: wrapHook(bunTest.beforeAll),
|
|
347
|
+
afterAll: wrapHook(bunTest.afterAll),
|
|
348
|
+
beforeEach: wrapHook(bunTest.beforeEach),
|
|
349
|
+
afterEach: wrapHook(bunTest.afterEach),
|
|
350
|
+
}));
|
|
351
|
+
const wrappedNodeTest = wrapRegistration(nodeTest.test, cache);
|
|
352
|
+
bunTest.mock.module("node:test", () => ({
|
|
353
|
+
...nodeTest,
|
|
354
|
+
default: wrappedNodeTest,
|
|
355
|
+
test: wrappedNodeTest,
|
|
356
|
+
it: wrapRegistration(nodeTest.it, cache),
|
|
357
|
+
before: wrapHook(nodeTest.before),
|
|
358
|
+
after: wrapHook(nodeTest.after),
|
|
359
|
+
beforeEach: wrapHook(nodeTest.beforeEach),
|
|
360
|
+
afterEach: wrapHook(nodeTest.afterEach),
|
|
361
|
+
}));
|
|
362
|
+
const controlledNodeAssert = wrapNodeAssertion(nodeAssert);
|
|
363
|
+
bunTest.mock.module("node:assert/strict", () => ({
|
|
364
|
+
...nodeAssert,
|
|
365
|
+
...Object.fromEntries(
|
|
366
|
+
Object.entries(nodeAssert).map(([name, value]) => [
|
|
367
|
+
name,
|
|
368
|
+
typeof value === "function" && name !== "AssertionError"
|
|
369
|
+
? wrapNodeAssertion(value, name)
|
|
370
|
+
: value,
|
|
371
|
+
]),
|
|
372
|
+
),
|
|
373
|
+
default: controlledNodeAssert,
|
|
374
|
+
}));
|
|
375
|
+
record({ kind: "ready" });
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: assertledger
|
|
3
3
|
description: Analyze a repository, submit candidate tests, and accept only deterministic AssertLedger evidence.
|
|
4
4
|
metadata:
|
|
5
|
-
version: "1.
|
|
5
|
+
version: "1.1.0"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# AssertLedger skill
|
|
@@ -16,10 +16,14 @@ window.
|
|
|
16
16
|
## Preferred path: a committed regression
|
|
17
17
|
|
|
18
18
|
1. Run `assertledger doctor <repo> --json` for static readiness. Runtime probes require the separate
|
|
19
|
-
`doctor --runtime --allow-unsafe-execution` command and prior operator authorization.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`
|
|
19
|
+
`doctor --runtime --allow-unsafe-execution` command and prior operator authorization. Doctor
|
|
20
|
+
judges each repository link against the module closure of the tests that would run: a link
|
|
21
|
+
outside every closure is reported as `REPOSITORY_LINK_OUTSIDE_TEST_CLOSURE`, while a link in a
|
|
22
|
+
closure (`REPOSITORY_LINK_IN_TEST_CLOSURE`) or leaving the repository
|
|
23
|
+
(`REPOSITORY_LINK_ESCAPES_ROOT`) stays a `CONFLICT` that names the path. A refusal is never
|
|
24
|
+
yours to work around: only the operator may replace the link or declare the local-only entry that
|
|
25
|
+
holds it with `--exclude NAME`. Never add names to `repository.exclude` yourself; the configured
|
|
26
|
+
list never removes files from campaign evidence.
|
|
23
27
|
2. Ask the operator or harness for the buggy, corrected and neutral revisions, the neutral reason,
|
|
24
28
|
candidate path and unchanged base tests. Do not invent a neutral control's meaning.
|
|
25
29
|
3. For committed dependency-free JavaScript `node:test`, use `assertledger check` or the MCP
|
|
@@ -29,6 +33,22 @@ window.
|
|
|
29
33
|
5. The configuration, skill and server do not grant trust. Never treat a crash, timeout, compilation
|
|
30
34
|
error or missing test as detection. The returned limitations remain part of the result.
|
|
31
35
|
|
|
36
|
+
## Replaying a recorded red/green witness
|
|
37
|
+
|
|
38
|
+
1. When the operator supplies a witness (mutated target files with their base digests, one existing
|
|
39
|
+
designated test named by file and describe path, and its expected first failure line), write it
|
|
40
|
+
as a `witness-import-request.v1.json` document; do not change the targets, test or line.
|
|
41
|
+
2. The operator chooses the backend; a witness never does. Run
|
|
42
|
+
`assertledger import-witness <witness.json> --out <new-directory> --json` with exactly one of
|
|
43
|
+
`--container-image NAME@sha256:DIGEST [--container-runtime JSON_ARGV]`,
|
|
44
|
+
`--allow-unsafe-execution` or `--allow-windows-native-execution`, as authorized. The resulting
|
|
45
|
+
isolation level, `CONTAINER`, `UNSANDBOXED` or `WINDOWS_NATIVE_UNSANDBOXED`, is part of the
|
|
46
|
+
result and must be reported with it.
|
|
47
|
+
3. Only an assertion failure attributed to the designated test counts as red. A timeout, crash,
|
|
48
|
+
collection or compile error, or a test that is not found is never red, and diverging repeated runs
|
|
49
|
+
make the candidate `UNSTABLE`. Accept the witness only on `VERIFIED` plus `assertledger replay` with
|
|
50
|
+
`valid: true`.
|
|
51
|
+
|
|
32
52
|
## Lower-level verification requests
|
|
33
53
|
|
|
34
54
|
1. Run `assertledger init <repo> --dry-run --json`, review conflicts and planned bytes, then run
|