assertledger 1.1.1 → 1.2.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.
Files changed (64) hide show
  1. package/README.fr.md +14 -4
  2. package/README.md +14 -4
  3. package/conformance/schema-extensions.json +25 -0
  4. package/dist/build-info.json +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +68 -20
  7. package/dist/cli.js.map +1 -1
  8. package/dist/contracts/index.d.ts +510 -4
  9. package/dist/contracts/index.d.ts.map +1 -1
  10. package/dist/contracts/index.js +230 -16
  11. package/dist/contracts/index.js.map +1 -1
  12. package/dist/contracts/runtime-doctor.d.ts +123 -0
  13. package/dist/contracts/runtime-doctor.d.ts.map +1 -1
  14. package/dist/contracts/runtime-doctor.js +64 -0
  15. package/dist/contracts/runtime-doctor.js.map +1 -1
  16. package/dist/core/index.d.ts.map +1 -1
  17. package/dist/core/index.js +5 -2
  18. package/dist/core/index.js.map +1 -1
  19. package/dist/diagnostics.d.ts.map +1 -1
  20. package/dist/diagnostics.js +26 -2
  21. package/dist/diagnostics.js.map +1 -1
  22. package/dist/engine/adapters/bun-test-profile.d.ts +17 -0
  23. package/dist/engine/adapters/bun-test-profile.d.ts.map +1 -0
  24. package/dist/engine/adapters/bun-test-profile.js +17 -0
  25. package/dist/engine/adapters/bun-test-profile.js.map +1 -0
  26. package/dist/engine/git-regression.d.ts +2 -2
  27. package/dist/engine/git-regression.d.ts.map +1 -1
  28. package/dist/engine/git-regression.js.map +1 -1
  29. package/dist/engine/index.d.ts +11 -5
  30. package/dist/engine/index.d.ts.map +1 -1
  31. package/dist/engine/index.js +454 -49
  32. package/dist/engine/index.js.map +1 -1
  33. package/dist/engine/runtime-doctor.d.ts +13 -4
  34. package/dist/engine/runtime-doctor.d.ts.map +1 -1
  35. package/dist/engine/runtime-doctor.js +80 -2
  36. package/dist/engine/runtime-doctor.js.map +1 -1
  37. package/dist/mcp/index.d.ts.map +1 -1
  38. package/dist/mcp/index.js +36 -6
  39. package/dist/mcp/index.js.map +1 -1
  40. package/dist/sdk/index.d.ts +7 -6
  41. package/dist/sdk/index.d.ts.map +1 -1
  42. package/dist/sdk/index.js +20 -7
  43. package/dist/sdk/index.js.map +1 -1
  44. package/docs/adapter-protocol.md +61 -0
  45. package/docs/architecture.md +4 -1
  46. package/docs/developer-experience.md +7 -5
  47. package/docs/distribution.md +8 -3
  48. package/docs/migration-verification-v3.md +33 -0
  49. package/docs/project-intent.md +7 -5
  50. package/docs/reference.md +6 -5
  51. package/docs/repository-init.md +38 -7
  52. package/docs/runtime-doctor.md +12 -9
  53. package/integrations/bun/assertions.d.mts +4 -0
  54. package/integrations/bun/assertions.mjs +25 -0
  55. package/integrations/bun/driver.d.mts +27 -0
  56. package/integrations/bun/driver.mjs +383 -0
  57. package/integrations/bun/preload.mjs +165 -0
  58. package/integrations/skill/SKILL.md +4 -1
  59. package/package.json +7 -3
  60. package/schemas/evidence-manifest.v3.json +897 -0
  61. package/schemas/repository-init-config.v2.json +210 -0
  62. package/schemas/repository-init-lock.v2.json +162 -0
  63. package/schemas/repository-init-result.v2.json +212 -0
  64. package/schemas/verification-request.v3.json +484 -0
@@ -37,11 +37,13 @@ execution remains unavailable unless an operator separately starts it with
37
37
  `--allow-unsafe-execution`; that mode is explicitly UNSANDBOXED trusted-local.
38
38
 
