@citeark/agent 0.3.20

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 (347) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +128 -0
  3. package/data/dataset-source-registry.v1.json +300 -0
  4. package/dist/arkgraph/boot.js +6 -0
  5. package/dist/arkgraph/index.html +1 -0
  6. package/dist/arkgraph/viewer.css +1 -0
  7. package/dist/arkgraph/viewer.en.css +1 -0
  8. package/dist/arkgraph/viewer.en.js +49 -0
  9. package/dist/arkgraph/viewer.en.js.LEGAL.txt +56 -0
  10. package/dist/arkgraph/viewer.js +49 -0
  11. package/dist/arkgraph/viewer.js.LEGAL.txt +56 -0
  12. package/docker/claude-code/Dockerfile +97 -0
  13. package/docker/claude-code/codex-pro-relay.mjs +466 -0
  14. package/docker/claude-code/runtime-contract-check.mjs +79 -0
  15. package/docs/arkgraph-reading.md +79 -0
  16. package/docs/configuration.md +100 -0
  17. package/docs/integration.md +92 -0
  18. package/docs/maturity-plan.md +27 -0
  19. package/docs/npm-release.md +44 -0
  20. package/docs/paper-reading.md +40 -0
  21. package/docs/research-plan-granularity.md +27 -0
  22. package/docs/terminal.md +49 -0
  23. package/examples/toy-evaluation/compile-task.json +27 -0
  24. package/examples/toy-evaluation/paper.md +5 -0
  25. package/examples/toy-evaluation/repository/README.md +9 -0
  26. package/examples/toy-evaluation/repository/checkpoint.json +4 -0
  27. package/examples/toy-evaluation/repository/evaluate.py +17 -0
  28. package/examples/toy-evaluation/task.json +81 -0
  29. package/package.json +59 -0
  30. package/prompts/compile-research.md +58 -0
  31. package/prompts/execute-contract.md +72 -0
  32. package/prompts/execute-workspace-simple.md +51 -0
  33. package/prompts/execute-workspace.md +34 -0
  34. package/prompts/prepare-reproduction.md +82 -0
  35. package/prompts/repair-research.md +45 -0
  36. package/protocol/CAP.md +129 -0
  37. package/protocol/LICENSE +12 -0
  38. package/protocol/MAPPINGS.md +72 -0
  39. package/protocol/README.md +38 -0
  40. package/protocol/conformance-v2.0-alpha.1.json +36 -0
  41. package/protocol/examples/arkgraph/checkpoint-evaluation.json +309 -0
  42. package/protocol/examples/arkgraph/fixtures.mjs +49 -0
  43. package/protocol/examples/arkgraph/paper-free.json +291 -0
  44. package/protocol/examples/arkgraph/partial-failure.json +344 -0
  45. package/protocol/examples/arkgraph/training-evaluation.json +443 -0
  46. package/protocol/profiles/agent-trace.md +16 -0
  47. package/protocol/profiles/computational-run.md +16 -0
  48. package/protocol/profiles/core.md +15 -0
  49. package/protocol/profiles/public-bundle.md +18 -0
  50. package/protocol/profiles/reproduction.md +29 -0
  51. package/protocol/profiles/research-compilation.md +44 -0
  52. package/protocol/profiles/research-plan.md +39 -0
  53. package/protocol/profiles/restricted-evidence.md +15 -0
  54. package/runtime/bootstrap-autodl-runtime.sh +314 -0
  55. package/runtime/create-runtime-venv.sh +41 -0
  56. package/runtime/install-local-cpu-runtime.sh +23 -0
  57. package/runtime/install-scientific-runtime.sh +153 -0
  58. package/runtime/mineru/parse.py +62 -0
  59. package/runtime/mineru/requirements.txt +4 -0
  60. package/runtime/requirements-baseline.txt +38 -0
  61. package/schemas/cap/v2/activity.schema.json +47 -0
  62. package/schemas/cap/v2/agent.schema.json +32 -0
  63. package/schemas/cap/v2/assertion.schema.json +110 -0
  64. package/schemas/cap/v2/descriptor.schema.json +243 -0
  65. package/schemas/cap/v2/entity.schema.json +64 -0
  66. package/schemas/cap/v2/manifest.schema.json +67 -0
  67. package/schemas/cap/v2/relation.schema.json +82 -0
  68. package/schemas/compute-catalog.schema.json +63 -0
  69. package/schemas/compute-decision.schema.json +27 -0
  70. package/schemas/execution-contract.schema.json +1024 -0
  71. package/schemas/research-card.schema.json +30 -0
  72. package/schemas/research-inventory-draft.schema.json +366 -0
  73. package/schemas/research.schema.json +1044 -0
  74. package/schemas/result.schema.json +173 -0
  75. package/schemas/verification-policy.schema.json +47 -0
  76. package/schemas/verified-conclusion.schema.json +58 -0
  77. package/schemas/workspace-summary.schema.json +24 -0
  78. package/scripts/build-arkgraph-view.mjs +12 -0
  79. package/scripts/check-execution-feasibility.mjs +24 -0
  80. package/scripts/check-syntax.mjs +15 -0
  81. package/scripts/deterministic-asset-preparation.py +438 -0
  82. package/scripts/package-cap.mjs +23 -0
  83. package/scripts/package-local-agent.mjs +23 -0
  84. package/scripts/preview-arkgraph.mjs +25 -0
  85. package/scripts/replay-research-compiler-candidate.mjs +134 -0
  86. package/scripts/review-compiler-sources.mjs +44 -0
  87. package/scripts/run-asset-preparation.sh +17 -0
  88. package/scripts/run-research-plan.mjs +98 -0
  89. package/scripts/validate-asset-preparation.py +290 -0
  90. package/scripts/verify-local-runtime.mjs +57 -0
  91. package/scripts/verify-npm-package.mjs +57 -0
  92. package/src/adapters/paper2agent.mjs +107 -0
  93. package/src/assets/cache.mjs +159 -0
  94. package/src/assets/compute.mjs +98 -0
  95. package/src/assets/executor.mjs +145 -0
  96. package/src/assets/lifecycle.mjs +213 -0
  97. package/src/assets/manifest.mjs +242 -0
  98. package/src/assets/opportunistic-preparation.mjs +81 -0
  99. package/src/assets/plan.mjs +411 -0
  100. package/src/assets/prompts.mjs +29 -0
  101. package/src/assets/public-asset-probe.mjs +525 -0
  102. package/src/assets/qualification.mjs +119 -0
  103. package/src/assets/readiness.mjs +130 -0
  104. package/src/assets/reproduction-admission.mjs +355 -0
  105. package/src/assets/requirements.mjs +152 -0
  106. package/src/assets/source-grounding.mjs +341 -0
  107. package/src/assets/source-policy.mjs +118 -0
  108. package/src/autodl/client.mjs +260 -0
  109. package/src/autodl/ssh.mjs +380 -0
  110. package/src/autodl/tools.mjs +129 -0
  111. package/src/cap/redaction.mjs +38 -0
  112. package/src/cap/v2/archive.mjs +152 -0
  113. package/src/cap/v2/attestation.mjs +204 -0
  114. package/src/cap/v2/canonical-json.mjs +114 -0
  115. package/src/cap/v2/compilation-artifact.mjs +240 -0
  116. package/src/cap/v2/core.mjs +282 -0
  117. package/src/cap/v2/measurement-assessment-records.mjs +23 -0
  118. package/src/cap/v2/pipeline-artifact.mjs +922 -0
  119. package/src/cap/v2/read.mjs +41 -0
  120. package/src/cap/v2/reassessment-artifact.mjs +383 -0
  121. package/src/cap/v2/research-artifact.mjs +231 -0
  122. package/src/cap/v2/research-map-records.mjs +46 -0
  123. package/src/cap/v2/research-object-records.mjs +163 -0
  124. package/src/cap/v2/research-records.mjs +187 -0
  125. package/src/cap/v2/verify.mjs +642 -0
  126. package/src/cli.mjs +1146 -0
  127. package/src/compute/autodl-pro-compiler.mjs +347 -0
  128. package/src/compute/autodl-pro-executor.mjs +459 -0
  129. package/src/compute/autodl-pro-job.mjs +843 -0
  130. package/src/compute/autodl-pro-network.mjs +295 -0
  131. package/src/compute/autodl-pro-remote.mjs +810 -0
  132. package/src/compute/autodl-pro-staging.mjs +117 -0
  133. package/src/compute/campaign.mjs +110 -0
  134. package/src/compute/catalog.mjs +123 -0
  135. package/src/compute/checkpoint-protocol.mjs +154 -0
  136. package/src/compute/codex-account-lock.mjs +111 -0
  137. package/src/compute/codex-account-session.mjs +107 -0
  138. package/src/compute/compiler-profile.mjs +38 -0
  139. package/src/compute/compiler-router.mjs +23 -0
  140. package/src/compute/coordinator-recovery.mjs +210 -0
  141. package/src/compute/executor-router.mjs +29 -0
  142. package/src/compute/gcp-batch-compiler.mjs +685 -0
  143. package/src/compute/gcp-batch-executor.mjs +1215 -0
  144. package/src/compute/gcp-batch-failure.mjs +92 -0
  145. package/src/compute/gcp-batch-job.mjs +527 -0
  146. package/src/compute/gcp-batch-lifecycle.mjs +81 -0
  147. package/src/compute/gcp-checkpoint-worker.mjs +1633 -0
  148. package/src/compute/local-codex-compiler.mjs +52 -0
  149. package/src/compute/measurement-hardware.mjs +128 -0
  150. package/src/compute/remote-attempt.mjs +226 -0
  151. package/src/compute/requirements.mjs +124 -0
  152. package/src/compute/research-phases.mjs +48 -0
  153. package/src/compute/scheduler.mjs +452 -0
  154. package/src/compute/shared-workloads.mjs +26 -0
  155. package/src/compute/stage-archive.mjs +79 -0
  156. package/src/contracts/campaign-contract.mjs +52 -0
  157. package/src/contracts/execution-contract.mjs +819 -0
  158. package/src/contracts/execution-mode.mjs +19 -0
  159. package/src/contracts/execution-timeouts.mjs +45 -0
  160. package/src/contracts/execution-workload.mjs +68 -0
  161. package/src/contracts/preflight-schema.mjs +25 -0
  162. package/src/contracts/public-contract.mjs +63 -0
  163. package/src/contracts/subject-tags.mjs +31 -0
  164. package/src/dashboard/data.mjs +898 -0
  165. package/src/dashboard/server.mjs +79 -0
  166. package/src/dashboard/static/dashboard.css +366 -0
  167. package/src/dashboard/static/dashboard.js +560 -0
  168. package/src/dashboard/static/index.html +85 -0
  169. package/src/deployment/community-policy.mjs +9 -0
  170. package/src/deployment/environment.mjs +112 -0
  171. package/src/deployment/guided.mjs +98 -0
  172. package/src/deployment/handoff.mjs +102 -0
  173. package/src/deployment/local-contract.mjs +31 -0
  174. package/src/deployment/local.mjs +100 -0
  175. package/src/deployment/prepare.mjs +46 -0
  176. package/src/deployment/recipe.mjs +108 -0
  177. package/src/deployment/supplement.mjs +51 -0
  178. package/src/deployment/terminal.mjs +43 -0
  179. package/src/diagnosis/renderer.mjs +75 -0
  180. package/src/diagnosis/target-failure.mjs +46 -0
  181. package/src/evidence/parser-registry.mjs +54 -0
  182. package/src/evidence/parsers/fasttext-classification.mjs +82 -0
  183. package/src/evidence/parsers/json-scalar.mjs +96 -0
  184. package/src/evidence/parsers/simcse-senteval.mjs +104 -0
  185. package/src/evidence/parsers/starspace-classification.mjs +78 -0
  186. package/src/evidence/registry.mjs +147 -0
  187. package/src/execution/runner-audit.mjs +473 -0
  188. package/src/gcp/auth.mjs +106 -0
  189. package/src/gcp/batch-client.mjs +120 -0
  190. package/src/gcp/resource-discovery.mjs +177 -0
  191. package/src/gcp/rest.mjs +82 -0
  192. package/src/gcp/secret-manager.mjs +34 -0
  193. package/src/gcp/signed-url.mjs +133 -0
  194. package/src/gcp/storage.mjs +220 -0
  195. package/src/graph/command.mjs +41 -0
  196. package/src/graph/execution.mjs +97 -0
  197. package/src/graph/model.mjs +37 -0
  198. package/src/graph/presentation.mjs +110 -0
  199. package/src/graph/query.mjs +159 -0
  200. package/src/graph/research-relations.mjs +69 -0
  201. package/src/graph/source-page.mjs +12 -0
  202. package/src/graph/source-preview.mjs +34 -0
  203. package/src/graph/validate.mjs +76 -0
  204. package/src/job.mjs +496 -0
  205. package/src/network/autodl-routing-proxy.mjs +462 -0
  206. package/src/network/egress-proxy.mjs +158 -0
  207. package/src/observability/event-contract.mjs +230 -0
  208. package/src/observability/pipeline-monitor.mjs +166 -0
  209. package/src/pipeline/orchestrator.mjs +1281 -0
  210. package/src/pipeline/recovery-error.mjs +11 -0
  211. package/src/pipeline/replay.mjs +304 -0
  212. package/src/pipeline/shared-execution.mjs +115 -0
  213. package/src/pipeline/stage-checkpoint.mjs +86 -0
  214. package/src/pipeline/stage-recovery.mjs +101 -0
  215. package/src/pipeline/targets.mjs +110 -0
  216. package/src/process.mjs +143 -0
  217. package/src/protocol.mjs +312 -0
  218. package/src/provider/codex-account.mjs +44 -0
  219. package/src/provider/codex-completion.mjs +49 -0
  220. package/src/provider/completion.mjs +292 -0
  221. package/src/provider/model-client.mjs +44 -0
  222. package/src/provider/model-route.mjs +29 -0
  223. package/src/provider/openrouter-readiness.mjs +189 -0
  224. package/src/provider/reader-bridge.mjs +35 -0
  225. package/src/provider/relay.mjs +263 -0
  226. package/src/provider/runtime-auth.mjs +40 -0
  227. package/src/public/cap.d.mts +90 -0
  228. package/src/public/cap.mjs +12 -0
  229. package/src/public/contracts.d.mts +2 -0
  230. package/src/public/host.mjs +171 -0
  231. package/src/public/operations.d.mts +11 -0
  232. package/src/public/presentation.d.mts +4 -0
  233. package/src/records/views.mjs +26 -0
  234. package/src/remote/command.mjs +178 -0
  235. package/src/remote/ssh.mjs +59 -0
  236. package/src/repository-origin.mjs +81 -0
  237. package/src/reproduction/evidence-feedback.mjs +96 -0
  238. package/src/reproduction/incomplete-initialization.mjs +25 -0
  239. package/src/reproduction/lifecycle.mjs +253 -0
  240. package/src/reproduction/plan.mjs +132 -0
  241. package/src/reproduction/prompts.mjs +70 -0
  242. package/src/reproduction/runner.mjs +188 -0
  243. package/src/reproduction/summary.mjs +130 -0
  244. package/src/reproduction/workspace-mode.mjs +7 -0
  245. package/src/research/automatic-admission.mjs +156 -0
  246. package/src/research/compiler-coverage.mjs +85 -0
  247. package/src/research/compiler-failure.mjs +24 -0
  248. package/src/research/compiler-normalization-guards.mjs +112 -0
  249. package/src/research/compiler-repair.mjs +3 -0
  250. package/src/research/compiler.mjs +853 -0
  251. package/src/research/continuation-selection.mjs +26 -0
  252. package/src/research/execution-graph-context.mjs +43 -0
  253. package/src/research/experiment-importance.mjs +15 -0
  254. package/src/research/inventory-handoff.mjs +104 -0
  255. package/src/research/inventory-revisions.mjs +32 -0
  256. package/src/research/mineru-local.mjs +73 -0
  257. package/src/research/paper-command.mjs +19 -0
  258. package/src/research/paper-markdown.mjs +180 -0
  259. package/src/research/paper-source-map.mjs +69 -0
  260. package/src/research/planning-policy.mjs +88 -0
  261. package/src/research/reference-materials.mjs +11 -0
  262. package/src/research/reproduction-scope.mjs +30 -0
  263. package/src/research/research-map.mjs +94 -0
  264. package/src/research/research-objects.mjs +88 -0
  265. package/src/research/source-discovery.mjs +646 -0
  266. package/src/research/source-observations.mjs +75 -0
  267. package/src/research/source-review-cli-mcp.mjs +26 -0
  268. package/src/research/source-review-input.mjs +209 -0
  269. package/src/research/source-review-local-codex.mjs +36 -0
  270. package/src/research/source-review-model.mjs +70 -0
  271. package/src/research/source-review.mjs +173 -0
  272. package/src/research/structure.mjs +3163 -0
  273. package/src/research-card/renderer.mjs +277 -0
  274. package/src/research-card/verified-conclusion.mjs +143 -0
  275. package/src/results/output-registry.mjs +183 -0
  276. package/src/runtime/claude-code.mjs +52 -0
  277. package/src/runtime/codex-capacity-retry.mjs +87 -0
  278. package/src/runtime/codex.mjs +64 -0
  279. package/src/runtime/config.mjs +157 -0
  280. package/src/runtime/final-output.mjs +40 -0
  281. package/src/runtime/index.mjs +21 -0
  282. package/src/runtime/local-codex.mjs +74 -0
  283. package/src/runtime/opencode.mjs +95 -0
  284. package/src/runtime/prompt.mjs +13 -0
  285. package/src/sandbox/docker.mjs +363 -0
  286. package/src/settings/command.mjs +297 -0
  287. package/src/settings/store.mjs +119 -0
  288. package/src/telemetry/pricing.mjs +68 -0
  289. package/src/telemetry/usage.mjs +265 -0
  290. package/src/terminal/events.mjs +97 -0
  291. package/src/terminal/input.mjs +40 -0
  292. package/src/terminal/plain.mjs +40 -0
  293. package/src/terminal/remote-stream.mjs +22 -0
  294. package/src/terminal/screen.mjs +214 -0
  295. package/src/terminal/transcript.mjs +69 -0
  296. package/src/util.mjs +107 -0
  297. package/src/verification/ai-assessor.mjs +534 -0
  298. package/src/verification/claim-evaluator.mjs +242 -0
  299. package/src/verification/evidence-context.mjs +165 -0
  300. package/src/verification/evidence-reader.mjs +95 -0
  301. package/src/verification/integrity.mjs +570 -0
  302. package/src/verification/tolerance.mjs +32 -0
  303. package/src/workloads/cpu-research-preparation.mjs +56 -0
  304. package/src/workloads/definition.mjs +74 -0
  305. package/src/workloads/phase-aware-reproduction.mjs +46 -0
  306. package/src/workloads/reproduction.mjs +85 -0
  307. package/src/workspace/command.mjs +242 -0
  308. package/src/workspace/control.mjs +49 -0
  309. package/src/workspace/entry.mjs +28 -0
  310. package/src/workspace/input.mjs +93 -0
  311. package/src/workspace/interactive.mjs +94 -0
  312. package/src/workspace/jobs.mjs +418 -0
  313. package/src/workspace/session.mjs +97 -0
  314. package/src/workspace/worker.mjs +137 -0
  315. package/ui/arkgraph/ambient-motion.mjs +10 -0
  316. package/ui/arkgraph/app.jsx +153 -0
  317. package/ui/arkgraph/boot.js +6 -0
  318. package/ui/arkgraph/camera-motion.mjs +20 -0
  319. package/ui/arkgraph/context-reveal.mjs +39 -0
  320. package/ui/arkgraph/details.css +3 -0
  321. package/ui/arkgraph/entry.jsx +28 -0
  322. package/ui/arkgraph/experiment-curves.mjs +17 -0
  323. package/ui/arkgraph/experiment-selection.mjs +15 -0
  324. package/ui/arkgraph/experiment-style.css +26 -0
  325. package/ui/arkgraph/experiment-ui.jsx +32 -0
  326. package/ui/arkgraph/frame.html +1 -0
  327. package/ui/arkgraph/graph-gestures.mjs +62 -0
  328. package/ui/arkgraph/label-layout.mjs +57 -0
  329. package/ui/arkgraph/locales/en.json +229 -0
  330. package/ui/arkgraph/locales/source-types.json +15 -0
  331. package/ui/arkgraph/localization-build.mjs +27 -0
  332. package/ui/arkgraph/material-build.mjs +23 -0
  333. package/ui/arkgraph/material-colors.mjs +39 -0
  334. package/ui/arkgraph/material-style.css +15 -0
  335. package/ui/arkgraph/open-graph.jsx +326 -0
  336. package/ui/arkgraph/outline.jsx +49 -0
  337. package/ui/arkgraph/package-lock.json +888 -0
  338. package/ui/arkgraph/package.json +17 -0
  339. package/ui/arkgraph/reading-layout.mjs +130 -0
  340. package/ui/arkgraph/reading-presentation.mjs +73 -0
  341. package/ui/arkgraph/record-detail.css +51 -0
  342. package/ui/arkgraph/record-details.jsx +29 -0
  343. package/ui/arkgraph/research-types.mjs +31 -0
  344. package/ui/arkgraph/selection-mark.jsx +6 -0
  345. package/ui/arkgraph/soft-spine.mjs +26 -0
  346. package/ui/arkgraph/steering-style.css +187 -0
  347. package/ui/arkgraph/style.css +272 -0
