@tiangong-ai/cli 0.0.61 → 0.0.63

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 (216) hide show
  1. package/AGENTS.md +17 -6
  2. package/CONTRIBUTING.md +5 -5
  3. package/README.md +417 -36
  4. package/dist/cli.js +2 -2
  5. package/dist/data/builtins.js +33 -5
  6. package/dist/data/builtins.js.map +1 -1
  7. package/dist/data/catalog.js +46 -0
  8. package/dist/data/catalog.js.map +1 -1
  9. package/dist/data/commands.js +15 -1
  10. package/dist/data/commands.js.map +1 -1
  11. package/dist/data/connectors/airnow-hourly-observations.js +60 -10
  12. package/dist/data/connectors/airnow-hourly-observations.js.map +1 -1
  13. package/dist/data/connectors/epa-eis-records.js +16 -2
  14. package/dist/data/connectors/epa-eis-records.js.map +1 -1
  15. package/dist/data/connectors/gdelt-doc-search.js +54 -18
  16. package/dist/data/connectors/gdelt-doc-search.js.map +1 -1
  17. package/dist/data/connectors/gdelt-file-feeds.js +20 -11
  18. package/dist/data/connectors/gdelt-file-feeds.js.map +1 -1
  19. package/dist/data/connectors/gdelt-web-ngrams.d.ts +2 -0
  20. package/dist/data/connectors/gdelt-web-ngrams.js +350 -0
  21. package/dist/data/connectors/gdelt-web-ngrams.js.map +1 -0
  22. package/dist/data/connectors/gdelt-web-ngrams.schemas.d.ts +188 -0
  23. package/dist/data/connectors/gdelt-web-ngrams.schemas.js +138 -0
  24. package/dist/data/connectors/gdelt-web-ngrams.schemas.js.map +1 -0
  25. package/dist/data/connectors/nasa-firms-fire.js +4 -0
  26. package/dist/data/connectors/nasa-firms-fire.js.map +1 -1
  27. package/dist/data/connectors/open-meteo-air-quality.js +27 -22
  28. package/dist/data/connectors/open-meteo-air-quality.js.map +1 -1
  29. package/dist/data/connectors/open-meteo-air-quality.schemas.d.ts +4 -1
  30. package/dist/data/connectors/open-meteo-air-quality.schemas.js +21 -1
  31. package/dist/data/connectors/open-meteo-air-quality.schemas.js.map +1 -1
  32. package/dist/data/connectors/open-meteo-flood.js +28 -23
  33. package/dist/data/connectors/open-meteo-flood.js.map +1 -1
  34. package/dist/data/connectors/open-meteo-flood.schemas.d.ts +4 -1
  35. package/dist/data/connectors/open-meteo-flood.schemas.js +21 -1
  36. package/dist/data/connectors/open-meteo-flood.schemas.js.map +1 -1
  37. package/dist/data/connectors/open-meteo-historical-weather.js +26 -21
  38. package/dist/data/connectors/open-meteo-historical-weather.js.map +1 -1
  39. package/dist/data/connectors/open-meteo-historical-weather.schemas.d.ts +4 -1
  40. package/dist/data/connectors/open-meteo-historical-weather.schemas.js +21 -1
  41. package/dist/data/connectors/open-meteo-historical-weather.schemas.js.map +1 -1
  42. package/dist/data/connectors/openaq-air-quality.js +50 -12
  43. package/dist/data/connectors/openaq-air-quality.js.map +1 -1
  44. package/dist/data/connectors/openaq-air-quality.schemas.d.ts +0 -4
  45. package/dist/data/connectors/openaq-air-quality.schemas.js +2 -2
  46. package/dist/data/connectors/openaq-air-quality.schemas.js.map +1 -1
  47. package/dist/data/connectors/youtube-public-content.js +76 -6
  48. package/dist/data/connectors/youtube-public-content.js.map +1 -1
  49. package/dist/data/connectors/youtube-public-content.schemas.d.ts +63 -3
  50. package/dist/data/connectors/youtube-public-content.schemas.js +56 -2
  51. package/dist/data/connectors/youtube-public-content.schemas.js.map +1 -1
  52. package/dist/data/contracts.d.ts +22 -1
  53. package/dist/data/runtime/bounded-http.d.ts +2 -0
  54. package/dist/data/runtime/bounded-http.js +221 -59
  55. package/dist/data/runtime/bounded-http.js.map +1 -1
  56. package/dist/data/runtime/execute.d.ts +2 -0
  57. package/dist/data/runtime/execute.js +82 -60
  58. package/dist/data/runtime/execute.js.map +1 -1
  59. package/dist/data/runtime/http-timing.d.ts +15 -0
  60. package/dist/data/runtime/http-timing.js +59 -0
  61. package/dist/data/runtime/http-timing.js.map +1 -0
  62. package/dist/data/runtime/http-transport.d.ts +1 -0
  63. package/dist/data/runtime/http-transport.js +27 -0
  64. package/dist/data/runtime/http-transport.js.map +1 -0
  65. package/dist/data/schemas/catalog.v1.json +46 -0
  66. package/dist/data/schemas/discovery.v1.json +12 -0
  67. package/dist/data/schemas/error.v1.json +1 -0
  68. package/dist/data/schemas/manifest.v1.json +19 -1
  69. package/dist/research/orchestration.js +233 -10
  70. package/dist/research/orchestration.js.map +1 -1
  71. package/dist/research/setup-command.js +37 -6
  72. package/dist/research/setup-command.js.map +1 -1
  73. package/dist/research/workspace/audit-bundle.js +28 -8
  74. package/dist/research/workspace/audit-bundle.js.map +1 -1
  75. package/dist/research/workspace/broker.js +25 -4
  76. package/dist/research/workspace/broker.js.map +1 -1
  77. package/dist/research/workspace/constants.d.ts +1 -0
  78. package/dist/research/workspace/constants.js +1 -0
  79. package/dist/research/workspace/constants.js.map +1 -1
  80. package/dist/research/workspace/content-evidence.d.ts +1 -0
  81. package/dist/research/workspace/content-evidence.js +1 -1
  82. package/dist/research/workspace/content-evidence.js.map +1 -1
  83. package/dist/research/workspace/credentials.js +2 -1
  84. package/dist/research/workspace/credentials.js.map +1 -1
  85. package/dist/research/workspace/data-evidence-adapter.d.ts +71 -0
  86. package/dist/research/workspace/data-evidence-adapter.js +269 -59
  87. package/dist/research/workspace/data-evidence-adapter.js.map +1 -1
  88. package/dist/research/workspace/data-evidence-view.d.ts +58 -0
  89. package/dist/research/workspace/data-evidence-view.js +411 -0
  90. package/dist/research/workspace/data-evidence-view.js.map +1 -0
  91. package/dist/research/workspace/evidence-ledger.js +32 -29
  92. package/dist/research/workspace/evidence-ledger.js.map +1 -1
  93. package/dist/research/workspace/evidence.d.ts +28 -0
  94. package/dist/research/workspace/evidence.js +48 -0
  95. package/dist/research/workspace/evidence.js.map +1 -1
  96. package/dist/research/workspace/executor.d.ts +27 -1
  97. package/dist/research/workspace/executor.js +180 -20
  98. package/dist/research/workspace/executor.js.map +1 -1
  99. package/dist/research/workspace/external-skills.d.ts +1 -0
  100. package/dist/research/workspace/external-skills.js +4 -2
  101. package/dist/research/workspace/external-skills.js.map +1 -1
  102. package/dist/research/workspace/investigation-attempt.d.ts +159 -0
  103. package/dist/research/workspace/investigation-attempt.js +619 -0
  104. package/dist/research/workspace/investigation-attempt.js.map +1 -0
  105. package/dist/research/workspace/investigation-audit.d.ts +20 -0
  106. package/dist/research/workspace/investigation-audit.js +306 -0
  107. package/dist/research/workspace/investigation-audit.js.map +1 -0
  108. package/dist/research/workspace/investigation-candidate.d.ts +84 -0
  109. package/dist/research/workspace/investigation-candidate.js +156 -0
  110. package/dist/research/workspace/investigation-candidate.js.map +1 -0
  111. package/dist/research/workspace/investigation-certification.d.ts +39 -0
  112. package/dist/research/workspace/investigation-certification.js +152 -0
  113. package/dist/research/workspace/investigation-certification.js.map +1 -0
  114. package/dist/research/workspace/investigation-close.d.ts +47 -0
  115. package/dist/research/workspace/investigation-close.js +153 -0
  116. package/dist/research/workspace/investigation-close.js.map +1 -0
  117. package/dist/research/workspace/investigation-lineage.d.ts +8 -0
  118. package/dist/research/workspace/investigation-lineage.js +10 -0
  119. package/dist/research/workspace/investigation-lineage.js.map +1 -0
  120. package/dist/research/workspace/investigation-observer.d.ts +22 -0
  121. package/dist/research/workspace/investigation-observer.js +122 -0
  122. package/dist/research/workspace/investigation-observer.js.map +1 -0
  123. package/dist/research/workspace/investigation-promotion.d.ts +129 -0
  124. package/dist/research/workspace/investigation-promotion.js +316 -0
  125. package/dist/research/workspace/investigation-promotion.js.map +1 -0
  126. package/dist/research/workspace/investigation-requirements.d.ts +5 -0
  127. package/dist/research/workspace/investigation-requirements.js +32 -0
  128. package/dist/research/workspace/investigation-requirements.js.map +1 -0
  129. package/dist/research/workspace/investigation-resources.d.ts +12 -0
  130. package/dist/research/workspace/investigation-resources.js +78 -0
  131. package/dist/research/workspace/investigation-resources.js.map +1 -0
  132. package/dist/research/workspace/investigation-source-export.d.ts +6 -0
  133. package/dist/research/workspace/investigation-source-export.js +68 -0
  134. package/dist/research/workspace/investigation-source-export.js.map +1 -0
  135. package/dist/research/workspace/investigation-store.d.ts +9 -0
  136. package/dist/research/workspace/investigation-store.js +55 -0
  137. package/dist/research/workspace/investigation-store.js.map +1 -0
  138. package/dist/research/workspace/investigation.d.ts +424 -0
  139. package/dist/research/workspace/investigation.js +716 -0
  140. package/dist/research/workspace/investigation.js.map +1 -0
  141. package/dist/research/workspace/native-process-supervisor.d.ts +1 -0
  142. package/dist/research/workspace/native-process-supervisor.js +97 -0
  143. package/dist/research/workspace/native-process-supervisor.js.map +1 -0
  144. package/dist/research/workspace/native-run.d.ts +79 -6
  145. package/dist/research/workspace/native-run.js +293 -51
  146. package/dist/research/workspace/native-run.js.map +1 -1
  147. package/dist/research/workspace/preflight.d.ts +3 -0
  148. package/dist/research/workspace/preflight.js +11 -6
  149. package/dist/research/workspace/preflight.js.map +1 -1
  150. package/dist/research/workspace/project-authority.js +2 -0
  151. package/dist/research/workspace/project-authority.js.map +1 -1
  152. package/dist/research/workspace/project-budget.d.ts +41 -0
  153. package/dist/research/workspace/project-budget.js +265 -0
  154. package/dist/research/workspace/project-budget.js.map +1 -0
  155. package/dist/research/workspace/project-mutations.d.ts +1 -1
  156. package/dist/research/workspace/project-mutations.js +27 -3
  157. package/dist/research/workspace/project-mutations.js.map +1 -1
  158. package/dist/research/workspace/projects.d.ts +66 -0
  159. package/dist/research/workspace/projects.js +181 -3
  160. package/dist/research/workspace/projects.js.map +1 -1
  161. package/dist/research/workspace/provider-budget.d.ts +3 -0
  162. package/dist/research/workspace/provider-budget.js +45 -0
  163. package/dist/research/workspace/provider-budget.js.map +1 -0
  164. package/dist/research/workspace/provider-state.d.ts +1 -0
  165. package/dist/research/workspace/provider-state.js +20 -0
  166. package/dist/research/workspace/provider-state.js.map +1 -0
  167. package/dist/research/workspace/publication-lineage.d.ts +34 -0
  168. package/dist/research/workspace/publication-lineage.js +165 -0
  169. package/dist/research/workspace/publication-lineage.js.map +1 -0
  170. package/dist/research/workspace/publication-workflow.d.ts +24 -0
  171. package/dist/research/workspace/publication-workflow.js +118 -14
  172. package/dist/research/workspace/publication-workflow.js.map +1 -1
  173. package/dist/research/workspace/review-executor.d.ts +6 -1
  174. package/dist/research/workspace/review-executor.js +17 -3
  175. package/dist/research/workspace/review-executor.js.map +1 -1
  176. package/dist/research/workspace/runtime.d.ts +12 -1
  177. package/dist/research/workspace/runtime.js +143 -19
  178. package/dist/research/workspace/runtime.js.map +1 -1
  179. package/dist/research/workspace/scientific-amendment.d.ts +103 -0
  180. package/dist/research/workspace/scientific-amendment.js +537 -0
  181. package/dist/research/workspace/scientific-amendment.js.map +1 -0
  182. package/dist/research/workspace/scientific-fulfillment-audit.d.ts +20 -2
  183. package/dist/research/workspace/scientific-fulfillment-audit.js +87 -7
  184. package/dist/research/workspace/scientific-fulfillment-audit.js.map +1 -1
  185. package/dist/research/workspace/scientific-fulfillment.d.ts +10 -1
  186. package/dist/research/workspace/scientific-fulfillment.js +20 -6
  187. package/dist/research/workspace/scientific-fulfillment.js.map +1 -1
  188. package/dist/research/workspace/scientific-review-execution.js +162 -22
  189. package/dist/research/workspace/scientific-review-execution.js.map +1 -1
  190. package/dist/research/workspace/scientific-review.d.ts +2 -1
  191. package/dist/research/workspace/scientific-review.js +33 -2
  192. package/dist/research/workspace/scientific-review.js.map +1 -1
  193. package/dist/research/workspace/setup-catalog.js +13 -13
  194. package/dist/research/workspace/setup-catalog.js.map +1 -1
  195. package/dist/research/workspace/setup-release.d.ts +22 -0
  196. package/dist/research/workspace/setup-release.js +426 -0
  197. package/dist/research/workspace/setup-release.js.map +1 -0
  198. package/dist/research/workspace/setup-runtime-integrity.d.ts +2 -0
  199. package/dist/research/workspace/setup-runtime-integrity.js +31 -0
  200. package/dist/research/workspace/setup-runtime-integrity.js.map +1 -0
  201. package/dist/research/workspace/setup-upgrade.d.ts +17 -0
  202. package/dist/research/workspace/setup-upgrade.js +784 -0
  203. package/dist/research/workspace/setup-upgrade.js.map +1 -0
  204. package/dist/research/workspace/setup.d.ts +31 -39
  205. package/dist/research/workspace/setup.js +228 -19
  206. package/dist/research/workspace/setup.js.map +1 -1
  207. package/dist/research/workspace/task-acceptance.d.ts +8 -1
  208. package/dist/research/workspace/task-acceptance.js +37 -5
  209. package/dist/research/workspace/task-acceptance.js.map +1 -1
  210. package/dist/research/workspace/task-audit.d.ts +5 -1
  211. package/dist/research/workspace/task-audit.js +33 -18
  212. package/dist/research/workspace/task-audit.js.map +1 -1
  213. package/dist/research/workspace/types.d.ts +44 -0
  214. package/dist/research/workspace/workspace.js +34 -4
  215. package/dist/research/workspace/workspace.js.map +1 -1
  216. package/package.json +5 -4
