@tiangong-ai/cli 0.0.60 → 0.0.62
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/README.md +577 -55
- 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 +341 -28
- 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/acquisition-routes.d.ts +1 -1
- package/dist/research/workspace/acquisition-routes.js +2 -11
- package/dist/research/workspace/acquisition-routes.js.map +1 -1
- package/dist/research/workspace/artifact-read-audit.d.ts +16 -0
- package/dist/research/workspace/artifact-read-audit.js +176 -0
- package/dist/research/workspace/artifact-read-audit.js.map +1 -0
- package/dist/research/workspace/artifact-trace.d.ts +12 -0
- package/dist/research/workspace/artifact-trace.js +109 -0
- package/dist/research/workspace/artifact-trace.js.map +1 -0
- package/dist/research/workspace/artifact-view-mcp.d.ts +75 -0
- package/dist/research/workspace/artifact-view-mcp.js +214 -0
- package/dist/research/workspace/artifact-view-mcp.js.map +1 -0
- package/dist/research/workspace/artifact-views.d.ts +100 -0
- package/dist/research/workspace/artifact-views.js +395 -0
- package/dist/research/workspace/artifact-views.js.map +1 -0
- package/dist/research/workspace/audit-bundle.d.ts +4 -0
- package/dist/research/workspace/audit-bundle.js +130 -11
- 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 +32 -2
- package/dist/research/workspace/executor.js +286 -42
- package/dist/research/workspace/executor.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 +150 -0
- package/dist/research/workspace/native-run.js +879 -0
- package/dist/research/workspace/native-run.js.map +1 -0
- package/dist/research/workspace/preflight.d.ts +11 -2
- package/dist/research/workspace/preflight.js +28 -20
- 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 +29 -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 +182 -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/request-provenance.d.ts +67 -0
- package/dist/research/workspace/request-provenance.js +115 -0
- package/dist/research/workspace/request-provenance.js.map +1 -0
- package/dist/research/workspace/review-executor.d.ts +6 -1
- package/dist/research/workspace/review-executor.js +39 -14
- package/dist/research/workspace/review-executor.js.map +1 -1
- package/dist/research/workspace/runtime.d.ts +64 -1
- package/dist/research/workspace/runtime.js +359 -112
- package/dist/research/workspace/runtime.js.map +1 -1
- package/dist/research/workspace/sanitization.js +1 -1
- package/dist/research/workspace/sanitization.js.map +1 -1
- package/dist/research/workspace/schema-compatibility.d.ts +5 -0
- package/dist/research/workspace/schema-compatibility.js +55 -0
- package/dist/research/workspace/schema-compatibility.js.map +1 -0
- 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-design.js +8 -17
- package/dist/research/workspace/scientific-design.js.map +1 -1
- package/dist/research/workspace/scientific-fulfillment-audit.d.ts +21 -0
- package/dist/research/workspace/scientific-fulfillment-audit.js +217 -0
- package/dist/research/workspace/scientific-fulfillment-audit.js.map +1 -0
- package/dist/research/workspace/scientific-fulfillment.d.ts +130 -0
- package/dist/research/workspace/scientific-fulfillment.js +553 -0
- package/dist/research/workspace/scientific-fulfillment.js.map +1 -0
- package/dist/research/workspace/scientific-review-execution.js +236 -75
- package/dist/research/workspace/scientific-review-execution.js.map +1 -1
- package/dist/research/workspace/scientific-review.d.ts +6 -1
- package/dist/research/workspace/scientific-review.js +228 -52
- package/dist/research/workspace/scientific-review.js.map +1 -1
- package/dist/research/workspace/setup-catalog.js +3 -3
- 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 +220 -17
- package/dist/research/workspace/setup.js.map +1 -1
- package/dist/research/workspace/task-acceptance.d.ts +17 -2
- package/dist/research/workspace/task-acceptance.js +106 -10
- 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 +70 -5
- package/dist/research/workspace/task-audit.js.map +1 -1
- package/dist/research/workspace/task-contract.d.ts +4 -1
- package/dist/research/workspace/task-contract.js +26 -5
- package/dist/research/workspace/task-contract.js.map +1 -1
- package/dist/research/workspace/types.d.ts +50 -3
- package/dist/research/workspace/workspace.js +34 -4
- package/dist/research/workspace/workspace.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
2
|
-
import {
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { chmod, copyFile, lstat, mkdir, readFile } from "node:fs/promises";
|
|
3
4
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { scientificAmendmentLocator } from "./scientific-amendment.js";
|
|
4
6
|
import { CliError } from "../../errors.js";
|
|
5
7
|
import { taskContext } from "./task-contract.js";
|
|
8
|
+
import { nativeRunArtifactRecords } from "./native-run.js";
|
|
9
|
+
import { loadEvidenceArtifactRecords } from "./artifacts.js";
|
|
6
10
|
import { sourceTypeRequirementGaps } from "./evidence-role-coverage.js";
|
|
7
11
|
import { appendJournalEvent, verifyJournal } from "./journal.js";
|
|
8
12
|
import { loadProject, nextScientificGate, saveProject } from "./projects.js";
|
|
9
13
|
import { projectWithEffectiveAuthority, readProjectAuthorityIndex } from "./project-authority.js";
|
|
10
14
|
import { assertResearchPolicyBinding } from "./research-policy.js";
|
|
11
|
-
import { evaluateScientificDesign,
|
|
15
|
+
import { evaluateScientificDesign, scientificDesignPolicyGaps, } from "./scientific-design.js";
|
|
12
16
|
import { resolveScientificObjectBinding } from "./scientific-objects.js";
|
|
17
|
+
import { loadScientificFulfillmentView, scientificFulfillmentLocator, } from "./scientific-fulfillment.js";
|
|
13
18
|
import { sanitizeResearchValue } from "./sanitization.js";
|
|
14
19
|
import { canonicalJson, fileRecord, isObject, pathExists, resolveContained, sha256File, sha256Text, workspacePaths, writeBytesAtomic, writeJsonAtomic, writeTextAtomic, } from "./storage.js";
|
|
15
20
|
import { loadWorkspaceConfig, verifyDoctorAttestation, withWorkspaceLock } from "./workspace.js";
|
|
@@ -265,7 +270,8 @@ export async function prepareScientificReview(input) {
|
|
|
265
270
|
exitCode: 3,
|
|
266
271
|
});
|
|
267
272
|
}
|
|
268
|
-
const
|
|
273
|
+
const fulfillment = await loadScientificFulfillmentView(input.root, project, input.role);
|
|
274
|
+
const design = fulfillment.contract;
|
|
269
275
|
const assessment = await readAssessment(input.assessmentPath, input.role);
|
|
270
276
|
if (assessment.designSha256 !== project.scientificDesign.designSha256) {
|
|
271
277
|
throw new CliError("Scientific assessment does not match the frozen design.", {
|
|
@@ -274,6 +280,90 @@ export async function prepareScientificReview(input) {
|
|
|
274
280
|
});
|
|
275
281
|
}
|
|
276
282
|
const stageInputs = await stageInputRecords(input.root, project, input.role, design, assessment, input.canaryArtifactPaths ?? []);
|
|
283
|
+
const currentTask = await taskContext(input.root, project.id);
|
|
284
|
+
if (currentTask) {
|
|
285
|
+
for (const record of await nativeRunArtifactRecords(input.root, project.id)) {
|
|
286
|
+
const locator = `projects/${project.id}/${record.path}`;
|
|
287
|
+
stageInputs.push({
|
|
288
|
+
...record,
|
|
289
|
+
path: locator,
|
|
290
|
+
sourceLocator: locator,
|
|
291
|
+
purpose: "native-calculation",
|
|
292
|
+
ownerId: project.id,
|
|
293
|
+
hashBasis: "raw-file-bytes",
|
|
294
|
+
mediaType: record.path.endsWith(".json")
|
|
295
|
+
? "application/json"
|
|
296
|
+
: "application/octet-stream",
|
|
297
|
+
objectKind: null,
|
|
298
|
+
registrationRecordSha256: null,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const requestSource = currentTask?.requestProvenance.source;
|
|
303
|
+
if (requestSource) {
|
|
304
|
+
const locator = `projects/${project.id}/task/request-sources/${requestSource.objectSha256}.json`;
|
|
305
|
+
stageInputs.push({
|
|
306
|
+
...(await fileRecord(resolveContained(paths.control, locator), locator)),
|
|
307
|
+
purpose: "original-request-source",
|
|
308
|
+
ownerId: project.id,
|
|
309
|
+
sourceLocator: locator,
|
|
310
|
+
hashBasis: "raw-file-bytes",
|
|
311
|
+
mediaType: "application/json",
|
|
312
|
+
objectKind: null,
|
|
313
|
+
registrationRecordSha256: null,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
if (input.role !== "research-design")
|
|
317
|
+
stageInputs.push(...(await modelObjectInputs(input.root, project.id, design)));
|
|
318
|
+
const amendmentInputs = new Set();
|
|
319
|
+
for (const record of fulfillment.amendments) {
|
|
320
|
+
for (const locator of [
|
|
321
|
+
scientificAmendmentLocator(project.id, record.recordSha256),
|
|
322
|
+
record.amendmentAuthorization.sourceLocator,
|
|
323
|
+
record.design.objectLocator,
|
|
324
|
+
]) {
|
|
325
|
+
if (amendmentInputs.has(locator))
|
|
326
|
+
continue;
|
|
327
|
+
amendmentInputs.add(locator);
|
|
328
|
+
stageInputs.push({
|
|
329
|
+
...(await fileRecord(resolveContained(paths.control, locator), locator)),
|
|
330
|
+
purpose: "scientific-amendment",
|
|
331
|
+
ownerId: project.id,
|
|
332
|
+
sourceLocator: locator,
|
|
333
|
+
hashBasis: "raw-file-bytes",
|
|
334
|
+
mediaType: locator.endsWith(".txt") ? "text/plain" : "application/json",
|
|
335
|
+
objectKind: null,
|
|
336
|
+
registrationRecordSha256: null,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (fulfillment.records.length || fulfillment.amendments.length) {
|
|
341
|
+
for (const record of fulfillment.records) {
|
|
342
|
+
const locator = scientificFulfillmentLocator(project.id, record.recordSha256);
|
|
343
|
+
stageInputs.push({
|
|
344
|
+
...(await fileRecord(resolveContained(paths.control, locator), locator)),
|
|
345
|
+
purpose: "scientific-fulfillment",
|
|
346
|
+
ownerId: project.id,
|
|
347
|
+
sourceLocator: locator,
|
|
348
|
+
hashBasis: "raw-file-bytes",
|
|
349
|
+
mediaType: "application/json",
|
|
350
|
+
objectKind: null,
|
|
351
|
+
registrationRecordSha256: null,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
const locator = `projects/${project.id}/scientific/design/views/${fulfillment.effectiveSha256}.json`;
|
|
355
|
+
await writeImmutableJson(resolveContained(paths.control, locator), design);
|
|
356
|
+
stageInputs.push({
|
|
357
|
+
...(await fileRecord(resolveContained(paths.control, locator), locator)),
|
|
358
|
+
purpose: "effective-scientific-design",
|
|
359
|
+
ownerId: project.id,
|
|
360
|
+
sourceLocator: locator,
|
|
361
|
+
hashBasis: "raw-file-bytes",
|
|
362
|
+
mediaType: "application/json",
|
|
363
|
+
objectKind: null,
|
|
364
|
+
registrationRecordSha256: null,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
277
367
|
const assessmentSha256 = exactJsonSha256(assessment);
|
|
278
368
|
const assessmentLocator = `projects/${project.id}/scientific/assessments/${input.role}/${assessmentSha256}.json`;
|
|
279
369
|
await writeImmutableJson(join(paths.control, assessmentLocator), assessment);
|
|
@@ -289,7 +379,7 @@ export async function prepareScientificReview(input) {
|
|
|
289
379
|
};
|
|
290
380
|
await assertBoundPolicyObject(input.root, project, packetPolicy);
|
|
291
381
|
const designEvaluation = evaluateScientificDesign(design);
|
|
292
|
-
const issues = evaluateAssessment(input.role, assessment, design, project.evidenceRequirements, project.publicationPolicy, await assessmentEvidenceContext(input.root, project, input.role, stageInputs));
|
|
382
|
+
const issues = evaluateAssessment(input.role, assessment, design, project.evidenceRequirements, project.publicationPolicy, await assessmentEvidenceContext(input.root, project, input.role, stageInputs), fulfillment.deferredObjectRuleIds);
|
|
293
383
|
const futureGateObligations = scientificFutureGateObligations(design, input.role);
|
|
294
384
|
const packetCore = {
|
|
295
385
|
schemaVersion: 1,
|
|
@@ -299,12 +389,17 @@ export async function prepareScientificReview(input) {
|
|
|
299
389
|
design: {
|
|
300
390
|
sha256: project.scientificDesign.designSha256,
|
|
301
391
|
objectLocator: project.scientificDesign.objectLocator,
|
|
392
|
+
amendmentSha256: project.scientificDesign.amendmentSha256 ?? null,
|
|
393
|
+
fulfillment: {
|
|
394
|
+
headSha256: fulfillment.headSha256,
|
|
395
|
+
effectiveSha256: fulfillment.effectiveSha256,
|
|
396
|
+
},
|
|
302
397
|
},
|
|
303
398
|
policy: packetPolicy,
|
|
304
399
|
reviewer: { agent: input.reviewerAgent, sessionSha256: reviewerSessionSha256 },
|
|
305
400
|
preparedAt: new Date().toISOString(),
|
|
306
401
|
stageInputs,
|
|
307
|
-
taskContract:
|
|
402
|
+
taskContract: currentTask,
|
|
308
403
|
assessment: { sha256: assessmentSha256, objectLocator: assessmentLocator },
|
|
309
404
|
mechanicalAssessment: {
|
|
310
405
|
canPass: issues.length === 0 && assessment.recommendation === "pass",
|
|
@@ -331,7 +426,10 @@ export async function prepareScientificReview(input) {
|
|
|
331
426
|
newGenerationOnMaterialChange: true,
|
|
332
427
|
revisionReserveIncluded: true,
|
|
333
428
|
},
|
|
334
|
-
instructions:
|
|
429
|
+
instructions: [
|
|
430
|
+
...reviewInstructions(input.role),
|
|
431
|
+
"Scientific fulfillment files supply only previously declared pending object slots. Original design bytes remain immutable. Owner-authorized lifecycle/binding amendments, when present, are staged with their exact changes, new design version and supplied authorization source. Review the current effective design; supplied confirmation does not authenticate human identity or establish scientific satisfaction. Discharging an object-filing blocker does not establish calibration, justification or scientific correctness: independently assess the actual code, locks and source-bound values under every original policy rule.",
|
|
432
|
+
],
|
|
335
433
|
};
|
|
336
434
|
const packetSha256 = sha256Text(canonicalJson(packetCore));
|
|
337
435
|
const packet = { ...packetCore, packetSha256 };
|
|
@@ -503,8 +601,9 @@ export { readReview as readScientificReviewOutput };
|
|
|
503
601
|
export async function assertScientificGateForStage(root, project, stage) {
|
|
504
602
|
if (!project.scientificDesign)
|
|
505
603
|
return;
|
|
506
|
-
const design = await loadBoundScientificDesign(root, project);
|
|
507
604
|
for (const role of requiredGateRoles(stage)) {
|
|
605
|
+
const fulfillment = await loadScientificFulfillmentView(root, project, role);
|
|
606
|
+
const design = fulfillment.contract;
|
|
508
607
|
const gate = project.scientificDesign.gates[role];
|
|
509
608
|
if (gate.status !== "passed" || !gate.packetSha256 || !gate.reviewSha256) {
|
|
510
609
|
throw new CliError(`Scientific ${role} review must pass before ${stage}.`, {
|
|
@@ -516,7 +615,7 @@ export async function assertScientificGateForStage(root, project, stage) {
|
|
|
516
615
|
const packet = await loadBoundPacket(root, project, role, gate.packetSha256);
|
|
517
616
|
const review = await loadBoundReview(root, project, role, gate.reviewSha256);
|
|
518
617
|
const assessment = await loadBoundAssessment(root, project, role, packet);
|
|
519
|
-
const currentIssues = evaluateAssessment(role, assessment, design, project.evidenceRequirements, project.publicationPolicy, await assessmentEvidenceContext(root, project, role, packet.stageInputs)).map((issue) => issue.code);
|
|
618
|
+
const currentIssues = evaluateAssessment(role, assessment, design, project.evidenceRequirements, project.publicationPolicy, await assessmentEvidenceContext(root, project, role, packet.stageInputs), fulfillment.deferredObjectRuleIds).map((issue) => issue.code);
|
|
520
619
|
if (packet.assessment.sha256 !== gate.assessmentSha256 ||
|
|
521
620
|
packet.reviewer.sessionSha256 !== gate.reviewerSessionSha256 ||
|
|
522
621
|
review.packetSha256 !== packet.packetSha256 ||
|
|
@@ -550,7 +649,7 @@ export async function inspectScientificReviewStatus(root, projectId, knownProjec
|
|
|
550
649
|
gates: structuredClone(project.scientificDesign.gates),
|
|
551
650
|
};
|
|
552
651
|
}
|
|
553
|
-
function evaluateAssessment(role, assessment, design, requirements, policy, evidenceContext = null) {
|
|
652
|
+
function evaluateAssessment(role, assessment, design, requirements, policy, evidenceContext = null, deferredObjectRuleIds = []) {
|
|
554
653
|
const issues = new Map();
|
|
555
654
|
const add = (code, message, objectIds = []) => {
|
|
556
655
|
if (!issues.has(code))
|
|
@@ -566,25 +665,26 @@ function evaluateAssessment(role, assessment, design, requirements, policy, evid
|
|
|
566
665
|
add(`POLICY_${gap.replaceAll(/[^A-Za-z0-9]+/gu, "_").toUpperCase()}`, "The frozen scientific design no longer matches its approved Research Policy disposition contract.", [gap]);
|
|
567
666
|
}
|
|
568
667
|
const duePolicyRules = design.policyRuleDispositions.filter((disposition) => disposition.status === "planned" &&
|
|
668
|
+
!deferredObjectRuleIds.includes(disposition.ruleId) &&
|
|
569
669
|
disposition.dueGate !== "publication-freeze" &&
|
|
570
670
|
gateRank[disposition.dueGate] <= gateRank[role]);
|
|
571
671
|
if (duePolicyRules.length) {
|
|
572
|
-
add("POLICY_RULE_DUE_UNRESOLVED", "A planned Research Policy rule reached its
|
|
672
|
+
add("POLICY_RULE_DUE_UNRESOLVED", "A planned Research Policy rule reached its early-review gate without its declared object fulfillment or reviewed design resolution.", duePolicyRules.map((disposition) => disposition.ruleId));
|
|
573
673
|
}
|
|
574
674
|
const dueUncertaintyFreezes = design.uncertaintyParameters.filter((parameter) => parameter.stateValueStatus === "pending-source-acquisition" &&
|
|
575
675
|
gateRank[parameter.freezeBeforeGate] <= gateRank[role]);
|
|
576
676
|
if (dueUncertaintyFreezes.length) {
|
|
577
|
-
add("UNCERTAINTY_STATE_VALUES_NOT_FROZEN", "Source-derived uncertainty values reached their
|
|
677
|
+
add("UNCERTAINTY_STATE_VALUES_NOT_FROZEN", "Source-derived uncertainty values reached their freeze gate without exact states and admitted source bindings.", dueUncertaintyFreezes.map((parameter) => parameter.id));
|
|
578
678
|
}
|
|
579
679
|
const dueModelImplementations = design.identity.modelStructures.filter((model) => model.implementationStatus === "pending-source-acquisition" &&
|
|
580
680
|
gateRank[model.implementationFreezeBeforeGate] <= gateRank[role]);
|
|
581
681
|
if (dueModelImplementations.length) {
|
|
582
|
-
add("MODEL_IMPLEMENTATION_NOT_FROZEN", "A model implementation reached its
|
|
682
|
+
add("MODEL_IMPLEMENTATION_NOT_FROZEN", "A model implementation reached its freeze gate without binding the declared executable bytes.", dueModelImplementations.map((model) => model.id));
|
|
583
683
|
}
|
|
584
684
|
const dueModelEnvironmentLocks = design.identity.modelStructures.filter((model) => model.environmentLockStatus === "pending-runtime-lock" &&
|
|
585
685
|
gateRank[model.environmentLockFreezeBeforeGate] <= gateRank[role]);
|
|
586
686
|
if (dueModelEnvironmentLocks.length) {
|
|
587
|
-
add("MODEL_ENVIRONMENT_LOCK_NOT_FROZEN", "A model environment reached its
|
|
687
|
+
add("MODEL_ENVIRONMENT_LOCK_NOT_FROZEN", "A model environment reached its freeze gate without an exact runtime and dependency lock.", dueModelEnvironmentLocks.map((model) => model.id));
|
|
588
688
|
}
|
|
589
689
|
if (role === "research-design") {
|
|
590
690
|
const value = assessment;
|
|
@@ -848,30 +948,7 @@ async function stageInputRecords(root, project, role, design, assessment, canary
|
|
|
848
948
|
registrationRecordSha256: null,
|
|
849
949
|
});
|
|
850
950
|
};
|
|
851
|
-
|
|
852
|
-
if (model.implementationStatus === "executable-frozen") {
|
|
853
|
-
records.push(await promoteRegisteredModelObject({
|
|
854
|
-
root,
|
|
855
|
-
projectId: project.id,
|
|
856
|
-
objectKind: "model-implementation",
|
|
857
|
-
sourceLocator: model.implementationArtifactLocator,
|
|
858
|
-
sha256: model.implementationArtifactSha256,
|
|
859
|
-
purpose: "model-implementation",
|
|
860
|
-
ownerId: model.id,
|
|
861
|
-
}));
|
|
862
|
-
}
|
|
863
|
-
if (model.environmentLockStatus === "exact-frozen") {
|
|
864
|
-
records.push(await promoteRegisteredModelObject({
|
|
865
|
-
root,
|
|
866
|
-
projectId: project.id,
|
|
867
|
-
objectKind: "environment-lock",
|
|
868
|
-
sourceLocator: model.environmentLockLocator,
|
|
869
|
-
sha256: model.environmentLockSha256,
|
|
870
|
-
purpose: "model-environment-lock",
|
|
871
|
-
ownerId: model.id,
|
|
872
|
-
}));
|
|
873
|
-
}
|
|
874
|
-
}
|
|
951
|
+
records.push(...(await modelObjectInputs(root, project.id, design)));
|
|
875
952
|
for (const gap of design.knownGaps) {
|
|
876
953
|
for (const artifact of gap.sourceArtifacts) {
|
|
877
954
|
await promoteLegacyJson({
|
|
@@ -895,8 +972,7 @@ async function stageInputRecords(root, project, role, design, assessment, canary
|
|
|
895
972
|
});
|
|
896
973
|
}
|
|
897
974
|
const relativeOutputs = ["outputs/acquisition.json", "outputs/evidence-snapshot.json"];
|
|
898
|
-
if (
|
|
899
|
-
(await pathExists(join(projectRoot, "outputs", "content-snapshot.json")))) {
|
|
975
|
+
if (await pathExists(join(projectRoot, "outputs", "content-snapshot.json"))) {
|
|
900
976
|
relativeOutputs.push("outputs/content-snapshot.json");
|
|
901
977
|
}
|
|
902
978
|
const stageOutputs = await Promise.all(relativeOutputs.map(async (relativePath) => {
|
|
@@ -912,6 +988,62 @@ async function stageInputRecords(root, project, role, design, assessment, canary
|
|
|
912
988
|
registrationRecordSha256: null,
|
|
913
989
|
};
|
|
914
990
|
}));
|
|
991
|
+
const snapshot = await readExactJson(join(projectRoot, "outputs/evidence-snapshot.json"), "Scientific evidence snapshot", "RESEARCH_SCIENTIFIC_GATE_INVALID");
|
|
992
|
+
if (!isObject(snapshot) || !Array.isArray(snapshot.artifacts))
|
|
993
|
+
throw scientificGateError("Scientific evidence snapshot has no exact artifact set.", role);
|
|
994
|
+
if (snapshot.artifacts.length) {
|
|
995
|
+
const registered = new Map((await loadEvidenceArtifactRecords(root, project.id)).map((artifact) => [
|
|
996
|
+
artifact.artifactId,
|
|
997
|
+
artifact,
|
|
998
|
+
]));
|
|
999
|
+
for (const selected of snapshot.artifacts) {
|
|
1000
|
+
const artifact = isObject(selected) && typeof selected.artifactId === "string"
|
|
1001
|
+
? registered.get(selected.artifactId)
|
|
1002
|
+
: undefined;
|
|
1003
|
+
if (!artifact ||
|
|
1004
|
+
!isObject(selected) ||
|
|
1005
|
+
artifact.sha256 !== selected.sha256 ||
|
|
1006
|
+
artifact.bytes !== selected.bytes ||
|
|
1007
|
+
artifact.locator !== selected.locator ||
|
|
1008
|
+
artifact.mediaType !== selected.mediaType)
|
|
1009
|
+
throw scientificGateError("Scientific review source is not the exact frozen registered artifact.", role);
|
|
1010
|
+
let directory = projectRoot;
|
|
1011
|
+
for (const part of ["scientific", "evidence-objects", artifact.sha256]) {
|
|
1012
|
+
directory = join(directory, part);
|
|
1013
|
+
await mkdir(directory, { mode: 0o700 }).catch((error) => {
|
|
1014
|
+
if (error.code !== "EEXIST")
|
|
1015
|
+
throw error;
|
|
1016
|
+
});
|
|
1017
|
+
const info = await lstat(directory);
|
|
1018
|
+
if (!info.isDirectory() || info.isSymbolicLink())
|
|
1019
|
+
throw scientificGateError("Scientific evidence directories cannot redirect through links.", role);
|
|
1020
|
+
}
|
|
1021
|
+
const path = `projects/${project.id}/scientific/evidence-objects/${artifact.sha256}/blob`;
|
|
1022
|
+
const target = resolveContained(paths.control, path);
|
|
1023
|
+
if (!(await pathExists(target))) {
|
|
1024
|
+
await copyFile(resolveContained(paths.control, artifact.locator), target, constants.COPYFILE_EXCL | constants.COPYFILE_FICLONE);
|
|
1025
|
+
await chmod(target, 0o444);
|
|
1026
|
+
}
|
|
1027
|
+
const info = await lstat(target);
|
|
1028
|
+
if (!info.isFile() ||
|
|
1029
|
+
info.isSymbolicLink() ||
|
|
1030
|
+
info.size !== artifact.bytes ||
|
|
1031
|
+
(await sha256File(target)) !== artifact.sha256)
|
|
1032
|
+
throw scientificGateError("Scientific review source bytes changed while staging.", role);
|
|
1033
|
+
stageOutputs.push({
|
|
1034
|
+
path,
|
|
1035
|
+
sha256: artifact.sha256,
|
|
1036
|
+
bytes: artifact.bytes,
|
|
1037
|
+
purpose: "acquired-evidence",
|
|
1038
|
+
ownerId: artifact.artifactId,
|
|
1039
|
+
sourceLocator: artifact.locator,
|
|
1040
|
+
hashBasis: "raw-file-bytes",
|
|
1041
|
+
mediaType: artifact.mediaType,
|
|
1042
|
+
objectKind: null,
|
|
1043
|
+
registrationRecordSha256: null,
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
915
1047
|
if (role !== "evidence-construct") {
|
|
916
1048
|
if (canaryArtifactPaths.length)
|
|
917
1049
|
throw canaryArtifactError("Only evidence-construct accepts canary artifacts.");
|
|
@@ -985,6 +1117,32 @@ async function stageInputRecords(root, project, role, design, assessment, canary
|
|
|
985
1117
|
}
|
|
986
1118
|
return [...stageOutputs, ...canaryRecords];
|
|
987
1119
|
}
|
|
1120
|
+
async function modelObjectInputs(root, projectId, design) {
|
|
1121
|
+
const records = [];
|
|
1122
|
+
for (const model of design.identity.modelStructures) {
|
|
1123
|
+
if (model.implementationStatus === "executable-frozen")
|
|
1124
|
+
records.push(await promoteRegisteredModelObject({
|
|
1125
|
+
root,
|
|
1126
|
+
projectId,
|
|
1127
|
+
objectKind: "model-implementation",
|
|
1128
|
+
sourceLocator: model.implementationArtifactLocator,
|
|
1129
|
+
sha256: model.implementationArtifactSha256,
|
|
1130
|
+
purpose: "model-implementation",
|
|
1131
|
+
ownerId: model.id,
|
|
1132
|
+
}));
|
|
1133
|
+
if (model.environmentLockStatus === "exact-frozen")
|
|
1134
|
+
records.push(await promoteRegisteredModelObject({
|
|
1135
|
+
root,
|
|
1136
|
+
projectId,
|
|
1137
|
+
objectKind: "environment-lock",
|
|
1138
|
+
sourceLocator: model.environmentLockLocator,
|
|
1139
|
+
sha256: model.environmentLockSha256,
|
|
1140
|
+
purpose: "model-environment-lock",
|
|
1141
|
+
ownerId: model.id,
|
|
1142
|
+
}));
|
|
1143
|
+
}
|
|
1144
|
+
return records;
|
|
1145
|
+
}
|
|
988
1146
|
async function promoteRegisteredModelObject(input) {
|
|
989
1147
|
const resolvedObject = await resolveScientificObjectBinding({
|
|
990
1148
|
root: input.root,
|
|
@@ -1120,19 +1278,7 @@ async function assessmentEvidenceContext(root, project, role, stageInputs) {
|
|
|
1120
1278
|
};
|
|
1121
1279
|
}
|
|
1122
1280
|
async function loadBoundScientificDesign(root, project) {
|
|
1123
|
-
|
|
1124
|
-
if (!binding)
|
|
1125
|
-
throw scientificGateError("Project has no scientific design binding.");
|
|
1126
|
-
const path = join(workspacePaths(root).control, binding.objectLocator);
|
|
1127
|
-
const value = await readExactJson(path, "Scientific design", "RESEARCH_SCIENTIFIC_GATE_INVALID");
|
|
1128
|
-
const design = parseScientificDesign(value);
|
|
1129
|
-
const normalized = normalizedJson(design);
|
|
1130
|
-
if (design.projectId !== project.id ||
|
|
1131
|
-
sha256Text(normalized) !== binding.designSha256 ||
|
|
1132
|
-
(await readFile(path, "utf8")) !== normalized) {
|
|
1133
|
-
throw scientificGateError("Frozen scientific design failed its exact binding.");
|
|
1134
|
-
}
|
|
1135
|
-
return design;
|
|
1281
|
+
return (await loadScientificFulfillmentView(root, project)).contract;
|
|
1136
1282
|
}
|
|
1137
1283
|
async function loadBoundPacket(root, project, role, packetSha256) {
|
|
1138
1284
|
const path = join(workspacePaths(root).projects, project.id, "scientific", "review-packets", role, `${packetSha256}.json`);
|
|
@@ -1147,6 +1293,19 @@ async function loadBoundPacket(root, project, role, packetSha256) {
|
|
|
1147
1293
|
throw scientificGateError("Scientific review packet failed its immutable hash binding.", role);
|
|
1148
1294
|
}
|
|
1149
1295
|
await assertBoundPolicyObject(root, project, value.policy);
|
|
1296
|
+
const fulfillment = await loadScientificFulfillmentView(root, project, role);
|
|
1297
|
+
if ((value.design.amendmentSha256 ?? null) !== (project.scientificDesign?.amendmentSha256 ?? null))
|
|
1298
|
+
throw scientificGateError("Scientific review has a stale amendment generation.", role);
|
|
1299
|
+
if (value.design.fulfillment) {
|
|
1300
|
+
if (value.design.fulfillment.effectiveSha256 !== fulfillment.effectiveSha256 ||
|
|
1301
|
+
(value.design.fulfillment.headSha256 !== null &&
|
|
1302
|
+
!fulfillment.records.some((record) => record.recordSha256 === value.design.fulfillment.headSha256))) {
|
|
1303
|
+
throw scientificGateError("Scientific review has a stale or uncommitted fulfillment view.", role);
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
else if (canonicalJson(fulfillment.contract) !== canonicalJson(fulfillment.base)) {
|
|
1307
|
+
throw scientificGateError("Scientific review does not bind the fulfilled scientific objects.", role);
|
|
1308
|
+
}
|
|
1150
1309
|
if (canonicalJson(value.taskContract ?? null) !== canonicalJson(await taskContext(root, project.id))) {
|
|
1151
1310
|
throw scientificGateError("Scientific review no longer binds the current authorized task scope.", role);
|
|
1152
1311
|
}
|
|
@@ -1312,6 +1471,17 @@ function isScientificReviewPacket(value, projectId, role, finalPublicationReview
|
|
|
1312
1471
|
isObject(value.design) &&
|
|
1313
1472
|
typeof value.design.sha256 === "string" &&
|
|
1314
1473
|
typeof value.design.objectLocator === "string" &&
|
|
1474
|
+
(value.design.amendmentSha256 === undefined ||
|
|
1475
|
+
value.design.amendmentSha256 === null ||
|
|
1476
|
+
(typeof value.design.amendmentSha256 === "string" &&
|
|
1477
|
+
new RegExp(SHA256_PATTERN).test(value.design.amendmentSha256))) &&
|
|
1478
|
+
(value.design.fulfillment === undefined ||
|
|
1479
|
+
(isObject(value.design.fulfillment) &&
|
|
1480
|
+
(value.design.fulfillment.headSha256 === null ||
|
|
1481
|
+
(typeof value.design.fulfillment.headSha256 === "string" &&
|
|
1482
|
+
new RegExp(SHA256_PATTERN).test(value.design.fulfillment.headSha256))) &&
|
|
1483
|
+
typeof value.design.fulfillment.effectiveSha256 === "string" &&
|
|
1484
|
+
new RegExp(SHA256_PATTERN).test(value.design.fulfillment.effectiveSha256))) &&
|
|
1315
1485
|
isObject(value.policy) &&
|
|
1316
1486
|
typeof value.policy.resolvedPolicySha256 === "string" &&
|
|
1317
1487
|
typeof value.policy.approvalSha256 === "string" &&
|
|
@@ -1337,6 +1507,12 @@ function isScientificReviewPacket(value, projectId, role, finalPublicationReview
|
|
|
1337
1507
|
"inherited-gap",
|
|
1338
1508
|
"model-implementation",
|
|
1339
1509
|
"model-environment-lock",
|
|
1510
|
+
"scientific-fulfillment",
|
|
1511
|
+
"scientific-amendment",
|
|
1512
|
+
"effective-scientific-design",
|
|
1513
|
+
"original-request-source",
|
|
1514
|
+
"native-calculation",
|
|
1515
|
+
"acquired-evidence",
|
|
1340
1516
|
"stage-output",
|
|
1341
1517
|
].includes(String(record.purpose)) &&
|
|
1342
1518
|
typeof record.ownerId === "string" &&
|