@@ -0,0 +1,922 @@
1
+ import { measurementAssessmentRecords } from './measurement-assessment-records.mjs';
2
+ import { researchObjectRecords } from './research-object-records.mjs';
3
+ import { enrichExecutionGraph } from '../../graph/execution.mjs';
4
+ import { lstat, mkdir, readFile, writeFile } from "node:fs/promises";
5
+ import path from "node:path";
6
+
7
+ import {
8
+ executionContractMeasurements,
9
+ verificationPolicyCommitmentInput,
10
+ } from "../../contracts/execution-contract.mjs";
11
+ import { mediaTypeForPath } from "../../results/output-registry.mjs";
12
+ import { metricMeasurements } from "../../records/views.mjs";
13
+ import {
14
+ CiteArkError,
15
+ pathExists,
16
+ safeRelativePath,
17
+ sha256File,
18
+ } from "../../util.mjs";
19
+ import {
20
+ attachCapAttestation,
21
+ signCapArtifact,
22
+ } from "./attestation.mjs";
23
+ import {
24
+ CAP_PROFILE,
25
+ CAP_RECORD_TYPE,
26
+ assembleCapDirectory,
27
+ } from "./core.mjs";
28
+ import {
29
+ canonicalJsonBytes,
30
+ sha256Bytes,
31
+ } from "./canonical-json.mjs";
32
+ import { verifyCapDirectory } from "./verify.mjs";
33
+
34
+ export const CAP_INLINE_BLOB_MAX_BYTES = 64 * 1024 * 1024;
35
+
36
+ const SCHEMA = Object.freeze({
37
+ actor: "https://citeark.com/schemas/cap/v2/agent.schema.json",
38
+ sourceWork: "https://citeark.com/schemas/cap/v2/entity.schema.json",
39
+ claim: "https://citeark.com/schemas/cap/v2/assertion.schema.json",
40
+ experiment: "https://citeark.com/schemas/cap/v2/entity.schema.json",
41
+ execution: "https://citeark.com/schemas/cap/v2/activity.schema.json",
42
+ evidence: "https://citeark.com/schemas/cap/v2/entity.schema.json",
43
+ assessment: "https://citeark.com/schemas/cap/v2/assertion.schema.json",
44
+ });
45
+
46
+ export async function assemblePipelineCap({
47
+ directory,
48
+ deploymentHandoff,
49
+ sourceReproductionDigest,
50
+ research,
51
+ contract,
52
+ executionContract,
53
+ policy,
54
+ runDirectory,
55
+ run,
56
+ result,
57
+ metrics,
58
+ outputs,
59
+ integrity,
60
+ assessment,
61
+ diagnosis,
62
+ verifiedConclusion,
63
+ researchCard,
64
+ runnerAudit,
65
+ evidenceContext,
66
+ pipelineSummary,
67
+ signingKeyPath,
68
+ processingBinding,
69
+ researchArtifactDigest,
70
+ inlineBlobMaximumBytes = CAP_INLINE_BLOB_MAX_BYTES,
71
+ }) {
72
+ const claim = research.claims.find((item) => item.versionId === contract.research.claimVersionId);
73
+ const experiment = research.experiments.find((item) => item.versionId === contract.research.experimentVersionId);
74
+ if (!claim || !experiment) throw new CiteArkError("CAP 2.0 组装找不到执行契约绑定的 Claim 或 Experiment");
75
+ if (contract.verificationPolicyCommitment !== policy.commitment?.digest) {
76
+ throw new CiteArkError("CAP 2.0 组装拒绝未绑定执行契约的验证策略承诺");
77
+ }
78
+ if (!validDigest(researchArtifactDigest)) {
79
+ throw new CiteArkError("复现 CAP 必须引用已验证的 Research Plan CAP 摘要");
80
+ }
81
+
82
+ const physicalRunId = run.sharedExecutionRunId ?? run.runId;
83
+ const actualContract = executionContract ?? contract;
84
+ const ids = {
85
+ assembler: recordId("actor", "citeark-artifact-assembler"),
86
+ executor: recordId("actor", `${physicalRunId}:${run.runtime ?? contract.agent?.runtime ?? "agent"}`),
87
+ assessor: recordId(
88
+ "actor",
89
+ `${assessment.assessor?.kind ?? "service"}:${assessment.assessor?.provider ?? "citeark"}:${assessment.assessor?.name ?? "citeark-verification-engine"}:${assessment.assessor?.rubricVersion ?? assessment.assessor?.version ?? "unknown"}`,
90
+ ),
91
+ work: recordId("work", research.work.id),
92
+ claim: recordId("claim", `${research.work.id}:${claim.id}`),
93
+ experiment: recordId("experiment", `${research.work.id}:${experiment.id}`),
94
+ execution: recordId("execution", physicalRunId),
95
+ assessment: recordId("assessment", `${run.runId}:${assessment.assessmentDigest}`),
96
+ };
97
+ const ref = (id) => ({ ref: id });
98
+ const outputDirectory = path.join(runDirectory, "output");
99
+ const blobByDigest = new Map();
100
+ const blobDigestBySourcePath = new Map();
101
+ const externalObjects = [];
102
+
103
+ const addBytesBlob = ({ id, roles, mediaType, bytes, rights = generatedRights() }) => {
104
+ const body = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes);
105
+ return addBlob({
106
+ id,
107
+ roles,
108
+ mediaType,
109
+ availability: "embedded",
110
+ bytes: body,
111
+ digest: sha256Bytes(body),
112
+ size: body.byteLength,
113
+ rights,
114
+ });
115
+ };
116
+ const addFileBlob = async ({
117
+ sourcePath,
118
+ roles,
119
+ mediaType,
120
+ forceExternal = false,
121
+ withhold = false,
122
+ }) => {
123
+ if (!safeRelativePath(sourcePath)) throw new CiteArkError(`CAP Blob 源路径无效:${sourcePath}`);
124
+ const filename = path.resolve(outputDirectory, sourcePath);
125
+ if (!filename.startsWith(`${path.resolve(outputDirectory)}${path.sep}`)) {
126
+ throw new CiteArkError(`CAP Blob 源路径逃逸:${sourcePath}`);
127
+ }
128
+ const stat = await lstat(filename);
129
+ if (!stat.isFile()) throw new CiteArkError(`CAP Blob 源不是普通文件:${sourcePath}`);
130
+ const digest = `sha256:${await sha256File(filename)}`;
131
+ withhold ||= (outputs?.outputs ?? []).some(output => output.digest === digest && output.storage === "withheld");
132
+ const availability = withhold
133
+ ? "withheld"
134
+ : forceExternal || stat.size > inlineBlobMaximumBytes ? "external" : "embedded";
135
+ const blob = addBlob({
136
+ id: recordId("blob", digest),
137
+ roles,
138
+ mediaType: mediaType ?? mediaTypeForPath(sourcePath),
139
+ availability,
140
+ ...(availability === "embedded" ? { bytes: await readFile(filename) } : {}),
141
+ digest,
142
+ size: stat.size,
143
+ rights: generatedRights(),
144
+ ...(availability !== "embedded"
145
+ ? {
146
+ accessPolicy: {
147
+ level: availability === "withheld"
148
+ ? "retention-excluded"
149
+ : "content-addressed-download",
150
+ integrityRequired: true,
151
+ },
152
+ }
153
+ : {}),
154
+ });
155
+ blobDigestBySourcePath.set(sourcePath, digest);
156
+ if (availability === "external" && !externalObjects.some((item) => item.digest === digest)) {
157
+ externalObjects.push({
158
+ sourcePath,
159
+ digest,
160
+ byteSize: stat.size,
161
+ mediaType: blob.mediaType,
162
+ });
163
+ }
164
+ return blob;
165
+ };
166
+ function addBlob(entry) {
167
+ const existing = blobByDigest.get(entry.digest);
168
+ if (existing) {
169
+ existing.roles = [...new Set([...existing.roles, ...entry.roles])].sort();
170
+ return existing;
171
+ }
172
+ blobByDigest.set(entry.digest, entry);
173
+ return entry;
174
+ }
175
+
176
+ const sharedContractBlob = executionContract?.campaign ? addBytesBlob({
177
+ id: recordId("blob", executionContract.contractDigest), roles: ["shared-execution-contract"],
178
+ mediaType: "application/json", bytes: JSON.stringify(executionContract),
179
+ }) : null;
180
+ if (deploymentHandoff) addBytesBlob({ id: recordId("blob", "deployment-handoff"),
181
+ roles: ["deployment-handoff"], mediaType: "application/json", bytes: JSON.stringify(deploymentHandoff) });
182
+ const rawEvidenceRecords = [];
183
+ const rawEvidenceIdBySourcePath = new Map();
184
+ for (const item of result.evidence ?? []) {
185
+ if (rawEvidenceIdBySourcePath.has(item.path)) continue;
186
+ const blob = await addFileBlob({
187
+ sourcePath: item.path,
188
+ roles: ["raw-evidence"],
189
+ mediaType: mediaTypeForPath(item.path),
190
+ });
191
+ const evidenceId = recordId("evidence", `${run.runId}:raw:${item.path}`);
192
+ rawEvidenceIdBySourcePath.set(item.path, evidenceId);
193
+ rawEvidenceRecords.push({
194
+ $schema: SCHEMA.evidence,
195
+ id: evidenceId,
196
+ type: CAP_RECORD_TYPE.entity, role: "observation",
197
+ kind: evidenceKindForPath(item.path),
198
+ basis: "observed",
199
+ generatedBy: ref(ids.execution),
200
+ blobs: [blob.digest],
201
+ limitations: [],
202
+ citeark: {
203
+ sourcePath: item.path,
204
+ summary: item.description ?? "Execution evidence",
205
+ role: "raw-evidence",
206
+ },
207
+ });
208
+ }
209
+
210
+ const measurementRecords = [];
211
+ const measurementEvidenceIdByMeasurement = new Map();
212
+ const contractMeasurements = executionContractMeasurements(contract);
213
+ for (const parsed of metricMeasurements(metrics)) {
214
+ const measurementId = parsed.measurementId ?? parsed.primary?.metric;
215
+ const declared = contractMeasurements.find((item) => item.measurementId === measurementId);
216
+ const sourcePath = declared?.parser?.evidencePath ?? parsed.sourceEvidence?.path;
217
+ let sourceBlob = sourcePath ? blobByDigest.get(blobDigestBySourcePath.get(sourcePath)) : null;
218
+ if (!sourceBlob && sourcePath && await pathExists(path.join(outputDirectory, sourcePath))) {
219
+ sourceBlob = await addFileBlob({
220
+ sourcePath,
221
+ roles: ["raw-evidence", "parser-input"],
222
+ mediaType: mediaTypeForPath(sourcePath),
223
+ });
224
+ } else if (sourceBlob) {
225
+ sourceBlob.roles = [...new Set([...sourceBlob.roles, "parser-input"])].sort();
226
+ }
227
+ const evidenceId = recordId("evidence", `${run.runId}:measurement:${measurementId}`);
228
+ measurementEvidenceIdByMeasurement.set(measurementId, evidenceId);
229
+ const observed = parsed.primary?.value;
230
+ measurementRecords.push({
231
+ $schema: SCHEMA.evidence,
232
+ id: evidenceId,
233
+ type: CAP_RECORD_TYPE.entity, role: "observation",
234
+ title: `${parsed.primary?.metric ?? measurementId}: ${typeof observed === 'number' ? `${observed} ${parsed.primary?.unit ?? ''}` : 'unavailable'}`,
235
+ kind: typeof observed === "number" && Number.isFinite(observed) ? "measurement" : "failure",
236
+ basis: "observed",
237
+ generatedBy: ref(ids.execution),
238
+ ...(sourcePath && rawEvidenceIdBySourcePath.has(sourcePath)
239
+ ? { derivedFrom: [ref(rawEvidenceIdBySourcePath.get(sourcePath))] }
240
+ : {}),
241
+ ...(typeof observed === "number" && Number.isFinite(observed)
242
+ ? {
243
+ measurement: {
244
+ measurementId,
245
+ metric: parsed.primary.metric,
246
+ value: { decimal: decimal(observed) },
247
+ unit: parsed.primary.unit,
248
+ ...(parsed.primary.scope ? { scope: parsed.primary.scope } : {}),
249
+ },
250
+ }
251
+ : {}),
252
+ ...(parsed.parser
253
+ ? {
254
+ parser: {
255
+ id: parsed.parser.id,
256
+ version: parsed.parser.version,
257
+ ...(validDigest(parsed.parser.implementationDigest)
258
+ ? { implementationDigest: parsed.parser.implementationDigest }
259
+ : {}),
260
+ inputs: sourceBlob ? [sourceBlob.digest] : [],
261
+ config: declared?.parser?.config ?? {},
262
+ },
263
+ }
264
+ : {}),
265
+ ...(sourceBlob ? { blobs: [sourceBlob.digest] } : {}),
266
+ limitations: parsed.availability?.status === "unavailable"
267
+ ? [parsed.availability.reason ?? "No trustworthy observation was available"]
268
+ : [],
269
+ citeark: {
270
+ sourcePath: sourcePath ?? null,
271
+ checks: parsed.checks ?? [],
272
+ observations: parsed.observations ?? [],
273
+ availability: parsed.availability ?? null,
274
+ },
275
+ });
276
+ }
277
+
278
+ const outputRecords = [];
279
+ const scientificOutputRecords = [];
280
+ const outputBindings = [];
281
+ const outputEvidenceIdByOutput = new Map();
282
+ for (const output of outputs?.outputs ?? []) {
283
+ const blob = await addFileBlob({
284
+ sourcePath: output.sourcePath,
285
+ roles: ["scientific-output", output.role, output.kind],
286
+ mediaType: output.mediaType,
287
+ forceExternal: output.storage === "external",
288
+ withhold: output.storage === "withheld",
289
+ });
290
+ const evidenceId = recordId("evidence", `${run.runId}:output:${output.id}`);
291
+ outputEvidenceIdByOutput.set(output.id, evidenceId);
292
+ if (['model', 'checkpoint', 'dataset'].includes(output.kind)) {
293
+ const materialId = recordId('scientific-output', `${physicalRunId}:${output.id}`);
294
+ scientificOutputRecords.push({ $schema: SCHEMA.evidence, id: materialId,
295
+ type: CAP_RECORD_TYPE.entity, role: output.kind, title: output.description ?? output.id,
296
+ basis: 'observed', prospective: false, generatedBy: ref(ids.execution), blobs: [blob.digest],
297
+ identity: { digest: blob.digest, byteSize: blob.size, mediaType: blob.mediaType },
298
+ availability: blob.availability, citeark: { outputId: output.id, sourcePath: output.sourcePath } });
299
+ if (output.scientificObjectId) outputBindings.push({ scientificObjectId: output.scientificObjectId, materialId, kind: output.kind });
300
+ }
301
+ outputRecords.push({
302
+ $schema: SCHEMA.evidence,
303
+ id: evidenceId,
304
+ type: CAP_RECORD_TYPE.entity, role: "observation",
305
+ kind: capEvidenceKind(output.kind),
306
+ basis: "observed",
307
+ generatedBy: ref(ids.execution),
308
+ blobs: [blob.digest],
309
+ limitations: [],
310
+ citeark: {
311
+ outputId: output.id,
312
+ sourcePath: output.sourcePath,
313
+ description: output.description,
314
+ role: output.role,
315
+ storage: output.storage,
316
+ relatedEvidencePaths: output.relatedEvidencePaths ?? [],
317
+ },
318
+ });
319
+ }
320
+
321
+ const traceBytes = Buffer.from(buildTrace({
322
+ run,
323
+ metrics,
324
+ integrity,
325
+ assessment,
326
+ runnerAudit,
327
+ actorId: ids.executor,
328
+ executionId: ids.execution,
329
+ evidenceIds: [...measurementRecords, ...outputRecords].map((item) => item.id),
330
+ }), "utf8");
331
+ const traceBlob = addBytesBlob({
332
+ id: recordId("blob", `${run.runId}:trace`),
333
+ roles: ["execution-trace"],
334
+ mediaType: "application/x-ndjson",
335
+ bytes: traceBytes,
336
+ });
337
+ const commandRecordsBlob = runnerAudit?.audit ? addBytesBlob({
338
+ id: recordId("blob", `${run.runId}:runner-command-records`),
339
+ roles: ["runner-command-records"],
340
+ mediaType: "application/x-ndjson",
341
+ bytes: Buffer.from((runnerAudit.commandRecords ?? []).map((record) => JSON.stringify(record)).join("\n") + "\n", "utf8"),
342
+ }) : null;
343
+ const evidenceContextBlob = evidenceContext ? addBytesBlob({
344
+ id: recordId("blob", `${run.runId}:assessment-evidence-context`),
345
+ roles: ["assessment-evidence-context"], mediaType: "application/json",
346
+ bytes: canonicalJsonBytes(evidenceContext),
347
+ rights: { statement: "Source excerpts and implementation retain their original rights; inclusion for evidence review grants no additional redistribution permission." },
348
+ }) : null;
349
+
350
+ const sourceWork = {
351
+ $schema: SCHEMA.sourceWork,
352
+ id: ids.work,
353
+ type: CAP_RECORD_TYPE.entity, role: "source-work",
354
+ title: research.work.title,
355
+ abstract: research.work.abstract,
356
+ sources: research.sources.map((source) => sourceDescriptor({
357
+ source,
358
+ research,
359
+ processingBinding,
360
+ })),
361
+ citeark: {
362
+ workId: research.work.id,
363
+ ...(research.reproductionScope ? { reproductionScope: research.reproductionScope } : {}),
364
+ ...(research.work.significance ? { significance: research.work.significance } : {}),
365
+ authorNames: research.work.authors ?? [],
366
+ subjects: research.work.subjects ?? [],
367
+ license: research.license ?? {},
368
+ provenance: research.provenance ?? {},
369
+ },
370
+ };
371
+ const claimRecord = {
372
+ $schema: SCHEMA.claim,
373
+ id: ids.claim,
374
+ type: CAP_RECORD_TYPE.assertion, role: "claim",
375
+ statement: { value: claim.statement, language: "en" },
376
+ kind: claimKind(claim.type),
377
+ scope: {
378
+ reportedMeasurements: (claim.reportedMeasurements ?? []).map((item) => ({
379
+ id: item.id,
380
+ metric: item.metric,
381
+ value: decimal(item.value),
382
+ unit: item.unit,
383
+ })),
384
+ reproduction: claim.reproduction ?? {},
385
+ },
386
+ source: {
387
+ work: ref(ids.work),
388
+ selector: {
389
+ type: "TextQuoteSelector",
390
+ exact: claim.statement,
391
+ label: `${claim.sourceLocator.sourceId}:${claim.sourceLocator.locator}`,
392
+ },
393
+ selectedTextDigest: sha256Bytes(Buffer.from(claim.statement, "utf8")),
394
+ },
395
+ origin: {
396
+ basis: "inferred",
397
+ actor: ref(ids.assembler),
398
+ ...(research.provenance?.reviewStatus === "human-reviewed" ? { confidence: 1 } : {}),
399
+ },
400
+ citeark: {
401
+ localId: claim.id,
402
+ versionId: claim.versionId,
403
+ type: claim.type,
404
+ sourceLocator: claim.sourceLocator,
405
+ reportedMeasurements: claim.reportedMeasurements ?? [],
406
+ reproduction: claim.reproduction ?? {},
407
+ },
408
+ };
409
+ const experimentRecord = {
410
+ $schema: SCHEMA.experiment,
411
+ id: ids.experiment,
412
+ type: CAP_RECORD_TYPE.entity, role: "procedure",
413
+ tests: [ref(ids.claim)],
414
+ publicContract: {
415
+ implementation: {
416
+ repository: contract.repository.url,
417
+ revision: contract.repository.commit,
418
+ ...(contract.protocol.executionMode ? { executionMode: contract.protocol.executionMode } : {}),
419
+ ...(contract.protocol.objective ? { objective: contract.protocol.objective } : {}),
420
+ entrypoint: contract.protocol.entrypoint,
421
+ requiredCommandFragments: contract.protocol.requiredCommandFragments ?? [],
422
+ },
423
+ parameters: contract.protocol.parameters ?? [],
424
+ measurements: contractMeasurements.map((measurement) => ({
425
+ id: measurement.measurementId,
426
+ metric: measurement.metric,
427
+ unit: measurement.unit,
428
+ evidenceParser: {
429
+ id: measurement.parser.id,
430
+ version: measurement.parser.version,
431
+ ...(validDigest(measurement.parser.implementationDigest)
432
+ ? { implementationDigest: measurement.parser.implementationDigest }
433
+ : {}),
434
+ },
435
+ })),
436
+ resources: {
437
+ computeRequirement: contract.computeRequirement ?? {},
438
+ environment: contract.environment ?? {},
439
+ },
440
+ ...(contract.protocol.preflight ? { preflight: contract.protocol.preflight } : {}),
441
+ verificationPolicyCommitment: contract.verificationPolicyCommitment,
442
+ },
443
+ evaluationPlan: {
444
+ title: experiment.title,
445
+ reproductionLevel: contract.reproductionLevel,
446
+ ...(contract.reproductionScope ? { reproductionScope: contract.reproductionScope, requiredReproductionScope: contract.requiredReproductionScope } : {}),
447
+ reconstructionFidelity: contract.reconstructionFidelity,
448
+ instructions: contract.protocol.instructions ?? null,
449
+ },
450
+ citeark: {
451
+ localId: experiment.id,
452
+ versionId: experiment.versionId,
453
+ title: experiment.title,
454
+ claimIds: experiment.claimIds,
455
+ reproductionLevel: experiment.reproductionLevel,
456
+ ...(experiment.reproductionScope ? { reproductionScope: experiment.reproductionScope, requiredReproductionScope: experiment.requiredReproductionScope } : {}),
457
+ implementationOrigin: experiment.implementationOrigin ?? experiment.repository?.implementationOrigin ?? "official",
458
+ reconstructionFidelity: experiment.reconstructionFidelity ?? contract.reconstructionFidelity,
459
+ repository: experiment.repository,
460
+ protocol: experiment.protocol,
461
+ ...(contract.executionPlan ? { executionPlan: contract.executionPlan } : {}),
462
+ measurements: experiment.measurements,
463
+ compute: experiment.compute ?? null,
464
+ environment: experiment.environment ?? null,
465
+ },
466
+ };
467
+
468
+ const allEvidenceRecords = [...rawEvidenceRecords, ...measurementRecords, ...outputRecords];
469
+ const lastAttempt = run.attempts?.at(-1) ?? {};
470
+ const endedAt = isoDate(run.finishedAt ?? run.startedAt ?? run.createdAt);
471
+ const startedAt = isoDate(run.startedAt ?? run.attempts?.[0]?.startedAt ?? endedAt);
472
+ const executionRecord = {
473
+ $schema: SCHEMA.execution,
474
+ id: ids.execution,
475
+ type: CAP_RECORD_TYPE.activity, role: "execution",
476
+ status: executionStatus(result.execution?.status, run.status),
477
+ startedAt,
478
+ endedAt,
479
+ actors: [
480
+ { actor: ref(ids.executor), role: "executor" },
481
+ { actor: ref(ids.assembler), role: "orchestrator" },
482
+ ],
483
+ environment: {
484
+ image: run.image ?? actualContract.environment?.image ?? null,
485
+ sandbox: run.sandbox ?? null,
486
+ computeDecision: run.computeDecision ?? null,
487
+ imageIdentity: run.sandboxImageIdentity ?? null,
488
+ cloudExecution: run.cloudExecution ?? null,
489
+ },
490
+ codeState: {
491
+ repository: actualContract.repository.url,
492
+ revision: actualContract.repository.commit,
493
+ observedRevision: run.repository?.commit ?? null,
494
+ },
495
+ inputs: [
496
+ { role: "public-contract", digest: actualContract.contractDigest },
497
+ ...(sharedContractBlob ? [{ role: "shared-execution-contract", digest: sharedContractBlob.digest }] : []),
498
+
499
+ ...(processingBinding?.sourceDigest
500
+ ? [{ role: "source-work", digest: processingBinding.sourceDigest }]
501
+ : []),
502
+ ],
503
+ trace: { blobDigest: traceBlob.digest },
504
+ exit: {
505
+ code: Number.isInteger(lastAttempt.exitCode) ? lastAttempt.exitCode : null,
506
+ signal: typeof lastAttempt.signal === "string" ? lastAttempt.signal : null,
507
+ },
508
+ ...(result.execution?.failure ? { failure: result.execution.failure } : {}),
509
+ citeark: {
510
+ runId: physicalRunId,
511
+ runtime: run.runtime ?? contract.agent?.runtime ?? null,
512
+ agent: run.agent ?? { runtime: run.runtime ?? null },
513
+ usage: run.usage ?? null,
514
+ attempts: run.attempts ?? [],
515
+ outcome: result.execution ?? {},
516
+
517
+
518
+
519
+ commands: (runnerAudit?.commandRecords ?? [])
520
+ .map((record) => record?.command?.text)
521
+ .filter((command) => typeof command === "string" && command),
522
+ audit: runnerAudit?.audit ?? null,
523
+ ...(commandRecordsBlob ? { commandRecordsBlobDigest: commandRecordsBlob.digest } : {}),
524
+ capture: { granularity: 'runner-process', unknownPhases: true },
525
+ },
526
+ };
527
+
528
+ const assessmentRecord = {
529
+ $schema: SCHEMA.assessment,
530
+ id: ids.assessment,
531
+ type: CAP_RECORD_TYPE.assertion, role: "assessment",
532
+ claim: ref(ids.claim),
533
+ experiment: ref(ids.experiment),
534
+ execution: ref(ids.execution),
535
+ evidence: allEvidenceRecords.map((item) => ref(item.id)),
536
+ method: {
537
+ type: assessment.assessor?.kind === "model" ? "QualitativeReview" : "MetricComparison",
538
+ policyCommitment: contract.verificationPolicyCommitment,
539
+ policyReveal: {
540
+ algorithm: "citeark-policy-commitment-v1",
541
+ nonce: policy.commitment.nonce,
542
+ policy: verificationPolicyCommitmentInput(policy),
543
+ },
544
+ comparisons: (assessment.measurementAssessments ?? []).map((item) => ({
545
+ measurementId: item.measurementId,
546
+ verdict: item.verdict,
547
+ verificationStatus: item.verificationStatus,
548
+ reason: item.reason,
549
+ ...(item.comparison ? { comparison: decimalComparison(item.comparison) } : {}),
550
+ })),
551
+ },
552
+ conclusion: assessmentConclusion(assessment.verdict),
553
+ scope: assessmentScope(contract),
554
+ limitations: [...new Set([
555
+ ...(result.limitations ?? []),
556
+ ...(assessment.limitations ?? []),
557
+ ...(contract.research?.claimCoverage === "partial"
558
+ ? [`This execution covers only part of the compound claim; unassessed measurements: ${(contract.research.uncoveredMeasurementIds ?? []).join(", ")}.`]
559
+ : []),
560
+ ...(assessment.verificationStatus === "inconclusive" ? [assessment.reason] : []),
561
+ ].filter(Boolean))],
562
+ performedBy: ref(ids.assessor),
563
+ createdAt: isoDate(assessment.assessedAt ?? endedAt),
564
+ citeark: {
565
+ verificationStatus: assessment.verificationStatus,
566
+ integrityStatus: assessment.integrityStatus,
567
+ reason: assessment.reason,
568
+ confidence: assessment.confidence ?? null,
569
+ protocolComparability: assessment.protocolComparability ?? "unknown",
570
+ ...(assessment.executionEvidence ? { executionEvidence: assessment.executionEvidence } : {}),
571
+ assessor: assessment.assessor ?? null,
572
+ ...(evidenceContextBlob ? { evidenceContextBlobDigest: evidenceContextBlob.digest } : {}),
573
+ evidenceReads: assessment.evidenceReads ?? [],
574
+ assessorFailure: assessment.assessorFailure ?? null,
575
+ assessmentUsage: assessment.usage ?? null,
576
+ measurementAssessments: assessment.measurementAssessments ?? [],
577
+ outputAssessments: assessment.outputAssessments ?? [],
578
+ claimCoverage: contract.research?.claimCoverage ?? "complete",
579
+ uncoveredMeasurementIds: contract.research?.uncoveredMeasurementIds ?? [],
580
+ diagnosis: diagnosis ?? null,
581
+ },
582
+ };
583
+
584
+ const objectGraph = researchObjectRecords({ research: { ...research, claims: [claim] }, sourceWorkId: ids.work, actorId: ids.assembler });
585
+ claimRecord.objects = (claim.objectIds ?? []).map(id => ref(objectGraph.objectIds.get(id)));
586
+ for (const observation of measurementRecords) {
587
+ const measurement = claim.reportedMeasurements.find(item => item.id === observation.measurement?.measurementId);
588
+ observation.contextObjects = (measurement?.objectIds ?? claim.objectIds ?? []).map(id => ref(objectGraph.objectIds.get(id)));
589
+ observation.contextBasis = 'declared';
590
+ }
591
+ experimentRecord.inputs = (objectGraph.inputIdsByExperiment.get(experiment.id) ?? []).map(id => ({ record: ref(id), role: 'scientific-input', basis: 'declared' }));
592
+ experimentRecord.steps = (objectGraph.stepIdsByExperiment.get(experiment.id) ?? []).map(ref);
593
+ // Per-measurement judgments retain their own evidence and scope. The compound claim's
594
+ // original assessment remains authoritative and may still be inconclusive.
595
+ const componentAssessments = measurementAssessmentRecords({ assessment, parent: assessmentRecord,
596
+ identity: `${run.runId}:${assessment.assessmentDigest}`, evidenceByMeasurement: measurementEvidenceIdByMeasurement });
597
+ assessmentRecord.componentAssessments = componentAssessments.map(item => ref(item.id));
598
+ const records = [
599
+ actorRecord(ids.assembler, "CiteArk Artifact Assembler", "runner", "2.0.0-alpha.2"),
600
+ actorRecord(ids.executor, `${run.runtime ?? contract.agent?.runtime ?? "Research"} execution Agent`, "agent", run.agent?.model),
601
+ actorRecord(
602
+ ids.assessor,
603
+ assessment.assessor?.name ?? "CiteArk Verification Engine",
604
+ assessment.assessor?.kind === "model" ? "model" : "runner",
605
+ assessment.assessor?.model ?? assessment.assessor?.version,
606
+ ),
607
+ sourceWork,
608
+ claimRecord,
609
+ experimentRecord,
610
+ executionRecord,
611
+ ...allEvidenceRecords,
612
+ ...scientificOutputRecords,
613
+ ...objectGraph.records,
614
+ ...componentAssessments,
615
+ assessmentRecord,
616
+ ];
617
+ records.push(...await enrichExecutionGraph({ runDirectory, physicalRunId, ids, actualContract,
618
+ executionRecord, experimentRecord, assessmentRecord, runnerAudit, evidenceContext,
619
+ rawEvidenceRecords, blobByDigest, addBytesBlob, objectGraph }));
620
+ for (const binding of outputBindings) {
621
+ const expectedId = objectGraph.objectIds.get(binding.scientificObjectId);
622
+ const specification = research.researchObjects?.find(object => object.id === binding.scientificObjectId);
623
+ if (!expectedId || specification?.prospective !== true) throw new CiteArkError(`Output must bind a prospective research object: ${binding.scientificObjectId}`);
624
+ records.push({ $schema: "https://citeark.com/schemas/cap/v2/relation.schema.json",
625
+ id: recordId("relation", `${physicalRunId}:${binding.scientificObjectId}:${binding.materialId}`),
626
+ type: CAP_RECORD_TYPE.relation, role: "provenance", predicate: "describes",
627
+ subject: ref(expectedId), object: ref(binding.materialId), basis: "declared",
628
+ attributedTo: ref(ids.executor), sources: [ref(expectedId)], qualifiers: { binding: "declared-output-identity" } });
629
+ }
630
+ const profiles = [
631
+ CAP_PROFILE.computationalRun,
632
+ CAP_PROFILE.reproduction,
633
+ CAP_PROFILE.agentTrace,
634
+ CAP_PROFILE.publicBundle,
635
+ ...([...blobByDigest.values()].some((blob) => blob.availability !== "embedded")
636
+ ? [CAP_PROFILE.restrictedEvidence]
637
+ : []),
638
+ ];
639
+ const built = await assembleCapDirectory({
640
+ directory,
641
+ artifact: {
642
+ id: recordId("artifact", run.runId),
643
+ createdAt: isoDate(assessment.assessedAt ?? endedAt),
644
+ createdBy: ids.assembler,
645
+ },
646
+ profiles,
647
+ roots: [
648
+ { role: "primaryClaim", ref: ids.claim },
649
+ { role: "primaryExecution", ref: ids.execution },
650
+ { role: "primaryAssessment", ref: ids.assessment },
651
+ ],
652
+ records,
653
+ blobs: [...blobByDigest.values()],
654
+ relations: [{
655
+ relationship: "reproduces",
656
+ artifactDigest: researchArtifactDigest,
657
+ summary: "Reproduces the immutable Research Plan CAP snapshot.",
658
+ }, ...(sourceReproductionDigest ? [{ relationship: "derivedFrom", artifactDigest: sourceReproductionDigest, summary: "Continues the completed execution handoff with fresh local observations." }] : [])],
659
+ });
660
+
661
+ await writeBundleAttachments({
662
+ directory,
663
+ built,
664
+ sourceWork,
665
+ claimRecord,
666
+ experimentRecord,
667
+ executionRecord,
668
+ assessmentRecord,
669
+ metrics,
670
+ integrity,
671
+ researchCard,
672
+ verifiedConclusion,
673
+ pipelineSummary,
674
+ diagnosis,
675
+ });
676
+ const attestation = await signCapArtifact({
677
+ artifactDigest: built.artifactDigest,
678
+ actor: ref(ids.assembler),
679
+ role: "artifactAssembler",
680
+ createdAt: isoDate(assessment.assessedAt ?? endedAt),
681
+ signingKeyPath,
682
+ processingBinding,
683
+ });
684
+ const attachedAttestation = await attachCapAttestation({ directory, attestation });
685
+ const verification = await verifyCapDirectory(directory);
686
+ if (!verification.valid) {
687
+ throw new CiteArkError(`流水线生成的 CAP 2.0 未通过自校验:\n- ${verification.issues.join("\n- ")}`);
688
+ }
689
+ return {
690
+ ...built,
691
+ verification,
692
+ attestation: attachedAttestation,
693
+ recordIds: ids,
694
+ externalObjects,
695
+ };
696
+ }
697
+
698
+ async function writeBundleAttachments({
699
+ directory,
700
+ built,
701
+ sourceWork,
702
+ claimRecord,
703
+ experimentRecord,
704
+ executionRecord,
705
+ assessmentRecord,
706
+ metrics,
707
+ integrity,
708
+ researchCard,
709
+ verifiedConclusion,
710
+ pipelineSummary,
711
+ diagnosis,
712
+ }) {
713
+ await Promise.all([
714
+ mkdir(path.join(directory, "preview"), { recursive: true }),
715
+ mkdir(path.join(directory, "projections", "citeark"), { recursive: true }),
716
+ ]);
717
+ const crate = {
718
+ "@context": "https://w3id.org/ro/crate/1.3/context",
719
+ "@graph": [
720
+ { "@id": "ro-crate-metadata.json", "@type": "CreativeWork", about: { "@id": "./" } },
721
+ {
722
+ "@id": "./",
723
+ "@type": "Dataset",
724
+ name: sourceWork.title,
725
+ identifier: built.artifactDigest,
726
+ hasPart: built.manifest.records.map((record) => ({ "@id": record.path })),
727
+ },
728
+ ...built.manifest.records.map((record) => ({
729
+ "@id": record.path,
730
+ "@type": "CreativeWork",
731
+ identifier: record.digest,
732
+ encodingFormat: record.mediaType,
733
+ })),
734
+ ],
735
+ };
736
+ await Promise.all([
737
+ writeFile(path.join(directory, "ro-crate-metadata.json"), `${JSON.stringify(crate, null, 2)}\n`),
738
+ writeFile(
739
+ path.join(directory, "preview", "README.md"),
740
+ researchCard?.markdown ?? `# ${sourceWork.title}\n\nCAP Artifact: \`${built.artifactDigest}\`\n`,
741
+ ),
742
+ writeProjection(directory, "claim.json", claimRecord),
743
+ writeProjection(directory, "experiment.json", experimentRecord),
744
+ writeProjection(directory, "execution.json", executionRecord),
745
+ writeProjection(directory, "assessment.json", assessmentRecord),
746
+ writeProjection(directory, "metrics.json", metrics),
747
+ writeProjection(directory, "integrity.json", integrity),
748
+ ...(researchCard ? [writeProjection(directory, "research-card.json", researchCard.card)] : []),
749
+ ...(verifiedConclusion ? [writeProjection(directory, "verified-conclusion.json", verifiedConclusion)] : []),
750
+ ...(pipelineSummary ? [writeProjection(directory, "pipeline-summary.json", pipelineSummary)] : []),
751
+ ...(diagnosis?.markdown
752
+ ? [writeFile(path.join(directory, "preview", "diagnosis.md"), diagnosis.markdown)]
753
+ : []),
754
+ ]);
755
+ }
756
+
757
+ function writeProjection(directory, filename, value) {
758
+ return writeFile(path.join(directory, "projections", "citeark", filename), canonicalJsonBytes(value));
759
+ }
760
+
761
+ function actorRecord(id, name, kind, version) {
762
+ return {
763
+ $schema: SCHEMA.actor,
764
+ id,
765
+ type: CAP_RECORD_TYPE.agent, role: "agent",
766
+ kind,
767
+ name,
768
+ ...(version ? { version: String(version) } : {}),
769
+ };
770
+ }
771
+
772
+ function sourceDescriptor({ source, research, processingBinding }) {
773
+ const paperDigest = source.kind === "paper" && validDigest(processingBinding?.sourceDigest)
774
+ ? processingBinding.sourceDigest
775
+ : validDigest(source.digest) ? source.digest : null;
776
+ return {
777
+ kind: sourceKind(source.kind),
778
+ uri: source.uri,
779
+ ...(source.version ? { version: String(source.version) } : {}),
780
+ integrity: paperDigest ? "verified" : "unverified",
781
+ ...(paperDigest ? { digest: paperDigest } : {}),
782
+ rights: sourceRights(source.kind, research.license),
783
+ };
784
+ }
785
+
786
+ function sourceRights(kind, license = {}) {
787
+ const key = kind === "repository" ? "code" : kind === "paper" ? "paper" : kind === "dataset" ? "dataset" : kind === "checkpoint" ? "checkpoint" : null;
788
+ return {
789
+ statement: key && license?.[key]
790
+ ? String(license[key])
791
+ : "Source-specific rights; CAP records identity and provenance but grants no redistribution permission.",
792
+ };
793
+ }
794
+
795
+ function generatedRights() {
796
+ return { statement: "CiteArk-generated execution material; redistribution remains subject to referenced source rights." };
797
+ }
798
+
799
+ function buildTrace({ run, metrics, integrity, assessment, runnerAudit, actorId, executionId, evidenceIds }) {
800
+ const endedAt = isoDate(run.finishedAt ?? run.startedAt ?? run.createdAt);
801
+ const events = [];
802
+ const push = (eventType, occurredAt, attributes = {}, outputs = []) => {
803
+ events.push({
804
+ sequence: events.length + 1,
805
+ eventType,
806
+ occurredAt: isoDate(occurredAt ?? endedAt),
807
+ observedAt: isoDate(occurredAt ?? endedAt),
808
+ actor: actorId,
809
+ execution: executionId,
810
+ ...(outputs.length ? { outputs } : {}),
811
+ attributes,
812
+ });
813
+ };
814
+ for (const attempt of run.attempts ?? []) {
815
+ push("execution.attempt", attempt.finishedAt ?? attempt.startedAt, {
816
+ attempt: attempt.attempt,
817
+ exitCode: attempt.exitCode ?? null,
818
+ signal: attempt.signal ?? null,
819
+ timedOut: Boolean(attempt.timedOut),
820
+ });
821
+ }
822
+ for (const command of runnerAudit?.commandRecords ?? []) {
823
+ push("tool.command", command.finishedAt ?? command.startedAt, {
824
+ recordId: command.recordId,
825
+ command: command.command?.text ?? "",
826
+ exitCode: command.exitCode ?? null,
827
+ provenance: command.provenance ?? null,
828
+ });
829
+ }
830
+ push("execution.finished", endedAt, { status: run.status ?? 'unknown' });
831
+ return `${events.map((event) => JSON.stringify(event)).join("\n")}\n`;
832
+ }
833
+
834
+ function recordId(kind, value) {
835
+ return `urn:citeark:${kind}:${sha256Bytes(Buffer.from(String(value), "utf8")).slice(7)}`;
836
+ }
837
+
838
+ function claimKind(value) {
839
+ return ({
840
+ finding: "descriptive",
841
+ method: "methodological",
842
+ measurement: "descriptive",
843
+ limitation: "limitation",
844
+ })[value] ?? "descriptive";
845
+ }
846
+
847
+ function sourceKind(value) {
848
+ if (value === "checkpoint") return "model";
849
+ return new Set(["paper", "repository", "dataset", "model", "supplement", "other"]).has(value)
850
+ ? value
851
+ : "other";
852
+ }
853
+
854
+ function evidenceKindForPath(value) {
855
+ const mediaType = mediaTypeForPath(value);
856
+ if (mediaType.startsWith("image/")) return "figure";
857
+ if (mediaType.startsWith("text/")) return "text";
858
+ return "observation";
859
+ }
860
+
861
+ function capEvidenceKind(value) {
862
+ return new Set(["figure", "table", "dataset", "model", "checkpoint", "text"]).has(value)
863
+ ? value
864
+ : "other";
865
+ }
866
+
867
+ function executionStatus(resultStatus, runStatus) {
868
+ if (resultStatus === "succeeded") return "succeeded";
869
+ if (resultStatus === "partial") return "partial";
870
+ if (resultStatus === "cancelled") return "cancelled";
871
+ if (resultStatus === "timed_out" || runStatus === "timed_out") return "timedOut";
872
+ if (resultStatus === "failed" || runStatus === "failed" || runStatus === "invalid_result") return "failed";
873
+ return "unknown";
874
+ }
875
+
876
+ function assessmentConclusion(value) {
877
+ return new Set(["supports", "challenges", "contradicts", "inconclusive"]).has(value)
878
+ ? value
879
+ : "inconclusive";
880
+ }
881
+
882
+ function assessmentScope(contract) {
883
+ const origin = contract.repository?.implementationOrigin ?? "official";
884
+ const fidelity = contract.reconstructionFidelity ?? "faithful";
885
+ return {
886
+ operator: "independent",
887
+ implementation: origin === "citeark_reconstruction"
888
+ ? fidelity === "exact" || fidelity === "faithful" ? "independent" : "modified"
889
+ : "same",
890
+ environment: "reconstructed",
891
+ hardware: "independent",
892
+ data: origin === "citeark_reconstruction" && new Set(["approximate", "proxy"]).has(fidelity)
893
+ ? "modified"
894
+ : "same",
895
+ };
896
+ }
897
+
898
+ function decimalComparison(value) {
899
+ const output = {};
900
+ for (const [key, item] of Object.entries(value)) {
901
+ output[key] = typeof item === "number" && Number.isFinite(item)
902
+ ? { decimal: decimal(item) }
903
+ : item;
904
+ }
905
+ return output;
906
+ }
907
+
908
+ function decimal(value) {
909
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
910
+ if (typeof value === "string" && /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/.test(value)) return value;
911
+ throw new CiteArkError(`CAP 科研数值无法表示为十进制字符串:${String(value)}`);
912
+ }
913
+
914
+ function validDigest(value) {
915
+ return typeof value === "string" && /^sha256:[0-9a-f]{64}$/.test(value);
916
+ }
917
+
918
+ function isoDate(value) {
919
+ const date = new Date(value ?? Date.now());
920
+ if (!Number.isFinite(date.getTime())) throw new CiteArkError(`CAP 时间无效:${String(value)}`);
921
+ return date.toISOString();
922
+ }