assertledger 1.4.0 → 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.
Files changed (107) hide show
  1. package/README.fr.md +5 -2
  2. package/README.md +5 -2
  3. package/conformance/schema-extensions.json +15 -0
  4. package/dist/build-info.json +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +213 -8
  7. package/dist/cli.js.map +1 -1
  8. package/dist/contracts/index.d.ts +467 -4
  9. package/dist/contracts/index.d.ts.map +1 -1
  10. package/dist/contracts/index.js +256 -3
  11. package/dist/contracts/index.js.map +1 -1
  12. package/dist/contracts/qualification.d.ts +528 -0
  13. package/dist/contracts/qualification.d.ts.map +1 -0
  14. package/dist/contracts/qualification.js +357 -0
  15. package/dist/contracts/qualification.js.map +1 -0
  16. package/dist/core/index.d.ts +9 -0
  17. package/dist/core/index.d.ts.map +1 -1
  18. package/dist/core/index.js +46 -2
  19. package/dist/core/index.js.map +1 -1
  20. package/dist/core/qualification.d.ts +17 -0
  21. package/dist/core/qualification.d.ts.map +1 -0
  22. package/dist/core/qualification.js +571 -0
  23. package/dist/core/qualification.js.map +1 -0
  24. package/dist/diagnostics.d.ts.map +1 -1
  25. package/dist/diagnostics.js +81 -4
  26. package/dist/diagnostics.js.map +1 -1
  27. package/dist/engine/adapters/bun-native.d.ts +35 -0
  28. package/dist/engine/adapters/bun-native.d.ts.map +1 -0
  29. package/dist/engine/adapters/bun-native.js +179 -0
  30. package/dist/engine/adapters/bun-native.js.map +1 -0
  31. package/dist/engine/adapters/bun-test-designated-profile.d.ts +23 -0
  32. package/dist/engine/adapters/bun-test-designated-profile.d.ts.map +1 -0
  33. package/dist/engine/adapters/bun-test-designated-profile.js +24 -0
  34. package/dist/engine/adapters/bun-test-designated-profile.js.map +1 -0
  35. package/dist/engine/container.d.ts +7 -0
  36. package/dist/engine/container.d.ts.map +1 -1
  37. package/dist/engine/container.js +22 -7
  38. package/dist/engine/container.js.map +1 -1
  39. package/dist/engine/git-regression.d.ts.map +1 -1
  40. package/dist/engine/git-regression.js +9 -2
  41. package/dist/engine/git-regression.js.map +1 -1
  42. package/dist/engine/index.d.ts +14 -0
  43. package/dist/engine/index.d.ts.map +1 -1
  44. package/dist/engine/index.js +828 -97
  45. package/dist/engine/index.js.map +1 -1
  46. package/dist/engine/qualification.d.ts +154 -0
  47. package/dist/engine/qualification.d.ts.map +1 -0
  48. package/dist/engine/qualification.js +725 -0
  49. package/dist/engine/qualification.js.map +1 -0
  50. package/dist/engine/test-closure.d.ts +39 -0
  51. package/dist/engine/test-closure.d.ts.map +1 -0
  52. package/dist/engine/test-closure.js +513 -0
  53. package/dist/engine/test-closure.js.map +1 -0
  54. package/dist/engine/witness-import.d.ts +31 -0
  55. package/dist/engine/witness-import.d.ts.map +1 -0
  56. package/dist/engine/witness-import.js +224 -0
  57. package/dist/engine/witness-import.js.map +1 -0
  58. package/dist/index.d.ts +3 -0
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +3 -0
  61. package/dist/index.js.map +1 -1
  62. package/dist/mcp/index.d.ts.map +1 -1
  63. package/dist/mcp/index.js +65 -0
  64. package/dist/mcp/index.js.map +1 -1
  65. package/dist/sdk/index.d.ts +292 -3
  66. package/dist/sdk/index.d.ts.map +1 -1
  67. package/dist/sdk/index.js +39 -1
  68. package/dist/sdk/index.js.map +1 -1
  69. package/docs/container-isolation.md +6 -0
  70. package/docs/migration-orchestration-qualification-v1.md +109 -0
  71. package/docs/migration-verification-v4.md +94 -0
  72. package/docs/orchestration-qualification.md +167 -0
  73. package/docs/reference.md +7 -0
  74. package/docs/witness-import.md +67 -0
  75. package/examples/orchestration/README.md +68 -0
  76. package/examples/orchestration/consumer-obligations.json +101 -0
  77. package/examples/orchestration/workspace/bitbucket-pipelines.yml +10 -0
  78. package/examples/orchestration/workspace/command-shape.test.mjs +25 -0
  79. package/examples/orchestration/workspace/commands.mjs +14 -0
  80. package/examples/orchestration/workspace/completion.mjs +39 -0
  81. package/examples/orchestration/workspace/faults.json +1 -0
  82. package/examples/orchestration/workspace/native.test.ts +4 -0
  83. package/examples/orchestration/workspace/package.json +6 -0
  84. package/examples/orchestration/workspace/packages/app/package.json +12 -0
  85. package/examples/orchestration/workspace/packages/app/verdict.txt +1 -0
  86. package/examples/orchestration/workspace/packages/leaf/package.json +9 -0
  87. package/examples/orchestration/workspace/packages/leaf/verdict.txt +1 -0
  88. package/examples/orchestration/workspace/pnpm-lock.yaml +170 -0
  89. package/examples/orchestration/workspace/pnpm-workspace.yaml +2 -0
  90. package/examples/orchestration/workspace/task.mjs +23 -0
  91. package/examples/orchestration/workspace/turbo.json +38 -0
  92. package/examples/orchestration/workspace/wrapper.mjs +45 -0
  93. package/integrations/bun/driver.d.mts +30 -1
  94. package/integrations/bun/driver.mjs +215 -6
  95. package/integrations/bun/preload.mjs +186 -5
  96. package/integrations/bun-native/README.md +86 -0
  97. package/integrations/bun-native/driver.mjs +396 -0
  98. package/integrations/bun-native/preload.mjs +375 -0
  99. package/integrations/skill/SKILL.md +25 -5
  100. package/package.json +1 -1
  101. package/schemas/evidence-manifest.v4.json +995 -0
  102. package/schemas/qualification-execution-request.v1.json +571 -0
  103. package/schemas/qualification-plan.v1.json +528 -0
  104. package/schemas/qualification-receipt.v1.json +821 -0
  105. package/schemas/qualification-replay-result.v1.json +37 -0
  106. package/schemas/verification-request.v4.json +590 -0
  107. package/schemas/witness-import-request.v1.json +231 -0