@@ -0,0 +1,34 @@
1
+ import { CliError } from "../../errors.js";
2
+ import type { OutputRecord } from "./types.js";
3
+ export declare const RESULT_CORE_FILES: readonly ["analysis.json", "report.md", "claim-evidence-graph.json", "content-snapshot.json", "inference-snapshot.json", "evidence-snapshot.json"];
4
+ export interface AnalysisLineage {
5
+ projectId: string;
6
+ analysisRunId: string;
7
+ analysisSha256: string;
8
+ claimEvidenceGraphSha256: string;
9
+ reportSha256: string;
10
+ }
11
+ export interface MaterialResultFile {
12
+ role: string;
13
+ sha256: string;
14
+ analysisSha256: string;
15
+ }
16
+ export interface PublicationResultLineage extends AnalysisLineage {
17
+ schemaVersion: 1;
18
+ files: MaterialResultFile[];
19
+ }
20
+ export declare function lineageError(code: string, message: string, details?: Record<string, unknown>): CliError;
21
+ export declare function publicationResultLineageSchema(): Record<string, unknown>;
22
+ /** Bind the already closed/reviewed core, without a new authority ledger. */
23
+ export declare function closedAnalysisLineage(input: {
24
+ root: string;
25
+ projectId: string;
26
+ closure: Record<string, unknown>;
27
+ analysis: Record<string, unknown>;
28
+ records: Record<string, OutputRecord>;
29
+ }): Promise<AnalysisLineage>;
30
+ export declare function analysisGenerationId(base: AnalysisLineage): string;
31
+ export declare function verifyMaterialResultLineage(value: unknown, base: AnalysisLineage, files: Array<{
32
+ role: string;
33
+ sha256: string;
34
+ }>): PublicationResultLineage;
@@ -0,0 +1,165 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { CliError } from "../../errors.js";
4
+ import { canonicalJson, isObject, sha256Text, workspacePaths } from "./storage.js";
5
+ const HASH = /^[a-f0-9]{64}$/;
6
+ export const RESULT_CORE_FILES = [
7
+ "analysis.json",
8
+ "report.md",
9
+ "claim-evidence-graph.json",
10
+ "content-snapshot.json",
11
+ "inference-snapshot.json",
12
+ "evidence-snapshot.json",
13
+ ];
14
+ export function lineageError(code, message, details) {
15
+ return new CliError(message, { code, exitCode: 3, ...(details ? { details } : {}) });
16
+ }
17
+ export function publicationResultLineageSchema() {
18
+ const hash = { type: "string", pattern: HASH.source };
19
+ return {
20
+ type: "object",
21
+ additionalProperties: false,
22
+ required: [
23
+ "schemaVersion",
24
+ "projectId",
25
+ "analysisRunId",
26
+ "analysisSha256",
27
+ "claimEvidenceGraphSha256",
28
+ "reportSha256",
29
+ "files",
30
+ ],
31
+ properties: {
32
+ schemaVersion: { const: 1 },
33
+ projectId: { type: "string", minLength: 1 },
34
+ analysisRunId: { type: "string", minLength: 1 },
35
+ analysisSha256: hash,
36
+ claimEvidenceGraphSha256: hash,
37
+ reportSha256: hash,
38
+ files: {
39
+ type: "array",
40
+ minItems: 2,
41
+ items: {
42
+ type: "object",
43
+ additionalProperties: false,
44
+ required: ["role", "sha256", "analysisSha256"],
45
+ properties: {
46
+ role: { type: "string", minLength: 1 },
47
+ sha256: hash,
48
+ analysisSha256: hash,
49
+ },
50
+ },
51
+ },
52
+ },
53
+ };
54
+ }
55
+ /** Bind the already closed/reviewed core, without a new authority ledger. */
56
+ export async function closedAnalysisLineage(input) {
57
+ const assertRecord = (records, name, source) => {
58
+ const path = `outputs/${name}`;
59
+ const matches = Array.isArray(records)
60
+ ? records.filter((record) => isObject(record) && record.path === path)
61
+ : [];
62
+ const expected = matches.length === 1 ? matches[0] : null;
63
+ const actual = input.records[name];
64
+ if (!isObject(expected) ||
65
+ !actual ||
66
+ expected.sha256 !== actual.sha256 ||
67
+ expected.bytes !== actual.bytes) {
68
+ throw lineageError("RESEARCH_PUBLICATION_ANALYSIS_BINDING_STALE", "Publication requires the exact analysis and material core from its closed review; rebuild the affected base outputs before freezing.", {
69
+ object: path,
70
+ binding: source,
71
+ expectedSha256: isObject(expected) ? (expected.sha256 ?? null) : null,
72
+ currentSha256: actual?.sha256 ?? null,
73
+ expectedBytes: isObject(expected) ? (expected.bytes ?? null) : null,
74
+ currentBytes: actual?.bytes ?? null,
75
+ });
76
+ }
77
+ };
78
+ for (const name of ["analysis.json", "report.md"])
79
+ assertRecord(input.closure.artifacts, name, "base-closure");
80
+ const binding = input.closure.reviewPacket;
81
+ if (!isObject(binding) ||
82
+ typeof binding.packetSha256 !== "string" ||
83
+ !HASH.test(binding.packetSha256)) {
84
+ throw lineageError("RESEARCH_PUBLICATION_ANALYSIS_BINDING_STALE", "The base closure has no verifiable reviewed result binding.", { object: "closure.reviewPacket" });
85
+ }
86
+ const { loadVerifiedReviewPacket } = await import("./runtime.js");
87
+ const verified = await loadVerifiedReviewPacket(input.root, input.projectId, binding.packetSha256);
88
+ if (binding.path !== verified.path ||
89
+ binding.sha256 !== verified.sha256 ||
90
+ binding.bytes !== verified.bytes) {
91
+ throw lineageError("RESEARCH_PUBLICATION_ANALYSIS_BINDING_STALE", "The closure's reviewed packet changed.", {
92
+ object: "closure.reviewPacket",
93
+ expectedSha256: binding.sha256 ?? null,
94
+ currentSha256: verified.sha256,
95
+ });
96
+ }
97
+ const bytes = await readFile(join(workspacePaths(input.root).projects, input.projectId, verified.path), "utf8");
98
+ if (sha256Text(bytes) !== verified.sha256)
99
+ throw lineageError("RESEARCH_PUBLICATION_ANALYSIS_BINDING_STALE", "The reviewed packet changed during inspection.", { object: verified.path });
100
+ const packet = JSON.parse(bytes);
101
+ for (const name of RESULT_CORE_FILES)
102
+ assertRecord(packet.artifacts, name, "review-packet");
103
+ const run = input.analysis.analysisRun;
104
+ if (!isObject(run) || typeof run.id !== "string" || !run.id)
105
+ throw lineageError("RESEARCH_PUBLICATION_ANALYSIS_BINDING_STALE", "The closed analysis has no run identity.", { object: "analysis.analysisRun.id" });
106
+ return {
107
+ projectId: input.projectId,
108
+ analysisRunId: run.id,
109
+ analysisSha256: input.records["analysis.json"].sha256,
110
+ claimEvidenceGraphSha256: input.records["claim-evidence-graph.json"].sha256,
111
+ reportSha256: input.records["report.md"].sha256,
112
+ };
113
+ }
114
+ export function analysisGenerationId(base) {
115
+ return sha256Text(canonicalJson(base));
116
+ }
117
+ export function verifyMaterialResultLineage(value, base, files) {
118
+ if (!isObject(value) || value.schemaVersion !== 1 || !Array.isArray(value.files)) {
119
+ throw lineageError("RESEARCH_PUBLICATION_RESULT_LINEAGE_REQUIRED", "Provide resultLineage in the submission manifest, recorded from the closed analysis when producing the material files. Inspect research publication lineage and its schema; do not relabel stale results.");
120
+ }
121
+ const allowed = new Set(["schemaVersion", "files", ...Object.keys(base)]);
122
+ if (Object.keys(value).some((key) => !allowed.has(key)))
123
+ throw lineageError("RESEARCH_PUBLICATION_RESULT_LINEAGE_MISMATCH", "The material result manifest has unknown fields.");
124
+ for (const [key, expected] of Object.entries(base)) {
125
+ if (value[key] !== expected)
126
+ throw lineageError("RESEARCH_PUBLICATION_ANALYSIS_BINDING_STALE", "The prepared material results belong to a different closed analysis generation.", {
127
+ object: key,
128
+ expected,
129
+ supplied: value[key] ?? null,
130
+ currentAnalysisGenerationId: analysisGenerationId(base),
131
+ });
132
+ }
133
+ const expected = new Map(files.map((file) => [file.role, file.sha256]));
134
+ const seen = new Set();
135
+ for (const file of value.files) {
136
+ if (!isObject(file) ||
137
+ typeof file.role !== "string" ||
138
+ typeof file.sha256 !== "string" ||
139
+ !HASH.test(file.sha256) ||
140
+ Object.keys(file).some((key) => !["role", "sha256", "analysisSha256"].includes(key)) ||
141
+ seen.has(file.role) ||
142
+ expected.get(file.role) !== file.sha256 ||
143
+ file.analysisSha256 !== base.analysisSha256) {
144
+ throw lineageError("RESEARCH_PUBLICATION_RESULT_LINEAGE_MISMATCH", "Every material file must match its prepared bytes and the same closed analysis.", {
145
+ object: isObject(file) ? (file.role ?? null) : null,
146
+ expectedSha256: isObject(file) && typeof file.role === "string"
147
+ ? (expected.get(file.role) ?? null)
148
+ : null,
149
+ expectedAnalysisSha256: base.analysisSha256,
150
+ });
151
+ }
152
+ seen.add(file.role);
153
+ }
154
+ const missing = files.filter((file) => !seen.has(file.role)).map((file) => file.role);
155
+ if (missing.length)
156
+ throw lineageError("RESEARCH_PUBLICATION_RESULT_LINEAGE_MISMATCH", "The material result manifest omits frozen files.", { missingRoles: missing });
157
+ return {
158
+ schemaVersion: 1,
159
+ ...base,
160
+ files: files
161
+ .map((file) => ({ ...file, analysisSha256: base.analysisSha256 }))
162
+ .sort((a, b) => (a.role < b.role ? -1 : a.role > b.role ? 1 : 0)),
163
+ };
164
+ }
165
+ //# sourceMappingURL=publication-lineage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publication-lineage.js","sourceRoot":"","sources":["../../../src/research/workspace/publication-lineage.ts"],"names":[],"mappings":"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"}
@@ -57,6 +57,8 @@ interface PublicationGeneration {
57
57
  assessmentResult: TopJournalAssessmentResult;
58
58
  requiredReviewRoles: PublicationReviewRole[];
59
59
  taskAcceptanceSha256?: string | null;
60
+ analysisGenerationId?: string;
61
+ materialResultsManifest?: FrozenFile;
60
62
  }
