@quolu/lattice 0.52.3 → 0.53.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 (188) hide show
  1. package/LICENSE +147 -147
  2. package/README.ja.md +355 -355
  3. package/README.md +258 -258
  4. package/bin/lattice-bridge.mjs +25 -0
  5. package/bin/lattice-hub.mjs +67 -0
  6. package/bin/lattice-mcp.mjs +0 -0
  7. package/bin/lattice-scripted-adapter.mjs +0 -0
  8. package/bin/lattice-scripted-worker.mjs +0 -0
  9. package/bin/lattice-work-order-adapter.mjs +0 -0
  10. package/bin/lattice.mjs +0 -0
  11. package/docs/bridge-setup.md +132 -132
  12. package/docs/schemas/lattice.executor_packet.v1.schema.json +57 -57
  13. package/docs/schemas/lattice.executor_receipt.v1.schema.json +66 -66
  14. package/docs/schemas/lattice.phase_todo_revision.v3.schema.json +360 -360
  15. package/docs/schemas/lattice.plan_create_input.v1.schema.json +56 -56
  16. package/docs/schemas/lattice.plan_create_input.v2.schema.json +72 -72
  17. package/docs/schemas/lattice.plan_create_input.v3.schema.json +81 -81
  18. package/docs/schemas/lattice.plan_create_input.v4.schema.json +85 -85
  19. package/docs/schemas/lattice.run_request.v1.schema.json +238 -238
  20. package/docs/schemas/lattice.runtime_adapter_capabilities.v2.schema.json +55 -55
  21. package/docs/schemas/lattice.runtime_adapter_registration_input.v1.schema.json +78 -78
  22. package/docs/schemas/lattice.runtime_adapter_registration_input.v2.schema.json +86 -86
  23. package/docs/schemas/lattice.todo_extraction.v2.schema.json +298 -298
  24. package/docs/schemas/lattice.todo_extraction.v3.schema.json +146 -146
  25. package/docs/schemas/lattice.todo_revision.v2.schema.json +260 -260
  26. package/docs/schemas/lattice.todo_revision_set.v3.schema.json +363 -363
  27. package/package.json +103 -103
  28. package/sensor/LICENSE +21 -21
  29. package/sensor/NOTICE +19 -19
  30. package/sensor/dist/bin/lattice-sensor.js +9 -9
  31. package/sensor/dist/db/index.js +24 -24
  32. package/sensor/dist/db/migrations.js +41 -41
  33. package/sensor/dist/db/queries.js +164 -164
  34. package/sensor/dist/db/schema.sql +205 -205
  35. package/sensor/dist/directory.js +5 -5
  36. package/sensor/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  37. package/sensor/dist/extraction/wasm/tree-sitter-cfml.wasm +0 -0
  38. package/sensor/dist/extraction/wasm/tree-sitter-cfquery.wasm +0 -0
  39. package/sensor/dist/extraction/wasm/tree-sitter-cfscript.wasm +0 -0
  40. package/sensor/dist/extraction/wasm/tree-sitter-cobol.wasm +0 -0
  41. package/sensor/dist/extraction/wasm/tree-sitter-erlang.wasm +0 -0
  42. package/sensor/dist/extraction/wasm/tree-sitter-go.wasm +0 -0
  43. package/sensor/dist/extraction/wasm/tree-sitter-java.wasm +0 -0
  44. package/sensor/dist/extraction/wasm/tree-sitter-javascript.wasm +0 -0
  45. package/sensor/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  46. package/sensor/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  47. package/sensor/dist/extraction/wasm/tree-sitter-python.wasm +0 -0
  48. package/sensor/dist/extraction/wasm/tree-sitter-tsx.wasm +0 -0
  49. package/sensor/dist/extraction/wasm/tree-sitter-typescript.wasm +0 -0
  50. package/sensor/dist/extraction/wasm/tree-sitter-vbnet.wasm +0 -0
  51. package/sensor/dist/mcp/liveness-watchdog.js +53 -53
  52. package/sensor/dist/mcp/server-instructions.js +95 -95
  53. package/sensor/package.json +56 -56
  54. package/src/artifact-contracts-v2.mjs +325 -325
  55. package/src/artifact-contracts.mjs +895 -895
  56. package/src/boundary-compiler.mjs +712 -712
  57. package/src/boundary-observation-compiler-v2.mjs +344 -344
  58. package/src/bounded-seam.mjs +230 -230
  59. package/src/bridge-address.mjs +107 -107
  60. package/src/bridge-cli.mjs +303 -297
  61. package/src/bridge-config.mjs +382 -346
  62. package/src/bridge-daemon.mjs +378 -378
  63. package/src/bridge-hub-heartbeat.mjs +170 -0
  64. package/src/bridge-hub-protocol.mjs +198 -0
  65. package/src/bridge-hub-server.mjs +544 -0
  66. package/src/bridge-launch-agent.mjs +323 -323
  67. package/src/bridge-registrar.mjs +102 -102
  68. package/src/bridge-server.mjs +376 -376
  69. package/src/cli-help.mjs +308 -308
  70. package/src/cli-stdio.mjs +40 -40
  71. package/src/control-compiler.mjs +532 -532
  72. package/src/dag-chain.mjs +261 -261
  73. package/src/factory-diagnostics.mjs +188 -188
  74. package/src/git-process.mjs +72 -0
  75. package/src/hash-chain.mjs +76 -76
  76. package/src/hooks-cli.mjs +1057 -1057
  77. package/src/isolation-runner.mjs +409 -409
  78. package/src/node-version-guard.mjs +44 -44
  79. package/src/project-cli.mjs +3 -3
  80. package/src/project-identity.mjs +130 -130
  81. package/src/rc1-black-box-oracle.mjs +906 -906
  82. package/src/rc1-comparison.mjs +154 -154
  83. package/src/rc1-evidence-bundle.mjs +456 -456
  84. package/src/rc1-v4-campaign.mjs +708 -708
  85. package/src/rc1-v4-transform.mjs +467 -467
  86. package/src/rc1-v5-artifact-set.mjs +855 -855
  87. package/src/rc1-v5-behavior-evidence.mjs +594 -594
  88. package/src/rc1-v5-campaign.mjs +797 -797
  89. package/src/rc1-v5-transform.mjs +421 -421
  90. package/src/rc1-v6-artifact-set.mjs +807 -807
  91. package/src/rc1-v6-behavior-evidence.mjs +273 -273
  92. package/src/rc1-v6-campaign.mjs +623 -623
  93. package/src/rc1-v6-causal-binding.mjs +473 -473
  94. package/src/rc1-v6-measurement.mjs +313 -313
  95. package/src/rc2-artifact-set.mjs +1584 -1584
  96. package/src/rc2-campaign.mjs +1506 -1506
  97. package/src/rc2-delivery-policy-front-end.mjs +1079 -1079
  98. package/src/rc2-delivery-policy-oracle.mjs +134 -134
  99. package/src/rc2-delivery-policy-transform.mjs +1127 -1127
  100. package/src/rc2-rc1-transfer-front-end.mjs +511 -511
  101. package/src/rc3-actual-dogfood.mjs +652 -652
  102. package/src/rc3-dogfood-scaffold.mjs +315 -315
  103. package/src/rc3-scripted-campaign.mjs +1383 -1383
  104. package/src/rc4-stage1-dogfood.mjs +673 -673
  105. package/src/runtime-adapter-registry.mjs +524 -524
  106. package/src/runtime-cli.mjs +4588 -4588
  107. package/src/runtime-contracts.mjs +824 -824
  108. package/src/runtime-control-store.mjs +604 -604
  109. package/src/runtime-controller-protocol.mjs +587 -587
  110. package/src/runtime-decision-verifier.mjs +701 -701
  111. package/src/runtime-diff-observer.mjs +361 -361
  112. package/src/runtime-direct-os-observer.mjs +301 -301
  113. package/src/runtime-driver-state.mjs +166 -166
  114. package/src/runtime-engine.mjs +779 -779
  115. package/src/runtime-errors.mjs +356 -356
  116. package/src/runtime-event-store.mjs +189 -189
  117. package/src/runtime-front-end.mjs +925 -925
  118. package/src/runtime-gate-store.mjs +481 -481
  119. package/src/runtime-hold-recompile.mjs +916 -916
  120. package/src/runtime-io-sentinel.mjs +391 -391
  121. package/src/runtime-lifecycle-lock.mjs +294 -294
  122. package/src/runtime-managed-supervisor.mjs +1490 -1490
  123. package/src/runtime-multi-epoch-store.mjs +838 -838
  124. package/src/runtime-projection.mjs +269 -269
  125. package/src/runtime-pull-intake.mjs +1192 -1192
  126. package/src/runtime-scripted-adapter-controller.mjs +1160 -1160
  127. package/src/runtime-scripted-executor.mjs +163 -163
  128. package/src/runtime-scripted-worktree.mjs +104 -104
  129. package/src/runtime-seam-resolve.mjs +428 -428
  130. package/src/runtime-seam-treatment.mjs +173 -173
  131. package/src/runtime-socket-owner.mjs +125 -125
  132. package/src/runtime-work-order-contracts.mjs +91 -91
  133. package/src/runtime-work-order-controller.mjs +1171 -1171
  134. package/src/runtime-worktree-executor.mjs +199 -199
  135. package/src/schedulability-compiler-v2.mjs +303 -303
  136. package/src/schedulability-verifier-v2.mjs +317 -317
  137. package/src/seam-apply.mjs +549 -549
  138. package/src/seam-commit-shared.mjs +22 -22
  139. package/src/seam-commit-transform.mjs +81 -81
  140. package/src/seam-commit.mjs +18 -18
  141. package/src/seam-cost.mjs +322 -322
  142. package/src/seam-derivation.mjs +188 -188
  143. package/src/seam-gate.mjs +146 -146
  144. package/src/seam-proposal-contracts.mjs +446 -446
  145. package/src/seam-proposal-queries.mjs +521 -521
  146. package/src/seam-proposal.mjs +2011 -2011
  147. package/src/seam-ref.mjs +33 -33
  148. package/src/seam-rewrite.mjs +286 -286
  149. package/src/seam-transform.mjs +554 -554
  150. package/src/seam-verification.mjs +260 -260
  151. package/src/sensor-adapter.mjs +432 -432
  152. package/src/sensor-cli.mjs +139 -139
  153. package/src/sensor-diff.mjs +661 -661
  154. package/src/sensor-node-runtime.mjs +53 -53
  155. package/src/sensor-runtime.mjs +52 -52
  156. package/src/timestamp-contract.mjs +8 -8
  157. package/src/todo-audit-pending.mjs +91 -91
  158. package/src/todo-chain.mjs +178 -178
  159. package/src/todo-cli.mjs +8 -8
  160. package/src/todo-contracts.mjs +728 -728
  161. package/src/todo-dashboard-registry.mjs +573 -573
  162. package/src/todo-dispatch-shape.mjs +190 -190
  163. package/src/todo-gantt-html-independence.mjs +239 -239
  164. package/src/todo-gantt-html-shared.mjs +226 -226
  165. package/src/todo-gantt-html-style.mjs +131 -131
  166. package/src/todo-gantt-html.mjs +248 -248
  167. package/src/todo-gantt-layout.mjs +974 -974
  168. package/src/todo-gantt-live.mjs +361 -361
  169. package/src/todo-gantt-nested.mjs +263 -263
  170. package/src/todo-gantt-presentation.mjs +217 -217
  171. package/src/todo-gantt-scope.mjs +123 -123
  172. package/src/todo-gantt-svg.mjs +353 -353
  173. package/src/todo-independence-contracts.mjs +595 -595
  174. package/src/todo-independence-guidance.mjs +322 -322
  175. package/src/todo-independence.mjs +640 -640
  176. package/src/todo-markdown-renderer.mjs +260 -260
  177. package/src/todo-migration.mjs +448 -448
  178. package/src/todo-narrative-anchor.mjs +130 -130
  179. package/src/todo-note-store.mjs +629 -629
  180. package/src/todo-parallel-candidates.mjs +114 -114
  181. package/src/todo-revision.mjs +995 -995
  182. package/src/todo-split.mjs +472 -472
  183. package/src/todo-status.mjs +690 -690
  184. package/src/todo-store-git-transaction.mjs +418 -418
  185. package/src/todo-store.mjs +42 -21
  186. package/src/treatment-compiler.mjs +728 -728
  187. package/src/treatment-runner.mjs +656 -656
  188. package/src/witness-scaffold.mjs +180 -180