@@ -0,0 +1,94 @@
1
+ # Verification v4: designated Bun tests, closure-scoped links and declared isolation
2
+
3
+ Verification request and evidence manifest v4 add three things a v3 campaign cannot express. V1 to
4
+ v3 schema bytes, parsers, adapters and historical manifests are unchanged and replayable; every
5
+ refusal they apply still applies to them.
6
+
7
+ ## Existing Bun tests, run by name
8
+
9
+ The `bun-test-designated` adapter verifies an existing `bun:test` test instead of a new candidate
10
+ file. A candidate names the test and, optionally, the first line of the failure it must produce:
11
+
12
+ ```json
13
+ {
14
+ "id": "gate-routing",
15
+ "test": { "file": "tools/gates.test.ts", "path": ["gate routing", "keeps docs"] },
16
+ "expectedFailure": "error: expect(received).toContain(expected)"
17
+ }
18
+ ```
19
+
20
+ `path` is the describe path ending with the test name. The adapter is declared as
21
+ `{ "kind": "bun-test-designated", "executable": "bun", "testTimeoutMs": null }`; `testTimeoutMs`
22
+ sets Bun's per-test timeout (`bun test --timeout`) and must be shorter than
23
+ `timeoutMsPerExecution`. `null` keeps Bun's default of 5 seconds.
24
+
25
+ Each run executes only that test, selected by an anchored, escaped `--test-name-pattern`. The
26
+ taxonomy is the one every adapter applies:
27
+
28
+ - red is only an assertion failure attributed to the designated test: a failure thrown inside it by
29
+ a `bun:test` built-in matcher, in Bun's matcher message form, or by `assertSame`; with an expected
30
+ line, its first message line must be equal to it;
31
+ - a timeout is never red, including a test that Bun timed out and whose callback threw a matcher
32
+ failure afterwards; nor is a crash, a hook failure, a collection or compile error, a custom matcher,
33
+ a matcher usage error, a forged message, or a test that is not found (`NO_TEST_DISCOVERED`);
34
+ - repeated runs that diverge make the candidate `UNSTABLE`.
35
+
36
+ Controls load every designated file without running a test. They show that each world evaluates the
37
+ file and its imports and still registers the designated test exactly once. The designated test file
38
+ must not be rewritten by any world (`DESIGNATED_TEST_OVERLAID`).
39
+
40
+ Every campaign first runs the designated preflight: fixed probes, including a synchronous test that
41
+ overruns its timeout and then fails a matcher, must each produce their expected outcome, or the
42
+ campaign stops.
43
+
44
+ ## Repository links judged against the executed tests
45
+
46
+ A v4 campaign collects the repository's links instead of refusing the first one. A link that leaves
47
+ the repository root (`REPOSITORY_LINK_ESCAPES_ROOT`) or lies on the module closure of a test the
48
+ adapter executes (`REPOSITORY_LINK_IN_TEST_CLOSURE`) refuses the campaign, and the error names its
49
+ path. When a test's imports are computed or absolute, the closure is unbounded and any link refuses
50
+ the campaign (`TEST_CLOSURE_UNBOUNDED`). The others are left out of the snapshot and listed, sorted,
51
+ in `evidenceContext.repository.omittedLinks`, which the decision digest covers.
52
+
53
+ The closure follows literal `import`, `export … from`, `import()`, `require()` and `mock.module()`
54
+ specifiers, `tsconfig`/`jsconfig` paths, workspace packages, including their installed
55
+ `node_modules` entry, declared dependencies of installed packages, and `bunfig.toml` test preloads.
56
+ It does not cover files a test reads at run time; that limitation is recorded in the manifest.
57
+
58
+ `doctor` applies the same rule to the tests the framework would discover, and reports the closure
59
+ as non-blocking when it is unbounded. A link through which a runner could discover a test, named as
60
+ a test itself or a directory holding one, is in the closure. `analyze`, `audit` and v1 to v3
61
+ campaigns still refuse any link.
62
+
63
+ ## Snapshot options
64
+
65
+ `repository.includeDependencies` keeps `node_modules` in the snapshot and its digest as found;
66
+ AssertLedger neither installs nor verifies it. `repository.git` is `excluded`, the snapshot without
67
+ Git metadata, or `synthesized`: the snapshot is committed once into a fresh repository with a fixed
68
+ identity and date and no hooks, template or global configuration, honoring its own `.gitignore`.
69
+ The tree identity and Git version are recorded in `evidenceContext.repository.git`. The source
70
+ repository's own `.git` and history never enter the snapshot.
71
+
72
+ ## Declared isolation
73
+
74
+ `isolation` is one of:
75
+
76
+ - `container`: a fresh container per execution from a digest-pinned local image, through the
77
+ operator's runtime argv, for example the native WSL2 engine
78
+ `["wsl.exe","-d","Ubuntu","--exec","docker"]`. The designated adapter runs there; `bun-test`
79
+ with candidate files is still refused in a container (`BUN_TEST_CONTAINER_UNSUPPORTED`).
80
+ - `trusted-local`: unsandboxed, level `UNSANDBOXED`.
81
+ - `windows-native`: unsandboxed on a Windows host for tests that only run natively there, level
82
+ `WINDOWS_NATIVE_UNSANDBOXED`. It is never implied: the request declares the kind, the operator
83
+ acknowledges it (`--allow-windows-native-execution`), and it is refused on any other host
84
+ (`WINDOWS_NATIVE_HOST_REQUIRED`).
85
+
86
+ The level is in `isolation.level` of every manifest and in every witness summary, and the decision
87
+ digest binds it.
88
+
89
+ ## Compatibility
90
+
91
+ `verify` and the SDK's `verifyV4` accept v4 requests; `replay` accepts v1 to v4 manifests. The v4
92
+ request, v4 manifest and witness import request schemas are added to the post-conformance schema
93
+ lock; the frozen v1 bundle and its root digest are unchanged. To replay a recorded red/green
94
+ witness, see [witness import](witness-import.md).
@@ -0,0 +1,167 @@
1
+ # Orchestration qualification v1
2
+
3
+ ## Delivery contract
4
+
5
+ Deliver a named, version-pinned qualification profile with a sealed obligation inventory, executable
6
+ reference/fault/neutral worlds, independent observations, deterministic receipts and domain-aware
7
+ replay. A test-only receipt cannot satisfy a cache, wrapper, selection or CI obligation.
8
+
9
+ The public reproduction is authored from scratch. Private consumer sources and resources are not
10
+ distributed. Approval gates, brokers, shared skills and merge authorization are outside this change.
11
+
12
+ ## Work and acceptance
13
+
14
+ 1. Inventory the published CLI/SDK/MCP and consumer version/test forms; preserve concurrent work.
15
+ 2. Add versioned contracts/core and qualify native Bun assertions without changing frozen v1-v4.
16
+ 3. Execute real pinned Turbo cold/warm/invalidation, selection and failure propagation campaigns.
17
+ 4. Expose CLI/SDK/MCP, retain receipts/replay and the obligation matrix; run `pnpm check`.
18
+ 5. Freeze the aggregate candidate and obtain independent proof-integrity review before publishing PR.
19
+
20
+ Contracts/core and Bun collection are authored in separate worktrees. The lead integrates, owns the
21
+ campaign, verifies the complete candidate and publishes. Operational errors never earn detection.
22
+
23
+ ## Qualification boundary
24
+
25
+ Local configuration analysis, local command execution and hosted CI observation are separate
26
+ obligations. No local fixture establishes Bitbucket execution. Unknown/omitted consumer suites stay
27
+ open. A qualified public reproduction does not establish whole-consumer readiness.
28
+
29
+ `trusted-local` is explicitly unsandboxed. Digests establish replay integrity and domain binding;
30
+ they do not authenticate the producer or cause test reexecution.
31
+
32
+ ## Existing capabilities and the consumer
33
+
34
+ The existing `verify` CLI, SDK `verify`/`verifyV2`/`verifyV3`/`verifyV4`, MCP verification tools and versioned
35
+ manifest replay remain available. Node uses its owned reporter and shallow `ERR_ASSERTION`
36
+ attribution. The frozen Bun v3 profile admits only errors issued by `assertledger/bun` helpers.
37
+ The integrated v4 baseline also supports designated Bun tests with native expectations and imported
38
+ witness execution. Its controls load/register a selected test without executing it; this qualification
39
+ profile executes its declared complete suites in the reference and neutral worlds.
40
+ Those paths prove their declared test campaign; they do not supply Turbo, wrapper or hosted-CI
41
+ qualification. The native qualification collector retains a separate event/JUnit protocol and
42
+ passive matcher boundary, and reuses the v4 parser's per-test timeout refusal rule.
43
+ The existing structured-command v1 protocol requires exit zero for `PASS`; it cannot represent a
44
+ successful propagation check whose tested command intentionally exits seven. This extension reuses
45
+ the bounded process runner, canonical hashing, Node reporter and existing versioned contracts.
46
+
47
+ Source inspection of the incident consumer found Bun **1.4.2**, pnpm **12.9.1** and Turbo **2.11.7**;
48
+ the Vitest compatibility dependency is **5.0.3**. Thirty workspace manifests declare tests, nine
49
+ declare E2E, and ten delivery suites use `node:test` under Bun. Actual forms include callback
50
+ contexts, generated registrations, TypeScript/TSX, helper imports, native assertions, preloads,
51
+ isolation, filters and a development-condition test route. These pins and counts are an inventory,
52
+ not qualification of those private suites. The published reproduction contains independently
53
+ authored generic sources only.
54
+
55
+ The new Bun profile pins the full runtime revision and qualifies five passive native matchers and
56
+ three Node assertions. Its [capability boundary](../integrations/bun-native/README.md) names every
57
+ qualified form. Unsupported assertions, active operands, custom matchers, absent files, skips,
58
+ hooks, compilation/collection failures and no tests stay operational. They cannot earn detection.
59
+
60
+ ## Actions, observations and decisions
61
+
62
+ An operator seals the inventory, worlds, actions and exact expected checks before execution. Only
63
+ candidate file overlays on explicitly allowed paths are accepted afterward. The sealed plan digest
64
+ must be anchored outside candidate control; this API does not create an authority or security boundary.
65
+
66
+ Adapters collect facts. The pure core compares them with the operator checks, requires stable
67
+ reference and neutral runs and the specifically declared target mismatches in every attempt.
68
+ Missing reports, duplicates, contradictions, unobserved suite files and undeclared mismatches are
69
+ not admission. Receipts list `coveredGuaranteeIds`, `openGuaranteeIds`, reasons and check IDs.
70
+
71
+ | Guarantee | Action and observations | Oracle | Executable discriminant |
72
+ |---|---|---|---|
73
+ | Extracted command tests | Owned Node reporter, actual started files and TAP completion | Positive discovery, assertion attribution, exit consistency | Changed command shape |
74
+ | Native Bun tests | Pinned runtime, signed event pipe, JUnit concordance, actual files | Matcher-issued error belongs to the same test | Changed native assertion value; getter/crash refusals |
75
+ | Selection | Real Turbo summary, selected IDs and dependency closure | Exact required task IDs | Required dependency removed; actual empty filter |
76
+ | Cache | Cold/warm/invalidation; fresh external execution traces; deleted/restored output bytes | Required executions and hashes equal expected values | Forgotten input; incomplete restoration; wrongly cached disabled task |
77
+ | Propagation and wrappers | Actual leaf → Turbo → wrappers → terminal exit, fresh semantic completion report and required-stage traces | Exact expected nonzero exit, matching attestation and mandatory trace | Absorbed exit, omitted stage, empty filter, later success, stale success; ordinary throws/compile/spawn errors stay operational |
78
+ | CI configuration | Bun YAML parser outside the mutated candidate route; anchors resolved; step/stage/parallel ancestry, conditions and triggers retained | Required route/step/command/execution-policy relationships | Pull-request gate disconnected, conditional gate, manual gate |
79
+ | Hosted CI | Independently signed external observation | Pinned observer, exact commit/base/plan/input, executed steps and terminal success | Bad signature, wrong domain, missing/skipped step, failed terminal |
80
+ | Receipt/replay | Strict parsing, recomputed bindings, decisions, digests and external domain | Complete evidence matches its actual validity domain | Tamper, resealed summary forgery, duplicate/missing/rebound report, changed mechanism |
81
+
82
+ The public campaign executes fifteen fault worlds, reference and neutral twice. Cache directories
83
+ are distinct for each world/attempt and reused only between that attempt's ordered phases. Task
84
+ traces use a fresh nonce and a file outside the cache on every action. A cached log is never an
85
+ execution observation. Configurations, tool binaries, environments and source inputs are sealed;
86
+ the loaded proof mechanism has a separate digest. Root dependency directories are excluded from
87
+ this snapshot profile; repositories needing them require an explicit admitted dependency snapshot.
88
+
89
+ ## CLI, SDK and MCP
90
+
91
+ ```text
92
+ assertledger qualification-plan plan.json
93
+ assertledger qualify request.json --allow-unsafe-execution
94
+ assertledger qualification-replay receipt.json --domain current-domain.json
95
+ assertledger qualification-ci receipt.json --observation signed-pipeline.json
96
+ ```
97
+
98
+ `qualify` exits 0 for `QUALIFIED`, 3 for `OPEN`, 2 for `REJECTED`; unauthorized execution and invalid
99
+ replay exit 4. SDK methods are `sealQualificationPlan`, `qualifyOrchestration`, `replayQualification`
100
+ and `admitQualificationCi`. MCP exposes plan, replay and CI import in read-only mode; execution is
101
+ available only when the server operator enabled unsafe execution, within its allowed repository roots.
102
+
103
+ Structured command observations can additionally use `observe.report`: the adapter writes canonical
104
+ JSON plus a newline to `ASSERTLEDGER_QUALIFICATION_RESULT_FILE`, with exactly `protocolVersion`,
105
+ `nonce` and `facts`. The nonce is `ASSERTLEDGER_QUALIFICATION_NONCE`. These facts remain in the
106
+ `report` namespace and never override engine-measured process facts. Missing, incomplete, stale or
107
+ noncanonical reports become collection failures. Such reports are adapter-reported observations.
108
+
109
+ For the `command` adapter, a normal zero exit supplies `commandOutcome: PASS`. A numeric nonzero
110
+ exit is operational unless the fresh canonical report additionally attests
111
+ `facts.commandOutcome: EXPECTED_FAILURE` and `facts.exitCode` equal to the engine-measured exit.
112
+ The collector exposes that admitted semantic outcome separately from `report`; report fields never
113
+ replace the measured exit. The public fixture writes the report only after normal leaf/wrapper
114
+ completion, checks each child report before forwarding a nonzero exit, and emits no completion
115
+ on spawn errors, signals, null statuses, ordinary exceptions or compilation failures. Completion
116
+ reports are trusted-local adapter statements, not authenticated evidence: a malicious unsandboxed
117
+ candidate can forge them. They cannot select operator checks or establish hosted CI admission.
118
+ The Turbo summary collector has no qualified nonzero completion protocol; a numeric Turbo
119
+ failure remains a collection error even when its summary is present. The propagation fixture
120
+ qualifies its terminal command separately through the leaf/wrapper completion chain.
121
+
122
+ The CI configuration collector retains the complete route/step/stage/parallel ancestry, positions,
123
+ conditions, manual/automatic triggers and parallel fail-fast settings. Commands alone do not
124
+ establish an unconditional gate. This narrow local profile supports only the documented execution
125
+ fields; other step/group fields and pipeline kinds are refused as collection failures rather than
126
+ silently projected away. Preserved conditions are configuration facts, not a claim that a hosted
127
+ runner evaluated or executed them.
128
+
129
+ ## Independent hosted CI admission
130
+
131
+ `ciTrust` is part of the operator-sealed plan, including a pinned Ed25519 public key, observer,
132
+ repository URI identity (`workspace/repository`) and required step command digests. No local
133
+ campaign generates this trust root or a hosted-pipeline observation. The external observer signs
134
+ canonical JSON `{domain: "ASSERTLEDGER_CI_OBSERVATION_V1", statement: unsignedStatement}`.
135
+ The statement names the pipeline URL/identity, repository, exact commit/base, plan/input/candidate
136
+ digests, actual executed steps and terminal result. The key's owner must observe those facts through
137
+ an independently trusted CI channel. A signature alone cannot establish that its signer is honest.
138
+
139
+ CI import accepts only an intact existing receipt with that unchanged trust root. Unit signing
140
+ fixtures test the verifier; they are never observations of Bitbucket. No real consumer pipeline
141
+ observation is delivered by this local public campaign, so the consumer's hosted CI obligation stays open.
142
+
143
+ ## Replay and reuse
144
+
145
+ Replay recalculates schema, integrity, completeness, deterministic decisions and the optional
146
+ external validity domain. `reexecuted` and `producerAuthenticated` are always false. It does not
147
+ rerun tests, authenticate local observations or independently confirm a pipeline. The CI verifier
148
+ checks a supplied observation signature against the trust policy; it does not authenticate the
149
+ receipt maker or infer a hosted observation from local execution.
150
+
151
+ For reuse, supply the current externally trusted plan/candidate/input/commit/base and loaded
152
+ `mechanismDigest`. An old saved domain file establishes its original domain only. Changed tools,
153
+ inputs, environment, scope, adapters or proof policy invalidate the dependent qualification. Every
154
+ change to the proof mechanism or policy requires fresh independent validation. Receipt replay never
155
+ grants approval or merge authority.
156
+
157
+ ## Compatibility and delivery
158
+
159
+ The four qualification v1 schemas are additive. Existing verification/manifest v1–v4, conformance
160
+ v1 projections, schema bytes and the old extension lock remain unchanged. The new schema lock is
161
+ separate. [Migration notes](migration-orchestration-qualification-v1.md) describe opt-in adoption.
162
+
163
+ The complete candidate is verified with `pnpm check`, package smoke, the pinned public campaign,
164
+ negative gate witnesses and a fresh independent aggregate proof review. Local verification, hosted
165
+ GitHub CI, review and PR publication are separate delivery facts. Consumer readiness is conditional
166
+ on the [required obligation inventory](../examples/orchestration/consumer-obligations.json), never
167
+ on the public profile's success alone.
package/docs/reference.md CHANGED
@@ -22,6 +22,8 @@ assertledger analyze . --json
22
22
  assertledger schema verification-request --json