61
63
  export interface PublicationReviewPacket {
62
64
  schemaVersion: 1;
@@ -88,6 +90,8 @@ export interface PublicationReviewPacket {
88
90
  taskAcceptance: TaskAcceptanceContext | null;
89
91
  instructions: string[];
90
92
  packetSha256: string;
93
+ analysisGenerationId: string;
94
+ materialResultsManifest: FrozenFile;
91
95
  }
92
96
  export interface PublicationStatus {
93
97
  schemaVersion: 1;
@@ -109,6 +113,8 @@ export interface PublicationStatus {
109
113
  readinessVerdict: PublicationReadinessVerdict;
110
114
  boundedStatement: string;
111
115
  closureSha256: string | null;
116
+ analysisGenerationId?: string;
117
+ materialResultsManifestSha256?: string;
112
118
  }
113
119
  export interface PublicationClosure {
114
120
  schemaVersion: 1;
@@ -135,6 +141,8 @@ export interface PublicationClosure {
135
141
  readinessVerdict: PublicationReadinessVerdict;
136
142
  boundedStatement: string;
137
143
  closureSha256: string;
144
+ analysisGenerationId: string;
145
+ materialResultsManifest: FrozenFile;
138
146
  }
139
147
  export declare function publicationAssessmentSchema(): Record<string, unknown>;
140
148
  export declare function publicationReviewSchema(role: PublicationReviewRole): Record<string, unknown>;
@@ -148,6 +156,7 @@ export declare function freezePublicationManuscript(input: {
148
156
  role: PublicationSubmissionRole;
149
157
  path: string;
150
158
  }>;
159
+ resultLineage?: unknown;
151
160
  producerAgent: AgentKind;
152
161
  producerSessionId: string;
153
162
  }): Promise<PublicationGeneration & {
@@ -172,4 +181,19 @@ export declare function submitPublicationReview(input: {
172
181
  }>;
173
182
  export declare function inspectPublicationStatus(root: string, projectId: string): Promise<PublicationStatus>;
174
183
  export declare function closePublication(root: string, projectId: string): Promise<PublicationClosure>;
184
+ /** Read the current authority before authoring material; never label existing files automatically. */
185
+ export declare function inspectPublicationLineage(root: string, projectId: string): Promise<{
186
+ analysisGenerationId: string;
187
+ resultLineage: {
188
+ projectId: string;
189
+ analysisRunId: string;
190
+ analysisSha256: string;
191
+ claimEvidenceGraphSha256: string;
192
+ reportSha256: string;
193
+ schemaVersion: number;
194
+ files: never[];
195
+ };
196
+ bindingScope: string;
197
+ nextAction: string;
198
+ }>;
175
199
  export {};
@@ -10,8 +10,9 @@ import { loadProject } from "./projects.js";
10
10
  import { assertResearchPolicyBinding } from "./research-policy.js";
11
11
  import { assertScientificGateForStage } from "./scientific-review.js";
12
12
  import { compileTaskAcceptanceContext } from "./task-acceptance.js";
13
+ import { analysisGenerationId, closedAnalysisLineage, RESULT_CORE_FILES, verifyMaterialResultLineage, lineageError, } from "./publication-lineage.js";
13
14
  import { validateTaskObject } from "./task-contract.js";
14
- import { canonicalJson, ensureDirectory, isObject, pathExists, readJsonFile, sha256File, sha256Text, workspacePaths, writeJsonAtomic, } from "./storage.js";
15
+ import { canonicalJson, ensureDirectory, fileRecord, isObject, pathExists, readJsonFile, sha256File, sha256Text, workspacePaths, writeJsonAtomic, } from "./storage.js";
15
16
  import { loadWorkspaceConfig, withWorkspaceLock } from "./workspace.js";
16
17
  const REQUIRED_REVIEW_ROLES = [
17
18
  "evidence",
@@ -250,17 +251,12 @@ export async function freezePublicationManuscript(input) {
250
251
  if ((await usedReviewerSessionHashes(input.root, project.id)).has(producerSessionSha256)) {
251
252
  throw publicationError("RESEARCH_PUBLICATION_REVIEW_NOT_INDEPENDENT", "A native producer session must not reuse any prior independent reviewer session.");
252
253
  }
253
- const assessmentValue = parsePublicationAssessment(JSON.parse(await readRegularTextFile(input.assessmentPath, "publication assessment")));
254
- await validateSubmissionManuscript(input.manuscriptPath);
255
254
  const projectRoot = projectDirectory(input.root, project.id);
256
255
  const outputRoot = join(projectRoot, "outputs");
257
- const snapshotValue = await readJsonFile(join(outputRoot, "evidence-snapshot.json"), "Frozen evidence snapshot");
258
- const snapshotSha256 = verifiedSnapshotSha256(project, snapshotValue);
259
- const closureValue = await readJsonFile(join(outputRoot, "closure.json"), "Base research closure");
260
- assertBaseClosure(project, closureValue, snapshotSha256);
261
- const submissionBindings = await validateSubmissionBindings(outputRoot, project.id, String(snapshotValue.snapshotId), snapshotSha256, project.publicationPolicy.resolvedPolicySha256);
262
256
  const manuscript = await storePublicationObject(input.root, project.id, input.manuscriptPath, "manuscript");
263
257
  const assessment = await storePublicationObject(input.root, project.id, input.assessmentPath, "publication-assessment");
258
+ const assessmentValue = parsePublicationAssessment(JSON.parse(await readRegularTextFile(join(projectRoot, assessment.objectLocator), "publication assessment")));
259
+ await validateSubmissionManuscript(join(projectRoot, manuscript.objectLocator));
264
260
  const supplements = [];
265
261
  for (const [index, path] of [...new Set(input.supplementPaths)].entries()) {
266
262
  supplements.push(await storePublicationObject(input.root, project.id, path, `supplement-${index + 1}`));
@@ -291,6 +287,38 @@ export async function freezePublicationManuscript(input) {
291
287
  const contentSnapshot = await storePublicationObject(input.root, project.id, join(outputRoot, "content-snapshot.json"), "content-snapshot", true);
292
288
  const inferenceSnapshot = await storePublicationObject(input.root, project.id, join(outputRoot, "inference-snapshot.json"), "inference-snapshot", true);
293
289
  const claimEvidenceGraph = await storePublicationObject(input.root, project.id, join(outputRoot, "claim-evidence-graph.json"), "claim-evidence-graph", true);
290
+ const coreObjects = {
291
+ "analysis.json": baseResearch.analysis,
292
+ "report.md": baseResearch.report,
293
+ "claim-evidence-graph.json": claimEvidenceGraph,
294
+ "content-snapshot.json": contentSnapshot,
295
+ "inference-snapshot.json": inferenceSnapshot,
296
+ "evidence-snapshot.json": evidenceSnapshot,
297
+ };
298
+ const frozenPaths = Object.fromEntries(Object.entries(coreObjects).map(([name, file]) => [
299
+ name,
300
+ join(projectRoot, file.objectLocator),
301
+ ]));
302
+ const snapshotValue = await readJsonFile(frozenPaths["evidence-snapshot.json"], "Frozen evidence snapshot");
303
+ const snapshotSha256 = verifiedSnapshotSha256(project, snapshotValue);
304
+ const closureValue = await readJsonFile(join(projectRoot, baseResearch.closure.objectLocator), "Base research closure");
305
+ assertBaseClosure(project, closureValue, snapshotSha256);
306
+ const submissionBindings = await validateSubmissionBindings(outputRoot, project.id, String(snapshotValue.snapshotId), snapshotSha256, project.publicationPolicy.resolvedPolicySha256, frozenPaths);
307
+ const baseLineage = await closedAnalysisLineage({
308
+ root: input.root,
309
+ projectId: project.id,
310
+ closure: closureValue,
311
+ analysis: submissionBindings.analysis,
312
+ records: Object.fromEntries(Object.entries(coreObjects).map(([name, file]) => [
313
+ name,
314
+ { path: `outputs/${name}`, sha256: file.sha256, bytes: file.bytes },
315
+ ])),
316
+ });
317
+ const materialLineage = verifyMaterialResultLineage(input.resultLineage, baseLineage, materialFiles(manuscript, assessment, supplements, submissionFiles));
318
+ const materialResultsPath = join(outputRoot, "material-results-manifest.json");
319
+ await writeJsonAtomic(materialResultsPath, materialLineage);
320
+ const materialResultsManifest = await storePublicationObject(input.root, project.id, materialResultsPath, "material-results-manifest", true);
321
+ const resultGenerationId = analysisGenerationId(baseLineage);
294
322
  const analysisValue = submissionBindings.analysis;
295
323
  const reproducibilityPath = join(outputRoot, "submission-reproducibility.json");
296
324
  await writeJsonAtomic(reproducibilityPath, {
@@ -299,6 +327,8 @@ export async function freezePublicationManuscript(input) {
299
327
  projectId: project.id,
300
328
  analysisRun: isObject(analysisValue.analysisRun) ? analysisValue.analysisRun : null,
301
329
  bindings: {
330
+ analysisGenerationId: resultGenerationId,
331
+ materialResultsManifestSha256: materialResultsManifest.sha256,
302
332
  evidenceSnapshotSha256: evidenceSnapshot.sha256,
303
333
  contentSnapshotSha256: contentSnapshot.sha256,
304
334
  inferenceSnapshotSha256: inferenceSnapshot.sha256,
@@ -344,6 +374,8 @@ export async function freezePublicationManuscript(input) {
344
374
  assessment,
345
375
  supplements,
346
376
  submissionPackage,
377
+ analysisGenerationId: resultGenerationId,
378
+ materialResultsManifest,
347
379
  assessmentResult,
348
380
  requiredReviewRoles: requiredReviewRoles(project.publicationPolicy),
349
381
  taskAcceptanceSha256: (await compileTaskAcceptanceContext(input.root, project))?.contextSha256 ?? null,
@@ -431,6 +463,8 @@ export async function preparePublicationReview(input) {
431
463
  assessment: generation.assessment,
432
464
  supplements: generation.supplements,
433
465
  submissionPackage: generation.submissionPackage,
466
+ analysisGenerationId: generation.analysisGenerationId,
467
+ materialResultsManifest: generation.materialResultsManifest,
434
468
  mechanicalAssessment: generation.assessmentResult,
435
469
  taskAcceptance: await compileTaskAcceptanceContext(input.root, project),
436
470
  instructions: reviewInstructions(input.role),
@@ -571,6 +605,8 @@ export async function inspectPublicationStatus(root, projectId) {
571
605
  readinessVerdict,
572
606
  boundedStatement: boundedStatement(readinessVerdict),
573
607
  closureSha256,
608
+ analysisGenerationId: generation.analysisGenerationId,
609
+ materialResultsManifestSha256: generation.materialResultsManifest.sha256,
574
610
  };
575
611
  }
576
612
  export async function closePublication(root, projectId) {
@@ -601,6 +637,8 @@ export async function closePublication(root, projectId) {
601
637
  assessment: generation.assessment,
602
638
  supplements: generation.supplements,
603
639
  submissionPackage: generation.submissionPackage,
640
+ analysisGenerationId: generation.analysisGenerationId,
641
+ materialResultsManifest: generation.materialResultsManifest,
604
642
  reviews: reviews.map((entry) => ({
605
643
  role: entry.role,
606
644
  packetSha256: entry.packet.packetSha256,
@@ -679,6 +717,9 @@ async function validateSubmissionManuscript(path) {
679
717
  }
680
718
  const content = await readRegularTextFile(canonical, "submission manuscript");
681
719
  const headings = [...content.matchAll(/^#{1,6}\s+(.+?)\s*$/gmu)].map((match) => (match[1] ?? "")
720
+ // Strip only a conservative section number ("1", "3.1") optionally closed
721
+ // by "." or ")" and followed by required whitespace; "1Introduction" stays.
722
+ .replace(/^\d+(?:\.\d+)*[.)]?[ \t]+/u, "")
682
723
  .trim()
683
724
  .toLowerCase()
684
725
  .replaceAll(/[^a-z0-9]+/gu, " ")
@@ -700,12 +741,13 @@ async function validateSubmissionManuscript(path) {
700
741
  throw publicationError("RESEARCH_PUBLICATION_MANUSCRIPT_INCOMPLETE", "The manuscript is missing required submission sections.", 3, { missingSections });
701
742
  }
702
743
  }
703
- async function validateSubmissionBindings(outputRoot, projectId, evidenceSnapshotId, evidenceSnapshotSha256, policySha256) {
744
+ async function validateSubmissionBindings(outputRoot, projectId, evidenceSnapshotId, evidenceSnapshotSha256, policySha256, frozenPaths) {
745
+ const inputPath = (name) => frozenPaths?.[name] ?? join(outputRoot, name);
704
746
  const [content, inference, analysis, graph] = await Promise.all([
705
- readJsonFile(join(outputRoot, "content-snapshot.json"), "Frozen content snapshot"),
706
- readJsonFile(join(outputRoot, "inference-snapshot.json"), "Frozen inference snapshot"),
707
- readJsonFile(join(outputRoot, "analysis.json"), "Frozen analysis"),
708
- readJsonFile(join(outputRoot, "claim-evidence-graph.json"), "Frozen Claim-Evidence Graph"),
747
+ readJsonFile(inputPath("content-snapshot.json"), "Frozen content snapshot"),
748
+ readJsonFile(inputPath("inference-snapshot.json"), "Frozen inference snapshot"),
749
+ readJsonFile(inputPath("analysis.json"), "Frozen analysis"),
750
+ readJsonFile(inputPath("claim-evidence-graph.json"), "Frozen Claim-Evidence Graph"),
709
751
  ]);
710
752
  const contentGate = isObject(content.gate) ? content.gate : {};
711
753
  const inferenceGate = isObject(inference.gate) ? inference.gate : {};
@@ -752,7 +794,7 @@ async function validateSubmissionBindings(outputRoot, projectId, evidenceSnapsho
752
794
  graph.kind !== "tiangong-claim-evidence-graph" ||
753
795
  graph.projectId !== projectId ||
754
796
  graph.inferenceSnapshotSha256 !== inference.snapshotSha256 ||
755
- graph.analysisSha256 !== (await sha256File(join(outputRoot, "analysis.json"))) ||
797
+ graph.analysisSha256 !== (await sha256File(inputPath("analysis.json"))) ||
756
798
  graph.analysisRunId !== analysisRun.id ||
757
799
  nodeById.size !== graphNodes.length ||
758
800
  new Set(graphEdges.flatMap((edge) => (typeof edge.id === "string" ? [edge.id] : []))).size !==
@@ -944,7 +986,11 @@ async function loadCurrentGeneration(root, projectId) {
944
986
  if (sha256Text(canonicalJson(submissionPackageCore)) !== packageSha256) {
945
987
  throw publicationError("RESEARCH_PUBLICATION_STATE_INVALID", "The publication submission package hash binding is invalid.");
946
988
  }
989
+ if (!generation.materialResultsManifest || !generation.analysisGenerationId) {
990
+ throw lineageError("RESEARCH_PUBLICATION_RESULT_LINEAGE_REQUIRED", "This legacy publication has no material result lineage. Preserve its history and refreeze with a prepared resultLineage before another review or readiness claim.");
991
+ }
947
992
  await verifyFrozenFiles(root, projectId, [
993
+ generation.materialResultsManifest,
948
994
  generation.manuscript,
949
995
  generation.assessment,
950
996
  generation.evidenceSnapshot.object,
@@ -958,12 +1004,70 @@ async function loadCurrentGeneration(root, projectId) {
958
1004
  generation.submissionPackage.claimEvidenceGraph,
959
1005
  generation.submissionPackage.reproducibilityManifest,
960
1006
  ]);
1007
+ const base = await currentClosedAnalysisLineage(root, projectId);
1008
+ const manifest = await readJsonFile(join(projectDirectory(root, projectId), generation.materialResultsManifest.objectLocator), "Frozen material results manifest");
1009
+ verifyMaterialResultLineage(manifest, base, materialFiles(generation.manuscript, generation.assessment, generation.supplements, generation.submissionPackage.files));
1010
+ if (generation.analysisGenerationId !== analysisGenerationId(base) ||
1011
+ generation.baseResearch.analysis.sha256 !== base.analysisSha256 ||
1012
+ generation.baseResearch.report.sha256 !== base.reportSha256 ||
1013
+ generation.submissionPackage.claimEvidenceGraph.sha256 !== base.claimEvidenceGraphSha256) {
1014
+ throw lineageError("RESEARCH_PUBLICATION_ANALYSIS_BINDING_STALE", "The publication no longer binds the current closed result generation.", {
1015
+ object: "analysisGenerationId",
1016
+ expected: analysisGenerationId(base),
1017
+ supplied: generation.analysisGenerationId,
1018
+ });
1019
+ }
1020
+ const reproduction = await readJsonFile(join(projectDirectory(root, projectId), generation.submissionPackage.reproducibilityManifest.objectLocator), "Frozen reproducibility manifest");
1021
+ const expectedReproduction = {
1022
+ analysisGenerationId: generation.analysisGenerationId,
1023
+ materialResultsManifestSha256: generation.materialResultsManifest.sha256,
1024
+ evidenceSnapshotSha256: generation.evidenceSnapshot.object.sha256,
1025
+ contentSnapshotSha256: generation.submissionPackage.contentSnapshot.sha256,
1026
+ inferenceSnapshotSha256: generation.submissionPackage.inferenceSnapshot.sha256,
1027
+ claimEvidenceGraphSha256: generation.submissionPackage.claimEvidenceGraph.sha256,
1028
+ analysisSha256: generation.baseResearch.analysis.sha256,
1029
+ };
1030
+ const frozenAnalysis = await readJsonFile(join(projectDirectory(root, projectId), generation.baseResearch.analysis.objectLocator), "Frozen analysis");
1031
+ if (reproduction.projectId !== projectId ||
1032
+ canonicalJson(reproduction.bindings) !== canonicalJson(expectedReproduction) ||
1033
+ canonicalJson(reproduction.analysisRun) !== canonicalJson(frozenAnalysis.analysisRun)) {
1034
+ throw lineageError("RESEARCH_PUBLICATION_RESULT_LINEAGE_MISMATCH", "The reproducibility record does not bind the frozen material results and analysis.", { object: "submission-reproducibility" });
1035
+ }
961
1036
  const currentTask = await compileTaskAcceptanceContext(root, await loadProject(root, projectId));
962
1037
  if ((generation.taskAcceptanceSha256 ?? null) !== (currentTask?.contextSha256 ?? null)) {
963
1038
  throw publicationError("RESEARCH_PUBLICATION_TASK_BINDING_INVALID", "The frozen publication generation no longer binds the current task checks.");
964
1039
  }
965
1040
  return generation;
966
1041
  }
1042
+ function materialFiles(manuscript, assessment, supplements, submission) {
1043
+ return [
1044
+ { role: "manuscript", sha256: manuscript.sha256 },
1045
+ { role: "assessment", sha256: assessment.sha256 },
1046
+ ...supplements.map((file) => ({ role: file.logicalName, sha256: file.sha256 })),
1047
+ ...submission.map((file) => ({ role: file.role, sha256: file.sha256 })),
1048
+ ];
1049
+ }
1050
+ async function currentClosedAnalysisLineage(root, projectId) {
1051
+ const project = await requireClosedTopJournalProject(root, projectId);
1052
+ const outputRoot = join(projectDirectory(root, projectId), "outputs");
1053
+ const snapshot = await readJsonFile(join(outputRoot, "evidence-snapshot.json"), "Closed evidence snapshot");
1054
+ const snapshotSha256 = verifiedSnapshotSha256(project, snapshot);
1055
+ const closure = await readJsonFile(join(outputRoot, "closure.json"), "Base closure");
1056
+ assertBaseClosure(project, closure, snapshotSha256);
1057
+ const { analysis } = await validateSubmissionBindings(outputRoot, projectId, String(snapshot.snapshotId), snapshotSha256, project.publicationPolicy.resolvedPolicySha256);
1058
+ const records = Object.fromEntries(await Promise.all(RESULT_CORE_FILES.map(async (name) => [name, await fileRecord(join(outputRoot, name), `outputs/${name}`)])));
1059
+ return closedAnalysisLineage({ root, projectId, closure, analysis, records });
1060
+ }
1061
+ /** Read the current authority before authoring material; never label existing files automatically. */
1062
+ export async function inspectPublicationLineage(root, projectId) {
1063
+ const base = await currentClosedAnalysisLineage(root, projectId);
1064
+ return {
1065
+ analysisGenerationId: analysisGenerationId(base),
1066
+ resultLineage: { schemaVersion: 1, ...base, files: [] },
1067
+ bindingScope: "producer-declared-derivation-with-verified-bytes-and-closed-analysis",
1068
+ nextAction: "Record each material file's role, SHA-256 and source analysisSha256 when producing it. Fill every manuscript, assessment, submission and supplement-N entry; do not relabel stale artifacts. Freeze verifies this prepared manifest. Scientific fidelity still requires independent review.",
1069
+ };
1070
+ }
967
1071
  async function verifyFrozenFiles(root, projectId, files) {
968
1072
  for (const file of files) {
969
1073
  const path = join(projectDirectory(root, projectId), file.objectLocator);