39
39
  On a connected read-only server, agents may call `assertledger_doctor` (or the legacy
40
- `testforge_doctor` alias) with `{ "root": "..." }`. It returns the same static repository
41
- initialization result as `assertledger doctor . --json`, after enforcing the server's allowed-root
42
- boundary. The tool writes no configuration and does not execute repository code. It reports
43
- configuration readiness only; dynamic dependency, reporter, permission and liveness diagnostics
44
- remain outside this static check.
40
+ `testforge_doctor` alias) with `{ "root": "..." }`, plus optional `"exclude"` entry names that follow
41
+ the `--exclude` rules; an empty array replaces the configured list (see
42
+ [repository initialization](repository-init.md)). It returns the
43
+ same static repository initialization result as `assertledger doctor . --json`, after enforcing the
44
+ server's allowed-root boundary. The tool writes no configuration and does not execute repository
45
+ code. It reports configuration readiness only; dynamic dependency, reporter, permission and liveness
46
+ diagnostics remain outside this static check.
45
47
 
46
48
  Use the separate [runtime doctor](runtime-doctor.md) after initialization to run controlled probes
47
49
  with explicit authorization. [Reason-code explanations](diagnostics.md) remain available without
@@ -10,8 +10,8 @@ The smoke exercises both installed command aliases (`assertledger`, `testforge`)
10
10
  both SDK class aliases, the public root and core exports, static init and audit,
11
11
  and the bundled trusted `node:test` example. The strong test must be selected,
12
12
  the weak test must be rejected as `WEAK_ORACLE`, and every replay rail must pass.
13
- It also checks the advertised schemas, conformance bundle, integration skill and
14
- runtime files in the tarball and installed package.
13
+ It also checks the advertised schemas, conformance bundle, integration skill, Bun assertion
14
+ helper, callback preload and driver, and runtime files in the tarball and installed package.
15
15
  It also compiles a TypeScript consumer against the installed root/core exports
16
16
  using the checkout's pinned compiler and Node type definitions. Consumer imports
17
17
  resolve from the temporary project; no source-package alias is configured.