@@ -1,1506 +1,1506 @@
1
- import { spawn } from 'node:child_process';
2
- import { createHash } from 'node:crypto';
3
- import {
4
- lstat,
5
- mkdir,
6
- mkdtemp,
7
- open,
8
- readFile,
9
- realpath,
10
- rename,
11
- rm,
12
- writeFile,
13
- } from 'node:fs/promises';
14
- import path from 'node:path';
15
- import { performance } from 'node:perf_hooks';
16
- import { isDeepStrictEqual } from 'node:util';
17
-
18
- import {
19
- canonicalizeArtifact,
20
- digestArtifact,
21
- } from './artifact-contracts.mjs';
22
- import { compileBoundaryCondition } from './boundary-compiler.mjs';
23
- import { collectSensorEvidence } from './sensor-adapter.mjs';
24
- import { invokeSensorCli, LATTICE_SENSOR_CLI } from './sensor-runtime.mjs';
25
- import { runIsolatedTransform } from './isolation-runner.mjs';
26
- import { runRc1BlackBoxOracle } from './rc1-black-box-oracle.mjs';
27
- import { createRc1EvidenceBundle } from './rc1-evidence-bundle.mjs';
28
- import {
29
- RC2_ARTIFACT_PATHS,
30
- verifyRc2CampaignArtifactSet,
31
- } from './rc2-artifact-set.mjs';
32
- import { compileDeliveryPolicyBoundaryBundleV2 } from './rc2-delivery-policy-front-end.mjs';
33
- import { runRc2DeliveryPolicyOracle } from './rc2-delivery-policy-oracle.mjs';
34
- import {
35
- applyRc2DeliveryPolicyTransform,
36
- RC2_DELIVERY_POLICY_TRANSFORM_PATHS,
37
- runRc2DeliveryPolicySeamTransform,
38
- } from './rc2-delivery-policy-transform.mjs';
39
- import { compileRc1TransferBundleV2 } from './rc2-rc1-transfer-front-end.mjs';
40
- import { compileSchedulabilityGraphV2 } from './schedulability-compiler-v2.mjs';
41
- import { verifySchedulabilityPlanV2 } from './schedulability-verifier-v2.mjs';
42
-
43
- const ARTIFACT_VERSION = 'v4';
44
- const ARTIFACT_ROOTS = Object.freeze({
45
- v1: 'research/campaigns/rc2/artifacts/v1',
46
- v2: 'research/campaigns/rc2/artifacts/v2',
47
- v3: 'research/campaigns/rc2/artifacts/v3',
48
- v4: 'research/campaigns/rc2/artifacts/v4',
49
- });
50
- const SENSOR_CONFIG_REPO_PATH = 'lattice-sensor.json';
51
- const SENSOR_CONFIG_ARTIFACT_PATH = 'identity/lattice-sensor-config.json';
52
- // live実行は現在ratifiedなconfig(ADR 0053 Decision 3 epoch)との完全一致だけを受理する。
53
- // 過去epochのartifact検証はrc2-artifact-set.mjsのratified epoch allowlistが担う。
54
- const SENSOR_CONFIG_BYTES = Buffer.from(
55
- '{"exclude":["research/campaigns/**/artifacts/**/identity/","research/runs/",".lattice/todo/",".lattice/generated/"]}\n',
56
- 'utf8',
57
- );
58
- const V2_ONLY_ARTIFACT_PATHS = Object.freeze([
59
- SENSOR_CONFIG_ARTIFACT_PATH,
60
- 'predecessors/adr-0040.md',
61
- 'predecessors/rc2-v1-artifact-manifest.json',
62
- 'predecessors/rc2-v1-new-plan-version.json',
63
- ]);
64
- const V3_ONLY_ARTIFACT_PATHS = Object.freeze([
65
- 'predecessors/adr-0041.md',
66
- 'predecessors/rc2-semantic-reseal-characterization.md',
67
- 'predecessors/rc2-v2-artifact-manifest.json',
68
- 'predecessors/rc2-v2-new-plan-version.json',
69
- ]);
70
- const V4_ONLY_ARTIFACT_PATHS = Object.freeze([
71
- 'predecessors/adr-0042.md',
72
- 'predecessors/rc2-v3-version-downgrade-refutation.md',
73
- 'predecessors/rc2-v3-artifact-manifest.json',
74
- 'predecessors/rc2-v3-new-plan-version.json',
75
- ]);
76
- const RC2_V3_ARTIFACT_PATHS = Object.freeze(RC2_ARTIFACT_PATHS.filter((relativePath) => (
77
- !V4_ONLY_ARTIFACT_PATHS.includes(relativePath)
78
- )));
79
- const RC2_V2_ARTIFACT_PATHS = Object.freeze(RC2_V3_ARTIFACT_PATHS.filter((relativePath) => (
80
- !V3_ONLY_ARTIFACT_PATHS.includes(relativePath)
81
- )));
82
- const RC2_V1_ARTIFACT_PATHS = Object.freeze(RC2_V2_ARTIFACT_PATHS.filter((relativePath) => (
83
- !V2_ONLY_ARTIFACT_PATHS.includes(relativePath)
84
- )));
85
- const GIT_SHA1 = /^[0-9a-f]{40}$/;
86
- const SEMVER = /^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/;
87
- const RAW_EVIDENCE_CHUNK_BYTES = 12_000;
88
-
89
- const PRIMARY_INPUT_FILES = Object.freeze({
90
- planInput: 'research/campaigns/rc2/inputs/plan-input.json',
91
- candidateSpec: 'research/campaigns/rc2/inputs/candidate-spec-v1.json',
92
- normalManualEvidence: 'research/campaigns/rc2/inputs/manual-evidence.normal.json',
93
- partialManualEvidence: 'research/campaigns/rc2/inputs/manual-evidence.partial-state-negative.json',
94
- unknownManualEvidence: 'research/campaigns/rc2/inputs/manual-evidence.third-only-unknown.json',
95
- querySet: 'research/campaigns/rc2/inputs/query-set-v2.json',
96
- });
97
-
98
- const RC1_ARTIFACT_ROOT = 'research/campaigns/rc1/artifacts/v6';
99
- const RC1_INPUT_FILES = Object.freeze({
100
- planInput: `${RC1_ARTIFACT_ROOT}/inputs/plan-input.json`,
101
- candidateSpec: `${RC1_ARTIFACT_ROOT}/inputs/candidate-spec-v2.json`,
102
- normalManualEvidence: `${RC1_ARTIFACT_ROOT}/inputs/manual-evidence.normal.json`,
103
- negativeManualEvidence: `${RC1_ARTIFACT_ROOT}/inputs/manual-evidence.shared-state-negative.json`,
104
- querySet: `${RC1_ARTIFACT_ROOT}/inputs/query-set-v2.json`,
105
- oracle: `${RC1_ARTIFACT_ROOT}/inputs/behavior-oracle-v2.json`,
106
- });
107
-
108
- const PRIMARY_ARTIFACT_INPUTS = Object.freeze({
109
- planInput: 'inputs/primary/plan-input.json',
110
- candidateSpec: 'inputs/primary/candidate-spec-v1.json',
111
- normalManualEvidence: 'inputs/primary/manual-evidence.normal.json',
112
- partialManualEvidence: 'inputs/primary/manual-evidence.partial-state-negative.json',
113
- unknownManualEvidence: 'inputs/primary/manual-evidence.third-only-unknown.json',
114
- querySet: 'inputs/primary/query-set-v2.json',
115
- });
116
-
117
- const RC1_ARTIFACT_INPUTS = Object.freeze({
118
- planInput: 'inputs/rc1/plan-input.json',
119
- candidateSpec: 'inputs/rc1/candidate-spec-v2.json',
120
- normalManualEvidence: 'inputs/rc1/manual-evidence.normal.json',
121
- negativeManualEvidence: 'inputs/rc1/manual-evidence.shared-state-negative.json',
122
- querySet: 'inputs/rc1/query-set-v2.json',
123
- oracle: 'inputs/rc1/behavior-oracle-v2.json',
124
- });
125
-
126
- const SOURCE_IDENTITIES = Object.freeze([
127
- ['src/sensor-adapter.mjs', 'identity/lattice-sensor-adapter.mjs', new URL('./sensor-adapter.mjs', import.meta.url)],
128
- ['src/rc1-black-box-oracle.mjs', 'identity/rc1-black-box-oracle.mjs', new URL('./rc1-black-box-oracle.mjs', import.meta.url)],
129
- ['src/boundary-compiler.mjs', 'identity/rc1-boundary-compiler.mjs', new URL('./boundary-compiler.mjs', import.meta.url)],
130
- ['src/rc1-evidence-bundle.mjs', 'identity/rc1-evidence-bundle.mjs', new URL('./rc1-evidence-bundle.mjs', import.meta.url)],
131
- ['src/rc2-artifact-set.mjs', 'identity/rc2-artifact-set.mjs', new URL('./rc2-artifact-set.mjs', import.meta.url)],
132
- ['src/rc2-campaign.mjs', 'identity/rc2-campaign.mjs', new URL(import.meta.url)],
133
- ['src/rc2-delivery-policy-front-end.mjs', 'identity/rc2-delivery-policy-front-end.mjs', new URL('./rc2-delivery-policy-front-end.mjs', import.meta.url)],
134
- ['src/rc2-delivery-policy-oracle.mjs', 'identity/rc2-delivery-policy-oracle.mjs', new URL('./rc2-delivery-policy-oracle.mjs', import.meta.url)],
135
- ['src/rc2-delivery-policy-transform.mjs', 'identity/rc2-delivery-policy-transform.mjs', new URL('./rc2-delivery-policy-transform.mjs', import.meta.url)],
136
- ['src/rc2-rc1-transfer-front-end.mjs', 'identity/rc2-rc1-transfer-front-end.mjs', new URL('./rc2-rc1-transfer-front-end.mjs', import.meta.url)],
137
- ['src/schedulability-compiler-v2.mjs', 'identity/schedulability-compiler-v2.mjs', new URL('./schedulability-compiler-v2.mjs', import.meta.url)],
138
- ['src/schedulability-verifier-v2.mjs', 'identity/schedulability-verifier-v2.mjs', new URL('./schedulability-verifier-v2.mjs', import.meta.url)],
139
- ]);
140
-
141
- const PREDECESSOR_FILES = Object.freeze({
142
- 'predecessors/adr-0031.md': 'docs/adr/0031-rc1-v6-phase-gate-support.md',
143
- 'predecessors/adr-0032.md': 'docs/adr/0032-rc2-bounded-graph-compiler-and-three-way-seam.md',
144
- 'predecessors/adr-0037.md': 'docs/adr/0037-rc2-delivery-policy-transform-transaction.md',
145
- 'predecessors/adr-0038.md': 'docs/adr/0038-rc2-closed-loop-version-and-artifact-contract.md',
146
- 'predecessors/rc1-v6-artifact-manifest.json': `${RC1_ARTIFACT_ROOT}/artifact-manifest.json`,
147
- 'predecessors/rc1-v6-plan.md': 'docs/archive/2026-07-16-plan-lattice-research-campaign-1-v6-phase-supported.md',
148
- 'predecessors/rc1-v6-seam.patch': `${RC1_ARTIFACT_ROOT}/transform/seam.patch`,
149
- 'predecessors/rc1-v6-transform-artifact.json': `${RC1_ARTIFACT_ROOT}/transform/transform-artifact.json`,
150
- 'predecessors/rc1-v6-transform-receipt.json': `${RC1_ARTIFACT_ROOT}/transform/transform-receipt.json`,
151
- 'predecessors/adr-0040.md': 'docs/adr/0040-rc2-post-publication-lattice-sensor-scope-and-artifact-v2.md',
152
- 'predecessors/rc2-v1-artifact-manifest.json': `${ARTIFACT_ROOTS.v1}/artifact-manifest.json`,
153
- 'predecessors/rc2-v1-new-plan-version.json': `${ARTIFACT_ROOTS.v1}/new-plan-version.json`,
154
- 'predecessors/adr-0041.md': 'docs/adr/0041-rc2-artifact-semantic-oracle-mutation-binding.md',
155
- 'predecessors/rc2-semantic-reseal-characterization.md': 'docs/evidence/2026-07-16-rc2-artifact-semantic-reseal-characterization.md',
156
- 'predecessors/rc2-v2-artifact-manifest.json': `${ARTIFACT_ROOTS.v2}/artifact-manifest.json`,
157
- 'predecessors/rc2-v2-new-plan-version.json': `${ARTIFACT_ROOTS.v2}/new-plan-version.json`,
158
- 'predecessors/adr-0042.md': 'docs/adr/0042-rc2-artifact-version-witness-epoch-and-v4.md',
159
- 'predecessors/rc2-v3-version-downgrade-refutation.md': 'docs/evidence/2026-07-16-rc2-v3-version-downgrade-refutation.md',
160
- 'predecessors/rc2-v3-artifact-manifest.json': `${ARTIFACT_ROOTS.v3}/artifact-manifest.json`,
161
- 'predecessors/rc2-v3-new-plan-version.json': `${ARTIFACT_ROOTS.v3}/new-plan-version.json`,
162
- });
163
-
164
- const RUN_IDS = Object.freeze([
165
- 'primary-control-1',
166
- 'primary-control-2',
167
- 'primary-treatment-1',
168
- 'primary-treatment-2',
169
- 'rc1-transfer-control-1',
170
- 'rc1-transfer-treatment-1',
171
- ]);
172
-
173
- function plainRecord(value) {
174
- return value !== null
175
- && typeof value === 'object'
176
- && !Array.isArray(value)
177
- && Object.getPrototypeOf(value) === Object.prototype;
178
- }
179
-
180
- function exactRecord(value, keys) {
181
- if (!plainRecord(value)) return false;
182
- const actual = Object.keys(value).sort();
183
- const expected = [...keys].sort();
184
- return actual.length === expected.length
185
- && actual.every((key, index) => key === expected[index]);
186
- }
187
-
188
- function sha256(bytes) {
189
- return createHash('sha256').update(bytes).digest('hex');
190
- }
191
-
192
- function jsonBytes(value) {
193
- return Buffer.from(canonicalizeArtifact(value), 'utf8');
194
- }
195
-
196
- function roundedMilliseconds(startedAt) {
197
- return Math.round((performance.now() - startedAt) * 1_000) / 1_000;
198
- }
199
-
200
- function run(command, args, { cwd, input, allowExitCodes = [0] } = {}) {
201
- return new Promise((resolve, reject) => {
202
- const child = spawn(command, args, {
203
- cwd,
204
- shell: false,
205
- stdio: ['pipe', 'pipe', 'pipe'],
206
- env: { ...process.env, NO_COLOR: '1' },
207
- });
208
- const stdout = [];
209
- const stderr = [];
210
- child.stdout.on('data', (chunk) => stdout.push(chunk));
211
- child.stderr.on('data', (chunk) => stderr.push(chunk));
212
- child.once('error', reject);
213
- child.once('close', (code, signal) => {
214
- const result = {
215
- code,
216
- signal,
217
- stdout: Buffer.concat(stdout),
218
- stderr: Buffer.concat(stderr),
219
- };
220
- if (allowExitCodes.includes(code) && signal === null) resolve(result);
221
- else reject(Object.assign(
222
- new Error(`${command} failed (${signal ?? code}): ${result.stderr.toString('utf8').trim()}`),
223
- result,
224
- ));
225
- });
226
- child.stdin.end(input);
227
- });
228
- }
229
-
230
- async function captureExecutionIdentity(repoRoot) {
231
- const sources = [];
232
- const payloads = new Map();
233
- for (const [runtimePath, artifactRef, sourceUrl] of SOURCE_IDENTITIES) {
234
- const bytes = await readFile(sourceUrl);
235
- payloads.set(artifactRef, bytes);
236
- sources.push({ runtime_path: runtimePath, artifact_ref: artifactRef, digest: sha256(bytes) });
237
- }
238
- const executableBytes = await readFile(LATTICE_SENSOR_CLI);
239
- const version = (await run(process.execPath, [LATTICE_SENSOR_CLI, '--version'])).stdout.toString('utf8').trim();
240
- if (!SEMVER.test(version)) throw new TypeError('LatticeSensor versionがsemverではない');
241
- const projectConfigBytes = await readFile(path.join(repoRoot, SENSOR_CONFIG_REPO_PATH));
242
- if (!projectConfigBytes.equals(SENSOR_CONFIG_BYTES)) {
243
- throw new TypeError('LatticeSensor project config bytesがRC2 v2 contractと一致しない');
244
- }
245
- const sensorIdentity = {
246
- schema: 'lattice.rc2.sensor_identity.v2',
247
- version,
248
- executable_ref: 'lattice-sensor',
249
- executable_digest: sha256(executableBytes),
250
- project_config_ref: SENSOR_CONFIG_ARTIFACT_PATH,
251
- project_config_digest: sha256(projectConfigBytes),
252
- };
253
- payloads.set('identity/lattice-sensor-executable', executableBytes);
254
- payloads.set(SENSOR_CONFIG_ARTIFACT_PATH, projectConfigBytes);
255
- const snapshot = { sources, sensor_identity: sensorIdentity };
256
- return { snapshot, digest: digestArtifact(snapshot), payloads };
257
- }
258
-
259
- async function readJson(repoRoot, relativePath) {
260
- return JSON.parse(await readFile(path.join(repoRoot, relativePath), 'utf8'));
261
- }
262
-
263
- async function loadInputs(repoRoot) {
264
- const primary = {};
265
- const rc1 = {};
266
- for (const [key, relativePath] of Object.entries(PRIMARY_INPUT_FILES)) {
267
- primary[key] = await readJson(repoRoot, relativePath);
268
- }
269
- for (const [key, relativePath] of Object.entries(RC1_INPUT_FILES)) {
270
- rc1[key] = await readJson(repoRoot, relativePath);
271
- }
272
- return { primary, rc1 };
273
- }
274
-
275
- async function loadPredecessors(repoRoot) {
276
- const payloads = new Map();
277
- for (const [artifactPath, repoPath] of Object.entries(PREDECESSOR_FILES)) {
278
- payloads.set(artifactPath, await readFile(path.join(repoRoot, repoPath)));
279
- }
280
- return payloads;
281
- }
282
-
283
- function loadV3PlanPredecessor(payloads) {
284
- const manifestBytes = payloads.get('predecessors/rc2-v3-artifact-manifest.json');
285
- const planBytes = payloads.get('predecessors/rc2-v3-new-plan-version.json');
286
- let manifest;
287
- let plan;
288
- try {
289
- manifest = JSON.parse(manifestBytes.toString('utf8'));
290
- plan = JSON.parse(planBytes.toString('utf8'));
291
- } catch {
292
- throw new TypeError('RC2 v3 predecessor JSONをparseできない');
293
- }
294
- const paths = Array.isArray(manifest?.files)
295
- ? manifest.files.map(({ path: relativePath }) => relativePath).sort()
296
- : [];
297
- const entry = manifest?.files?.find(({ path: relativePath }) => (
298
- relativePath === 'new-plan-version.json'
299
- ));
300
- if (!jsonBytes(manifest).equals(manifestBytes)
301
- || !jsonBytes(plan).equals(planBytes)
302
- || manifest.schema !== 'lattice.rc2.artifact_manifest.v3'
303
- || !isDeepStrictEqual(paths, [...RC2_V3_ARTIFACT_PATHS])
304
- || !exactRecord(entry, ['path', 'media_type', 'bytes', 'sha256'])
305
- || entry.media_type !== 'application/json'
306
- || entry.bytes !== planBytes.byteLength
307
- || entry.sha256 !== sha256(planBytes)
308
- || plan.schema !== 'lattice.rc2.plan_version.v1'
309
- || plan.version !== 'rc2-delivery-policy-v4'
310
- || plan.plan_digest !== digestArtifact(plan.plan)) {
311
- throw new TypeError('RC2 v3 predecessor manifest/plan bindingが不正');
312
- }
313
- return plan;
314
- }
315
-
316
- async function resolveRepository(repoRoot, baseRef) {
317
- if (typeof repoRoot !== 'string' || repoRoot.length === 0
318
- || typeof baseRef !== 'string' || baseRef.length === 0) {
319
- throw new TypeError('repoRoot and baseRef are required');
320
- }
321
- const root = await realpath(repoRoot);
322
- const status = await run('git', ['status', '--porcelain=v1', '-z'], { cwd: root });
323
- if (status.stdout.length > 0) throw new TypeError('RC2 campaign source repository must be clean');
324
- const head = (await run('git', ['rev-parse', '--verify', 'HEAD^{commit}'], { cwd: root }))
325
- .stdout.toString('utf8').trim();
326
- const baseSha = (await run('git', ['rev-parse', '--verify', `${baseRef}^{commit}`], { cwd: root }))
327
- .stdout.toString('utf8').trim();
328
- if (!GIT_SHA1.test(head) || !GIT_SHA1.test(baseSha) || head !== baseSha) {
329
- throw new TypeError('RC2 campaign requires clean HEAD === baseRef');
330
- }
331
- return { root, baseSha };
332
- }
333
-
334
- function surfacePaths(candidateSpec) {
335
- const result = new Set();
336
- if (candidateSpec.fixed_oracle?.path) result.add(candidateSpec.fixed_oracle.path);
337
- for (const todo of candidateSpec.todos) {
338
- for (const mode of ['current', 'proposed']) {
339
- result.add(todo[mode].production.path);
340
- todo[mode].tests.forEach(({ path: testPath }) => result.add(testPath));
341
- }
342
- }
343
- return [...result].sort();
344
- }
345
-
346
- async function captureSourceSnapshot(repoRoot, paths) {
347
- const files = [];
348
- for (const relativePath of paths) {
349
- const absolutePath = path.join(repoRoot, relativePath);
350
- try {
351
- const stat = await lstat(absolutePath);
352
- if (!stat.isFile() || stat.isSymbolicLink()) {
353
- throw new TypeError(`snapshot pathが通常fileではない: ${relativePath}`);
354
- }
355
- files.push({ path: relativePath, state: 'file', content_digest: sha256(await readFile(absolutePath)) });
356
- } catch (error) {
357
- if (error?.code !== 'ENOENT') throw error;
358
- files.push({ path: relativePath, state: 'absent', content_digest: null });
359
- }
360
- }
361
- return { schema_version: 'lattice.rc2.source_snapshot.v1', files };
362
- }
363
-
364
- async function applyPatch(worktreePath, patch) {
365
- if (!Buffer.isBuffer(patch) || patch.length === 0) throw new TypeError('accepted patch bytes are required');
366
- await run('git', ['apply', '--check', '--binary', '-'], { cwd: worktreePath, input: patch });
367
- await run('git', ['apply', '--binary', '-'], { cwd: worktreePath, input: patch });
368
- }
369
-
370
- async function captureLatticeSensorBootstrap(worktreePath) {
371
- try {
372
- return await readFile(path.join(worktreePath, '.lattice/sensor', '.gitignore'));
373
- } catch (error) {
374
- if (error?.code === 'ENOENT') return null;
375
- throw error;
376
- }
377
- }
378
-
379
- async function restoreLatticeSensorBootstrap(worktreePath, bootstrap) {
380
- const sensorPath = path.join(worktreePath, '.lattice/sensor');
381
- await rm(sensorPath, { recursive: true, force: true });
382
- if (bootstrap === null) return;
383
- await mkdir(sensorPath, { recursive: true });
384
- await writeFile(path.join(sensorPath, '.gitignore'), bootstrap);
385
- }
386
-
387
- async function worktreeCount(repoRoot) {
388
- const output = (await run('git', ['worktree', 'list', '--porcelain'], { cwd: repoRoot }))
389
- .stdout.toString('utf8');
390
- return output.split('\n').filter((line) => line.startsWith('worktree ')).length;
391
- }
392
-
393
- function measured(elapsedMs) {
394
- return { state: 'measured', elapsed_ms: elapsedMs };
395
- }
396
-
397
- async function observeFreshIndex({
398
- repoRoot,
399
- baseSha,
400
- family,
401
- condition,
402
- runId,
403
- querySet,
404
- paths,
405
- patch,
406
- allowedPaths,
407
- sensorIdentity,
408
- oracle,
409
- rc1Inputs,
410
- costMeasurements,
411
- }) {
412
- const beforeCount = await worktreeCount(repoRoot);
413
- let rawEvidence;
414
- let snapshot;
415
- let oracleReceipt;
416
- let isolationInstanceDigest;
417
- let indexElapsedMs;
418
- let queryElapsedMs;
419
- let oracleElapsedMs;
420
- const isolated = await runIsolatedTransform({
421
- repoRoot,
422
- baseRef: baseSha,
423
- allowedPaths,
424
- transform: async ({ worktreePath }) => {
425
- if (condition === 'treatment') await applyPatch(worktreePath, patch);
426
- },
427
- verifyCommands: [],
428
- observe: async ({ worktreePath }) => {
429
- isolationInstanceDigest = sha256(Buffer.from(worktreePath, 'utf8'));
430
- snapshot = await captureSourceSnapshot(worktreePath, paths);
431
- const oracleStarted = performance.now();
432
- oracleReceipt = family === 'primary'
433
- ? await runRc2DeliveryPolicyOracle({ repoRoot: worktreePath })
434
- : await runRc1BlackBoxOracle({ repoRoot: worktreePath, oracle });
435
- oracleElapsedMs = roundedMilliseconds(oracleStarted);
436
- if (oracleReceipt.outcome !== 'passed') throw new TypeError(`${runId} oracle failed`);
437
- const bootstrap = await captureLatticeSensorBootstrap(worktreePath);
438
- try {
439
- const indexStarted = performance.now();
440
- await invokeSensorCli(run, ['init', '.'], { cwd: worktreePath });
441
- indexElapsedMs = roundedMilliseconds(indexStarted);
442
- const queryStarted = performance.now();
443
- rawEvidence = await collectSensorEvidence({ cwd: worktreePath, querySet });
444
- queryElapsedMs = roundedMilliseconds(queryStarted);
445
- } finally {
446
- await restoreLatticeSensorBootstrap(worktreePath, bootstrap);
447
- }
448
- },
449
- });
450
- const afterCount = await worktreeCount(repoRoot);
451
- if (!rawEvidence || !snapshot || !oracleReceipt || !isolationInstanceDigest
452
- || isolated.sourceInvariant?.outcome !== 'passed'
453
- || beforeCount !== afterCount) {
454
- throw new TypeError(`${runId} fresh observation is incomplete`);
455
- }
456
- if (condition === 'treatment' && !isolated.patch.equals(patch)) {
457
- throw new TypeError(`${runId} did not replay the accepted patch exactly`);
458
- }
459
- if (condition === 'control' && isolated.patch.length !== 0) {
460
- throw new TypeError(`${runId} control unexpectedly changed source`);
461
- }
462
- const evidence = createRc1EvidenceBundle({ condition, runId, querySet, rawEvidence });
463
- const patchDigest = condition === 'treatment' ? sha256(isolated.patch) : null;
464
- const measurement = {
465
- schema: 'lattice.rc2.sensor_measurement.v1',
466
- base_sha: baseSha,
467
- patch_digest: patchDigest,
468
- snapshot,
469
- snapshot_digest: digestArtifact(snapshot),
470
- sensor_identity: structuredClone(sensorIdentity),
471
- sensor_identity_digest: digestArtifact(sensorIdentity),
472
- query_set_digest: digestArtifact(querySet),
473
- raw_evidence_digest: evidence.raw.payload_digest,
474
- };
475
- const cost = {
476
- index: measured(indexElapsedMs),
477
- query: measured(queryElapsedMs),
478
- oracle: measured(oracleElapsedMs),
479
- };
480
- for (const [stage, value] of Object.entries(cost)) {
481
- costMeasurements.push({
482
- stage: `run.${runId}.${stage}`,
483
- state: value.state,
484
- elapsed_ms: value.elapsed_ms,
485
- });
486
- }
487
- const runRecord = {
488
- schema: 'lattice.rc2.fresh_sensor_run.v1',
489
- family,
490
- condition,
491
- run_id: runId,
492
- fresh_index: true,
493
- isolation_instance_digest: isolationInstanceDigest,
494
- evidence,
495
- measurement,
496
- oracle_receipt: oracleReceipt,
497
- legacy_boundary_manifests: null,
498
- source_invariant: isolated.sourceInvariant,
499
- cleanup: {
500
- schema: 'lattice.rc2.worktree_cleanup_receipt.v1',
501
- outcome: 'passed',
502
- worktree_count_before: beforeCount,
503
- worktree_count_after: afterCount,
504
- },
505
- cost,
506
- };
507
- if (family === 'rc1_transfer') {
508
- const raw = JSON.parse(Buffer.from(evidence.raw.payload_base64, 'base64').toString('utf8'));
509
- const compileLegacy = (manualEvidence, label) => compileBoundaryCondition({
510
- planInput: rc1Inputs.planInput,
511
- candidateSpec: rc1Inputs.candidateSpec,
512
- manualEvidence,
513
- querySet: rc1Inputs.querySet,
514
- sensorEvidence: raw,
515
- codeSnapshotDigest: measurement.snapshot_digest,
516
- planVersion: `rc2-${condition}-transfer-${label}`,
517
- });
518
- runRecord.legacy_boundary_manifests = {
519
- normal: compileLegacy(rc1Inputs.normalManualEvidence, 'normal').boundary_manifest,
520
- negative: compileLegacy(rc1Inputs.negativeManualEvidence, 'negative').boundary_manifest,
521
- };
522
- }
523
- return runRecord;
524
- }
525
-
526
- function verdictFor(bundle, compiled) {
527
- return {
528
- schema_version: 'lattice.boundary_verdict.v2',
529
- normalized_graph_digest: bundle.graph_digest,
530
- verdicts: compiled.pairwise_verdicts,
531
- };
532
- }
533
-
534
- function timedSync(stage, costMeasurements, execute) {
535
- const startedAt = performance.now();
536
- const value = execute();
537
- costMeasurements.push({ stage, state: 'measured', elapsed_ms: roundedMilliseconds(startedAt) });
538
- return value;
539
- }
540
-
541
- function compiledRecord({ condition, runId, bundle, costMeasurements }) {
542
- const compiled = timedSync(`compile.${condition}.${runId}`, costMeasurements, () => (
543
- compileSchedulabilityGraphV2(bundle.graph)
544
- ));
545
- if (compiled.outcome !== 'compiled') throw new TypeError(`${condition} did not compile`);
546
- const verdict = verdictFor(bundle, compiled);
547
- const verification = timedSync(`verify.${condition}.${runId}`, costMeasurements, () => (
548
- verifySchedulabilityPlanV2(bundle.graph, compiled.plan)
549
- ));
550
- if (verification.outcome !== 'verified') throw new TypeError(`${condition} minimum is unverified`);
551
- const stable = { bundle, verdict, plan: compiled.plan, verification };
552
- return {
553
- schema: 'lattice.rc2.compiled_condition.v1',
554
- condition,
555
- run_id: runId,
556
- outcome: 'compiled',
557
- ...stable,
558
- artifact_digest: digestArtifact(stable),
559
- };
560
- }
561
-
562
- function compilePrimary({ runRecord, inputs, manualEvidence, planInput, condition, costMeasurements }) {
563
- const bundle = timedSync(`front-end.${condition}.${runRecord.run_id}`, costMeasurements, () => (
564
- compileDeliveryPolicyBoundaryBundleV2({
565
- planInput,
566
- candidateSpec: inputs.candidateSpec,
567
- manualEvidence,
568
- querySet: inputs.querySet,
569
- sourceSnapshot: runRecord.measurement.snapshot,
570
- sensorEvidence: runRecord.evidence.portable,
571
- })
572
- ));
573
- return compiledRecord({ condition, runId: runRecord.run_id, bundle, costMeasurements });
574
- }
575
-
576
- function compileUnknown({ runRecord, inputs, costMeasurements }) {
577
- const condition = 'primary-treatment-third-only-unknown';
578
- const bundle = timedSync(`front-end.${condition}.${runRecord.run_id}`, costMeasurements, () => (
579
- compileDeliveryPolicyBoundaryBundleV2({
580
- planInput: inputs.planInput,
581
- candidateSpec: inputs.candidateSpec,
582
- manualEvidence: inputs.unknownManualEvidence,
583
- querySet: inputs.querySet,
584
- sourceSnapshot: runRecord.measurement.snapshot,
585
- sensorEvidence: runRecord.evidence.portable,
586
- })
587
- ));
588
- const outcome = timedSync(`compile.${condition}.${runRecord.run_id}`, costMeasurements, () => (
589
- compileSchedulabilityGraphV2(bundle.graph)
590
- ));
591
- const verification = timedSync(`verify.${condition}.${runRecord.run_id}`, costMeasurements, () => (
592
- verifySchedulabilityPlanV2(bundle.graph, {
593
- schema_version: 'lattice.plan_graph.v2',
594
- waves: [],
595
- minimum_feasible_waves: 0,
596
- })
597
- ));
598
- if (outcome.outcome !== 'unknown' || verification.outcome !== 'unknown') {
599
- throw new TypeError('third-only unknown became dispatchable');
600
- }
601
- return {
602
- schema: 'lattice.rc2.non_dispatchable_condition.v1',
603
- condition,
604
- run_id: runRecord.run_id,
605
- bundle,
606
- outcome,
607
- verification,
608
- };
609
- }
610
-
611
- function compileTransfer({ runRecord, inputs, manualEvidence, label, costMeasurements }) {
612
- const condition = `rc1-transfer-${runRecord.condition}-${label}`;
613
- const transferred = timedSync(`front-end.${condition}.${runRecord.run_id}`, costMeasurements, () => (
614
- compileRc1TransferBundleV2({
615
- planInput: inputs.planInput,
616
- candidateSpec: inputs.candidateSpec,
617
- manualEvidence,
618
- querySet: inputs.querySet,
619
- boundaryManifest: runRecord.legacy_boundary_manifests[label],
620
- })
621
- ));
622
- const verification = timedSync(`verify.${condition}.${runRecord.run_id}`, costMeasurements, () => (
623
- verifySchedulabilityPlanV2(transferred.bundle.graph, transferred.plan)
624
- ));
625
- if (verification.outcome !== 'verified') throw new TypeError(`${condition} minimum is unverified`);
626
- const stable = {
627
- bundle: transferred.bundle,
628
- verdict: transferred.verdict,
629
- plan: transferred.plan,
630
- verification,
631
- };
632
- return {
633
- schema: 'lattice.rc2.compiled_condition.v1',
634
- condition,
635
- run_id: runRecord.run_id,
636
- outcome: 'compiled',
637
- ...stable,
638
- artifact_digest: digestArtifact(stable),
639
- };
640
- }
641
-
642
- function compileAllConditions({ runs, inputs, costMeasurements }) {
643
- const primaryControl = runs.primary.control.map((runRecord) => compilePrimary({
644
- runRecord,
645
- inputs: inputs.primary,
646
- manualEvidence: inputs.primary.normalManualEvidence,
647
- planInput: inputs.primary.planInput,
648
- condition: 'primary-control-normal',
649
- costMeasurements,
650
- }));
651
- const primaryTreatment = runs.primary.treatment.map((runRecord) => compilePrimary({
652
- runRecord,
653
- inputs: inputs.primary,
654
- manualEvidence: inputs.primary.normalManualEvidence,
655
- planInput: inputs.primary.planInput,
656
- condition: 'primary-treatment-normal',
657
- costMeasurements,
658
- }));
659
- if (new Set(primaryControl.map(({ artifact_digest: digest }) => digest)).size !== 1
660
- || new Set(primaryTreatment.map(({ artifact_digest: digest }) => digest)).size !== 1) {
661
- throw new TypeError('primary repeated compilation is not structurally reproducible');
662
- }
663
- const treatmentRun = runs.primary.treatment[0];
664
- const capacityPlan = structuredClone(inputs.primary.planInput);
665
- capacityPlan.capacity.writers = 2;
666
- const partialState = compilePrimary({
667
- runRecord: treatmentRun,
668
- inputs: inputs.primary,
669
- manualEvidence: inputs.primary.partialManualEvidence,
670
- planInput: inputs.primary.planInput,
671
- condition: 'primary-treatment-partial-state',
672
- costMeasurements,
673
- });
674
- const capacity2 = compilePrimary({
675
- runRecord: treatmentRun,
676
- inputs: inputs.primary,
677
- manualEvidence: inputs.primary.normalManualEvidence,
678
- planInput: capacityPlan,
679
- condition: 'primary-treatment-capacity-2',
680
- costMeasurements,
681
- });
682
- const unknown = compileUnknown({ runRecord: treatmentRun, inputs: inputs.primary, costMeasurements });
683
- const transferControl = runs.rc1_transfer.control[0];
684
- const transferTreatment = runs.rc1_transfer.treatment[0];
685
- return {
686
- primary: {
687
- control: primaryControl,
688
- treatment: primaryTreatment,
689
- partial_state: partialState,
690
- capacity_2: capacity2,
691
- third_only_unknown: unknown,
692
- },
693
- rc1_transfer: {
694
- control: {
695
- normal: compileTransfer({
696
- runRecord: transferControl,
697
- inputs: inputs.rc1,
698
- manualEvidence: inputs.rc1.normalManualEvidence,
699
- label: 'normal',
700
- costMeasurements,
701
- }),
702
- negative: compileTransfer({
703
- runRecord: transferControl,
704
- inputs: inputs.rc1,
705
- manualEvidence: inputs.rc1.negativeManualEvidence,
706
- label: 'negative',
707
- costMeasurements,
708
- }),
709
- },
710
- treatment: {
711
- normal: compileTransfer({
712
- runRecord: transferTreatment,
713
- inputs: inputs.rc1,
714
- manualEvidence: inputs.rc1.normalManualEvidence,
715
- label: 'normal',
716
- costMeasurements,
717
- }),
718
- negative: compileTransfer({
719
- runRecord: transferTreatment,
720
- inputs: inputs.rc1,
721
- manualEvidence: inputs.rc1.negativeManualEvidence,
722
- label: 'negative',
723
- costMeasurements,
724
- }),
725
- },
726
- },
727
- };
728
- }
729
-
730
- function compiledPayloads(compiled) {
731
- return new Map([
732
- ['compiled/primary-control-1-normal.json', jsonBytes(compiled.primary.control[0])],
733
- ['compiled/primary-control-2-normal.json', jsonBytes(compiled.primary.control[1])],
734
- ['compiled/primary-treatment-1-normal.json', jsonBytes(compiled.primary.treatment[0])],
735
- ['compiled/primary-treatment-2-normal.json', jsonBytes(compiled.primary.treatment[1])],
736
- ['compiled/primary-treatment-partial-state.json', jsonBytes(compiled.primary.partial_state)],
737
- ['compiled/primary-treatment-capacity-2.json', jsonBytes(compiled.primary.capacity_2)],
738
- ['compiled/primary-treatment-third-only-unknown.json', jsonBytes(compiled.primary.third_only_unknown)],
739
- ['compiled/rc1-transfer-control-normal.json', jsonBytes(compiled.rc1_transfer.control.normal)],
740
- ['compiled/rc1-transfer-control-negative.json', jsonBytes(compiled.rc1_transfer.control.negative)],
741
- ['compiled/rc1-transfer-treatment-normal.json', jsonBytes(compiled.rc1_transfer.treatment.normal)],
742
- ['compiled/rc1-transfer-treatment-negative.json', jsonBytes(compiled.rc1_transfer.treatment.negative)],
743
- ]);
744
- }
745
-
746
- function inputPayloads(inputs) {
747
- const result = new Map();
748
- for (const [key, relativePath] of Object.entries(PRIMARY_ARTIFACT_INPUTS)) {
749
- result.set(relativePath, jsonBytes(inputs.primary[key]));
750
- }
751
- for (const [key, relativePath] of Object.entries(RC1_ARTIFACT_INPUTS)) {
752
- result.set(relativePath, jsonBytes(inputs.rc1[key]));
753
- }
754
- return result;
755
- }
756
-
757
- function transformPayloads(transform) {
758
- return new Map([
759
- ['transform/accepted-artifact.json', jsonBytes(transform.accepted.artifact)],
760
- ['transform/accepted-receipt.json', jsonBytes(transform.accepted.receipt)],
761
- ['transform/behavior-evidence.json', jsonBytes(transform.accepted.behavior_evidence)],
762
- ['transform/mutation-evidence.json', jsonBytes(transform.accepted.mutation_evidence)],
763
- ['transform/rejected-incomplete-artifact.json', jsonBytes(transform.rejected.incomplete.artifact)],
764
- ['transform/rejected-incomplete-receipt.json', jsonBytes(transform.rejected.incomplete.receipt)],
765
- ['transform/rejected-scope-artifact.json', jsonBytes(transform.rejected.scope.artifact)],
766
- ['transform/rejected-scope-receipt.json', jsonBytes(transform.rejected.scope.receipt)],
767
- ['transform/seam.patch', Buffer.from(transform.accepted.patch)],
768
- ]);
769
- }
770
-
771
- function storedEvidence(evidence) {
772
- const raw = evidence?.raw;
773
- if (!exactRecord(raw, [
774
- 'schema',
775
- 'media_type',
776
- 'encoding',
777
- 'canonical_bytes',
778
- 'payload_digest',
779
- 'payload_base64',
780
- ])
781
- || raw.schema !== 'lattice.sensor_raw_opaque_receipt.v1'
782
- || raw.encoding !== 'canonical-json-base64'
783
- || typeof raw.payload_base64 !== 'string') {
784
- throw new TypeError('run raw evidence receipt is invalid');
785
- }
786
- const payload = Buffer.from(raw.payload_base64, 'base64');
787
- if (payload.toString('base64') !== raw.payload_base64
788
- || payload.byteLength !== raw.canonical_bytes
789
- || sha256(payload) !== raw.payload_digest) {
790
- throw new TypeError('run raw evidence payload binding is invalid');
791
- }
792
- const payloadBase64Chunks = [];
793
- for (let offset = 0; offset < raw.payload_base64.length; offset += RAW_EVIDENCE_CHUNK_BYTES) {
794
- payloadBase64Chunks.push(raw.payload_base64.slice(
795
- offset,
796
- offset + RAW_EVIDENCE_CHUNK_BYTES,
797
- ));
798
- }
799
- if (payloadBase64Chunks.length === 0
800
- || payloadBase64Chunks.slice(0, -1).some((chunk) => (
801
- Buffer.byteLength(chunk, 'utf8') !== RAW_EVIDENCE_CHUNK_BYTES
802
- ))
803
- || Buffer.byteLength(payloadBase64Chunks.at(-1), 'utf8') > RAW_EVIDENCE_CHUNK_BYTES) {
804
- throw new TypeError('run raw evidence chunks are invalid');
805
- }
806
- return {
807
- ...structuredClone(evidence),
808
- raw: {
809
- schema: 'lattice.rc2.chunked_sensor_raw_receipt.v1',
810
- source_schema: raw.schema,
811
- media_type: raw.media_type,
812
- source_encoding: raw.encoding,
813
- storage_encoding: 'ordered-base64-chunks',
814
- canonical_bytes: raw.canonical_bytes,
815
- payload_digest: raw.payload_digest,
816
- payload_base64_chunks: payloadBase64Chunks,
817
- },
818
- };
819
- }
820
-
821
- function storedRunRecord(runRecord) {
822
- return {
823
- ...structuredClone(runRecord),
824
- evidence: storedEvidence(runRecord.evidence),
825
- };
826
- }
827
-
828
- function runPayloads(runs) {
829
- const records = [
830
- ...runs.primary.control,
831
- ...runs.primary.treatment,
832
- ...runs.rc1_transfer.control,
833
- ...runs.rc1_transfer.treatment,
834
- ];
835
- return new Map(records.map((runRecord) => [
836
- `evidence/${runRecord.run_id}.json`,
837
- jsonBytes(storedRunRecord(runRecord)),
838
- ]));
839
- }
840
-
841
- function mergePayloads(...maps) {
842
- const result = new Map();
843
- for (const map of maps) {
844
- for (const [relativePath, bytes] of map) {
845
- if (result.has(relativePath)) throw new TypeError(`duplicate artifact payload: ${relativePath}`);
846
- result.set(relativePath, Buffer.from(bytes));
847
- }
848
- }
849
- return result;
850
- }
851
-
852
- function predecessorDescriptors(payloads) {
853
- const descriptors = [];
854
- const add = (kind, ref) => descriptors.push({ kind, ref, digest: sha256(payloads.get(ref)) });
855
- for (const ref of Object.keys(PREDECESSOR_FILES).sort()) {
856
- add(ref === 'predecessors/rc1-v6-plan.md' ? 'rc1_v6_phase_archive' : 'immutable_predecessor', ref);
857
- }
858
- add('accepted_transform', 'transform/accepted-artifact.json');
859
- add('behavior_envelope', 'transform/behavior-evidence.json');
860
- add('mutation_evidence', 'transform/mutation-evidence.json');
861
- for (const runId of RUN_IDS) add('evidence_bundle', `evidence/${runId}.json`);
862
- for (const ref of Object.values(PRIMARY_ARTIFACT_INPUTS).sort()) add('fixed_input', ref);
863
- add('execution_identity', 'identity.json');
864
- add('compiler_identity', 'identity/schedulability-compiler-v2.mjs');
865
- add('verifier_identity', 'identity/schedulability-verifier-v2.mjs');
866
- add('sensor_project_config', SENSOR_CONFIG_ARTIFACT_PATH);
867
- return descriptors;
868
- }
869
-
870
- function buildVersionBarrier({ inputs, compiled, predecessors, predecessorPlan }) {
871
- const treatment = compiled.primary.treatment[0];
872
- const affectedTodos = inputs.primary.planInput.todos.map(({ id }) => id);
873
- const newPlanVersion = {
874
- schema: 'lattice.rc2.plan_version.v1',
875
- version: 'rc2-delivery-policy-v5',
876
- predecessor_version: predecessorPlan.version,
877
- plan: treatment.plan,
878
- plan_digest: digestArtifact(treatment.plan),
879
- bundle_digest: digestArtifact(treatment.bundle),
880
- verdict_digest: digestArtifact(treatment.verdict),
881
- affected_todos: affectedTodos,
882
- causal_predecessors: predecessors,
883
- };
884
- const planDiff = {
885
- schema: 'lattice.rc2.plan_diff.v1',
886
- old_plan: {
887
- version: predecessorPlan.version,
888
- digest: digestArtifact(predecessorPlan),
889
- },
890
- new_plan: {
891
- version: newPlanVersion.version,
892
- digest: digestArtifact(newPlanVersion),
893
- },
894
- causal_predecessors: predecessors,
895
- affected_todos: affectedTodos,
896
- invalidated_contexts: [
897
- { kind: 'old_plan', ref: predecessorPlan.version,
898
- reason: 'the v4 artifact semantics cannot receive v5 bindings by append' },
899
- { kind: 'agent_context', ref: `${predecessorPlan.version}-agent-context`,
900
- reason: 'v4 agent context does not bind the version witness verifier identity' },
901
- { kind: 'partial_patch', ref: `${predecessorPlan.version}-partial-patch`,
902
- reason: 'patches compiled before version witness binding cannot cross the barrier' },
903
- { kind: 'interface_assumption', ref: `${predecessorPlan.version}-interface-assumption`,
904
- reason: 'v4 interface evidence does not bind the active witness epoch' },
905
- { kind: 'boundary_evidence', ref: `${predecessorPlan.version}-boundary-evidence`,
906
- reason: 'only fresh version-bound treatment evidence is valid for plan v5' },
907
- ],
908
- };
909
- return { newPlanVersion, planDiff };
910
- }
911
-
912
- function distinctConflictPairs(record) {
913
- return new Set(record.bundle.graph.conflicts.map(({ todo_ids: todoIds }) => (
914
- [...todoIds].sort().join('\u0000')
915
- ))).size;
916
- }
917
-
918
- function conditionMetrics(record) {
919
- return {
920
- conflict_records: record.bundle.graph.conflicts.length,
921
- distinct_conflict_pairs: distinctConflictPairs(record),
922
- minimum_feasible_waves: record.plan.minimum_feasible_waves,
923
- };
924
- }
925
-
926
- function buildComparison({ baseSha, identity, transform, compiled, newPlanVersion, planDiff }) {
927
- return {
928
- schema: 'lattice.rc2.control_treatment_comparison.v1',
929
- base_sha: baseSha,
930
- identity_digest: digestArtifact(identity),
931
- independent_variable: {
932
- kind: 'accepted_registry_shard_patch',
933
- transform_artifact_digest: digestArtifact(transform.accepted.artifact),
934
- patch_digest: transform.accepted.artifact.patch.digest,
935
- },
936
- condition_artifact_digests: {
937
- primary_control: compiled.primary.control.map(({ artifact_digest: digest }) => digest),
938
- primary_treatment: compiled.primary.treatment.map(({ artifact_digest: digest }) => digest),
939
- primary_partial_state: compiled.primary.partial_state.artifact_digest,
940
- primary_capacity_2: compiled.primary.capacity_2.artifact_digest,
941
- primary_unknown: digestArtifact(compiled.primary.third_only_unknown),
942
- rc1_transfer_control_normal: compiled.rc1_transfer.control.normal.artifact_digest,
943
- rc1_transfer_control_negative: compiled.rc1_transfer.control.negative.artifact_digest,
944
- rc1_transfer_treatment_normal: compiled.rc1_transfer.treatment.normal.artifact_digest,
945
- rc1_transfer_treatment_negative: compiled.rc1_transfer.treatment.negative.artifact_digest,
946
- },
947
- metrics: {
948
- primary_control: conditionMetrics(compiled.primary.control[0]),
949
- primary_treatment: conditionMetrics(compiled.primary.treatment[0]),
950
- primary_partial_state: conditionMetrics(compiled.primary.partial_state),
951
- primary_capacity_2: conditionMetrics(compiled.primary.capacity_2),
952
- rc1_transfer_control_normal: conditionMetrics(compiled.rc1_transfer.control.normal),
953
- rc1_transfer_control_negative: conditionMetrics(compiled.rc1_transfer.control.negative),
954
- rc1_transfer_treatment_normal: conditionMetrics(compiled.rc1_transfer.treatment.normal),
955
- rc1_transfer_treatment_negative: conditionMetrics(compiled.rc1_transfer.treatment.negative),
956
- },
957
- version_barrier: {
958
- new_plan_version_digest: digestArtifact(newPlanVersion),
959
- plan_diff_digest: digestArtifact(planDiff),
960
- causal_predecessors_digest: digestArtifact(planDiff.causal_predecessors),
961
- },
962
- };
963
- }
964
-
965
- function evaluateHypothesis(comparison, compiled, transform) {
966
- const checks = [
967
- { id: 'accepted_transform_only', passed: transform.accepted.status === 'accepted' },
968
- {
969
- id: 'primary_repeat_reproducible',
970
- passed: new Set(compiled.primary.control.map(({ artifact_digest: digest }) => digest)).size === 1
971
- && new Set(compiled.primary.treatment.map(({ artifact_digest: digest }) => digest)).size === 1,
972
- },
973
- {
974
- id: 'primary_conflicts_removed',
975
- passed: comparison.metrics.primary_control.conflict_records === 12
976
- && comparison.metrics.primary_control.distinct_conflict_pairs === 3
977
- && comparison.metrics.primary_treatment.conflict_records === 0,
978
- },
979
- {
980
- id: 'primary_waves_reduced',
981
- passed: comparison.metrics.primary_control.minimum_feasible_waves === 3
982
- && comparison.metrics.primary_treatment.minimum_feasible_waves === 1,
983
- },
984
- {
985
- id: 'partial_state_preserved',
986
- passed: comparison.metrics.primary_partial_state.conflict_records === 1
987
- && comparison.metrics.primary_partial_state.minimum_feasible_waves === 2,
988
- },
989
- {
990
- id: 'capacity_control_preserved',
991
- passed: comparison.metrics.primary_capacity_2.conflict_records === 0
992
- && comparison.metrics.primary_capacity_2.minimum_feasible_waves === 2,
993
- },
994
- {
995
- id: 'third_only_unknown_closed',
996
- passed: compiled.primary.third_only_unknown.outcome.outcome === 'unknown'
997
- && compiled.primary.third_only_unknown.verification.outcome === 'unknown',
998
- },
999
- {
1000
- id: 'rc1_transfer_isomorphic',
1001
- passed: comparison.metrics.rc1_transfer_control_normal.conflict_records === 3
1002
- && comparison.metrics.rc1_transfer_control_normal.minimum_feasible_waves === 2
1003
- && comparison.metrics.rc1_transfer_treatment_normal.conflict_records === 0
1004
- && comparison.metrics.rc1_transfer_treatment_normal.minimum_feasible_waves === 1
1005
- && comparison.metrics.rc1_transfer_control_negative.minimum_feasible_waves === 2
1006
- && comparison.metrics.rc1_transfer_treatment_negative.minimum_feasible_waves === 2,
1007
- },
1008
- {
1009
- id: 'all_minimum_verified',
1010
- passed: [
1011
- ...compiled.primary.control,
1012
- ...compiled.primary.treatment,
1013
- compiled.primary.partial_state,
1014
- compiled.primary.capacity_2,
1015
- compiled.rc1_transfer.control.normal,
1016
- compiled.rc1_transfer.control.negative,
1017
- compiled.rc1_transfer.treatment.normal,
1018
- compiled.rc1_transfer.treatment.negative,
1019
- ].every(({ verification }) => verification.outcome === 'verified'),
1020
- },
1021
- ];
1022
- return {
1023
- schema: 'lattice.rc2.hypothesis_evaluation.v1',
1024
- checks,
1025
- supported: checks.every(({ passed }) => passed),
1026
- failed_conditions: checks.filter(({ passed }) => !passed).map(({ id }) => id),
1027
- };
1028
- }
1029
-
1030
- function patchReviewLines(patch) {
1031
- return patch.toString('utf8').split('\n').filter((line) => (
1032
- (line.startsWith('+') && !line.startsWith('+++'))
1033
- || (line.startsWith('-') && !line.startsWith('---'))
1034
- )).length;
1035
- }
1036
-
1037
- function buildCost(costMeasurements, transform) {
1038
- const elapsed = Math.round(costMeasurements
1039
- .reduce((sum, measurement) => sum + measurement.elapsed_ms, 0) * 1_000) / 1_000;
1040
- return {
1041
- schema: 'lattice.rc2.stage_cost.v1',
1042
- measurements: costMeasurements,
1043
- aggregate: {
1044
- measured_count: costMeasurements.length,
1045
- not_measured_count: 0,
1046
- elapsed_ms: elapsed,
1047
- },
1048
- intervention: {
1049
- patch_bytes: transform.accepted.patch.byteLength,
1050
- files: transform.accepted.artifact.scope.changed_paths.length,
1051
- review_lines: patchReviewLines(transform.accepted.patch),
1052
- },
1053
- rework: { rejected_attempts: 2, retries: 0, rollbacks: 0 },
1054
- unverified: [
1055
- 'actual multi-agent wall-clock improvement',
1056
- 'elapsed measurement attestation beyond saved arithmetic binding',
1057
- 'arbitrary-repository seam success rate',
1058
- ],
1059
- };
1060
- }
1061
-
1062
- function allCompiledRecords(compiled) {
1063
- return [
1064
- ...compiled.primary.control,
1065
- ...compiled.primary.treatment,
1066
- compiled.primary.partial_state,
1067
- compiled.primary.capacity_2,
1068
- compiled.primary.third_only_unknown,
1069
- compiled.rc1_transfer.control.normal,
1070
- compiled.rc1_transfer.control.negative,
1071
- compiled.rc1_transfer.treatment.normal,
1072
- compiled.rc1_transfer.treatment.negative,
1073
- ];
1074
- }
1075
-
1076
- function buildExecutionEvidence({
1077
- baseSha,
1078
- identity,
1079
- transform,
1080
- runs,
1081
- compiled,
1082
- newPlanVersion,
1083
- planDiff,
1084
- comparison,
1085
- hypothesis,
1086
- cost,
1087
- }) {
1088
- const runRecords = [
1089
- ...runs.primary.control,
1090
- ...runs.primary.treatment,
1091
- ...runs.rc1_transfer.control,
1092
- ...runs.rc1_transfer.treatment,
1093
- ];
1094
- return {
1095
- schema: 'lattice.rc2.campaign_execution_evidence.v1',
1096
- base_sha: baseSha,
1097
- identity_digest: digestArtifact(identity),
1098
- transform_artifact_digest: digestArtifact(transform.accepted.artifact),
1099
- run_digests: runRecords.map((runRecord) => digestArtifact(storedRunRecord(runRecord))),
1100
- compiled_condition_digests: allCompiledRecords(compiled).map(digestArtifact),
1101
- new_plan_version_digest: digestArtifact(newPlanVersion),
1102
- plan_diff_digest: digestArtifact(planDiff),
1103
- comparison_digest: digestArtifact(comparison),
1104
- hypothesis_evaluation_digest: digestArtifact(hypothesis),
1105
- cost_digest: digestArtifact(cost),
1106
- };
1107
- }
1108
-
1109
- async function timedAsync(stage, costMeasurements, execute) {
1110
- const startedAt = performance.now();
1111
- const value = await execute();
1112
- costMeasurements.push({ stage, state: 'measured', elapsed_ms: roundedMilliseconds(startedAt) });
1113
- return value;
1114
- }
1115
-
1116
- async function runTransforms({ root, baseSha, candidateSpec, costMeasurements }) {
1117
- const accepted = await timedAsync('transform.accepted', costMeasurements, () => (
1118
- runRc2DeliveryPolicySeamTransform({ repoRoot: root, baseRef: baseSha, candidateSpec })
1119
- ));
1120
- if (accepted.status !== 'accepted' || !Buffer.isBuffer(accepted.patch)) {
1121
- throw new TypeError('default RC2 transform was not accepted');
1122
- }
1123
- const incomplete = await timedAsync('transform.rejected.incomplete', costMeasurements, () => (
1124
- runRc2DeliveryPolicySeamTransform({
1125
- repoRoot: root,
1126
- baseRef: baseSha,
1127
- candidateSpec,
1128
- transform: async ({ worktreePath }) => {
1129
- await applyRc2DeliveryPolicyTransform({ worktreePath });
1130
- await rm(path.join(worktreePath, 'test/rc2-delivery-policy-sms.test.mjs'));
1131
- },
1132
- })
1133
- ));
1134
- const scope = await timedAsync('transform.rejected.scope', costMeasurements, () => (
1135
- runRc2DeliveryPolicySeamTransform({
1136
- repoRoot: root,
1137
- baseRef: baseSha,
1138
- candidateSpec,
1139
- transform: async ({ worktreePath }) => {
1140
- await applyRc2DeliveryPolicyTransform({ worktreePath });
1141
- await writeFile(path.join(worktreePath, 'rc2-scope-violation.tmp'), 'forbidden\n');
1142
- },
1143
- })
1144
- ));
1145
- if (incomplete.status !== 'rejected'
1146
- || incomplete.artifact.rejection.kind !== 'incomplete_transform'
1147
- || scope.status !== 'rejected'
1148
- || scope.artifact.rejection.kind !== 'scope_violation') {
1149
- throw new TypeError('RC2 rejected transform controls did not preserve typed outcomes');
1150
- }
1151
- return { accepted, rejected: { incomplete, scope } };
1152
- }
1153
-
1154
- /** accepted seamを独立変数にfresh sensor→v2 compile→new planまで実行する。 */
1155
- export async function runRc2Campaign(options = {}) {
1156
- if (!exactRecord(options, ['repoRoot', 'baseRef', 'artifactVersion'])
1157
- || options.artifactVersion !== ARTIFACT_VERSION) {
1158
- throw new TypeError('runRc2Campaign options must select exact artifactVersion v4');
1159
- }
1160
- const { root, baseSha } = await resolveRepository(options.repoRoot, options.baseRef);
1161
- const [inputs, predecessors, identityBefore] = await Promise.all([
1162
- loadInputs(root),
1163
- loadPredecessors(root),
1164
- captureExecutionIdentity(root),
1165
- ]);
1166
- const predecessorPlan = loadV3PlanPredecessor(predecessors);
1167
- const costMeasurements = [];
1168
- const transform = await runTransforms({
1169
- root,
1170
- baseSha,
1171
- candidateSpec: inputs.primary.candidateSpec,
1172
- costMeasurements,
1173
- });
1174
- const primaryPaths = surfacePaths(inputs.primary.candidateSpec);
1175
- const rc1Paths = surfacePaths(inputs.rc1.candidateSpec);
1176
- if (primaryPaths.length !== 9 || rc1Paths.length !== 6) {
1177
- throw new TypeError('campaign fixed surface cardinality is invalid');
1178
- }
1179
- const rc1TransformArtifact = JSON.parse(
1180
- predecessors.get('predecessors/rc1-v6-transform-artifact.json').toString('utf8'),
1181
- );
1182
- const rc1Patch = predecessors.get('predecessors/rc1-v6-seam.patch');
1183
- if (rc1TransformArtifact.patch.digest !== sha256(rc1Patch)) {
1184
- throw new TypeError('RC1 v6 predecessor patch binding is invalid');
1185
- }
1186
- const common = {
1187
- repoRoot: root,
1188
- baseSha,
1189
- sensorIdentity: identityBefore.snapshot.sensor_identity,
1190
- costMeasurements,
1191
- };
1192
- const primaryControl = [];
1193
- const primaryTreatment = [];
1194
- for (let index = 1; index <= 2; index += 1) {
1195
- primaryControl.push(await observeFreshIndex({
1196
- ...common,
1197
- family: 'primary',
1198
- condition: 'control',
1199
- runId: `primary-control-${index}`,
1200
- querySet: inputs.primary.querySet,
1201
- paths: primaryPaths,
1202
- patch: null,
1203
- allowedPaths: [],
1204
- oracle: null,
1205
- rc1Inputs: null,
1206
- }));
1207
- }
1208
- for (let index = 1; index <= 2; index += 1) {
1209
- primaryTreatment.push(await observeFreshIndex({
1210
- ...common,
1211
- family: 'primary',
1212
- condition: 'treatment',
1213
- runId: `primary-treatment-${index}`,
1214
- querySet: inputs.primary.querySet,
1215
- paths: primaryPaths,
1216
- patch: transform.accepted.patch,
1217
- allowedPaths: [...RC2_DELIVERY_POLICY_TRANSFORM_PATHS],
1218
- oracle: null,
1219
- rc1Inputs: null,
1220
- }));
1221
- }
1222
- const transferControl = await observeFreshIndex({
1223
- ...common,
1224
- family: 'rc1_transfer',
1225
- condition: 'control',
1226
- runId: 'rc1-transfer-control-1',
1227
- querySet: inputs.rc1.querySet,
1228
- paths: rc1Paths,
1229
- patch: null,
1230
- allowedPaths: [],
1231
- oracle: inputs.rc1.oracle,
1232
- rc1Inputs: inputs.rc1,
1233
- });
1234
- const transferTreatment = await observeFreshIndex({
1235
- ...common,
1236
- family: 'rc1_transfer',
1237
- condition: 'treatment',
1238
- runId: 'rc1-transfer-treatment-1',
1239
- querySet: inputs.rc1.querySet,
1240
- paths: rc1Paths,
1241
- patch: rc1Patch,
1242
- allowedPaths: [...rc1TransformArtifact.scope.allowed_paths],
1243
- oracle: inputs.rc1.oracle,
1244
- rc1Inputs: inputs.rc1,
1245
- });
1246
- const runs = {
1247
- primary: { control: primaryControl, treatment: primaryTreatment },
1248
- rc1_transfer: { control: [transferControl], treatment: [transferTreatment] },
1249
- };
1250
- const compiled = compileAllConditions({ runs, inputs, costMeasurements });
1251
-
1252
- const identityAfter = await captureExecutionIdentity(root);
1253
- if (!isDeepStrictEqual(identityBefore.snapshot, identityAfter.snapshot)
1254
- || identityBefore.payloads.size !== identityAfter.payloads.size
1255
- || [...identityBefore.payloads].some(([relativePath, bytes]) => (
1256
- !identityAfter.payloads.get(relativePath)?.equals(bytes)
1257
- ))) {
1258
- throw new TypeError('RC2 execution identity drifted during campaign');
1259
- }
1260
- const identity = {
1261
- schema: 'lattice.rc2.execution_identity.v4',
1262
- sources: identityBefore.snapshot.sources,
1263
- sensor_identity: identityBefore.snapshot.sensor_identity,
1264
- sensor_identity_digest: digestArtifact(identityBefore.snapshot.sensor_identity),
1265
- before_digest: identityBefore.digest,
1266
- after_digest: identityAfter.digest,
1267
- };
1268
-
1269
- const preliminaryPayloads = mergePayloads(
1270
- inputPayloads(inputs),
1271
- identityBefore.payloads,
1272
- predecessors,
1273
- transformPayloads(transform),
1274
- runPayloads(runs),
1275
- new Map([['identity.json', jsonBytes(identity)]]),
1276
- );
1277
- const causalPredecessors = predecessorDescriptors(preliminaryPayloads);
1278
- const { newPlanVersion, planDiff } = buildVersionBarrier({
1279
- inputs,
1280
- compiled,
1281
- predecessors: causalPredecessors,
1282
- predecessorPlan,
1283
- });
1284
- const comparison = buildComparison({
1285
- baseSha,
1286
- identity,
1287
- transform,
1288
- compiled,
1289
- newPlanVersion,
1290
- planDiff,
1291
- });
1292
- const hypothesisEvaluation = evaluateHypothesis(comparison, compiled, transform);
1293
- if (!hypothesisEvaluation.supported) {
1294
- throw new TypeError(
1295
- `RC2 hypothesis is not supported: ${hypothesisEvaluation.failed_conditions.join(', ')}`,
1296
- );
1297
- }
1298
- const cost = buildCost(costMeasurements, transform);
1299
- const executionEvidence = buildExecutionEvidence({
1300
- baseSha,
1301
- identity,
1302
- transform,
1303
- runs,
1304
- compiled,
1305
- newPlanVersion,
1306
- planDiff,
1307
- comparison,
1308
- hypothesis: hypothesisEvaluation,
1309
- cost,
1310
- });
1311
- return {
1312
- schema: 'lattice.rc2.campaign_result.v4',
1313
- artifact_version: ARTIFACT_VERSION,
1314
- base_sha: baseSha,
1315
- fixed_inputs: inputs,
1316
- identity,
1317
- identity_payloads: identityBefore.payloads,
1318
- predecessor_payloads: predecessors,
1319
- transform,
1320
- runs,
1321
- compiled,
1322
- new_plan_version: newPlanVersion,
1323
- plan_diff: planDiff,
1324
- comparison,
1325
- hypothesis_evaluation: hypothesisEvaluation,
1326
- cost,
1327
- execution_evidence: executionEvidence,
1328
- };
1329
- }
1330
-
1331
- function artifactFiles(result) {
1332
- const files = mergePayloads(
1333
- inputPayloads(result.fixed_inputs),
1334
- result.identity_payloads,
1335
- result.predecessor_payloads,
1336
- transformPayloads(result.transform),
1337
- runPayloads(result.runs),
1338
- compiledPayloads(result.compiled),
1339
- new Map([
1340
- ['identity.json', jsonBytes(result.identity)],
1341
- ['new-plan-version.json', jsonBytes(result.new_plan_version)],
1342
- ['plan-diff.json', jsonBytes(result.plan_diff)],
1343
- ['comparison.json', jsonBytes(result.comparison)],
1344
- ['hypothesis-evaluation.json', jsonBytes(result.hypothesis_evaluation)],
1345
- ['cost.json', jsonBytes(result.cost)],
1346
- ['execution-evidence.json', jsonBytes(result.execution_evidence)],
1347
- ]),
1348
- );
1349
- if (!isDeepStrictEqual([...files.keys()].sort(), [...RC2_ARTIFACT_PATHS])) {
1350
- throw new TypeError('RC2 artifact payload set differs from the exact contract');
1351
- }
1352
- return files;
1353
- }
1354
-
1355
- function artifactMediaType(relativePath) {
1356
- if (relativePath.endsWith('.json')) return 'application/json';
1357
- if (relativePath.endsWith('.mjs')) return 'application/javascript';
1358
- if (relativePath.endsWith('.patch')) return 'text/x-diff';
1359
- if (relativePath.endsWith('.md')) return 'text/markdown';
1360
- return 'application/octet-stream';
1361
- }
1362
-
1363
- async function syncDirectory(directory) {
1364
- const handle = await open(directory, 'r');
1365
- // Windowsはdirectory handleのfsyncを許さず常にEPERM/EINVALを返す(Node仕様)。
1366
- // win32のこの2値だけ許容し、他OS・他エラーは従来どおり失敗させる。
1367
- try {
1368
- await handle.sync();
1369
- } catch (error) {
1370
- if (process.platform !== 'win32' || !['EPERM', 'EINVAL'].includes(error?.code)) throw error;
1371
- } finally {
1372
- await handle.close();
1373
- }
1374
- }
1375
-
1376
- async function writeDurableFile(target, bytes) {
1377
- await mkdir(path.dirname(target), { recursive: true });
1378
- const handle = await open(target, 'wx');
1379
- try {
1380
- await handle.writeFile(bytes);
1381
- await handle.sync();
1382
- } finally {
1383
- await handle.close();
1384
- }
1385
- }
1386
-
1387
- async function syncArtifactDirectories(root, relativePaths) {
1388
- const directories = new Set([root]);
1389
- for (const relativePath of relativePaths) {
1390
- let current = path.dirname(path.join(root, relativePath));
1391
- while (current.startsWith(root)) {
1392
- directories.add(current);
1393
- if (current === root) break;
1394
- current = path.dirname(current);
1395
- }
1396
- }
1397
- const ordered = [...directories].sort((left, right) => right.length - left.length);
1398
- for (const directory of ordered) await syncDirectory(directory);
1399
- }
1400
-
1401
- /** verified exact artifact setを既存rootへ上書きせずatomicに発行する。 */
1402
- export async function writeRc2CampaignArtifacts(options = {}) {
1403
- if (!exactRecord(options, ['repoRoot', 'result'])
1404
- || typeof options.repoRoot !== 'string'
1405
- || options.repoRoot.length === 0
1406
- || options.result?.schema !== 'lattice.rc2.campaign_result.v4'
1407
- || options.result?.artifact_version !== ARTIFACT_VERSION) {
1408
- throw new TypeError('writeRc2CampaignArtifacts options are invalid');
1409
- }
1410
- const root = await realpath(options.repoRoot);
1411
- const artifactRoot = path.join(root, ARTIFACT_ROOTS.v4);
1412
- try {
1413
- await lstat(artifactRoot);
1414
- throw new Error('RC2 artifact root already exists; immutable evidence is not overwritten');
1415
- } catch (error) {
1416
- if (error?.code !== 'ENOENT') throw error;
1417
- }
1418
- const files = artifactFiles(options.result);
1419
- const manifest = {
1420
- schema: 'lattice.rc2.artifact_manifest.v4',
1421
- base_sha: options.result.base_sha,
1422
- result_digest: digestArtifact(options.result.hypothesis_evaluation),
1423
- files: [...files].map(([relativePath, bytes]) => ({
1424
- path: relativePath,
1425
- media_type: artifactMediaType(relativePath),
1426
- bytes: bytes.byteLength,
1427
- sha256: sha256(bytes),
1428
- })).sort((left, right) => left.path.localeCompare(right.path)),
1429
- };
1430
- const payloads = [...files].map(([relativePath, bytes]) => ({ path: relativePath, bytes }));
1431
- const verification = verifyRc2CampaignArtifactSet({ manifest, payloads });
1432
- if (!verification.valid) {
1433
- throw new TypeError(
1434
- `RC2 artifact set is invalid: ${verification.failed_conditions.join(', ')}`,
1435
- );
1436
- }
1437
- const parent = path.dirname(artifactRoot);
1438
- await mkdir(parent, { recursive: true });
1439
- const temporaryRoot = await mkdtemp(path.join(parent, '.v4-write-'));
1440
- try {
1441
- for (const [relativePath, bytes] of files) {
1442
- await writeDurableFile(path.join(temporaryRoot, relativePath), bytes);
1443
- }
1444
- await writeDurableFile(path.join(temporaryRoot, 'artifact-manifest.json'), jsonBytes(manifest));
1445
- await syncArtifactDirectories(temporaryRoot, [
1446
- ...RC2_ARTIFACT_PATHS,
1447
- 'artifact-manifest.json',
1448
- ]);
1449
- await rename(temporaryRoot, artifactRoot);
1450
- await syncDirectory(parent);
1451
- } catch (error) {
1452
- await rm(temporaryRoot, { recursive: true, force: true });
1453
- throw error;
1454
- }
1455
- return manifest;
1456
- }
1457
-
1458
- function artifactPathsForVersion(artifactVersion) {
1459
- if (artifactVersion === 'v1') return RC2_V1_ARTIFACT_PATHS;
1460
- if (artifactVersion === 'v2') return RC2_V2_ARTIFACT_PATHS;
1461
- if (artifactVersion === 'v3') return RC2_V3_ARTIFACT_PATHS;
1462
- if (artifactVersion === 'v4') return RC2_ARTIFACT_PATHS;
1463
- return null;
1464
- }
1465
-
1466
- function invalidDiskReceipt(condition) {
1467
- return {
1468
- schema: 'lattice.rc2.artifact_set_verification.v1',
1469
- valid: false,
1470
- checks: [{ id: condition, passed: false }],
1471
- failed_conditions: [condition],
1472
- };
1473
- }
1474
-
1475
- /** immutable rootを再読し、in-memory resultなしでpure verifierへ渡す。 */
1476
- export async function verifyRc2CampaignArtifactsOnDisk(options = {}) {
1477
- if (!exactRecord(options, ['repoRoot', 'artifactVersion'])
1478
- || typeof options.repoRoot !== 'string'
1479
- || options.repoRoot.length === 0
1480
- || artifactPathsForVersion(options.artifactVersion) === null) {
1481
- throw new TypeError('verifyRc2CampaignArtifactsOnDisk options are invalid');
1482
- }
1483
- const artifactRoot = path.join(options.repoRoot, ARTIFACT_ROOTS[options.artifactVersion]);
1484
- const artifactPaths = artifactPathsForVersion(options.artifactVersion);
1485
- let manifestBytes;
1486
- let manifest;
1487
- try {
1488
- manifestBytes = await readFile(path.join(artifactRoot, 'artifact-manifest.json'));
1489
- manifest = JSON.parse(manifestBytes.toString('utf8'));
1490
- } catch {
1491
- return invalidDiskReceipt('manifest_canonical');
1492
- }
1493
- if (!jsonBytes(manifest).equals(manifestBytes)) {
1494
- return invalidDiskReceipt('manifest_canonical');
1495
- }
1496
- let payloads;
1497
- try {
1498
- payloads = await Promise.all(artifactPaths.map(async (relativePath) => ({
1499
- path: relativePath,
1500
- bytes: await readFile(path.join(artifactRoot, relativePath)),
1501
- })));
1502
- } catch {
1503
- return invalidDiskReceipt('exact_artifact_set');
1504
- }
1505
- return verifyRc2CampaignArtifactSet({ manifest, payloads });
1506
- }
1
+ import { spawn } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
3
+ import {
4
+ lstat,
5
+ mkdir,
6
+ mkdtemp,
7
+ open,
8
+ readFile,
9
+ realpath,
10
+ rename,
11
+ rm,
12
+ writeFile,
13
+ } from 'node:fs/promises';
14
+ import path from 'node:path';
15
+ import { performance } from 'node:perf_hooks';
16
+ import { isDeepStrictEqual } from 'node:util';
17
+
18
+ import {
19
+ canonicalizeArtifact,
20
+ digestArtifact,
21
+ } from './artifact-contracts.mjs';
22
+ import { compileBoundaryCondition } from './boundary-compiler.mjs';
23
+ import { collectSensorEvidence } from './sensor-adapter.mjs';
24
+ import { invokeSensorCli, LATTICE_SENSOR_CLI } from './sensor-runtime.mjs';
25
+ import { runIsolatedTransform } from './isolation-runner.mjs';
26
+ import { runRc1BlackBoxOracle } from './rc1-black-box-oracle.mjs';
27
+ import { createRc1EvidenceBundle } from './rc1-evidence-bundle.mjs';
28
+ import {
29
+ RC2_ARTIFACT_PATHS,
30
+ verifyRc2CampaignArtifactSet,
31
+ } from './rc2-artifact-set.mjs';
32
+ import { compileDeliveryPolicyBoundaryBundleV2 } from './rc2-delivery-policy-front-end.mjs';
33
+ import { runRc2DeliveryPolicyOracle } from './rc2-delivery-policy-oracle.mjs';
34
+ import {
35
+ applyRc2DeliveryPolicyTransform,
36
+ RC2_DELIVERY_POLICY_TRANSFORM_PATHS,
37
+ runRc2DeliveryPolicySeamTransform,
38
+ } from './rc2-delivery-policy-transform.mjs';
39
+ import { compileRc1TransferBundleV2 } from './rc2-rc1-transfer-front-end.mjs';
40
+ import { compileSchedulabilityGraphV2 } from './schedulability-compiler-v2.mjs';
41
+ import { verifySchedulabilityPlanV2 } from './schedulability-verifier-v2.mjs';
42
+
43
+ const ARTIFACT_VERSION = 'v4';
44
+ const ARTIFACT_ROOTS = Object.freeze({
45
+ v1: 'research/campaigns/rc2/artifacts/v1',
46
+ v2: 'research/campaigns/rc2/artifacts/v2',
47
+ v3: 'research/campaigns/rc2/artifacts/v3',
48
+ v4: 'research/campaigns/rc2/artifacts/v4',
49
+ });
50
+ const SENSOR_CONFIG_REPO_PATH = 'lattice-sensor.json';
51
+ const SENSOR_CONFIG_ARTIFACT_PATH = 'identity/lattice-sensor-config.json';
52
+ // live実行は現在ratifiedなconfig(ADR 0053 Decision 3 epoch)との完全一致だけを受理する。
53
+ // 過去epochのartifact検証はrc2-artifact-set.mjsのratified epoch allowlistが担う。
54
+ const SENSOR_CONFIG_BYTES = Buffer.from(
55
+ '{"exclude":["research/campaigns/**/artifacts/**/identity/","research/runs/",".lattice/todo/",".lattice/generated/"]}\n',
56
+ 'utf8',
57
+ );
58
+ const V2_ONLY_ARTIFACT_PATHS = Object.freeze([
59
+ SENSOR_CONFIG_ARTIFACT_PATH,
60
+ 'predecessors/adr-0040.md',
61
+ 'predecessors/rc2-v1-artifact-manifest.json',
62
+ 'predecessors/rc2-v1-new-plan-version.json',
63
+ ]);
64
+ const V3_ONLY_ARTIFACT_PATHS = Object.freeze([
65
+ 'predecessors/adr-0041.md',
66
+ 'predecessors/rc2-semantic-reseal-characterization.md',
67
+ 'predecessors/rc2-v2-artifact-manifest.json',
68
+ 'predecessors/rc2-v2-new-plan-version.json',
69
+ ]);
70
+ const V4_ONLY_ARTIFACT_PATHS = Object.freeze([
71
+ 'predecessors/adr-0042.md',
72
+ 'predecessors/rc2-v3-version-downgrade-refutation.md',
73
+ 'predecessors/rc2-v3-artifact-manifest.json',
74
+ 'predecessors/rc2-v3-new-plan-version.json',
75
+ ]);
76
+ const RC2_V3_ARTIFACT_PATHS = Object.freeze(RC2_ARTIFACT_PATHS.filter((relativePath) => (
77
+ !V4_ONLY_ARTIFACT_PATHS.includes(relativePath)
78
+ )));
79
+ const RC2_V2_ARTIFACT_PATHS = Object.freeze(RC2_V3_ARTIFACT_PATHS.filter((relativePath) => (
80
+ !V3_ONLY_ARTIFACT_PATHS.includes(relativePath)
81
+ )));
82
+ const RC2_V1_ARTIFACT_PATHS = Object.freeze(RC2_V2_ARTIFACT_PATHS.filter((relativePath) => (
83
+ !V2_ONLY_ARTIFACT_PATHS.includes(relativePath)
84
+ )));
85
+ const GIT_SHA1 = /^[0-9a-f]{40}$/;
86
+ const SEMVER = /^\d+\.\d+\.\d+(?:[-+][A-Za-z0-9.-]+)?$/;
87
+ const RAW_EVIDENCE_CHUNK_BYTES = 12_000;
88
+
89
+ const PRIMARY_INPUT_FILES = Object.freeze({
90
+ planInput: 'research/campaigns/rc2/inputs/plan-input.json',
91
+ candidateSpec: 'research/campaigns/rc2/inputs/candidate-spec-v1.json',
92
+ normalManualEvidence: 'research/campaigns/rc2/inputs/manual-evidence.normal.json',
93
+ partialManualEvidence: 'research/campaigns/rc2/inputs/manual-evidence.partial-state-negative.json',
94
+ unknownManualEvidence: 'research/campaigns/rc2/inputs/manual-evidence.third-only-unknown.json',
95
+ querySet: 'research/campaigns/rc2/inputs/query-set-v2.json',
96
+ });
97
+
98
+ const RC1_ARTIFACT_ROOT = 'research/campaigns/rc1/artifacts/v6';
99
+ const RC1_INPUT_FILES = Object.freeze({
100
+ planInput: `${RC1_ARTIFACT_ROOT}/inputs/plan-input.json`,
101
+ candidateSpec: `${RC1_ARTIFACT_ROOT}/inputs/candidate-spec-v2.json`,
102
+ normalManualEvidence: `${RC1_ARTIFACT_ROOT}/inputs/manual-evidence.normal.json`,
103
+ negativeManualEvidence: `${RC1_ARTIFACT_ROOT}/inputs/manual-evidence.shared-state-negative.json`,
104
+ querySet: `${RC1_ARTIFACT_ROOT}/inputs/query-set-v2.json`,
105
+ oracle: `${RC1_ARTIFACT_ROOT}/inputs/behavior-oracle-v2.json`,
106
+ });
107
+
108
+ const PRIMARY_ARTIFACT_INPUTS = Object.freeze({
109
+ planInput: 'inputs/primary/plan-input.json',
110
+ candidateSpec: 'inputs/primary/candidate-spec-v1.json',
111
+ normalManualEvidence: 'inputs/primary/manual-evidence.normal.json',
112
+ partialManualEvidence: 'inputs/primary/manual-evidence.partial-state-negative.json',
113
+ unknownManualEvidence: 'inputs/primary/manual-evidence.third-only-unknown.json',
114
+ querySet: 'inputs/primary/query-set-v2.json',
115
+ });
116
+
117
+ const RC1_ARTIFACT_INPUTS = Object.freeze({
118
+ planInput: 'inputs/rc1/plan-input.json',
119
+ candidateSpec: 'inputs/rc1/candidate-spec-v2.json',
120
+ normalManualEvidence: 'inputs/rc1/manual-evidence.normal.json',
121
+ negativeManualEvidence: 'inputs/rc1/manual-evidence.shared-state-negative.json',
122
+ querySet: 'inputs/rc1/query-set-v2.json',
123
+ oracle: 'inputs/rc1/behavior-oracle-v2.json',
124
+ });
125
+
126
+ const SOURCE_IDENTITIES = Object.freeze([
127
+ ['src/sensor-adapter.mjs', 'identity/lattice-sensor-adapter.mjs', new URL('./sensor-adapter.mjs', import.meta.url)],
128
+ ['src/rc1-black-box-oracle.mjs', 'identity/rc1-black-box-oracle.mjs', new URL('./rc1-black-box-oracle.mjs', import.meta.url)],
129
+ ['src/boundary-compiler.mjs', 'identity/rc1-boundary-compiler.mjs', new URL('./boundary-compiler.mjs', import.meta.url)],
130
+ ['src/rc1-evidence-bundle.mjs', 'identity/rc1-evidence-bundle.mjs', new URL('./rc1-evidence-bundle.mjs', import.meta.url)],
131
+ ['src/rc2-artifact-set.mjs', 'identity/rc2-artifact-set.mjs', new URL('./rc2-artifact-set.mjs', import.meta.url)],
132
+ ['src/rc2-campaign.mjs', 'identity/rc2-campaign.mjs', new URL(import.meta.url)],
133
+ ['src/rc2-delivery-policy-front-end.mjs', 'identity/rc2-delivery-policy-front-end.mjs', new URL('./rc2-delivery-policy-front-end.mjs', import.meta.url)],
134
+ ['src/rc2-delivery-policy-oracle.mjs', 'identity/rc2-delivery-policy-oracle.mjs', new URL('./rc2-delivery-policy-oracle.mjs', import.meta.url)],
135
+ ['src/rc2-delivery-policy-transform.mjs', 'identity/rc2-delivery-policy-transform.mjs', new URL('./rc2-delivery-policy-transform.mjs', import.meta.url)],
136
+ ['src/rc2-rc1-transfer-front-end.mjs', 'identity/rc2-rc1-transfer-front-end.mjs', new URL('./rc2-rc1-transfer-front-end.mjs', import.meta.url)],
137
+ ['src/schedulability-compiler-v2.mjs', 'identity/schedulability-compiler-v2.mjs', new URL('./schedulability-compiler-v2.mjs', import.meta.url)],
138
+ ['src/schedulability-verifier-v2.mjs', 'identity/schedulability-verifier-v2.mjs', new URL('./schedulability-verifier-v2.mjs', import.meta.url)],
139
+ ]);
140
+
141
+ const PREDECESSOR_FILES = Object.freeze({
142
+ 'predecessors/adr-0031.md': 'docs/adr/0031-rc1-v6-phase-gate-support.md',
143
+ 'predecessors/adr-0032.md': 'docs/adr/0032-rc2-bounded-graph-compiler-and-three-way-seam.md',
144
+ 'predecessors/adr-0037.md': 'docs/adr/0037-rc2-delivery-policy-transform-transaction.md',
145
+ 'predecessors/adr-0038.md': 'docs/adr/0038-rc2-closed-loop-version-and-artifact-contract.md',
146
+ 'predecessors/rc1-v6-artifact-manifest.json': `${RC1_ARTIFACT_ROOT}/artifact-manifest.json`,
147
+ 'predecessors/rc1-v6-plan.md': 'docs/archive/2026-07-16-plan-lattice-research-campaign-1-v6-phase-supported.md',
148
+ 'predecessors/rc1-v6-seam.patch': `${RC1_ARTIFACT_ROOT}/transform/seam.patch`,
149
+ 'predecessors/rc1-v6-transform-artifact.json': `${RC1_ARTIFACT_ROOT}/transform/transform-artifact.json`,
150
+ 'predecessors/rc1-v6-transform-receipt.json': `${RC1_ARTIFACT_ROOT}/transform/transform-receipt.json`,
151
+ 'predecessors/adr-0040.md': 'docs/adr/0040-rc2-post-publication-lattice-sensor-scope-and-artifact-v2.md',
152
+ 'predecessors/rc2-v1-artifact-manifest.json': `${ARTIFACT_ROOTS.v1}/artifact-manifest.json`,
153
+ 'predecessors/rc2-v1-new-plan-version.json': `${ARTIFACT_ROOTS.v1}/new-plan-version.json`,
154
+ 'predecessors/adr-0041.md': 'docs/adr/0041-rc2-artifact-semantic-oracle-mutation-binding.md',
155
+ 'predecessors/rc2-semantic-reseal-characterization.md': 'docs/evidence/2026-07-16-rc2-artifact-semantic-reseal-characterization.md',
156
+ 'predecessors/rc2-v2-artifact-manifest.json': `${ARTIFACT_ROOTS.v2}/artifact-manifest.json`,
157
+ 'predecessors/rc2-v2-new-plan-version.json': `${ARTIFACT_ROOTS.v2}/new-plan-version.json`,
158
+ 'predecessors/adr-0042.md': 'docs/adr/0042-rc2-artifact-version-witness-epoch-and-v4.md',
159
+ 'predecessors/rc2-v3-version-downgrade-refutation.md': 'docs/evidence/2026-07-16-rc2-v3-version-downgrade-refutation.md',
160
+ 'predecessors/rc2-v3-artifact-manifest.json': `${ARTIFACT_ROOTS.v3}/artifact-manifest.json`,
161
+ 'predecessors/rc2-v3-new-plan-version.json': `${ARTIFACT_ROOTS.v3}/new-plan-version.json`,
162
+ });
163
+
164
+ const RUN_IDS = Object.freeze([
165
+ 'primary-control-1',
166
+ 'primary-control-2',
167
+ 'primary-treatment-1',
168
+ 'primary-treatment-2',
169
+ 'rc1-transfer-control-1',
170
+ 'rc1-transfer-treatment-1',
171
+ ]);
172
+
173
+ function plainRecord(value) {
174
+ return value !== null
175
+ && typeof value === 'object'
176
+ && !Array.isArray(value)
177
+ && Object.getPrototypeOf(value) === Object.prototype;
178
+ }
179
+
180
+ function exactRecord(value, keys) {
181
+ if (!plainRecord(value)) return false;
182
+ const actual = Object.keys(value).sort();
183
+ const expected = [...keys].sort();
184
+ return actual.length === expected.length
185
+ && actual.every((key, index) => key === expected[index]);
186
+ }
187
+
188
+ function sha256(bytes) {
189
+ return createHash('sha256').update(bytes).digest('hex');
190
+ }
191
+
192
+ function jsonBytes(value) {
193
+ return Buffer.from(canonicalizeArtifact(value), 'utf8');
194
+ }
195
+
196
+ function roundedMilliseconds(startedAt) {
197
+ return Math.round((performance.now() - startedAt) * 1_000) / 1_000;
198
+ }
199
+
200
+ function run(command, args, { cwd, input, allowExitCodes = [0] } = {}) {
201
+ return new Promise((resolve, reject) => {
202
+ const child = spawn(command, args, {
203
+ cwd,
204
+ shell: false,
205
+ stdio: ['pipe', 'pipe', 'pipe'],
206
+ env: { ...process.env, NO_COLOR: '1' },
207
+ });
208
+ const stdout = [];
209
+ const stderr = [];
210
+ child.stdout.on('data', (chunk) => stdout.push(chunk));
211
+ child.stderr.on('data', (chunk) => stderr.push(chunk));
212
+ child.once('error', reject);
213
+ child.once('close', (code, signal) => {
214
+ const result = {
215
+ code,
216
+ signal,
217
+ stdout: Buffer.concat(stdout),
218
+ stderr: Buffer.concat(stderr),
219
+ };
220
+ if (allowExitCodes.includes(code) && signal === null) resolve(result);
221
+ else reject(Object.assign(
222
+ new Error(`${command} failed (${signal ?? code}): ${result.stderr.toString('utf8').trim()}`),
223
+ result,
224
+ ));
225
+ });
226
+ child.stdin.end(input);
227
+ });
228
+ }
229
+
230
+ async function captureExecutionIdentity(repoRoot) {
231
+ const sources = [];
232
+ const payloads = new Map();
233
+ for (const [runtimePath, artifactRef, sourceUrl] of SOURCE_IDENTITIES) {
234
+ const bytes = await readFile(sourceUrl);
235
+ payloads.set(artifactRef, bytes);
236
+ sources.push({ runtime_path: runtimePath, artifact_ref: artifactRef, digest: sha256(bytes) });
237
+ }
238
+ const executableBytes = await readFile(LATTICE_SENSOR_CLI);
239
+ const version = (await run(process.execPath, [LATTICE_SENSOR_CLI, '--version'])).stdout.toString('utf8').trim();
240
+ if (!SEMVER.test(version)) throw new TypeError('LatticeSensor versionがsemverではない');
241
+ const projectConfigBytes = await readFile(path.join(repoRoot, SENSOR_CONFIG_REPO_PATH));
242
+ if (!projectConfigBytes.equals(SENSOR_CONFIG_BYTES)) {
243
+ throw new TypeError('LatticeSensor project config bytesがRC2 v2 contractと一致しない');
244
+ }
245
+ const sensorIdentity = {
246
+ schema: 'lattice.rc2.sensor_identity.v2',
247
+ version,
248
+ executable_ref: 'lattice-sensor',
249
+ executable_digest: sha256(executableBytes),
250
+ project_config_ref: SENSOR_CONFIG_ARTIFACT_PATH,
251
+ project_config_digest: sha256(projectConfigBytes),
252
+ };
253
+ payloads.set('identity/lattice-sensor-executable', executableBytes);
254
+ payloads.set(SENSOR_CONFIG_ARTIFACT_PATH, projectConfigBytes);
255
+ const snapshot = { sources, sensor_identity: sensorIdentity };
256
+ return { snapshot, digest: digestArtifact(snapshot), payloads };
257
+ }
258
+
259
+ async function readJson(repoRoot, relativePath) {
260
+ return JSON.parse(await readFile(path.join(repoRoot, relativePath), 'utf8'));
261
+ }
262
+
263
+ async function loadInputs(repoRoot) {
264
+ const primary = {};
265
+ const rc1 = {};
266
+ for (const [key, relativePath] of Object.entries(PRIMARY_INPUT_FILES)) {
267
+ primary[key] = await readJson(repoRoot, relativePath);
268
+ }
269
+ for (const [key, relativePath] of Object.entries(RC1_INPUT_FILES)) {
270
+ rc1[key] = await readJson(repoRoot, relativePath);
271
+ }
272
+ return { primary, rc1 };
273
+ }
274
+
275
+ async function loadPredecessors(repoRoot) {
276
+ const payloads = new Map();
277
+ for (const [artifactPath, repoPath] of Object.entries(PREDECESSOR_FILES)) {
278
+ payloads.set(artifactPath, await readFile(path.join(repoRoot, repoPath)));
279
+ }
280
+ return payloads;
281
+ }
282
+
283
+ function loadV3PlanPredecessor(payloads) {
284
+ const manifestBytes = payloads.get('predecessors/rc2-v3-artifact-manifest.json');
285
+ const planBytes = payloads.get('predecessors/rc2-v3-new-plan-version.json');
286
+ let manifest;
287
+ let plan;
288
+ try {
289
+ manifest = JSON.parse(manifestBytes.toString('utf8'));
290
+ plan = JSON.parse(planBytes.toString('utf8'));
291
+ } catch {
292
+ throw new TypeError('RC2 v3 predecessor JSONをparseできない');
293
+ }
294
+ const paths = Array.isArray(manifest?.files)
295
+ ? manifest.files.map(({ path: relativePath }) => relativePath).sort()
296
+ : [];
297
+ const entry = manifest?.files?.find(({ path: relativePath }) => (
298
+ relativePath === 'new-plan-version.json'
299
+ ));
300
+ if (!jsonBytes(manifest).equals(manifestBytes)
301
+ || !jsonBytes(plan).equals(planBytes)
302
+ || manifest.schema !== 'lattice.rc2.artifact_manifest.v3'
303
+ || !isDeepStrictEqual(paths, [...RC2_V3_ARTIFACT_PATHS])
304
+ || !exactRecord(entry, ['path', 'media_type', 'bytes', 'sha256'])
305
+ || entry.media_type !== 'application/json'
306
+ || entry.bytes !== planBytes.byteLength
307
+ || entry.sha256 !== sha256(planBytes)
308
+ || plan.schema !== 'lattice.rc2.plan_version.v1'
309
+ || plan.version !== 'rc2-delivery-policy-v4'
310
+ || plan.plan_digest !== digestArtifact(plan.plan)) {
311
+ throw new TypeError('RC2 v3 predecessor manifest/plan bindingが不正');
312
+ }
313
+ return plan;
314
+ }
315
+
316
+ async function resolveRepository(repoRoot, baseRef) {
317
+ if (typeof repoRoot !== 'string' || repoRoot.length === 0
318
+ || typeof baseRef !== 'string' || baseRef.length === 0) {
319
+ throw new TypeError('repoRoot and baseRef are required');
320
+ }
321
+ const root = await realpath(repoRoot);
322
+ const status = await run('git', ['status', '--porcelain=v1', '-z'], { cwd: root });
323
+ if (status.stdout.length > 0) throw new TypeError('RC2 campaign source repository must be clean');
324
+ const head = (await run('git', ['rev-parse', '--verify', 'HEAD^{commit}'], { cwd: root }))
325
+ .stdout.toString('utf8').trim();
326
+ const baseSha = (await run('git', ['rev-parse', '--verify', `${baseRef}^{commit}`], { cwd: root }))
327
+ .stdout.toString('utf8').trim();
328
+ if (!GIT_SHA1.test(head) || !GIT_SHA1.test(baseSha) || head !== baseSha) {
329
+ throw new TypeError('RC2 campaign requires clean HEAD === baseRef');
330
+ }
331
+ return { root, baseSha };
332
+ }
333
+
334
+ function surfacePaths(candidateSpec) {
335
+ const result = new Set();
336
+ if (candidateSpec.fixed_oracle?.path) result.add(candidateSpec.fixed_oracle.path);
337
+ for (const todo of candidateSpec.todos) {
338
+ for (const mode of ['current', 'proposed']) {
339
+ result.add(todo[mode].production.path);
340
+ todo[mode].tests.forEach(({ path: testPath }) => result.add(testPath));
341
+ }
342
+ }
343
+ return [...result].sort();
344
+ }
345
+
346
+ async function captureSourceSnapshot(repoRoot, paths) {
347
+ const files = [];
348
+ for (const relativePath of paths) {
349
+ const absolutePath = path.join(repoRoot, relativePath);
350
+ try {
351
+ const stat = await lstat(absolutePath);
352
+ if (!stat.isFile() || stat.isSymbolicLink()) {
353
+ throw new TypeError(`snapshot pathが通常fileではない: ${relativePath}`);
354
+ }
355
+ files.push({ path: relativePath, state: 'file', content_digest: sha256(await readFile(absolutePath)) });
356
+ } catch (error) {
357
+ if (error?.code !== 'ENOENT') throw error;
358
+ files.push({ path: relativePath, state: 'absent', content_digest: null });
359
+ }
360
+ }
361
+ return { schema_version: 'lattice.rc2.source_snapshot.v1', files };
362
+ }
363
+
364
+ async function applyPatch(worktreePath, patch) {
365
+ if (!Buffer.isBuffer(patch) || patch.length === 0) throw new TypeError('accepted patch bytes are required');
366
+ await run('git', ['apply', '--check', '--binary', '-'], { cwd: worktreePath, input: patch });
367
+ await run('git', ['apply', '--binary', '-'], { cwd: worktreePath, input: patch });
368
+ }
369
+
370
+ async function captureLatticeSensorBootstrap(worktreePath) {
371
+ try {
372
+ return await readFile(path.join(worktreePath, '.lattice/sensor', '.gitignore'));
373
+ } catch (error) {
374
+ if (error?.code === 'ENOENT') return null;
375
+ throw error;
376
+ }
377
+ }
378
+
379
+ async function restoreLatticeSensorBootstrap(worktreePath, bootstrap) {
380
+ const sensorPath = path.join(worktreePath, '.lattice/sensor');
381
+ await rm(sensorPath, { recursive: true, force: true });
382
+ if (bootstrap === null) return;
383
+ await mkdir(sensorPath, { recursive: true });
384
+ await writeFile(path.join(sensorPath, '.gitignore'), bootstrap);
385
+ }
386
+
387
+ async function worktreeCount(repoRoot) {
388
+ const output = (await run('git', ['worktree', 'list', '--porcelain'], { cwd: repoRoot }))
389
+ .stdout.toString('utf8');
390
+ return output.split('\n').filter((line) => line.startsWith('worktree ')).length;
391
+ }
392
+
393
+ function measured(elapsedMs) {
394
+ return { state: 'measured', elapsed_ms: elapsedMs };
395
+ }
396
+
397
+ async function observeFreshIndex({
398
+ repoRoot,
399
+ baseSha,
400
+ family,
401
+ condition,
402
+ runId,
403
+ querySet,
404
+ paths,
405
+ patch,
406
+ allowedPaths,
407
+ sensorIdentity,
408
+ oracle,
409
+ rc1Inputs,
410
+ costMeasurements,
411
+ }) {
412
+ const beforeCount = await worktreeCount(repoRoot);
413
+ let rawEvidence;
414
+ let snapshot;
415
+ let oracleReceipt;
416
+ let isolationInstanceDigest;
417
+ let indexElapsedMs;
418
+ let queryElapsedMs;
419
+ let oracleElapsedMs;
420
+ const isolated = await runIsolatedTransform({
421
+ repoRoot,
422
+ baseRef: baseSha,
423
+ allowedPaths,
424
+ transform: async ({ worktreePath }) => {
425
+ if (condition === 'treatment') await applyPatch(worktreePath, patch);
426
+ },
427
+ verifyCommands: [],
428
+ observe: async ({ worktreePath }) => {
429
+ isolationInstanceDigest = sha256(Buffer.from(worktreePath, 'utf8'));
430
+ snapshot = await captureSourceSnapshot(worktreePath, paths);
431
+ const oracleStarted = performance.now();
432
+ oracleReceipt = family === 'primary'
433
+ ? await runRc2DeliveryPolicyOracle({ repoRoot: worktreePath })
434
+ : await runRc1BlackBoxOracle({ repoRoot: worktreePath, oracle });
435
+ oracleElapsedMs = roundedMilliseconds(oracleStarted);
436
+ if (oracleReceipt.outcome !== 'passed') throw new TypeError(`${runId} oracle failed`);
437
+ const bootstrap = await captureLatticeSensorBootstrap(worktreePath);
438
+ try {
439
+ const indexStarted = performance.now();
440
+ await invokeSensorCli(run, ['init', '.'], { cwd: worktreePath });
441
+ indexElapsedMs = roundedMilliseconds(indexStarted);
442
+ const queryStarted = performance.now();
443
+ rawEvidence = await collectSensorEvidence({ cwd: worktreePath, querySet });
444
+ queryElapsedMs = roundedMilliseconds(queryStarted);
445
+ } finally {
446
+ await restoreLatticeSensorBootstrap(worktreePath, bootstrap);
447
+ }
448
+ },
449
+ });
450
+ const afterCount = await worktreeCount(repoRoot);
451
+ if (!rawEvidence || !snapshot || !oracleReceipt || !isolationInstanceDigest
452
+ || isolated.sourceInvariant?.outcome !== 'passed'
453
+ || beforeCount !== afterCount) {
454
+ throw new TypeError(`${runId} fresh observation is incomplete`);
455
+ }
456
+ if (condition === 'treatment' && !isolated.patch.equals(patch)) {
457
+ throw new TypeError(`${runId} did not replay the accepted patch exactly`);
458
+ }
459
+ if (condition === 'control' && isolated.patch.length !== 0) {
460
+ throw new TypeError(`${runId} control unexpectedly changed source`);
461
+ }
462
+ const evidence = createRc1EvidenceBundle({ condition, runId, querySet, rawEvidence });
463
+ const patchDigest = condition === 'treatment' ? sha256(isolated.patch) : null;
464
+ const measurement = {
465
+ schema: 'lattice.rc2.sensor_measurement.v1',
466
+ base_sha: baseSha,
467
+ patch_digest: patchDigest,
468
+ snapshot,
469
+ snapshot_digest: digestArtifact(snapshot),
470
+ sensor_identity: structuredClone(sensorIdentity),
471
+ sensor_identity_digest: digestArtifact(sensorIdentity),
472
+ query_set_digest: digestArtifact(querySet),
473
+ raw_evidence_digest: evidence.raw.payload_digest,
474
+ };
475
+ const cost = {
476
+ index: measured(indexElapsedMs),
477
+ query: measured(queryElapsedMs),
478
+ oracle: measured(oracleElapsedMs),
479
+ };
480
+ for (const [stage, value] of Object.entries(cost)) {
481
+ costMeasurements.push({
482
+ stage: `run.${runId}.${stage}`,
483
+ state: value.state,
484
+ elapsed_ms: value.elapsed_ms,
485
+ });
486
+ }
487
+ const runRecord = {
488
+ schema: 'lattice.rc2.fresh_sensor_run.v1',
489
+ family,
490
+ condition,
491
+ run_id: runId,
492
+ fresh_index: true,
493
+ isolation_instance_digest: isolationInstanceDigest,
494
+ evidence,
495
+ measurement,
496
+ oracle_receipt: oracleReceipt,
497
+ legacy_boundary_manifests: null,
498
+ source_invariant: isolated.sourceInvariant,
499
+ cleanup: {
500
+ schema: 'lattice.rc2.worktree_cleanup_receipt.v1',
501
+ outcome: 'passed',
502
+ worktree_count_before: beforeCount,
503
+ worktree_count_after: afterCount,
504
+ },
505
+ cost,
506
+ };
507
+ if (family === 'rc1_transfer') {
508
+ const raw = JSON.parse(Buffer.from(evidence.raw.payload_base64, 'base64').toString('utf8'));
509
+ const compileLegacy = (manualEvidence, label) => compileBoundaryCondition({
510
+ planInput: rc1Inputs.planInput,
511
+ candidateSpec: rc1Inputs.candidateSpec,
512
+ manualEvidence,
513
+ querySet: rc1Inputs.querySet,
514
+ sensorEvidence: raw,
515
+ codeSnapshotDigest: measurement.snapshot_digest,
516
+ planVersion: `rc2-${condition}-transfer-${label}`,
517
+ });
518
+ runRecord.legacy_boundary_manifests = {
519
+ normal: compileLegacy(rc1Inputs.normalManualEvidence, 'normal').boundary_manifest,
520
+ negative: compileLegacy(rc1Inputs.negativeManualEvidence, 'negative').boundary_manifest,
521
+ };
522
+ }
523
+ return runRecord;
524
+ }
525
+
526
+ function verdictFor(bundle, compiled) {
527
+ return {
528
+ schema_version: 'lattice.boundary_verdict.v2',
529
+ normalized_graph_digest: bundle.graph_digest,
530
+ verdicts: compiled.pairwise_verdicts,
531
+ };
532
+ }
533
+
534
+ function timedSync(stage, costMeasurements, execute) {
535
+ const startedAt = performance.now();
536
+ const value = execute();
537
+ costMeasurements.push({ stage, state: 'measured', elapsed_ms: roundedMilliseconds(startedAt) });
538
+ return value;
539
+ }
540
+
541
+ function compiledRecord({ condition, runId, bundle, costMeasurements }) {
542
+ const compiled = timedSync(`compile.${condition}.${runId}`, costMeasurements, () => (
543
+ compileSchedulabilityGraphV2(bundle.graph)
544
+ ));
545
+ if (compiled.outcome !== 'compiled') throw new TypeError(`${condition} did not compile`);
546
+ const verdict = verdictFor(bundle, compiled);
547
+ const verification = timedSync(`verify.${condition}.${runId}`, costMeasurements, () => (
548
+ verifySchedulabilityPlanV2(bundle.graph, compiled.plan)
549
+ ));
550
+ if (verification.outcome !== 'verified') throw new TypeError(`${condition} minimum is unverified`);
551
+ const stable = { bundle, verdict, plan: compiled.plan, verification };
552
+ return {
553
+ schema: 'lattice.rc2.compiled_condition.v1',
554
+ condition,
555
+ run_id: runId,
556
+ outcome: 'compiled',
557
+ ...stable,
558
+ artifact_digest: digestArtifact(stable),
559
+ };
560
+ }
561
+
562
+ function compilePrimary({ runRecord, inputs, manualEvidence, planInput, condition, costMeasurements }) {
563
+ const bundle = timedSync(`front-end.${condition}.${runRecord.run_id}`, costMeasurements, () => (
564
+ compileDeliveryPolicyBoundaryBundleV2({
565
+ planInput,
566
+ candidateSpec: inputs.candidateSpec,
567
+ manualEvidence,
568
+ querySet: inputs.querySet,
569
+ sourceSnapshot: runRecord.measurement.snapshot,
570
+ sensorEvidence: runRecord.evidence.portable,
571
+ })
572
+ ));
573
+ return compiledRecord({ condition, runId: runRecord.run_id, bundle, costMeasurements });
574
+ }
575
+
576
+ function compileUnknown({ runRecord, inputs, costMeasurements }) {
577
+ const condition = 'primary-treatment-third-only-unknown';
578
+ const bundle = timedSync(`front-end.${condition}.${runRecord.run_id}`, costMeasurements, () => (
579
+ compileDeliveryPolicyBoundaryBundleV2({
580
+ planInput: inputs.planInput,
581
+ candidateSpec: inputs.candidateSpec,
582
+ manualEvidence: inputs.unknownManualEvidence,
583
+ querySet: inputs.querySet,
584
+ sourceSnapshot: runRecord.measurement.snapshot,
585
+ sensorEvidence: runRecord.evidence.portable,
586
+ })
587
+ ));
588
+ const outcome = timedSync(`compile.${condition}.${runRecord.run_id}`, costMeasurements, () => (
589
+ compileSchedulabilityGraphV2(bundle.graph)
590
+ ));
591
+ const verification = timedSync(`verify.${condition}.${runRecord.run_id}`, costMeasurements, () => (
592
+ verifySchedulabilityPlanV2(bundle.graph, {
593
+ schema_version: 'lattice.plan_graph.v2',
594
+ waves: [],
595
+ minimum_feasible_waves: 0,
596
+ })
597
+ ));
598
+ if (outcome.outcome !== 'unknown' || verification.outcome !== 'unknown') {
599
+ throw new TypeError('third-only unknown became dispatchable');
600
+ }
601
+ return {
602
+ schema: 'lattice.rc2.non_dispatchable_condition.v1',
603
+ condition,
604
+ run_id: runRecord.run_id,
605
+ bundle,
606
+ outcome,
607
+ verification,
608
+ };
609
+ }
610
+
611
+ function compileTransfer({ runRecord, inputs, manualEvidence, label, costMeasurements }) {
612
+ const condition = `rc1-transfer-${runRecord.condition}-${label}`;
613
+ const transferred = timedSync(`front-end.${condition}.${runRecord.run_id}`, costMeasurements, () => (
614
+ compileRc1TransferBundleV2({
615
+ planInput: inputs.planInput,
616
+ candidateSpec: inputs.candidateSpec,
617
+ manualEvidence,
618
+ querySet: inputs.querySet,
619
+ boundaryManifest: runRecord.legacy_boundary_manifests[label],
620
+ })
621
+ ));
622
+ const verification = timedSync(`verify.${condition}.${runRecord.run_id}`, costMeasurements, () => (
623
+ verifySchedulabilityPlanV2(transferred.bundle.graph, transferred.plan)
624
+ ));
625
+ if (verification.outcome !== 'verified') throw new TypeError(`${condition} minimum is unverified`);
626
+ const stable = {
627
+ bundle: transferred.bundle,
628
+ verdict: transferred.verdict,
629
+ plan: transferred.plan,
630
+ verification,
631
+ };
632
+ return {
633
+ schema: 'lattice.rc2.compiled_condition.v1',
634
+ condition,
635
+ run_id: runRecord.run_id,
636
+ outcome: 'compiled',
637
+ ...stable,
638
+ artifact_digest: digestArtifact(stable),
639
+ };
640
+ }
641
+
642
+ function compileAllConditions({ runs, inputs, costMeasurements }) {
643
+ const primaryControl = runs.primary.control.map((runRecord) => compilePrimary({
644
+ runRecord,
645
+ inputs: inputs.primary,
646
+ manualEvidence: inputs.primary.normalManualEvidence,
647
+ planInput: inputs.primary.planInput,
648
+ condition: 'primary-control-normal',
649
+ costMeasurements,
650
+ }));
651
+ const primaryTreatment = runs.primary.treatment.map((runRecord) => compilePrimary({
652
+ runRecord,
653
+ inputs: inputs.primary,
654
+ manualEvidence: inputs.primary.normalManualEvidence,
655
+ planInput: inputs.primary.planInput,
656
+ condition: 'primary-treatment-normal',
657
+ costMeasurements,
658
+ }));
659
+ if (new Set(primaryControl.map(({ artifact_digest: digest }) => digest)).size !== 1
660
+ || new Set(primaryTreatment.map(({ artifact_digest: digest }) => digest)).size !== 1) {
661
+ throw new TypeError('primary repeated compilation is not structurally reproducible');
662
+ }
663
+ const treatmentRun = runs.primary.treatment[0];
664
+ const capacityPlan = structuredClone(inputs.primary.planInput);
665
+ capacityPlan.capacity.writers = 2;
666
+ const partialState = compilePrimary({
667
+ runRecord: treatmentRun,
668
+ inputs: inputs.primary,
669
+ manualEvidence: inputs.primary.partialManualEvidence,
670
+ planInput: inputs.primary.planInput,
671
+ condition: 'primary-treatment-partial-state',
672
+ costMeasurements,
673
+ });
674
+ const capacity2 = compilePrimary({
675
+ runRecord: treatmentRun,
676
+ inputs: inputs.primary,
677
+ manualEvidence: inputs.primary.normalManualEvidence,
678
+ planInput: capacityPlan,
679
+ condition: 'primary-treatment-capacity-2',
680
+ costMeasurements,
681
+ });
682
+ const unknown = compileUnknown({ runRecord: treatmentRun, inputs: inputs.primary, costMeasurements });
683
+ const transferControl = runs.rc1_transfer.control[0];
684
+ const transferTreatment = runs.rc1_transfer.treatment[0];
685
+ return {
686
+ primary: {
687
+ control: primaryControl,
688
+ treatment: primaryTreatment,
689
+ partial_state: partialState,
690
+ capacity_2: capacity2,
691
+ third_only_unknown: unknown,
692
+ },
693
+ rc1_transfer: {
694
+ control: {
695
+ normal: compileTransfer({
696
+ runRecord: transferControl,
697
+ inputs: inputs.rc1,
698
+ manualEvidence: inputs.rc1.normalManualEvidence,
699
+ label: 'normal',
700
+ costMeasurements,
701
+ }),
702
+ negative: compileTransfer({
703
+ runRecord: transferControl,
704
+ inputs: inputs.rc1,
705
+ manualEvidence: inputs.rc1.negativeManualEvidence,
706
+ label: 'negative',
707
+ costMeasurements,
708
+ }),
709
+ },
710
+ treatment: {
711
+ normal: compileTransfer({
712
+ runRecord: transferTreatment,
713
+ inputs: inputs.rc1,
714
+ manualEvidence: inputs.rc1.normalManualEvidence,
715
+ label: 'normal',
716
+ costMeasurements,
717
+ }),
718
+ negative: compileTransfer({
719
+ runRecord: transferTreatment,
720
+ inputs: inputs.rc1,
721
+ manualEvidence: inputs.rc1.negativeManualEvidence,
722
+ label: 'negative',
723
+ costMeasurements,
724
+ }),
725
+ },
726
+ },
727
+ };
728
+ }
729
+
730
+ function compiledPayloads(compiled) {
731
+ return new Map([
732
+ ['compiled/primary-control-1-normal.json', jsonBytes(compiled.primary.control[0])],
733
+ ['compiled/primary-control-2-normal.json', jsonBytes(compiled.primary.control[1])],
734
+ ['compiled/primary-treatment-1-normal.json', jsonBytes(compiled.primary.treatment[0])],
735
+ ['compiled/primary-treatment-2-normal.json', jsonBytes(compiled.primary.treatment[1])],
736
+ ['compiled/primary-treatment-partial-state.json', jsonBytes(compiled.primary.partial_state)],
737
+ ['compiled/primary-treatment-capacity-2.json', jsonBytes(compiled.primary.capacity_2)],
738
+ ['compiled/primary-treatment-third-only-unknown.json', jsonBytes(compiled.primary.third_only_unknown)],
739
+ ['compiled/rc1-transfer-control-normal.json', jsonBytes(compiled.rc1_transfer.control.normal)],
740
+ ['compiled/rc1-transfer-control-negative.json', jsonBytes(compiled.rc1_transfer.control.negative)],
741
+ ['compiled/rc1-transfer-treatment-normal.json', jsonBytes(compiled.rc1_transfer.treatment.normal)],
742
+ ['compiled/rc1-transfer-treatment-negative.json', jsonBytes(compiled.rc1_transfer.treatment.negative)],
743
+ ]);
744
+ }
745
+
746
+ function inputPayloads(inputs) {
747
+ const result = new Map();
748
+ for (const [key, relativePath] of Object.entries(PRIMARY_ARTIFACT_INPUTS)) {
749
+ result.set(relativePath, jsonBytes(inputs.primary[key]));
750
+ }
751
+ for (const [key, relativePath] of Object.entries(RC1_ARTIFACT_INPUTS)) {
752
+ result.set(relativePath, jsonBytes(inputs.rc1[key]));
753
+ }
754
+ return result;
755
+ }
756
+
757
+ function transformPayloads(transform) {
758
+ return new Map([
759
+ ['transform/accepted-artifact.json', jsonBytes(transform.accepted.artifact)],
760
+ ['transform/accepted-receipt.json', jsonBytes(transform.accepted.receipt)],
761
+ ['transform/behavior-evidence.json', jsonBytes(transform.accepted.behavior_evidence)],
762
+ ['transform/mutation-evidence.json', jsonBytes(transform.accepted.mutation_evidence)],
763
+ ['transform/rejected-incomplete-artifact.json', jsonBytes(transform.rejected.incomplete.artifact)],
764
+ ['transform/rejected-incomplete-receipt.json', jsonBytes(transform.rejected.incomplete.receipt)],
765
+ ['transform/rejected-scope-artifact.json', jsonBytes(transform.rejected.scope.artifact)],
766
+ ['transform/rejected-scope-receipt.json', jsonBytes(transform.rejected.scope.receipt)],
767
+ ['transform/seam.patch', Buffer.from(transform.accepted.patch)],
768
+ ]);
769
+ }
770
+
771
+ function storedEvidence(evidence) {
772
+ const raw = evidence?.raw;
773
+ if (!exactRecord(raw, [
774
+ 'schema',
775
+ 'media_type',
776
+ 'encoding',
777
+ 'canonical_bytes',
778
+ 'payload_digest',
779
+ 'payload_base64',
780
+ ])
781
+ || raw.schema !== 'lattice.sensor_raw_opaque_receipt.v1'
782
+ || raw.encoding !== 'canonical-json-base64'
783
+ || typeof raw.payload_base64 !== 'string') {
784
+ throw new TypeError('run raw evidence receipt is invalid');
785
+ }
786
+ const payload = Buffer.from(raw.payload_base64, 'base64');
787
+ if (payload.toString('base64') !== raw.payload_base64
788
+ || payload.byteLength !== raw.canonical_bytes
789
+ || sha256(payload) !== raw.payload_digest) {
790
+ throw new TypeError('run raw evidence payload binding is invalid');
791
+ }
792
+ const payloadBase64Chunks = [];
793
+ for (let offset = 0; offset < raw.payload_base64.length; offset += RAW_EVIDENCE_CHUNK_BYTES) {
794
+ payloadBase64Chunks.push(raw.payload_base64.slice(
795
+ offset,
796
+ offset + RAW_EVIDENCE_CHUNK_BYTES,
797
+ ));
798
+ }
799
+ if (payloadBase64Chunks.length === 0
800
+ || payloadBase64Chunks.slice(0, -1).some((chunk) => (
801
+ Buffer.byteLength(chunk, 'utf8') !== RAW_EVIDENCE_CHUNK_BYTES
802
+ ))
803
+ || Buffer.byteLength(payloadBase64Chunks.at(-1), 'utf8') > RAW_EVIDENCE_CHUNK_BYTES) {
804
+ throw new TypeError('run raw evidence chunks are invalid');
805
+ }
806
+ return {
807
+ ...structuredClone(evidence),
808
+ raw: {
809
+ schema: 'lattice.rc2.chunked_sensor_raw_receipt.v1',
810
+ source_schema: raw.schema,
811
+ media_type: raw.media_type,
812
+ source_encoding: raw.encoding,
813
+ storage_encoding: 'ordered-base64-chunks',
814
+ canonical_bytes: raw.canonical_bytes,
815
+ payload_digest: raw.payload_digest,
816
+ payload_base64_chunks: payloadBase64Chunks,
817
+ },
818
+ };
819
+ }
820
+
821
+ function storedRunRecord(runRecord) {
822
+ return {
823
+ ...structuredClone(runRecord),
824
+ evidence: storedEvidence(runRecord.evidence),
825
+ };
826
+ }
827
+
828
+ function runPayloads(runs) {
829
+ const records = [
830
+ ...runs.primary.control,
831
+ ...runs.primary.treatment,
832
+ ...runs.rc1_transfer.control,
833
+ ...runs.rc1_transfer.treatment,
834
+ ];
835
+ return new Map(records.map((runRecord) => [
836
+ `evidence/${runRecord.run_id}.json`,
837
+ jsonBytes(storedRunRecord(runRecord)),
838
+ ]));
839
+ }
840
+
841
+ function mergePayloads(...maps) {
842
+ const result = new Map();
843
+ for (const map of maps) {
844
+ for (const [relativePath, bytes] of map) {
845
+ if (result.has(relativePath)) throw new TypeError(`duplicate artifact payload: ${relativePath}`);
846
+ result.set(relativePath, Buffer.from(bytes));
847
+ }
848
+ }
849
+ return result;
850
+ }
851
+
852
+ function predecessorDescriptors(payloads) {
853
+ const descriptors = [];
854
+ const add = (kind, ref) => descriptors.push({ kind, ref, digest: sha256(payloads.get(ref)) });
855
+ for (const ref of Object.keys(PREDECESSOR_FILES).sort()) {
856
+ add(ref === 'predecessors/rc1-v6-plan.md' ? 'rc1_v6_phase_archive' : 'immutable_predecessor', ref);
857
+ }
858
+ add('accepted_transform', 'transform/accepted-artifact.json');
859
+ add('behavior_envelope', 'transform/behavior-evidence.json');
860
+ add('mutation_evidence', 'transform/mutation-evidence.json');
861
+ for (const runId of RUN_IDS) add('evidence_bundle', `evidence/${runId}.json`);
862
+ for (const ref of Object.values(PRIMARY_ARTIFACT_INPUTS).sort()) add('fixed_input', ref);
863
+ add('execution_identity', 'identity.json');
864
+ add('compiler_identity', 'identity/schedulability-compiler-v2.mjs');
865
+ add('verifier_identity', 'identity/schedulability-verifier-v2.mjs');
866
+ add('sensor_project_config', SENSOR_CONFIG_ARTIFACT_PATH);
867
+ return descriptors;
868
+ }
869
+
870
+ function buildVersionBarrier({ inputs, compiled, predecessors, predecessorPlan }) {
871
+ const treatment = compiled.primary.treatment[0];
872
+ const affectedTodos = inputs.primary.planInput.todos.map(({ id }) => id);
873
+ const newPlanVersion = {
874
+ schema: 'lattice.rc2.plan_version.v1',
875
+ version: 'rc2-delivery-policy-v5',
876
+ predecessor_version: predecessorPlan.version,
877
+ plan: treatment.plan,
878
+ plan_digest: digestArtifact(treatment.plan),
879
+ bundle_digest: digestArtifact(treatment.bundle),
880
+ verdict_digest: digestArtifact(treatment.verdict),
881
+ affected_todos: affectedTodos,
882
+ causal_predecessors: predecessors,
883
+ };
884
+ const planDiff = {
885
+ schema: 'lattice.rc2.plan_diff.v1',
886
+ old_plan: {
887
+ version: predecessorPlan.version,
888
+ digest: digestArtifact(predecessorPlan),
889
+ },
890
+ new_plan: {
891
+ version: newPlanVersion.version,
892
+ digest: digestArtifact(newPlanVersion),
893
+ },
894
+ causal_predecessors: predecessors,
895
+ affected_todos: affectedTodos,
896
+ invalidated_contexts: [
897
+ { kind: 'old_plan', ref: predecessorPlan.version,
898
+ reason: 'the v4 artifact semantics cannot receive v5 bindings by append' },
899
+ { kind: 'agent_context', ref: `${predecessorPlan.version}-agent-context`,
900
+ reason: 'v4 agent context does not bind the version witness verifier identity' },
901
+ { kind: 'partial_patch', ref: `${predecessorPlan.version}-partial-patch`,
902
+ reason: 'patches compiled before version witness binding cannot cross the barrier' },
903
+ { kind: 'interface_assumption', ref: `${predecessorPlan.version}-interface-assumption`,
904
+ reason: 'v4 interface evidence does not bind the active witness epoch' },
905
+ { kind: 'boundary_evidence', ref: `${predecessorPlan.version}-boundary-evidence`,
906
+ reason: 'only fresh version-bound treatment evidence is valid for plan v5' },
907
+ ],
908
+ };
909
+ return { newPlanVersion, planDiff };
910
+ }
911
+
912
+ function distinctConflictPairs(record) {
913
+ return new Set(record.bundle.graph.conflicts.map(({ todo_ids: todoIds }) => (
914
+ [...todoIds].sort().join('\u0000')
915
+ ))).size;
916
+ }
917
+
918
+ function conditionMetrics(record) {
919
+ return {
920
+ conflict_records: record.bundle.graph.conflicts.length,
921
+ distinct_conflict_pairs: distinctConflictPairs(record),
922
+ minimum_feasible_waves: record.plan.minimum_feasible_waves,
923
+ };
924
+ }
925
+
926
+ function buildComparison({ baseSha, identity, transform, compiled, newPlanVersion, planDiff }) {
927
+ return {
928
+ schema: 'lattice.rc2.control_treatment_comparison.v1',
929
+ base_sha: baseSha,
930
+ identity_digest: digestArtifact(identity),
931
+ independent_variable: {
932
+ kind: 'accepted_registry_shard_patch',
933
+ transform_artifact_digest: digestArtifact(transform.accepted.artifact),
934
+ patch_digest: transform.accepted.artifact.patch.digest,
935
+ },
936
+ condition_artifact_digests: {
937
+ primary_control: compiled.primary.control.map(({ artifact_digest: digest }) => digest),
938
+ primary_treatment: compiled.primary.treatment.map(({ artifact_digest: digest }) => digest),
939
+ primary_partial_state: compiled.primary.partial_state.artifact_digest,
940
+ primary_capacity_2: compiled.primary.capacity_2.artifact_digest,
941
+ primary_unknown: digestArtifact(compiled.primary.third_only_unknown),
942
+ rc1_transfer_control_normal: compiled.rc1_transfer.control.normal.artifact_digest,
943
+ rc1_transfer_control_negative: compiled.rc1_transfer.control.negative.artifact_digest,
944
+ rc1_transfer_treatment_normal: compiled.rc1_transfer.treatment.normal.artifact_digest,
945
+ rc1_transfer_treatment_negative: compiled.rc1_transfer.treatment.negative.artifact_digest,
946
+ },
947
+ metrics: {
948
+ primary_control: conditionMetrics(compiled.primary.control[0]),
949
+ primary_treatment: conditionMetrics(compiled.primary.treatment[0]),
950
+ primary_partial_state: conditionMetrics(compiled.primary.partial_state),
951
+ primary_capacity_2: conditionMetrics(compiled.primary.capacity_2),
952
+ rc1_transfer_control_normal: conditionMetrics(compiled.rc1_transfer.control.normal),
953
+ rc1_transfer_control_negative: conditionMetrics(compiled.rc1_transfer.control.negative),
954
+ rc1_transfer_treatment_normal: conditionMetrics(compiled.rc1_transfer.treatment.normal),
955
+ rc1_transfer_treatment_negative: conditionMetrics(compiled.rc1_transfer.treatment.negative),
956
+ },
957
+ version_barrier: {
958
+ new_plan_version_digest: digestArtifact(newPlanVersion),
959
+ plan_diff_digest: digestArtifact(planDiff),
960
+ causal_predecessors_digest: digestArtifact(planDiff.causal_predecessors),
961
+ },
962
+ };
963
+ }
964
+
965
+ function evaluateHypothesis(comparison, compiled, transform) {
966
+ const checks = [
967
+ { id: 'accepted_transform_only', passed: transform.accepted.status === 'accepted' },
968
+ {
969
+ id: 'primary_repeat_reproducible',
970
+ passed: new Set(compiled.primary.control.map(({ artifact_digest: digest }) => digest)).size === 1
971
+ && new Set(compiled.primary.treatment.map(({ artifact_digest: digest }) => digest)).size === 1,
972
+ },
973
+ {
974
+ id: 'primary_conflicts_removed',
975
+ passed: comparison.metrics.primary_control.conflict_records === 12
976
+ && comparison.metrics.primary_control.distinct_conflict_pairs === 3
977
+ && comparison.metrics.primary_treatment.conflict_records === 0,
978
+ },
979
+ {
980
+ id: 'primary_waves_reduced',
981
+ passed: comparison.metrics.primary_control.minimum_feasible_waves === 3
982
+ && comparison.metrics.primary_treatment.minimum_feasible_waves === 1,
983
+ },
984
+ {
985
+ id: 'partial_state_preserved',
986
+ passed: comparison.metrics.primary_partial_state.conflict_records === 1
987
+ && comparison.metrics.primary_partial_state.minimum_feasible_waves === 2,
988
+ },
989
+ {
990
+ id: 'capacity_control_preserved',
991
+ passed: comparison.metrics.primary_capacity_2.conflict_records === 0
992
+ && comparison.metrics.primary_capacity_2.minimum_feasible_waves === 2,
993
+ },
994
+ {
995
+ id: 'third_only_unknown_closed',
996
+ passed: compiled.primary.third_only_unknown.outcome.outcome === 'unknown'
997
+ && compiled.primary.third_only_unknown.verification.outcome === 'unknown',
998
+ },
999
+ {
1000
+ id: 'rc1_transfer_isomorphic',
1001
+ passed: comparison.metrics.rc1_transfer_control_normal.conflict_records === 3
1002
+ && comparison.metrics.rc1_transfer_control_normal.minimum_feasible_waves === 2
1003
+ && comparison.metrics.rc1_transfer_treatment_normal.conflict_records === 0
1004
+ && comparison.metrics.rc1_transfer_treatment_normal.minimum_feasible_waves === 1
1005
+ && comparison.metrics.rc1_transfer_control_negative.minimum_feasible_waves === 2
1006
+ && comparison.metrics.rc1_transfer_treatment_negative.minimum_feasible_waves === 2,
1007
+ },
1008
+ {
1009
+ id: 'all_minimum_verified',
1010
+ passed: [
1011
+ ...compiled.primary.control,
1012
+ ...compiled.primary.treatment,
1013
+ compiled.primary.partial_state,
1014
+ compiled.primary.capacity_2,
1015
+ compiled.rc1_transfer.control.normal,
1016
+ compiled.rc1_transfer.control.negative,
1017
+ compiled.rc1_transfer.treatment.normal,
1018
+ compiled.rc1_transfer.treatment.negative,
1019
+ ].every(({ verification }) => verification.outcome === 'verified'),
1020
+ },
1021
+ ];
1022
+ return {
1023
+ schema: 'lattice.rc2.hypothesis_evaluation.v1',
1024
+ checks,
1025
+ supported: checks.every(({ passed }) => passed),
1026
+ failed_conditions: checks.filter(({ passed }) => !passed).map(({ id }) => id),
1027
+ };
1028
+ }
1029
+
1030
+ function patchReviewLines(patch) {
1031
+ return patch.toString('utf8').split('\n').filter((line) => (
1032
+ (line.startsWith('+') && !line.startsWith('+++'))
1033
+ || (line.startsWith('-') && !line.startsWith('---'))
1034
+ )).length;
1035
+ }
1036
+
1037
+ function buildCost(costMeasurements, transform) {
1038
+ const elapsed = Math.round(costMeasurements
1039
+ .reduce((sum, measurement) => sum + measurement.elapsed_ms, 0) * 1_000) / 1_000;
1040
+ return {
1041
+ schema: 'lattice.rc2.stage_cost.v1',
1042
+ measurements: costMeasurements,
1043
+ aggregate: {
1044
+ measured_count: costMeasurements.length,
1045
+ not_measured_count: 0,
1046
+ elapsed_ms: elapsed,
1047
+ },
1048
+ intervention: {
1049
+ patch_bytes: transform.accepted.patch.byteLength,
1050
+ files: transform.accepted.artifact.scope.changed_paths.length,
1051
+ review_lines: patchReviewLines(transform.accepted.patch),
1052
+ },
1053
+ rework: { rejected_attempts: 2, retries: 0, rollbacks: 0 },
1054
+ unverified: [
1055
+ 'actual multi-agent wall-clock improvement',
1056
+ 'elapsed measurement attestation beyond saved arithmetic binding',
1057
+ 'arbitrary-repository seam success rate',
1058
+ ],
1059
+ };
1060
+ }
1061
+
1062
+ function allCompiledRecords(compiled) {
1063
+ return [
1064
+ ...compiled.primary.control,
1065
+ ...compiled.primary.treatment,
1066
+ compiled.primary.partial_state,
1067
+ compiled.primary.capacity_2,
1068
+ compiled.primary.third_only_unknown,
1069
+ compiled.rc1_transfer.control.normal,
1070
+ compiled.rc1_transfer.control.negative,
1071
+ compiled.rc1_transfer.treatment.normal,
1072
+ compiled.rc1_transfer.treatment.negative,
1073
+ ];
1074
+ }
1075
+
1076
+ function buildExecutionEvidence({
1077
+ baseSha,
1078
+ identity,
1079
+ transform,
1080
+ runs,
1081
+ compiled,
1082
+ newPlanVersion,
1083
+ planDiff,
1084
+ comparison,
1085
+ hypothesis,
1086
+ cost,
1087
+ }) {
1088
+ const runRecords = [
1089
+ ...runs.primary.control,
1090
+ ...runs.primary.treatment,
1091
+ ...runs.rc1_transfer.control,
1092
+ ...runs.rc1_transfer.treatment,
1093
+ ];
1094
+ return {
1095
+ schema: 'lattice.rc2.campaign_execution_evidence.v1',
1096
+ base_sha: baseSha,
1097
+ identity_digest: digestArtifact(identity),
1098
+ transform_artifact_digest: digestArtifact(transform.accepted.artifact),
1099
+ run_digests: runRecords.map((runRecord) => digestArtifact(storedRunRecord(runRecord))),
1100
+ compiled_condition_digests: allCompiledRecords(compiled).map(digestArtifact),
1101
+ new_plan_version_digest: digestArtifact(newPlanVersion),
1102
+ plan_diff_digest: digestArtifact(planDiff),
1103
+ comparison_digest: digestArtifact(comparison),
1104
+ hypothesis_evaluation_digest: digestArtifact(hypothesis),
1105
+ cost_digest: digestArtifact(cost),
1106
+ };
1107
+ }
1108
+
1109
+ async function timedAsync(stage, costMeasurements, execute) {
1110
+ const startedAt = performance.now();
1111
+ const value = await execute();
1112
+ costMeasurements.push({ stage, state: 'measured', elapsed_ms: roundedMilliseconds(startedAt) });
1113
+ return value;
1114
+ }
1115
+
1116
+ async function runTransforms({ root, baseSha, candidateSpec, costMeasurements }) {
1117
+ const accepted = await timedAsync('transform.accepted', costMeasurements, () => (
1118
+ runRc2DeliveryPolicySeamTransform({ repoRoot: root, baseRef: baseSha, candidateSpec })
1119
+ ));
1120
+ if (accepted.status !== 'accepted' || !Buffer.isBuffer(accepted.patch)) {
1121
+ throw new TypeError('default RC2 transform was not accepted');
1122
+ }
1123
+ const incomplete = await timedAsync('transform.rejected.incomplete', costMeasurements, () => (
1124
+ runRc2DeliveryPolicySeamTransform({
1125
+ repoRoot: root,
1126
+ baseRef: baseSha,
1127
+ candidateSpec,
1128
+ transform: async ({ worktreePath }) => {
1129
+ await applyRc2DeliveryPolicyTransform({ worktreePath });
1130
+ await rm(path.join(worktreePath, 'test/rc2-delivery-policy-sms.test.mjs'));
1131
+ },
1132
+ })
1133
+ ));
1134
+ const scope = await timedAsync('transform.rejected.scope', costMeasurements, () => (
1135
+ runRc2DeliveryPolicySeamTransform({
1136
+ repoRoot: root,
1137
+ baseRef: baseSha,
1138
+ candidateSpec,
1139
+ transform: async ({ worktreePath }) => {
1140
+ await applyRc2DeliveryPolicyTransform({ worktreePath });
1141
+ await writeFile(path.join(worktreePath, 'rc2-scope-violation.tmp'), 'forbidden\n');
1142
+ },
1143
+ })
1144
+ ));
1145
+ if (incomplete.status !== 'rejected'
1146
+ || incomplete.artifact.rejection.kind !== 'incomplete_transform'
1147
+ || scope.status !== 'rejected'
1148
+ || scope.artifact.rejection.kind !== 'scope_violation') {
1149
+ throw new TypeError('RC2 rejected transform controls did not preserve typed outcomes');
1150
+ }
1151
+ return { accepted, rejected: { incomplete, scope } };
1152
+ }
1153
+
1154
+ /** accepted seamを独立変数にfresh sensor→v2 compile→new planまで実行する。 */
1155
+ export async function runRc2Campaign(options = {}) {
1156
+ if (!exactRecord(options, ['repoRoot', 'baseRef', 'artifactVersion'])
1157
+ || options.artifactVersion !== ARTIFACT_VERSION) {
1158
+ throw new TypeError('runRc2Campaign options must select exact artifactVersion v4');
1159
+ }
1160
+ const { root, baseSha } = await resolveRepository(options.repoRoot, options.baseRef);
1161
+ const [inputs, predecessors, identityBefore] = await Promise.all([
1162
+ loadInputs(root),
1163
+ loadPredecessors(root),
1164
+ captureExecutionIdentity(root),
1165
+ ]);
1166
+ const predecessorPlan = loadV3PlanPredecessor(predecessors);
1167
+ const costMeasurements = [];
1168
+ const transform = await runTransforms({
1169
+ root,
1170
+ baseSha,
1171
+ candidateSpec: inputs.primary.candidateSpec,
1172
+ costMeasurements,
1173
+ });
1174
+ const primaryPaths = surfacePaths(inputs.primary.candidateSpec);
1175
+ const rc1Paths = surfacePaths(inputs.rc1.candidateSpec);
1176
+ if (primaryPaths.length !== 9 || rc1Paths.length !== 6) {
1177
+ throw new TypeError('campaign fixed surface cardinality is invalid');
1178
+ }
1179
+ const rc1TransformArtifact = JSON.parse(
1180
+ predecessors.get('predecessors/rc1-v6-transform-artifact.json').toString('utf8'),
1181
+ );
1182
+ const rc1Patch = predecessors.get('predecessors/rc1-v6-seam.patch');
1183
+ if (rc1TransformArtifact.patch.digest !== sha256(rc1Patch)) {
1184
+ throw new TypeError('RC1 v6 predecessor patch binding is invalid');
1185
+ }
1186
+ const common = {
1187
+ repoRoot: root,
1188
+ baseSha,
1189
+ sensorIdentity: identityBefore.snapshot.sensor_identity,
1190
+ costMeasurements,
1191
+ };
1192
+ const primaryControl = [];
1193
+ const primaryTreatment = [];
1194
+ for (let index = 1; index <= 2; index += 1) {
1195
+ primaryControl.push(await observeFreshIndex({
1196
+ ...common,
1197
+ family: 'primary',
1198
+ condition: 'control',
1199
+ runId: `primary-control-${index}`,
1200
+ querySet: inputs.primary.querySet,
1201
+ paths: primaryPaths,
1202
+ patch: null,
1203
+ allowedPaths: [],
1204
+ oracle: null,
1205
+ rc1Inputs: null,
1206
+ }));
1207
+ }
1208
+ for (let index = 1; index <= 2; index += 1) {
1209
+ primaryTreatment.push(await observeFreshIndex({
1210
+ ...common,
1211
+ family: 'primary',
1212
+ condition: 'treatment',
1213
+ runId: `primary-treatment-${index}`,
1214
+ querySet: inputs.primary.querySet,
1215
+ paths: primaryPaths,
1216
+ patch: transform.accepted.patch,
1217
+ allowedPaths: [...RC2_DELIVERY_POLICY_TRANSFORM_PATHS],
1218
+ oracle: null,
1219
+ rc1Inputs: null,
1220
+ }));
1221
+ }
1222
+ const transferControl = await observeFreshIndex({
1223
+ ...common,
1224
+ family: 'rc1_transfer',
1225
+ condition: 'control',
1226
+ runId: 'rc1-transfer-control-1',
1227
+ querySet: inputs.rc1.querySet,
1228
+ paths: rc1Paths,
1229
+ patch: null,
1230
+ allowedPaths: [],
1231
+ oracle: inputs.rc1.oracle,
1232
+ rc1Inputs: inputs.rc1,
1233
+ });
1234
+ const transferTreatment = await observeFreshIndex({
1235
+ ...common,
1236
+ family: 'rc1_transfer',
1237
+ condition: 'treatment',
1238
+ runId: 'rc1-transfer-treatment-1',
1239
+ querySet: inputs.rc1.querySet,
1240
+ paths: rc1Paths,
1241
+ patch: rc1Patch,
1242
+ allowedPaths: [...rc1TransformArtifact.scope.allowed_paths],
1243
+ oracle: inputs.rc1.oracle,
1244
+ rc1Inputs: inputs.rc1,
1245
+ });
1246
+ const runs = {
1247
+ primary: { control: primaryControl, treatment: primaryTreatment },
1248
+ rc1_transfer: { control: [transferControl], treatment: [transferTreatment] },
1249
+ };
1250
+ const compiled = compileAllConditions({ runs, inputs, costMeasurements });
1251
+
1252
+ const identityAfter = await captureExecutionIdentity(root);
1253
+ if (!isDeepStrictEqual(identityBefore.snapshot, identityAfter.snapshot)
1254
+ || identityBefore.payloads.size !== identityAfter.payloads.size
1255
+ || [...identityBefore.payloads].some(([relativePath, bytes]) => (
1256
+ !identityAfter.payloads.get(relativePath)?.equals(bytes)
1257
+ ))) {
1258
+ throw new TypeError('RC2 execution identity drifted during campaign');
1259
+ }
1260
+ const identity = {
1261
+ schema: 'lattice.rc2.execution_identity.v4',
1262
+ sources: identityBefore.snapshot.sources,
1263
+ sensor_identity: identityBefore.snapshot.sensor_identity,
1264
+ sensor_identity_digest: digestArtifact(identityBefore.snapshot.sensor_identity),
1265
+ before_digest: identityBefore.digest,
1266
+ after_digest: identityAfter.digest,
1267
+ };
1268
+
1269
+ const preliminaryPayloads = mergePayloads(
1270
+ inputPayloads(inputs),
1271
+ identityBefore.payloads,
1272
+ predecessors,
1273
+ transformPayloads(transform),
1274
+ runPayloads(runs),
1275
+ new Map([['identity.json', jsonBytes(identity)]]),
1276
+ );
1277
+ const causalPredecessors = predecessorDescriptors(preliminaryPayloads);
1278
+ const { newPlanVersion, planDiff } = buildVersionBarrier({
1279
+ inputs,
1280
+ compiled,
1281
+ predecessors: causalPredecessors,
1282
+ predecessorPlan,
1283
+ });
1284
+ const comparison = buildComparison({
1285
+ baseSha,
1286
+ identity,
1287
+ transform,
1288
+ compiled,
1289
+ newPlanVersion,
1290
+ planDiff,
1291
+ });
1292
+ const hypothesisEvaluation = evaluateHypothesis(comparison, compiled, transform);
1293
+ if (!hypothesisEvaluation.supported) {
1294
+ throw new TypeError(
1295
+ `RC2 hypothesis is not supported: ${hypothesisEvaluation.failed_conditions.join(', ')}`,
1296
+ );
1297
+ }
1298
+ const cost = buildCost(costMeasurements, transform);
1299
+ const executionEvidence = buildExecutionEvidence({
1300
+ baseSha,
1301
+ identity,
1302
+ transform,
1303
+ runs,
1304
+ compiled,
1305
+ newPlanVersion,
1306
+ planDiff,
1307
+ comparison,
1308
+ hypothesis: hypothesisEvaluation,
1309
+ cost,
1310
+ });
1311
+ return {
1312
+ schema: 'lattice.rc2.campaign_result.v4',
1313
+ artifact_version: ARTIFACT_VERSION,
1314
+ base_sha: baseSha,
1315
+ fixed_inputs: inputs,
1316
+ identity,
1317
+ identity_payloads: identityBefore.payloads,
1318
+ predecessor_payloads: predecessors,
1319
+ transform,
1320
+ runs,
1321
+ compiled,
1322
+ new_plan_version: newPlanVersion,
1323
+ plan_diff: planDiff,
1324
+ comparison,
1325
+ hypothesis_evaluation: hypothesisEvaluation,
1326
+ cost,
1327
+ execution_evidence: executionEvidence,
1328
+ };
1329
+ }
1330
+
1331
+ function artifactFiles(result) {
1332
+ const files = mergePayloads(
1333
+ inputPayloads(result.fixed_inputs),
1334
+ result.identity_payloads,
1335
+ result.predecessor_payloads,
1336
+ transformPayloads(result.transform),
1337
+ runPayloads(result.runs),
1338
+ compiledPayloads(result.compiled),
1339
+ new Map([
1340
+ ['identity.json', jsonBytes(result.identity)],
1341
+ ['new-plan-version.json', jsonBytes(result.new_plan_version)],
1342
+ ['plan-diff.json', jsonBytes(result.plan_diff)],
1343
+ ['comparison.json', jsonBytes(result.comparison)],
1344
+ ['hypothesis-evaluation.json', jsonBytes(result.hypothesis_evaluation)],
1345
+ ['cost.json', jsonBytes(result.cost)],
1346
+ ['execution-evidence.json', jsonBytes(result.execution_evidence)],
1347
+ ]),
1348
+ );
1349
+ if (!isDeepStrictEqual([...files.keys()].sort(), [...RC2_ARTIFACT_PATHS])) {
1350
+ throw new TypeError('RC2 artifact payload set differs from the exact contract');
1351
+ }
1352
+ return files;
1353
+ }
1354
+
1355
+ function artifactMediaType(relativePath) {
1356
+ if (relativePath.endsWith('.json')) return 'application/json';
1357
+ if (relativePath.endsWith('.mjs')) return 'application/javascript';
1358
+ if (relativePath.endsWith('.patch')) return 'text/x-diff';
1359
+ if (relativePath.endsWith('.md')) return 'text/markdown';
1360
+ return 'application/octet-stream';
1361
+ }
1362
+
1363
+ async function syncDirectory(directory) {
1364
+ const handle = await open(directory, 'r');
1365
+ // Windowsはdirectory handleのfsyncを許さず常にEPERM/EINVALを返す(Node仕様)。
1366
+ // win32のこの2値だけ許容し、他OS・他エラーは従来どおり失敗させる。
1367
+ try {
1368
+ await handle.sync();
1369
+ } catch (error) {
1370
+ if (process.platform !== 'win32' || !['EPERM', 'EINVAL'].includes(error?.code)) throw error;
1371
+ } finally {
1372
+ await handle.close();
1373
+ }
1374
+ }
1375
+
1376
+ async function writeDurableFile(target, bytes) {
1377
+ await mkdir(path.dirname(target), { recursive: true });
1378
+ const handle = await open(target, 'wx');
1379
+ try {
1380
+ await handle.writeFile(bytes);
1381
+ await handle.sync();
1382
+ } finally {
1383
+ await handle.close();
1384
+ }
1385
+ }
1386
+
1387
+ async function syncArtifactDirectories(root, relativePaths) {
1388
+ const directories = new Set([root]);
1389
+ for (const relativePath of relativePaths) {
1390
+ let current = path.dirname(path.join(root, relativePath));
1391
+ while (current.startsWith(root)) {
1392
+ directories.add(current);
1393
+ if (current === root) break;
1394
+ current = path.dirname(current);
1395
+ }
1396
+ }
1397
+ const ordered = [...directories].sort((left, right) => right.length - left.length);
1398
+ for (const directory of ordered) await syncDirectory(directory);
1399
+ }
1400
+
1401
+ /** verified exact artifact setを既存rootへ上書きせずatomicに発行する。 */
1402
+ export async function writeRc2CampaignArtifacts(options = {}) {
1403
+ if (!exactRecord(options, ['repoRoot', 'result'])
1404
+ || typeof options.repoRoot !== 'string'
1405
+ || options.repoRoot.length === 0
1406
+ || options.result?.schema !== 'lattice.rc2.campaign_result.v4'
1407
+ || options.result?.artifact_version !== ARTIFACT_VERSION) {
1408
+ throw new TypeError('writeRc2CampaignArtifacts options are invalid');
1409
+ }
1410
+ const root = await realpath(options.repoRoot);
1411
+ const artifactRoot = path.join(root, ARTIFACT_ROOTS.v4);
1412
+ try {
1413
+ await lstat(artifactRoot);
1414
+ throw new Error('RC2 artifact root already exists; immutable evidence is not overwritten');
1415
+ } catch (error) {
1416
+ if (error?.code !== 'ENOENT') throw error;
1417
+ }
1418
+ const files = artifactFiles(options.result);
1419
+ const manifest = {
1420
+ schema: 'lattice.rc2.artifact_manifest.v4',
1421
+ base_sha: options.result.base_sha,
1422
+ result_digest: digestArtifact(options.result.hypothesis_evaluation),
1423
+ files: [...files].map(([relativePath, bytes]) => ({
1424
+ path: relativePath,
1425
+ media_type: artifactMediaType(relativePath),
1426
+ bytes: bytes.byteLength,
1427
+ sha256: sha256(bytes),
1428
+ })).sort((left, right) => left.path.localeCompare(right.path)),
1429
+ };
1430
+ const payloads = [...files].map(([relativePath, bytes]) => ({ path: relativePath, bytes }));
1431
+ const verification = verifyRc2CampaignArtifactSet({ manifest, payloads });
1432
+ if (!verification.valid) {
1433
+ throw new TypeError(
1434
+ `RC2 artifact set is invalid: ${verification.failed_conditions.join(', ')}`,
1435
+ );
1436
+ }
1437
+ const parent = path.dirname(artifactRoot);
1438
+ await mkdir(parent, { recursive: true });
1439
+ const temporaryRoot = await mkdtemp(path.join(parent, '.v4-write-'));
1440
+ try {
1441
+ for (const [relativePath, bytes] of files) {
1442
+ await writeDurableFile(path.join(temporaryRoot, relativePath), bytes);
1443
+ }
1444
+ await writeDurableFile(path.join(temporaryRoot, 'artifact-manifest.json'), jsonBytes(manifest));
1445
+ await syncArtifactDirectories(temporaryRoot, [
1446
+ ...RC2_ARTIFACT_PATHS,
1447
+ 'artifact-manifest.json',
1448
+ ]);
1449
+ await rename(temporaryRoot, artifactRoot);
1450
+ await syncDirectory(parent);
1451
+ } catch (error) {
1452
+ await rm(temporaryRoot, { recursive: true, force: true });
1453
+ throw error;
1454
+ }
1455
+ return manifest;
1456
+ }
1457
+
1458
+ function artifactPathsForVersion(artifactVersion) {
1459
+ if (artifactVersion === 'v1') return RC2_V1_ARTIFACT_PATHS;
1460
+ if (artifactVersion === 'v2') return RC2_V2_ARTIFACT_PATHS;
1461
+ if (artifactVersion === 'v3') return RC2_V3_ARTIFACT_PATHS;
1462
+ if (artifactVersion === 'v4') return RC2_ARTIFACT_PATHS;
1463
+ return null;
1464
+ }
1465
+
1466
+ function invalidDiskReceipt(condition) {
1467
+ return {
1468
+ schema: 'lattice.rc2.artifact_set_verification.v1',
1469
+ valid: false,
1470
+ checks: [{ id: condition, passed: false }],
1471
+ failed_conditions: [condition],
1472
+ };
1473
+ }
1474
+
1475
+ /** immutable rootを再読し、in-memory resultなしでpure verifierへ渡す。 */
1476
+ export async function verifyRc2CampaignArtifactsOnDisk(options = {}) {
1477
+ if (!exactRecord(options, ['repoRoot', 'artifactVersion'])
1478
+ || typeof options.repoRoot !== 'string'
1479
+ || options.repoRoot.length === 0
1480
+ || artifactPathsForVersion(options.artifactVersion) === null) {
1481
+ throw new TypeError('verifyRc2CampaignArtifactsOnDisk options are invalid');
1482
+ }
1483
+ const artifactRoot = path.join(options.repoRoot, ARTIFACT_ROOTS[options.artifactVersion]);
1484
+ const artifactPaths = artifactPathsForVersion(options.artifactVersion);
1485
+ let manifestBytes;
1486
+ let manifest;
1487
+ try {
1488
+ manifestBytes = await readFile(path.join(artifactRoot, 'artifact-manifest.json'));
1489
+ manifest = JSON.parse(manifestBytes.toString('utf8'));
1490
+ } catch {
1491
+ return invalidDiskReceipt('manifest_canonical');
1492
+ }
1493
+ if (!jsonBytes(manifest).equals(manifestBytes)) {
1494
+ return invalidDiskReceipt('manifest_canonical');
1495
+ }
1496
+ let payloads;
1497
+ try {
1498
+ payloads = await Promise.all(artifactPaths.map(async (relativePath) => ({
1499
+ path: relativePath,
1500
+ bytes: await readFile(path.join(artifactRoot, relativePath)),
1501
+ })));
1502
+ } catch {
1503
+ return invalidDiskReceipt('exact_artifact_set');
1504
+ }
1505
+ return verifyRc2CampaignArtifactSet({ manifest, payloads });
1506
+ }