23
23
  assertledger verify assertledger.request.json --allow-unsafe-execution --json
24
24
  assertledger verify assertledger.container-request.json --container-runtime '["docker"]' --json
25
+ assertledger verify assertledger.windows-request.json --allow-windows-native-execution --json
26
+ assertledger import-witness witness.json --out evidence --allow-unsafe-execution --json
25
27
  assertledger replay assertledger.manifest.json --json
26
28
  assertledger provider --json
27
29
  assertledger export assertledger.export-request.json --json
@@ -69,6 +71,11 @@ results go to stdout. Diagnostics go to stderr. `assertledger mcp` reserves stdo
69
71
  trusted-local campaign and sets the request's local acknowledgement before validation. The flag does
70
72
  not create a sandbox.
71
73
 
74
+ `--allow-windows-native-execution` authorizes a v4 request with `windows-native` isolation on a Windows
75
+ host, recorded as `WINDOWS_NATIVE_UNSANDBOXED`; it cannot be combined with the other backend flags.
76
+ `import-witness` replays a recorded red/green witness under exactly one operator-chosen backend; see
77
+ [witness import](witness-import.md) and the [v4 migration guide](migration-verification-v4.md).
78
+
72
79
  A v2 request with `container` isolation runs without that flag: each execution uses a fresh