@@ -26,7 +26,12 @@ the tarball and its SHA-256 are retained under `.testforge/package-smoke/<run-id
26
26
  Temporary consumers are removed after the run. The retained manifest is replayable;
27
27
  its original execution paths refer to the removed consumer.
28
28
 
29
- CI runs this smoke on Windows and Linux with Node 22.15.0 and 24 alongside `pnpm check`.
29
+ CI runs this smoke on Windows, Linux and macOS with Node 22.15.0 and 24 plus pinned Bun 1.4.2,
30
+ alongside `pnpm check`.
31
+ The verification matrix installs Bun 1.4.2 on Windows, Linux, and macOS and executes the
32
+ real Bun campaign and its negative witnesses. The package smoke also runs a Bun v3 campaign
33
+ and replay through the freshly installed CLI and checks the installed `assertledger/bun` export
34
+ and types.
30
35
  A successful local smoke proves that local tarball on the reported Node version
31
36
  and operating system. It does not establish npm publication, cross-platform CI
32
37
  success, hostile-code isolation, or adoption by external users. The example uses
@@ -0,0 +1,33 @@
1
+ # Verification v3 and Bun test migration
2
+
3
+ Verification request and evidence manifest v3 add the built-in `bun-test` adapter. V1 and v2
4
+ schema bytes, adapter unions, and historical manifests remain unchanged and replayable. V3 keeps
5
+ the v2 execution-backend record and accepts `node-test` and `testforge-command` as before.
6
+ `bun-test` requires `trusted-local`; a v3 Bun request with container isolation is refused before
7
+ execution.
8
+
9
+ `assertledger init` writes repository initialization config, lock, and result v2 for a detected
10
+ `bun:test` repository with one or more base test files. Existing Node initialization remains v1.
11
+ If a repository contains multiple test frameworks, select `--framework bun:test` explicitly.
12
+ Local-only linked entries still need explicit `--exclude NAME` declarations. Static initialization
13
+ does not execute tests or qualify the installed Bun binary. Run the unsafe runtime doctor, then
14
+ a v3 campaign with operator-supplied worlds and candidates.
15
+
16
+ New Bun candidate tests use `assertSame` from `assertledger/bun`. Existing `bun:test` tests can
17
+ remain as base controls. Their native `expect` failures block a campaign as operational failures;
18
+ they are not assertion evidence. The helper compares only with `Object.is`, so use an explicit
19
+ scalar or boolean observation when structural equality is needed. Calculation errors occur before
20
+ the helper and remain ordinary errors. The helper is installed from the package into each
21
+ disposable workspace; no adapter implementation is required in the consumer repository.
22
+
23
+ The Bun runtime profile pins version `1.4.2` and revision
24
+ `744846f844374847c902b5e7fd59b4342a51ef99`. New manifests bind the resolved executable
25
+ digest, Bun revision, driver digest, preload digest, helper digest, command shape,
26
+ and fresh runtime preflight. They may have new decision and artifact digests because v3 evidence
27
+ has a new schema identity. No old manifest is resealed. The v3 schemas are added to the
28
+ post-conformance schema lock; the frozen v1 bundle and its root digest are unchanged.
29
+
30
+ The v1 provider/export contracts still describe v1 manifests only. Consumers of v3 Bun evidence
31
+ should parse and replay the v3 manifest directly; they must not project it through the v1 export
32
+ contract. Windows, Linux, and macOS support is claimed only for the exact Bun version on which
33
+ the corresponding CI campaign gate passes.
@@ -50,13 +50,15 @@ référence, un monde neutre rouge, une observation instable ou une attribution
50
50
 
51
51
  Le socle local comprend les contrats versionnés, le noyau déterministe, les digests, le replay
52
52
  sémantique, le moteur d’exécution, les façades CLI/SDK/MCP, l’audit statique et l’initialisation.
53
- Le registre contient 38 schémas JSON : les 34 figés par le verrou de conformance v1 et les 4
54
- schémas d’export d’évidence, verrouillés par une extension additive qui ne modifie pas le bundle v1.
53
+ Le registre contient 45 schémas JSON : les 34 figés par le verrou de conformance v1 et 11
54
+ extensions, verrouillées sans modifier le bundle v1.
55
55
  L’ancien décompte de 30 dans la roadmap était périmé.
56
56
 
57
- L’adaptateur officiel intégré est `node:test`. Le protocole `testforge-command` permet d’intégrer
58
- un autre framework avec un adaptateur fourni par l’opérateur. Détecter Vitest, Jest, Bun ou pytest
59
- pendant `init` ne fournit pas leur adaptateur officiel.
57
+ Les adaptateurs officiels intégrés sont `node:test` et `bun:test`. Ce dernier est limité à Bun 1.4.2
58
+ et aux assertions explicites du helper `assertSame` ; les échecs natifs de `expect` ne deviennent pas
59
+ des preuves d’assertion. Le protocole `testforge-command` permet d’intégrer un autre framework avec
60
+ un adaptateur fourni par l’opérateur. Détecter Vitest, Jest ou pytest pendant `init` ne fournit pas
61
+ leur adaptateur officiel.
60
62
 
61
63
  Les profils v1 et v2, les artefacts de benchmark, les contrats de provenance et le protocole
62
64
  expérimental H3 sont présents. Le benchmark par phases refuse encore l’adaptateur `node:test` :
package/docs/reference.md CHANGED
@@ -310,20 +310,21 @@ caller decide whether the capability exists. The server resolves repository root
310
310
  confines them to the server process's current working directory by default. Programmatic operators
311
311
  may supply a different `allowedRepositoryRoots` allowlist.
312
312
 
313
- The doctor pair accepts a strict `{ "root": "..." }` input and returns the existing
314
- `repository-init-result` contract. It is read-only in both the default and operator-enabled server;
313
+ The doctor pair accepts a strict `{ "root": "...", "exclude": ["..."] }` input, where the optional
314
+ `exclude` entry names follow the [repository initialization](repository-init.md) exclusion rules,
315
+ and returns the existing `repository-init-result` contract. It is read-only in both the default and operator-enabled server;
315
316
  enabling unsafe execution does not change doctor behavior. Dynamic runtime and client diagnostics
316
317
  remain outside this static readiness result.
317
318
 
318
- `doctor_runtime` accepts the same strict root input and returns the separate
319
- [runtime diagnostic contract](runtime-doctor.md). `check` accepts the
319
+ `doctor_runtime` accepts only the strict root input and returns the separate
320
+ [runtime diagnostic contract](runtime-doctor.md), v2 for a generated Bun configuration. `check` accepts the
320
321
  [high-level Git options](git-regression.md), without a permission field, and returns the existing
321
322
  evidence manifest. The operator's capability is required for both tools.
322
323
 
323
324
  ## Continuous integration
324
325
 
325
326
  Run `pnpm check` on every change. The included GitHub Actions workflow runs this gate on Node.js 22
326
- and 24 on Ubuntu, Windows and macOS. A separate matrix installs and exercises the packed artifact
327
+ and 24 with Bun 1.4.2 on Ubuntu, Windows and macOS. A separate matrix installs and exercises the packed artifact
327
328
  on Ubuntu and Windows with Node.js 22.15.0 and 24. On Ubuntu, the gate also runs the real-daemon
328
329
  [container isolation](container-isolation.md) suite. A CI job that executes campaigns must
329
330
  also treat `trusted-local` as `UNSANDBOXED`: use an isolated runner without secrets or host
@@ -22,13 +22,45 @@ plausible test frameworks, composite shell scripts, contradictory overrides, and
22
22
  adapter configurations return `CONFLICT`. Multiple CI providers are only sorted evidence and do not
23
23
  block initialization.
24
24
 
25
+ The static inventory walks the filesystem, not the Git index, and never follows or copies a
26
+ symbolic link: a link inside it returns `CONFLICT` with `UNSUPPORTED_REPOSITORY_SYMLINK` and writes
27
+ nothing. It always skips entries named `.git`, `.testforge`, and `node_modules`, at any depth.
28
+ When a local-only entry holds a link that no campaign needs, the operator can declare its name:
29
+
30
+ ```sh
31
+ assertledger doctor . --exclude .claude --exclude .omx --json
32
+ assertledger init . --exclude .claude --exclude .omx --json
33
+ ```
34
+
35
+ Each `--exclude` value is one portable entry name without separators; every file or directory with
36
+ that name is skipped at any depth. Anything else returns `CONFLICT` with
37
+ `INVALID_REPOSITORY_EXCLUDE`. The declared names are written to `repository.exclude` beside the
38
+ defaults. `init` and `doctor` without `--exclude`, `analyze` and runtime doctor's configuration check
39
+ then reuse that list from a valid `assertledger.config.json`; an unreadable or invalid file leaves
40
+ only the defaults, which widens the inventory. A configured entry that contains a separator matches
41
+ nothing, as in a verification request. An explicit list, including an empty MCP `exclude` array,
42
+ replaces the configured one, so a different declaration never silently widens or narrows the
43
+ inventory: it fails closed, for example with `CONFIG_CONFLICT`, or with
44
+ `UNSUPPORTED_REPOSITORY_SYMLINK` when a narrower list exposes a link again. Links outside the declared names stay fail-closed.
45
+
46
+ The configured list governs only these static diagnostics and the evidence digests of
47
+ `assertledger.lock.json`; it produces no campaign evidence. `audit`, a campaign's repository copy and
48
+ its manifest `repositoryDigest` keep their own exclusions: the defaults, plus the verification
49
+ request's `repository.exclude` for a campaign. `audit` therefore still refuses a linked local-only
50
+ entry, and a request must declare the same names to leave it out of its copy; a committed
51
+ configuration can never remove files from campaign evidence. Declaring a name is an operator decision
52
+ recorded in a reviewable file, not a sandbox.
53
+
25
54
  Files at or below the managed `candidateRoots` are deliberately excluded from framework inference,
26
55
  evidence, built-in control tests, and repository-change comparison. Candidate generation therefore
27
56
  cannot silently redefine initialization facts or invalidate an otherwise unchanged lock.
28
57
 
29
- The built-in ready adapter is currently `node-test`. Bun, pytest, Vitest, and Jest can be detected,
30
- but initialization returns `BLOCKED` with `OFFICIAL_ADAPTER_UNAVAILABLE` unless the operator supplies
31
- an existing structured adapter configuration:
58
+ The built-in ready adapters are `node-test` and `bun-test`. Bun initialization writes v2 config,
59
+ lock, and result contracts; Node initialization stays v1. Bun's static plan does not qualify the
60
+ installed runtime. Run runtime doctor and use a v3 verification request before claiming campaign
61
+ evidence. Pytest, Vitest, and Jest can be detected, but initialization returns `BLOCKED` with
62
+ `OFFICIAL_ADAPTER_UNAVAILABLE` unless the operator supplies an existing structured adapter
63
+ configuration:
32
64
 
33
65
  ```sh
