assertledger 1.1.1 → 1.3.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 +34 -4
- package/README.md +33 -4
- package/conformance/schema-extensions.json +25 -0
- package/dist/build-info.json +1 -1
- package/dist/cli.d.ts +8 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +292 -25
- package/dist/cli.js.map +1 -1
- package/dist/contracts/index.d.ts +510 -4
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +230 -16
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/runtime-doctor.d.ts +123 -0
- package/dist/contracts/runtime-doctor.d.ts.map +1 -1
- package/dist/contracts/runtime-doctor.js +64 -0
- package/dist/contracts/runtime-doctor.js.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +5 -2
- package/dist/core/index.js.map +1 -1
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +26 -2
- package/dist/diagnostics.js.map +1 -1
- package/dist/engine/adapters/bun-test-profile.d.ts +17 -0
- package/dist/engine/adapters/bun-test-profile.d.ts.map +1 -0
- package/dist/engine/adapters/bun-test-profile.js +17 -0
- package/dist/engine/adapters/bun-test-profile.js.map +1 -0
- package/dist/engine/connection.d.ts +25 -1
- package/dist/engine/connection.d.ts.map +1 -1
- package/dist/engine/connection.js +144 -31
- package/dist/engine/connection.js.map +1 -1
- package/dist/engine/demo.d.ts +16 -0
- package/dist/engine/demo.d.ts.map +1 -0
- package/dist/engine/demo.js +51 -0
- package/dist/engine/demo.js.map +1 -0
- package/dist/engine/git-regression.d.ts +2 -2
- package/dist/engine/git-regression.d.ts.map +1 -1
- package/dist/engine/git-regression.js.map +1 -1
- package/dist/engine/index.d.ts +26 -5
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +539 -58
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/runtime-doctor.d.ts +13 -4
- package/dist/engine/runtime-doctor.d.ts.map +1 -1
- package/dist/engine/runtime-doctor.js +80 -2
- package/dist/engine/runtime-doctor.js.map +1 -1
- package/dist/engine/setup.d.ts +39 -0
- package/dist/engine/setup.d.ts.map +1 -0
- package/dist/engine/setup.js +411 -0
- package/dist/engine/setup.js.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +36 -6
- package/dist/mcp/index.js.map +1 -1
- package/dist/sdk/index.d.ts +7 -6
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +20 -7
- package/dist/sdk/index.js.map +1 -1
- package/docs/adapter-protocol.md +61 -0
- package/docs/architecture.md +4 -1
- package/docs/developer-experience.md +39 -5
- package/docs/distribution.md +8 -3
- package/docs/migration-verification-v3.md +33 -0
- package/docs/project-intent.md +7 -5
- package/docs/reference.md +9 -5
- package/docs/repository-init.md +52 -7
- package/docs/runtime-doctor.md +12 -9
- package/integrations/bun/assertions.d.mts +4 -0
- package/integrations/bun/assertions.mjs +25 -0
- package/integrations/bun/driver.d.mts +27 -0
- package/integrations/bun/driver.mjs +383 -0
- package/integrations/bun/preload.mjs +165 -0
- package/integrations/skill/SKILL.md +4 -1
- package/package.json +7 -3
- package/schemas/evidence-manifest.v3.json +897 -0
- package/schemas/repository-init-config.v2.json +210 -0
- package/schemas/repository-init-lock.v2.json +162 -0
- package/schemas/repository-init-result.v2.json +212 -0
- package/schemas/verification-request.v3.json +484 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { createHmac, randomUUID } from "node:crypto";
|
|
3
|
+
import { closeSync, readSync, writeSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import * as assertionHelper from "./bun.mjs";
|
|
7
|
+
import * as bunTest from "bun:test";
|
|
8
|
+
|
|
9
|
+
const root = process.env.ASSERTLEDGER_BUN_ROOT;
|
|
10
|
+
if (!path.isAbsolute(root ?? "")) {
|
|
11
|
+
throw new Error("ASSERTLEDGER_BUN_PRELOAD_CONFIGURATION_INVALID");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const preloadPath = fileURLToPath(import.meta.url);
|
|
15
|
+
const safeApply = Reflect.apply.bind(Reflect);
|
|
16
|
+
const safeGet = Reflect.get.bind(Reflect);
|
|
17
|
+
const rawAssertSame = assertionHelper.assertSame;
|
|
18
|
+
const verifyIssuedError = assertionHelper.isAssertSameFailure;
|
|
19
|
+
const issuedDuringTest = new WeakMap();
|
|
20
|
+
const markIssuingTest = WeakMap.prototype.set.bind(issuedDuringTest);
|
|
21
|
+
const issuingTest = WeakMap.prototype.get.bind(issuedDuringTest);
|
|
22
|
+
const activeTestExecution = new AsyncLocalStorage();
|
|
23
|
+
const currentTestId = AsyncLocalStorage.prototype.getStore.bind(activeTestExecution);
|
|
24
|
+
const runInTest = AsyncLocalStorage.prototype.run.bind(activeTestExecution);
|
|
25
|
+
function scopedAssertSame(...arguments_) {
|
|
26
|
+
try {
|
|
27
|
+
return safeApply(rawAssertSame, undefined, arguments_);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const id = currentTestId();
|
|
30
|
+
if (id !== undefined && verifyIssuedError(error)) markIssuingTest(error, id);
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const stringify = JSON.stringify.bind(JSON);
|
|
35
|
+
const evidenceKey = Buffer.alloc(32);
|
|
36
|
+
let received = 0;
|
|
37
|
+
while (received < evidenceKey.length) {
|
|
38
|
+
const length = readSync(4, evidenceKey, received, evidenceKey.length - received, null);
|
|
39
|
+
if (length === 0) throw new Error("ASSERTLEDGER_BUN_EVIDENCE_KEY_MISSING");
|
|
40
|
+
received += length;
|
|
41
|
+
}
|
|
42
|
+
closeSync(4);
|
|
43
|
+
function record(event) {
|
|
44
|
+
const body = stringify(event);
|
|
45
|
+
const mac = createHmac("sha256", evidenceKey).update(body).digest("hex");
|
|
46
|
+
writeSync(3, `${stringify({ event, mac })}\n`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function registrationFile() {
|
|
50
|
+
const lines = new Error().stack?.split(/\r?\n/u).slice(1) ?? [];
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
let location = line.trim().replace(/^at\s+/u, "");
|
|
53
|
+
if (location.endsWith(")") && location.includes("(")) {
|
|
54
|
+
location = location.slice(location.lastIndexOf("(") + 1, -1);
|
|
55
|
+
}
|
|
56
|
+
const match = location.match(/^(.+?):\d+(?::\d+)?$/u);
|
|
57
|
+
if (match === null) continue;
|
|
58
|
+
let file = match[1];
|
|
59
|
+
if (file.startsWith("file:")) {
|
|
60
|
+
try {
|
|
61
|
+
file = fileURLToPath(file);
|
|
62
|
+
} catch {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!path.isAbsolute(file)) continue;
|
|
67
|
+
const absolute = path.resolve(file);
|
|
68
|
+
if (absolute === preloadPath) continue;
|
|
69
|
+
return absolute;
|
|
70
|
+
}
|
|
71
|
+
throw new Error("ASSERTLEDGER_BUN_REGISTRATION_FILE_UNAVAILABLE");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function wrapRegistration(native, cache) {
|
|
75
|
+
if (cache.has(native)) return cache.get(native);
|
|
76
|
+
const wrapper = new Proxy(native, {
|
|
77
|
+
apply(target, thisArg, arguments_) {
|
|
78
|
+
const callbackIndex = arguments_.findIndex(
|
|
79
|
+
(value, index) => typeof value === "function" && (index > 0 || arguments_.length === 1),
|
|
80
|
+
);
|
|
81
|
+
if (callbackIndex < 0) {
|
|
82
|
+
const result = safeApply(target, thisArg, arguments_);
|
|
83
|
+
return typeof result === "function" ? wrapRegistration(result, cache) : result;
|
|
84
|
+
}
|
|
85
|
+
const callback = arguments_[callbackIndex];
|
|
86
|
+
const file = registrationFile();
|
|
87
|
+
const wrappedArguments = [...arguments_];
|
|
88
|
+
wrappedArguments[callbackIndex] = function (...callbackArguments) {
|
|
89
|
+
const id = randomUUID();
|
|
90
|
+
record({ kind: "found", id, file });
|
|
91
|
+
return runInTest(id, () => {
|
|
92
|
+
const passed = (value) => {
|
|
93
|
+
record({ kind: "end", id, status: "pass" });
|
|
94
|
+
return value;
|
|
95
|
+
};
|
|
96
|
+
const failed = (error) => {
|
|
97
|
+
record({
|
|
98
|
+
kind: "end",
|
|
99
|
+
id,
|
|
100
|
+
status: "fail",
|
|
101
|
+
owned: verifyIssuedError(error) && issuingTest(error) === id,
|
|
102
|
+
});
|
|
103
|
+
throw error;
|
|
104
|
+
};
|
|
105
|
+
try {
|
|
106
|
+
const result = safeApply(callback, this, callbackArguments);
|
|
107
|
+
return result && typeof result.then === "function"
|
|
108
|
+
? Promise.resolve(result).then(passed, failed)
|
|
109
|
+
: passed(result);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return failed(error);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
return safeApply(target, thisArg, wrappedArguments);
|
|
116
|
+
},
|
|
117
|
+
get(target, property, receiver) {
|
|
118
|
+
const value = safeGet(target, property, receiver);
|
|
119
|
+
return typeof value === "function" && property !== "constructor"
|
|
120
|
+
? wrapRegistration(value.bind(target), cache)
|
|
121
|
+
: value;
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
cache.set(native, wrapper);
|
|
125
|
+
return wrapper;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function wrapHook(nativeHook) {
|
|
129
|
+
return (callback, ...options) => {
|
|
130
|
+
if (typeof callback !== "function")
|
|
131
|
+
return safeApply(nativeHook, bunTest, [callback, ...options]);
|
|
132
|
+
const wrappedCallback = function (...arguments_) {
|
|
133
|
+
const failed = (error) => {
|
|
134
|
+
record({ kind: "hook-error" });
|
|
135
|
+
throw error;
|
|
136
|
+
};
|
|
137
|
+
try {
|
|
138
|
+
const result = safeApply(callback, this, arguments_);
|
|
139
|
+
return result && typeof result.then === "function"
|
|
140
|
+
? Promise.resolve(result).catch(failed)
|
|
141
|
+
: result;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return failed(error);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
return safeApply(nativeHook, bunTest, [wrappedCallback, ...options]);
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const cache = new WeakMap();
|
|
151
|
+
const wrappedTest = wrapRegistration(bunTest.test, cache);
|
|
152
|
+
const wrappedIt = wrapRegistration(bunTest.it, cache);
|
|
153
|
+
bunTest.mock.module("bun:test", () => ({
|
|
154
|
+
...bunTest,
|
|
155
|
+
test: wrappedTest,
|
|
156
|
+
it: wrappedIt,
|
|
157
|
+
beforeAll: wrapHook(bunTest.beforeAll),
|
|
158
|
+
afterAll: wrapHook(bunTest.afterAll),
|
|
159
|
+
beforeEach: wrapHook(bunTest.beforeEach),
|
|
160
|
+
afterEach: wrapHook(bunTest.afterEach),
|
|
161
|
+
}));
|
|
162
|
+
bunTest.mock.module("assertledger/bun", () => ({
|
|
163
|
+
...assertionHelper,
|
|
164
|
+
assertSame: scopedAssertSame,
|
|
165
|
+
}));
|
|
@@ -16,7 +16,10 @@ 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.
|
|
19
|
+
`doctor --runtime --allow-unsafe-execution` command and prior operator authorization. A
|
|
20
|
+
`UNSUPPORTED_REPOSITORY_SYMLINK` conflict stays a refusal: only the operator may declare the
|
|
21
|
+
local-only entry that holds the link with `--exclude NAME`. Never add names to
|
|
22
|
+
`repository.exclude` yourself; the configured list never removes files from campaign evidence.
|
|
20
23
|
2. Ask the operator or harness for the buggy, corrected and neutral revisions, the neutral reason,
|
|
21
24
|
candidate path and unchanged base tests. Do not invent a neutral control's meaning.
|
|
22
25
|
3. For committed dependency-free JavaScript `node:test`, use `assertledger check` or the MCP
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assertledger",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Verify that regression tests detect a declared bug, with deterministic verdicts and replayable evidence.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"./core": {
|
|
32
32
|
"types": "./dist/core/index.d.ts",
|
|
33
33
|
"import": "./dist/core/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./bun": {
|
|
36
|
+
"types": "./integrations/bun/assertions.d.mts",
|
|
37
|
+
"import": "./integrations/bun/assertions.mjs"
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
"files": [
|
|
@@ -73,9 +77,9 @@
|
|
|
73
77
|
"zod": "4.4.3"
|
|
74
78
|
},
|
|
75
79
|
"devDependencies": {
|
|
76
|
-
"@biomejs/biome": "2.5.
|
|
80
|
+
"@biomejs/biome": "2.5.14",
|
|
77
81
|
"@modelcontextprotocol/client": "2.0.0",
|
|
78
|
-
"@types/node": "22.20.
|
|
82
|
+
"@types/node": "22.20.4",
|
|
79
83
|
"tsx": "4.23.13"
|
|
80
84
|
},
|
|
81
85
|
"directories": {
|