@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.
- package/AGENTS.md +17 -6
- package/CONTRIBUTING.md +5 -5
- package/README.md +417 -36
- package/dist/cli.js +2 -2
- package/dist/data/builtins.js +33 -5
- package/dist/data/builtins.js.map +1 -1
- package/dist/data/catalog.js +46 -0
- package/dist/data/catalog.js.map +1 -1
- package/dist/data/commands.js +15 -1
- package/dist/data/commands.js.map +1 -1
- package/dist/data/connectors/airnow-hourly-observations.js +60 -10
- package/dist/data/connectors/airnow-hourly-observations.js.map +1 -1
- package/dist/data/connectors/epa-eis-records.js +16 -2
- package/dist/data/connectors/epa-eis-records.js.map +1 -1
- package/dist/data/connectors/gdelt-doc-search.js +54 -18
- package/dist/data/connectors/gdelt-doc-search.js.map +1 -1
- package/dist/data/connectors/gdelt-file-feeds.js +20 -11
- package/dist/data/connectors/gdelt-file-feeds.js.map +1 -1
- package/dist/data/connectors/gdelt-web-ngrams.d.ts +2 -0
- package/dist/data/connectors/gdelt-web-ngrams.js +350 -0
- package/dist/data/connectors/gdelt-web-ngrams.js.map +1 -0
- package/dist/data/connectors/gdelt-web-ngrams.schemas.d.ts +188 -0
- package/dist/data/connectors/gdelt-web-ngrams.schemas.js +138 -0
- package/dist/data/connectors/gdelt-web-ngrams.schemas.js.map +1 -0
- package/dist/data/connectors/nasa-firms-fire.js +4 -0
- package/dist/data/connectors/nasa-firms-fire.js.map +1 -1
- package/dist/data/connectors/open-meteo-air-quality.js +27 -22
- package/dist/data/connectors/open-meteo-air-quality.js.map +1 -1
- package/dist/data/connectors/open-meteo-air-quality.schemas.d.ts +4 -1
- package/dist/data/connectors/open-meteo-air-quality.schemas.js +21 -1
- package/dist/data/connectors/open-meteo-air-quality.schemas.js.map +1 -1
- package/dist/data/connectors/open-meteo-flood.js +28 -23
- package/dist/data/connectors/open-meteo-flood.js.map +1 -1
- package/dist/data/connectors/open-meteo-flood.schemas.d.ts +4 -1
- package/dist/data/connectors/open-meteo-flood.schemas.js +21 -1
- package/dist/data/connectors/open-meteo-flood.schemas.js.map +1 -1
- package/dist/data/connectors/open-meteo-historical-weather.js +26 -21
- package/dist/data/connectors/open-meteo-historical-weather.js.map +1 -1
- package/dist/data/connectors/open-meteo-historical-weather.schemas.d.ts +4 -1
- package/dist/data/connectors/open-meteo-historical-weather.schemas.js +21 -1
- package/dist/data/connectors/open-meteo-historical-weather.schemas.js.map +1 -1
- package/dist/data/connectors/openaq-air-quality.js +50 -12
- package/dist/data/connectors/openaq-air-quality.js.map +1 -1
- package/dist/data/connectors/openaq-air-quality.schemas.d.ts +0 -4
- package/dist/data/connectors/openaq-air-quality.schemas.js +2 -2
- package/dist/data/connectors/openaq-air-quality.schemas.js.map +1 -1
- package/dist/data/connectors/youtube-public-content.js +76 -6
- package/dist/data/connectors/youtube-public-content.js.map +1 -1
- package/dist/data/connectors/youtube-public-content.schemas.d.ts +63 -3
- package/dist/data/connectors/youtube-public-content.schemas.js +56 -2
- package/dist/data/connectors/youtube-public-content.schemas.js.map +1 -1
- package/dist/data/contracts.d.ts +22 -1
- package/dist/data/runtime/bounded-http.d.ts +2 -0
- package/dist/data/runtime/bounded-http.js +221 -59
- package/dist/data/runtime/bounded-http.js.map +1 -1
- package/dist/data/runtime/execute.d.ts +2 -0
- package/dist/data/runtime/execute.js +82 -60
- package/dist/data/runtime/execute.js.map +1 -1
- package/dist/data/runtime/http-timing.d.ts +15 -0
- package/dist/data/runtime/http-timing.js +59 -0
- package/dist/data/runtime/http-timing.js.map +1 -0
- package/dist/data/runtime/http-transport.d.ts +1 -0
- package/dist/data/runtime/http-transport.js +27 -0
- package/dist/data/runtime/http-transport.js.map +1 -0
- package/dist/data/schemas/catalog.v1.json +46 -0
- package/dist/data/schemas/discovery.v1.json +12 -0
- package/dist/data/schemas/error.v1.json +1 -0
- package/dist/data/schemas/manifest.v1.json +19 -1
- package/dist/research/orchestration.js +233 -10
- package/dist/research/orchestration.js.map +1 -1
- package/dist/research/setup-command.js +37 -6
- package/dist/research/setup-command.js.map +1 -1
- package/dist/research/workspace/audit-bundle.js +28 -8
- package/dist/research/workspace/audit-bundle.js.map +1 -1
- package/dist/research/workspace/broker.js +25 -4
- package/dist/research/workspace/broker.js.map +1 -1
- package/dist/research/workspace/constants.d.ts +1 -0
- package/dist/research/workspace/constants.js +1 -0
- package/dist/research/workspace/constants.js.map +1 -1
- package/dist/research/workspace/content-evidence.d.ts +1 -0
- package/dist/research/workspace/content-evidence.js +1 -1
- package/dist/research/workspace/content-evidence.js.map +1 -1
- package/dist/research/workspace/credentials.js +2 -1
- package/dist/research/workspace/credentials.js.map +1 -1
- package/dist/research/workspace/data-evidence-adapter.d.ts +71 -0
- package/dist/research/workspace/data-evidence-adapter.js +269 -59
- package/dist/research/workspace/data-evidence-adapter.js.map +1 -1
- package/dist/research/workspace/data-evidence-view.d.ts +58 -0
- package/dist/research/workspace/data-evidence-view.js +411 -0
- package/dist/research/workspace/data-evidence-view.js.map +1 -0
- package/dist/research/workspace/evidence-ledger.js +32 -29
- package/dist/research/workspace/evidence-ledger.js.map +1 -1
- package/dist/research/workspace/evidence.d.ts +28 -0
- package/dist/research/workspace/evidence.js +48 -0
- package/dist/research/workspace/evidence.js.map +1 -1
- package/dist/research/workspace/executor.d.ts +27 -1
- package/dist/research/workspace/executor.js +180 -20
- package/dist/research/workspace/executor.js.map +1 -1
- package/dist/research/workspace/external-skills.d.ts +1 -0
- package/dist/research/workspace/external-skills.js +4 -2
- package/dist/research/workspace/external-skills.js.map +1 -1
- package/dist/research/workspace/investigation-attempt.d.ts +159 -0
- package/dist/research/workspace/investigation-attempt.js +619 -0
- package/dist/research/workspace/investigation-attempt.js.map +1 -0
- package/dist/research/workspace/investigation-audit.d.ts +20 -0
- package/dist/research/workspace/investigation-audit.js +306 -0
- package/dist/research/workspace/investigation-audit.js.map +1 -0
- package/dist/research/workspace/investigation-candidate.d.ts +84 -0
- package/dist/research/workspace/investigation-candidate.js +156 -0
- package/dist/research/workspace/investigation-candidate.js.map +1 -0
- package/dist/research/workspace/investigation-certification.d.ts +39 -0
- package/dist/research/workspace/investigation-certification.js +152 -0
- package/dist/research/workspace/investigation-certification.js.map +1 -0
- package/dist/research/workspace/investigation-close.d.ts +47 -0
- package/dist/research/workspace/investigation-close.js +153 -0
- package/dist/research/workspace/investigation-close.js.map +1 -0
- package/dist/research/workspace/investigation-lineage.d.ts +8 -0
- package/dist/research/workspace/investigation-lineage.js +10 -0
- package/dist/research/workspace/investigation-lineage.js.map +1 -0
- package/dist/research/workspace/investigation-observer.d.ts +22 -0
- package/dist/research/workspace/investigation-observer.js +122 -0
- package/dist/research/workspace/investigation-observer.js.map +1 -0
- package/dist/research/workspace/investigation-promotion.d.ts +129 -0
- package/dist/research/workspace/investigation-promotion.js +316 -0
- package/dist/research/workspace/investigation-promotion.js.map +1 -0
- package/dist/research/workspace/investigation-requirements.d.ts +5 -0
- package/dist/research/workspace/investigation-requirements.js +32 -0
- package/dist/research/workspace/investigation-requirements.js.map +1 -0
- package/dist/research/workspace/investigation-resources.d.ts +12 -0
- package/dist/research/workspace/investigation-resources.js +78 -0
- package/dist/research/workspace/investigation-resources.js.map +1 -0
- package/dist/research/workspace/investigation-source-export.d.ts +6 -0
- package/dist/research/workspace/investigation-source-export.js +68 -0
- package/dist/research/workspace/investigation-source-export.js.map +1 -0
- package/dist/research/workspace/investigation-store.d.ts +9 -0
- package/dist/research/workspace/investigation-store.js +55 -0
- package/dist/research/workspace/investigation-store.js.map +1 -0
- package/dist/research/workspace/investigation.d.ts +424 -0
- package/dist/research/workspace/investigation.js +716 -0
- package/dist/research/workspace/investigation.js.map +1 -0
- package/dist/research/workspace/native-process-supervisor.d.ts +1 -0
- package/dist/research/workspace/native-process-supervisor.js +97 -0
- package/dist/research/workspace/native-process-supervisor.js.map +1 -0
- package/dist/research/workspace/native-run.d.ts +79 -6
- package/dist/research/workspace/native-run.js +293 -51
- package/dist/research/workspace/native-run.js.map +1 -1
- package/dist/research/workspace/preflight.d.ts +3 -0
- package/dist/research/workspace/preflight.js +11 -6
- package/dist/research/workspace/preflight.js.map +1 -1
- package/dist/research/workspace/project-authority.js +2 -0
- package/dist/research/workspace/project-authority.js.map +1 -1
- package/dist/research/workspace/project-budget.d.ts +41 -0
- package/dist/research/workspace/project-budget.js +265 -0
- package/dist/research/workspace/project-budget.js.map +1 -0
- package/dist/research/workspace/project-mutations.d.ts +1 -1
- package/dist/research/workspace/project-mutations.js +27 -3
- package/dist/research/workspace/project-mutations.js.map +1 -1
- package/dist/research/workspace/projects.d.ts +66 -0
- package/dist/research/workspace/projects.js +181 -3
- package/dist/research/workspace/projects.js.map +1 -1
- package/dist/research/workspace/provider-budget.d.ts +3 -0
- package/dist/research/workspace/provider-budget.js +45 -0
- package/dist/research/workspace/provider-budget.js.map +1 -0
- package/dist/research/workspace/provider-state.d.ts +1 -0
- package/dist/research/workspace/provider-state.js +20 -0
- package/dist/research/workspace/provider-state.js.map +1 -0
- package/dist/research/workspace/publication-lineage.d.ts +34 -0
- package/dist/research/workspace/publication-lineage.js +165 -0
- package/dist/research/workspace/publication-lineage.js.map +1 -0
- package/dist/research/workspace/publication-workflow.d.ts +24 -0
- package/dist/research/workspace/publication-workflow.js +118 -14
- package/dist/research/workspace/publication-workflow.js.map +1 -1
- package/dist/research/workspace/review-executor.d.ts +6 -1
- package/dist/research/workspace/review-executor.js +17 -3
- package/dist/research/workspace/review-executor.js.map +1 -1
- package/dist/research/workspace/runtime.d.ts +12 -1
- package/dist/research/workspace/runtime.js +143 -19
- package/dist/research/workspace/runtime.js.map +1 -1
- package/dist/research/workspace/scientific-amendment.d.ts +103 -0
- package/dist/research/workspace/scientific-amendment.js +537 -0
- package/dist/research/workspace/scientific-amendment.js.map +1 -0
- package/dist/research/workspace/scientific-fulfillment-audit.d.ts +20 -2
- package/dist/research/workspace/scientific-fulfillment-audit.js +87 -7
- package/dist/research/workspace/scientific-fulfillment-audit.js.map +1 -1
- package/dist/research/workspace/scientific-fulfillment.d.ts +10 -1
- package/dist/research/workspace/scientific-fulfillment.js +20 -6
- package/dist/research/workspace/scientific-fulfillment.js.map +1 -1
- package/dist/research/workspace/scientific-review-execution.js +162 -22
- package/dist/research/workspace/scientific-review-execution.js.map +1 -1
- package/dist/research/workspace/scientific-review.d.ts +2 -1
- package/dist/research/workspace/scientific-review.js +33 -2
- package/dist/research/workspace/scientific-review.js.map +1 -1
- package/dist/research/workspace/setup-catalog.js +13 -13
- package/dist/research/workspace/setup-catalog.js.map +1 -1
- package/dist/research/workspace/setup-release.d.ts +22 -0
- package/dist/research/workspace/setup-release.js +426 -0
- package/dist/research/workspace/setup-release.js.map +1 -0
- package/dist/research/workspace/setup-runtime-integrity.d.ts +2 -0
- package/dist/research/workspace/setup-runtime-integrity.js +31 -0
- package/dist/research/workspace/setup-runtime-integrity.js.map +1 -0
- package/dist/research/workspace/setup-upgrade.d.ts +17 -0
- package/dist/research/workspace/setup-upgrade.js +784 -0
- package/dist/research/workspace/setup-upgrade.js.map +1 -0
- package/dist/research/workspace/setup.d.ts +31 -39
- package/dist/research/workspace/setup.js +228 -19
- package/dist/research/workspace/setup.js.map +1 -1
- package/dist/research/workspace/task-acceptance.d.ts +8 -1
- package/dist/research/workspace/task-acceptance.js +37 -5
- package/dist/research/workspace/task-acceptance.js.map +1 -1
- package/dist/research/workspace/task-audit.d.ts +5 -1
- package/dist/research/workspace/task-audit.js +33 -18
- package/dist/research/workspace/task-audit.js.map +1 -1
- package/dist/research/workspace/types.d.ts +44 -0
- package/dist/research/workspace/workspace.js +34 -4
- package/dist/research/workspace/workspace.js.map +1 -1
- 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(
|
|
706
|
-
readJsonFile(
|
|
707
|
-
readJsonFile(
|
|
708
|
-
readJsonFile(
|
|
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(
|
|
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);
|