34
66
  assertledger init . --adapter-config integrations/my-adapter.json --json
@@ -37,8 +69,8 @@ assertledger init . --adapter-config integrations/my-adapter.json --json
37
69
  The adapter document is parsed through the public adapter contract and is operator-owned. Its
38
70
  executable is recorded as argv but is not resolved or executed by `init`. This is not an official
39
71
  adapter endorsement and does not reduce the later `trusted-local` execution boundary.
40
- `node-test` adapters are accepted only for the `node:test` framework; every other framework requires
41
- an operator-owned `testforge-command` adapter.
72
+ `node-test` adapters are accepted only for `node:test`; `bun-test` adapters are accepted only for
73
+ `bun:test`. Pytest, Vitest, and Jest require an operator-owned `testforge-command` adapter.
42
74
 
43
75
  All detections and evidence digests come from one byte snapshot. Immediately before returning or
44
76
  writing managed files, initialization rechecks the in-scope inventory and every evidence byte. A
@@ -54,7 +86,6 @@ Exit codes are `0` for `CREATED`, `UNCHANGED`, or `WOULD_CREATE`; `3` for `BLOCK
54
86
  ambiguity, invalid overrides, conflicts, or contract validation; `5` for unexpected I/O; and `64`
55
87
  for CLI usage errors.
56
88
 
57
- The public `repository-init-config.v1`, `repository-init-lock.v1`, and
58
- `repository-init-result.v1` contracts contain no timestamps, absolute repository roots, environment
89
+ The public v1 and v2 initialization contracts contain no timestamps, absolute repository roots, environment
59
90
  values, worlds, or candidates. The lock binds normalized detections and sorted evidence digests; it
60
91
  does not authenticate the detector, repository, adapter, or later execution evidence.
@@ -23,20 +23,23 @@ configuration inspection or executable probes. Unknown flags, repeated flags, an
23
23
 
24
24
  ## What it checks
25
25
 
26
- Version 1.0 supports the generated official `node:test` adapter. It fails closed for other
27
- frameworks and operator-supplied adapters. In order, it checks:
26
+ Version 1.0 supports the generated official `node:test` adapter. Version 2.0 supports the
27
+ generated official `bun:test` adapter on the qualified Bun 1.4.2 revision. Both fail closed for
28
+ other frameworks and operator-supplied adapters. In order, they check:
28
29
 
29
30
  1. explicit trusted-local authorization;
30
31
  2. a current AssertLedger configuration and evidence lock;
31
- 3. the supported generated `node:test` adapter;
32
- 4. a stable Node.js executable at version 22.15 or newer;
33
- 5. availability of Node's built-in `node:test` module;
32
+ 3. the supported generated adapter;
33
+ 4. a stable Node.js executable at version 22.15 or newer, or the exact Bun 1.4.2 revision;
34
+ 5. availability of the runner's built-in test module;
34
35
  6. permission to create, write, and clean up an operating-system temporary workspace;
35
36
  7. controlled reporter discovery, liveness, and attribution probes.
36
37
 
37
38
  The final probes use disposable synthetic tests. One assertion failure must be attributed to the
38
39
  candidate; a generic throw with a nested assertion cause must remain a process crash and must not
39
- be attributed. Malformed reporter output, missing discovery, cleanup failure, timeout, or process
40
+ be attributed. The Bun probes also check its owned `assertSame` helper against a plain throw,
41
+ a caught assertion, an operand error, and a native `expect` failure. Malformed reporter output,
42
+ missing discovery, cleanup failure, timeout, or process
40
43
  failure blocks readiness. AssertLedger does not write to the repository during runtime doctor.
41
44
 
42
45
  ## Result contract
@@ -44,15 +47,15 @@ failure blocks readiness. AssertLedger does not write to the repository during r
44
47
  The SDK exposes the same strict additive contract:
45
48
 
46
49
  ```ts