73
80
  container from a digest-pinned local image through the operator's `--container-runtime` JSON argv,
74
81
  which defaults to `["docker"]`. `check` selects the same backend with `--container-image`. Combining
@@ -0,0 +1,67 @@
1
+ # Witness import
2
+
3
+ `assertledger import-witness` replays a recorded red/green witness as a v4 campaign and verifies it.
4
+ The witness states what was observed; AssertLedger observes it again under its own taxonomy.
5
+
6
+ ## Request
7
+
8
+ A `witness-import-request.v1.json` document names:
9
+
10
+ - `repository`: the root, exclusions, `includeDependencies` and the `git` mode, as in a
11
+ [v4 request](migration-verification-v4.md);
12
+ - `adapter`: `bun-test-designated`, with its executable and per-test timeout;
13
+ - `test` and `expectedFailure`: the existing designated test and the first line of its failure;
14
+ - `targets`: each mutated file with its `beforeDigest`, the SHA-256 of the bytes it replaces, and
15
+ its mutated `content`;
16
+ - `neutral`: an operator-supplied neutral control and its reason, or `null`;
17
+ - `requiredAttempts` and `timeoutMsPerExecution`.
18
+
19
+ Each target must still hold its base bytes (`WITNESS_TARGET_BASE_MISMATCH`) and its content must
20
+ differ from them (`WITNESS_TARGET_UNCHANGED`).
21
+
22
+ ## Campaign
23
+
24
+ The repository is the reference world, the targets form the one target world, and the neutral
25
+ control is the operator's, or an identity control whose limit the manifest states. The policy is
26
+ fixed: every attempt in the target world must be an assertion failure attributed to the designated
27
+ test with the expected line, and every reference and neutral attempt must pass.
28
+
29
+ ## Backend
30
+
31
+ The witness never selects its backend. The operator passes exactly one of:
32
+
33
+ ```sh
34
+ assertledger import-witness witness.json --out evidence --container-image oven/bun@sha256:DIGEST \
35
+ --container-runtime '["wsl.exe","-d","Ubuntu","--exec","docker"]'
36
+ assertledger import-witness witness.json --out evidence --allow-unsafe-execution
37
+ assertledger import-witness witness.json --out evidence --allow-windows-native-execution
38
+ ```
39
+
40
+ `--env NAME` replaces the default host environment allowlist of a local backend. Without a backend
41
+ the command refuses with exit code 4 and writes nothing.
42
+
43
+ ## Output
44
+
45
+ `--out` must be a new directory outside the repository. It receives `executed-request.json`,
46
+ `summary.md` and, written last, `manifest.json`. The summary states the verdict, every observation,
47
+ the isolation level and the limitations; `--json` prints the manifest instead. The exit code follows
48
+ the verdict. Accept the witness only on `VERIFIED` plus `assertledger replay <manifest> --json`
49
+ with `valid: true`.
50
+
51
+ ## A repository with installed dependencies
52
+
53
+ A Bun workspace installed with the isolated linker holds links in `node_modules`. Replay such a
54
+ witness from an archive of the exact revision with a hoisted install, and keep the dependencies in
55
+ the snapshot:
56
+
57
+ ```sh
58
+ git -C <repository> archive --format=tar --output=archive.tar <revision>
59
+ tar -xf archive.tar -C witness-repository
60
+ cd witness-repository
61
+ bun install --frozen-lockfile --linker hoisted --ignore-scripts
62
+ ```
63
+
64
+ Set `includeDependencies: true`. Workspace packages stay linked in `node_modules`; a link outside
65
+ the designated test's closure is left out and recorded, and one inside it refuses the import. A
66
+ test that asks Git for its tracked files needs `git: "synthesized"`. When a snapshot copy is slow
67
+ to read the first time, for example under on-access scanning, declare a per-test timeout that fits.
@@ -0,0 +1,68 @@
1
+ # Public orchestration qualification
2
+
3
+ This independently authored workspace reproduces the evidence gap behind incident #911:
4
+ two extractible `node:test` assertions check command shapes and keep passing while actual
5
+ cache, failure propagation and pipeline routing faults are present. It contains no private
6
+ consumer source, URLs, credentials or hosted infrastructure.
7
+
8
+ The named profile `public-turbo-orchestration-2.11.7-bun-1.4.2` requires Turbo 2.11.7,
9
+ Bun 1.4.2, pnpm 12.9.1 and the exact executable identities recorded in its manifest.
10
+ Run the builder under Node with `tsx`, providing absolute paths to those executables:
11
+
12
+ ```text
13
+ node node_modules/tsx/dist/cli.mjs scripts/qualify-orchestration-profile.ts --turbo=/absolute/turbo --bun=/absolute/bun --pnpm=/absolute/pnpm --output=/absolute/evidence --commit=reviewed-commit --base=reviewed-base
14
+ ```
15
+
16
+ On Windows `--pnpm` names the installed native `pnpm.exe`, rather than a PowerShell
17
+ shim, Corepack entry point or shell string. The selected tool directories precede the
18
+ operator's PATH. PATH and essential Windows environment values are sealed in the plan.
19
+ No tool installation is performed by the campaign builder.
20
+
21
+ Provision qualification tools in a separate disposable directory, preserving its generated
22
+ lockfile, rather than adding dependencies to AssertLedger or a consumer repository:
23
+
24
+ ```text
25
+ pnpm --dir /absolute/qualification-tools add --ignore-scripts --save-exact turbo@2.11.7 pnpm@12.9.1
26
+ ```
27
+
28
+ Use the platform-specific native Turbo and pnpm executables inside that directory's
29
+ `node_modules/.pnpm` distributions. Install Bun 1.4.2 separately using the documented
30
+ platform installation method and pass its real executable. The builder probes versions
31
+ and hashes executable bytes. It deliberately refuses a different version. Provisioning
32
+ needs registry access; fixture setup runs offline with lifecycle scripts disabled.
33
+ Workspace links and normalized lockfiles are prepared before cold cache actions.
34
+
35
+ The operator's manifest declares seven obligations before execution. Reference and
36
+ documentation-only neutral worlds must pass. Fifteen declared fault worlds must mismatch
37
+ their named discriminants in two independent attempts. The leaf, Turbo and both wrapper
38
+ layers must return 7, as observed on this pinned profile. The expected nonzero reference result is not classified as a regression
39
+ assertion or a successful target kill.
40
+ Each layer validates the fresh nonce-bound semantic completion of its failed child, then emits
41
+ its own report with the observed exit. Ordinary errors, compilation failures and missing child
42
+ executables cannot supply that report. The trusted-local fixture reports are not authenticated.
43
+
44
+ Turbo build actions select the application and its leaf dependency. Each attempt has an
45
+ independent local cache, reused only across cold, warm and input-invalidation actions.
46
+ Warm actions delete generated outputs. Observations collect the real Turbo JSON summary,
47
+ selected task IDs, input task hashes, cache states, hashes of both required outputs, and
48
+ fresh action-specific nonce traces written by executing tasks. Cached console output does
49
+ not establish execution. The `check` task has caching disabled and must execute twice.
50
+
51
+ The cache worlds omit the verdict input, omit a restored output, or wrongly cache the
52
+ uncached task. Propagation worlds absorb an exit code, omit a mandatory stage, select no
53
+ work, overwrite failure with a later successful command, or reuse a seeded success.
54
+ The selected-task world removes the required dependency from the task graph. The CI worlds
55
+ disconnect the pull-request gate, add an unfulfilled changeset condition or make the gate manual
56
+ while preserving its command. The parser retains execution policy and full stage/parallel/step
57
+ ancestry, and refuses unsupported execution fields. It is an engine-owned asset outside the route
58
+ it observes. Configuration observation does not establish hosted execution.
59
+
60
+ Outputs are `manifest.json`, `receipt.json`, `replay.json`, `matrix.json` and
61
+ `incident-reproduction.json`. The builder exits nonzero if the profile is not QUALIFIED,
62
+ replay fails, or command-shape extraction detects an orchestration fault unexpectedly.
63
+
64
+ This public profile qualifies only its named local obligations. It never declares the
65
+ private consumer ready. Private suites, preloads, wrappers, browser/E2E/coverage behavior
66
+ and hosted Bitbucket execution require their own profile and evidence. Static CI routing
67
+ and local commands cannot discharge a hosted-pipeline obligation. Replay checks integrity
68
+ and deterministic semantics; it does not run commands again or authenticate their producer.
@@ -0,0 +1,101 @@
1
+ {
2
+ "profileId": "modelo-suite-911-consumer-v1",
3
+ "consumerReady": false,
4
+ "qualificationStatus": "OPEN",
5
+ "inventoryAuthority": "operator inventory from consumer source inspection; independent from candidate selection",
6
+ "sourcePins": {
7
+ "bun": "1.4.2",
8
+ "pnpm": "12.9.1",
9
+ "turbo": "2.11.7",
10
+ "vitestCompatibility": "5.0.3"
11
+ },
12
+ "suiteGroups": [
13
+ {
14
+ "id": "workspace-tests",
15
+ "declaringManifests": 30,
16
+ "extraction": "UNAVAILABLE",
17
+ "status": "OPEN"
18
+ },
19
+ {
20
+ "id": "workspace-e2e",
21
+ "declaringManifests": 9,
22
+ "extraction": "UNAVAILABLE",
23
+ "status": "OPEN"
24
+ },
25
+ { "id": "bun-node-delivery", "suiteFiles": 10, "extraction": "UNAVAILABLE", "status": "OPEN" },
26
+ {
27
+ "id": "repository-shell",
28
+ "suiteFiles": null,
29
+ "extraction": "UNAVAILABLE",
30
+ "status": "OPEN",
31
+ "reason": "exact executed shell suite inventory requires consumer campaign"
32
+ }
33
+ ],
34
+ "requiredObligations": [
35
+ {
36
+ "id": "complete-consumer-test-suites",
37
+ "kind": "tests",
38
+ "status": "OPEN",
39
+ "required": true,
40
+ "reason": "private complete suites have not been executed in this public campaign"
41
+ },
42
+ {
43
+ "id": "consumer-preloads-and-test-forms",
44
+ "kind": "tests",
45
+ "status": "OPEN",
46
+ "required": true,
47
+ "reason": "Vitest compatibility, mocks, DOM, isolation, development conditions and filter forms need exact-consumer witnesses"
48
+ },
49
+ {
50
+ "id": "consumer-task-selection",
51
+ "kind": "selection",
52
+ "status": "OPEN",
53
+ "required": true,
54
+ "reason": "public dependency-selection witnesses qualify the public fixture, not the consumer graph"
55
+ },
56
+ {
57
+ "id": "consumer-turbo-cache",
58
+ "kind": "cache",
59
+ "status": "OPEN",
60
+ "required": true,
61
+ "reason": "actual consumer configuration, persisted cache and dependencies need cold/warm/invalidation evidence"
62
+ },
63
+ {
64
+ "id": "consumer-error-propagation",
65
+ "kind": "propagation",
66
+ "status": "OPEN",
67
+ "required": true,
68
+ "reason": "actual consumer command route requires its own leaf-to-terminal witness"
69
+ },
70
+ {
71
+ "id": "consumer-wrappers",
72
+ "kind": "wrapper",
73
+ "status": "OPEN",
74
+ "required": true,
75
+ "reason": "shell/container/preflight wrapper closure requires actual route evidence"
76
+ },
77
+ {
78
+ "id": "consumer-ci-configuration",
79
+ "kind": "ci-config",
80
+ "status": "OPEN",
81
+ "required": true,
82
+ "reason": "public YAML qualification is separate from the consumer configuration"
83
+ },
84
+ {
85
+ "id": "consumer-ci-local-commands",
86
+ "kind": "ci-local",
87
+ "status": "OPEN",
88
+ "required": true,
89
+ "reason": "actual local CI commands and coverage gate must execute with controlled inputs"
90
+ },
91
+ {
92
+ "id": "consumer-bitbucket-pipeline",
93
+ "kind": "ci-live",
94
+ "status": "OPEN",
95
+ "required": true,
96
+ "reason": "no independent exact-commit/base executed-step and terminal pipeline observation supplied"
97
+ }
98
+ ],
99
+ "reusableQualification": "public-turbo-orchestration-2.11.7-bun-1.4.2 only under its exact receipt validity conditions",
100
+ "mergeAuthority": false
101
+ }
@@ -0,0 +1,10 @@
1
+ definitions:
2
+ steps:
3
+ - step: &gate
4
+ name: Public qualification gate
5
+ script:
6
+ - bun wrapper.mjs
7
+ pipelines:
8
+ pull-requests:
9
+ '**':
10
+ - step: *gate
@@ -0,0 +1,25 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { failCommandArgs, wrapperCommandArgs } from "./commands.mjs";
4
+
5
+ test("structured Turbo failure selection", () => {
6
+ assert.deepEqual(failCommandArgs({ cacheDirectory: "mock-cache" }), [
7
+ "run",
8
+ "fail",
9
+ "--filter=@public/app...",
10
+ "--cache=local:rw",
11
+ "--cache-dir=mock-cache",
12
+ "--summarize",
13
+ ]);
14
+ assert.deepEqual(failCommandArgs({ emptySelection: true, cacheDirectory: "mock-cache" }), [
15
+ "run",
16
+ "fail",
17
+ "--filter=!@public/*",
18
+ "--cache=local:rw",
19
+ "--cache-dir=mock-cache",
20
+ "--summarize",
21
+ ]);
22
+ });
23
+ test("structured wrapper command", () => {
24
+ assert.deepEqual(wrapperCommandArgs("wrapper.mjs"), ["wrapper.mjs", "orchestrator"]);
25
+ });
@@ -0,0 +1,14 @@
1
+ export function failCommandArgs({ emptySelection = false, cacheDirectory }) {
2
+ return [
3
+ "run",
4
+ "fail",
5
+ emptySelection ? "--filter=!@public/*" : "--filter=@public/app...",
6
+ "--cache=local:rw",
7
+ `--cache-dir=${cacheDirectory}`,
8
+ "--summarize",
9
+ ];
10
+ }
11
+
12
+ export function wrapperCommandArgs(wrapperPath) {
13
+ return [wrapperPath, "orchestrator"];
14
+ }
@@ -0,0 +1,39 @@
1
+ import { readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { spawnSync } from "node:child_process";
3
+
4
+ export function complete(exitCode) {
5
+ writeFileSync(
6
+ process.env.ASSERTLEDGER_QUALIFICATION_RESULT_FILE,
7
+ `${JSON.stringify({
8
+ facts: { commandOutcome: exitCode === 0 ? "PASS" : "EXPECTED_FAILURE", exitCode },
9
+ nonce: process.env.ASSERTLEDGER_QUALIFICATION_NONCE,
10
+ protocolVersion: "1.0.0",
11
+ })}\n`,
12
+ );
13
+ }
14
+
15
+ export function execute(executable, args, suffix) {
16
+ const report = `${process.env.ASSERTLEDGER_QUALIFICATION_RESULT_FILE}.${suffix}`;
17
+ rmSync(report, { force: true });
18
+ const result = spawnSync(executable, args, {
19
+ stdio: "inherit",
20
+ env: { ...process.env, ASSERTLEDGER_QUALIFICATION_RESULT_FILE: report },
21
+ });
22
+ if (result.error || result.signal || result.status === null) {
23
+ throw new Error("PUBLIC_CHILD_OPERATIONAL_FAILURE", { cause: result.error });
24
+ }
25
+ if (result.status !== 0) {
26
+ // A normal failed leaf must attest completion through each wrapper. Numeric
27
+ // exits, compiler diagnostics and thrown errors are not semantic attestations.
28
+ const document = JSON.parse(readFileSync(report, "utf8"));
29
+ if (
30
+ document.protocolVersion !== "1.0.0" ||
31
+ document.nonce !== process.env.ASSERTLEDGER_QUALIFICATION_NONCE ||
32
+ document.facts.commandOutcome !== "EXPECTED_FAILURE" ||
33
+ document.facts.exitCode !== result.status
34
+ ) {
35
+ throw new Error("PUBLIC_CHILD_COMPLETION_MISSING");
36
+ }
37
+ }
38
+ return result.status;
39
+ }
@@ -0,0 +1,4 @@
1
+ import { test, expect } from "bun:test";
2
+ test("native Bun assertion", () => {
3
+ expect(2 + 2).toBe(4);
4
+ });
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "public-orchestration-fixture",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "packageManager": "pnpm@12.9.1"
6
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@public/app",
3
+ "version": "1.0.0",
4
+ "dependencies": {
5
+ "@public/leaf": "workspace:*"
6
+ },
7
+ "scripts": {
8
+ "build": "bun ../../task.mjs build",
9
+ "check": "bun ../../task.mjs check",
10
+ "fail": "bun ../../task.mjs fail"
11
+ }
12
+ }