47
- import { AssertLedger, parseRuntimeDoctorResult } from "assertledger";
50
+ import { AssertLedger, parseVersionedRuntimeDoctorResult } from "assertledger";
48
51
 
49
52
  const result = await new AssertLedger().doctorRuntime(repositoryRoot, {
50
53
  allowUnsafeExecution: true,
51
54
  });
52
- parseRuntimeDoctorResult(result);
55
+ parseVersionedRuntimeDoctorResult(result);
53
56
  ```
54
57
 
55
- `schemaVersion` is `1.0.0`. Each check has `PASS`, `BLOCKED`, or `LIMITATION`, a stable reason code
58
+ `schemaVersion` is `1.0.0` for Node and `2.0.0` for Bun. Each check has `PASS`, `BLOCKED`, or `LIMITATION`, a stable reason code
56
59
  when relevant, a concise summary, and a safe next action. The top-level status is `READY` only when
57
60
  every supported runtime boundary passes. JSON results contain no subprocess output, environment
58
61
  values, credentials, or repository source.
@@ -0,0 +1,4 @@
1
+ export declare function assertSame(actual: unknown, expected: unknown): void;
2
+
3
+ /** @internal Identifies an error instance issued by this helper. */
4
+ export declare function isAssertSameFailure(error: unknown): boolean;
@@ -0,0 +1,25 @@
1
+ const ASSERTION_ERROR_NAME = "AssertLedgerBunAssertionError";
2
+ const ASSERTION_ERROR_MESSAGE = "AssertLedger assertSame failed";
3
+ const issuedErrors = new WeakSet();
4
+ const addIssuedError = WeakSet.prototype.add.bind(issuedErrors);
5
+ const hasIssuedError = WeakSet.prototype.has.bind(issuedErrors);
6
+
7
+ class AssertLedgerBunAssertionError extends Error {
8
+ constructor() {
9
+ super(ASSERTION_ERROR_MESSAGE);
10
+ this.name = ASSERTION_ERROR_NAME;
11
+ }
12
+ }
13
+
14
+ export function assertSame(actual, expected) {
15
+ if (!Object.is(actual, expected)) {
16
+ const error = new AssertLedgerBunAssertionError();
17
+ addIssuedError(error);
18
+ throw error;
19
+ }
20
+ }
21
+
22
+ /** @internal Identifies an error instance issued by this helper. */
23
+ export function isAssertSameFailure(error) {
24
+ return typeof error === "object" && error !== null && hasIssuedError(error);
25
+ }
@@ -0,0 +1,27 @@
1
+ export interface BunInstrumentedResult {
2
+ protocolVersion: "1.0.0";
3
+ outcome:
4
+ | "PASS"
5
+ | "ASSERTION_FAILURE"
6
+ | "PROCESS_CRASH"
7
+ | "TIMEOUT"
8
+ | "INFRA_ERROR"
9
+ | "NO_TEST_DISCOVERED";
10
+ testsDiscovered: number;
11
+ candidateTestsDiscovered: number;
12
+ attributed: boolean;
13
+ }
14
+
15
+ export type BunTestEvent =
16
+ | { kind: "found"; id: string; file: string }
17
+ | { kind: "end"; id: string; status: "pass" | "fail"; owned: boolean }
18
+ | { kind: "hook-error" };
19
+
20
+ export function classifyBunInstrumentedEvidence(
21
+ events: readonly BunTestEvent[] | undefined,
22
+ junit: { tests: number; failures: number; skipped: number } | undefined,
23
+ baseFiles: ReadonlySet<string>,
24
+ candidateFiles: ReadonlySet<string>,
25
+ exitCode: number | null,
26
+ operationalError?: boolean,
27
+ ): BunInstrumentedResult;