@tiangong-ai/cli 0.0.35 → 0.0.36
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 +15 -5
- package/README.md +90 -8
- package/dist/research/orchestration.js +298 -28
- package/dist/research/orchestration.js.map +1 -1
- package/dist/research/workspace/audit-bundle.d.ts +44 -0
- package/dist/research/workspace/audit-bundle.js +357 -0
- package/dist/research/workspace/audit-bundle.js.map +1 -0
- package/dist/research/workspace/preflight.d.ts +34 -2
- package/dist/research/workspace/preflight.js +145 -1
- package/dist/research/workspace/preflight.js.map +1 -1
- package/dist/research/workspace/projects.d.ts +28 -4
- package/dist/research/workspace/projects.js +249 -12
- package/dist/research/workspace/projects.js.map +1 -1
- package/dist/research/workspace/publication-workflow.js +2 -0
- package/dist/research/workspace/publication-workflow.js.map +1 -1
- package/dist/research/workspace/runtime.js +4 -1
- package/dist/research/workspace/runtime.js.map +1 -1
- package/dist/research/workspace/sanitization.js +9 -3
- package/dist/research/workspace/sanitization.js.map +1 -1
- package/dist/research/workspace/scientific-design.d.ts +336 -0
- package/dist/research/workspace/scientific-design.js +1845 -0
- package/dist/research/workspace/scientific-design.js.map +1 -0
- package/dist/research/workspace/scientific-review.d.ts +101 -0
- package/dist/research/workspace/scientific-review.js +1167 -0
- package/dist/research/workspace/scientific-review.js.map +1 -0
- package/dist/research/workspace/setup-catalog.js +2 -2
- package/dist/research/workspace/setup.js +12 -3
- package/dist/research/workspace/setup.js.map +1 -1
- package/dist/research/workspace/types.d.ts +27 -0
- package/dist/research/workspace/workspace.js +25 -7
- package/dist/research/workspace/workspace.js.map +1 -1
- package/package.json +2 -1
|
@@ -11,6 +11,7 @@ import { configureExternalSkillProfile, doctorExternalCapabilities, EXTERNAL_SKI
|
|
|
11
11
|
import { appendJournalEvent } from "./workspace/journal.js";
|
|
12
12
|
import { fetchNativeCandidateSource } from "./workspace/broker.js";
|
|
13
13
|
import { registerEvidenceArtifact } from "./workspace/artifacts.js";
|
|
14
|
+
import { exportProjectAuditBundle, verifyProjectAuditBundle } from "./workspace/audit-bundle.js";
|
|
14
15
|
import { loadCurrentEvidenceSnapshot } from "./workspace/acquisition.js";
|
|
15
16
|
import { inspectDiscoveryProgress } from "./workspace/discovery-status.js";
|
|
16
17
|
import { recordDiscoveryAssessmentBatch } from "./workspace/discovery.js";
|
|
@@ -25,6 +26,8 @@ import { approveResearchPolicy, initializeResearchPolicy, inspectResearchPolicyC
|
|
|
25
26
|
import { resolveInstalledResearchPolicySource, runInteractiveResearchPolicyWizard, } from "./workspace/research-policy-wizard.js";
|
|
26
27
|
import { abortNativeResearchStage, inspectNativeResearchStage, prepareNativeResearchStage, requestResearchHandoff, resolveResearchHandoff, runResearchWorkspace, submitNativeResearchStage, } from "./workspace/runtime.js";
|
|
27
28
|
import { schemaForStage } from "./workspace/schemas.js";
|
|
29
|
+
import { readAndVerifyScientificDesign, scientificDesignSchema, } from "./workspace/scientific-design.js";
|
|
30
|
+
import { inspectScientificReviewStatus, prepareScientificReview, scientificGateAssessmentSchema, scientificReviewSchema, submitScientificReview, } from "./workspace/scientific-review.js";
|
|
28
31
|
import { pathExists, sha256Text, workspacePaths } from "./workspace/storage.js";
|
|
29
32
|
import { doctorResearchWorkspace, initializeResearchWorkspace, requireResearchWorkspace, withWorkspaceLock, } from "./workspace/workspace.js";
|
|
30
33
|
const COMMON_OPTIONS = { help: "boolean", json: "boolean" };
|
|
@@ -78,16 +81,21 @@ export function researchOrchestrationHelp() {
|
|
|
78
81
|
tiangong-ai research publication review submit <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --review <absolute-json> [--workspace <path>] [--json]
|
|
79
82
|
tiangong-ai research publication status <project-id> [--workspace <path>] [--json]
|
|
80
83
|
tiangong-ai research publication close <project-id> [--workspace <path>] [--json]
|
|
81
|
-
tiangong-ai research project init <project-id> --question <question> [--goal evidence-report|top-journal] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--confirm-budget] [--workspace <path>] [--json]
|
|
82
|
-
tiangong-ai research project preflight --question <question> [--goal evidence-report|top-journal] [--policy-project <project-id>] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--workspace <path>] [--json]
|
|
84
|
+
tiangong-ai research project init <project-id> --question <question> [--goal evidence-report|top-journal] [--design <absolute-json> --design-producer-agent codex|claude --design-producer-session <opaque-id>] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--confirm-budget] [--workspace <path>] [--json]
|
|
85
|
+
tiangong-ai research project preflight --question <question> [--goal evidence-report|top-journal] [--policy-project <project-id> --design <absolute-json>] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--workspace <path>] [--json]
|
|
83
86
|
tiangong-ai research project input add <project-id> --path <absolute-file> [--role primary|reference|replication] [--trust-status verified-owner-input|unverified-owner-input|reference-only|replication-candidate] [--independently-reproduced] [--workspace <path>] [--json]
|
|
84
87
|
tiangong-ai research project retry <project-id> [--package <package-id>] [--workspace <path>] [--json]
|
|
85
|
-
tiangong-ai research project fork <source-project-id> --to <target-project-id> [--resume-through discover|acquire|analyze|synthesize] [--workspace <path>] [--json]
|
|
86
|
-
tiangong-ai research project addendum <closed-project-id> --to <target-project-id> [--workspace <path>] [--json]
|
|
88
|
+
tiangong-ai research project fork <source-project-id> --to <target-project-id> [--resume-through discover|acquire|analyze|synthesize] [--design <absolute-json> --design-producer-agent codex|claude --design-producer-session <opaque-id>] [--workspace <path>] [--json]
|
|
89
|
+
tiangong-ai research project addendum <closed-project-id> --to <target-project-id> [--design <absolute-json> --design-producer-agent codex|claude --design-producer-session <opaque-id>] [--workspace <path>] [--json]
|
|
87
90
|
tiangong-ai research project archive <project-id> --reason <text> [--workspace <path>] [--json]
|
|
88
91
|
tiangong-ai research project abandon <project-id> --reason <text> [--workspace <path>] [--json]
|
|
89
92
|
tiangong-ai research project handoff request <project-id> --record <absolute-json> [--workspace <path>] [--json]
|
|
90
93
|
tiangong-ai research project handoff resolve <project-id> --note <text> [--workspace <path>] [--json]
|
|
94
|
+
tiangong-ai research project scientific review prepare <project-id> --role research-design|evidence-construct|pilot-methods --assessment <absolute-json> --reviewer-agent codex|claude --reviewer-session <opaque-id> [--workspace <path>] [--json]
|
|
95
|
+
tiangong-ai research project scientific review submit <project-id> --role research-design|evidence-construct|pilot-methods --review <absolute-json> [--workspace <path>] [--json]
|
|
96
|
+
tiangong-ai research project scientific status <project-id> [--workspace <path>] [--json]
|
|
97
|
+
tiangong-ai research project audit export <project-id> --output <absolute-new-directory> [--workspace <path>] [--json]
|
|
98
|
+
tiangong-ai research project audit verify --bundle <absolute-directory> [--json]
|
|
91
99
|
tiangong-ai research project stage prepare <project-id> --stage discover|acquire|analyze|synthesize --host-agent codex|claude [--workspace <path>] [--json]
|
|
92
100
|
tiangong-ai research project stage submit <project-id> --session <id> --output <absolute-json> [--confirm-model <id>] [--workspace <path>] [--json]
|
|
93
101
|
tiangong-ai research project stage abort <project-id> --session <id> [--workspace <path>] [--json]
|
|
@@ -97,7 +105,7 @@ export function researchOrchestrationHelp() {
|
|
|
97
105
|
tiangong-ai research project evidence assessment record <project-id> --record <absolute-json> [--workspace <path>] [--json]
|
|
98
106
|
tiangong-ai research project evidence download bind <project-id> --candidate <id> --record <absolute-json> [--workspace <path>] [--json]
|
|
99
107
|
tiangong-ai research project evidence artifact register <project-id> --candidate <id> --path <absolute-file> [--download-binding <id> | --derived-from-artifact <id>] [--media-type <type>] [--source-url <https-url>] [--license <declared-license>] [--license-url <https-url>] [--host-type <type>] [--article-version <version>] [--workspace <path>] [--json]
|
|
100
|
-
tiangong-ai research schema show <discover|acquire|analyze|synthesize|review|doctor|publication-assessment|publication-review-evidence|publication-review-methods-reproducibility|publication-review-domain-novelty|publication-review-journal-editor> [--json]
|
|
108
|
+
tiangong-ai research schema show <discover|acquire|analyze|synthesize|review|doctor|scientific-design|scientific-assessment-research-design|scientific-assessment-evidence-construct|scientific-assessment-pilot-methods|scientific-review-research-design|scientific-review-evidence-construct|scientific-review-pilot-methods|publication-assessment|publication-review-evidence|publication-review-methods-reproducibility|publication-review-domain-novelty|publication-review-journal-editor> [--compatibility claude-code] [--json]
|
|
101
109
|
tiangong-ai research status [--project <project-id>] [--all] [--workspace <absolute-path>] [--json]
|
|
102
110
|
tiangong-ai research run [--project <project-id>] [--max-parallel <1-8>] [--max-cycles <1-100>] [--dry-run] [--progress-jsonl] [--workspace <absolute-path>] [--json]
|
|
103
111
|
|
|
@@ -303,33 +311,74 @@ async function runSchema(argv, io) {
|
|
|
303
311
|
return writeHelp(io);
|
|
304
312
|
if (action !== "show")
|
|
305
313
|
throw unknownAction("research schema", action);
|
|
306
|
-
const args = parseStrictArgs(rest, COMMON_OPTIONS, "research schema show");
|
|
314
|
+
const args = parseStrictArgs(rest, { ...COMMON_OPTIONS, compatibility: "string" }, "research schema show");
|
|
307
315
|
if (strictBoolean(args, "help"))
|
|
308
316
|
return writeHelp(io);
|
|
309
317
|
const stage = onePositional(args.positionals, "research schema show");
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
318
|
+
let schema;
|
|
319
|
+
if (stage === "scientific-design") {
|
|
320
|
+
schema = scientificDesignSchema();
|
|
321
|
+
}
|
|
322
|
+
else if (stage.startsWith("scientific-assessment-")) {
|
|
323
|
+
const role = scientificReviewRole(stage.slice("scientific-assessment-".length));
|
|
324
|
+
schema = scientificGateAssessmentSchema(role);
|
|
325
|
+
}
|
|
326
|
+
else if (stage.startsWith("scientific-review-")) {
|
|
327
|
+
const role = scientificReviewRole(stage.slice("scientific-review-".length));
|
|
328
|
+
schema = scientificReviewSchema(role);
|
|
329
|
+
}
|
|
330
|
+
else if (stage === "publication-assessment") {
|
|
331
|
+
schema = publicationAssessmentSchema();
|
|
313
332
|
}
|
|
314
|
-
if (stage.startsWith("publication-review-")) {
|
|
333
|
+
else if (stage.startsWith("publication-review-")) {
|
|
315
334
|
const role = publicationReviewRole(stage.slice("publication-review-".length));
|
|
316
|
-
|
|
317
|
-
|
|
335
|
+
schema = publicationReviewSchema(role);
|
|
336
|
+
}
|
|
337
|
+
else if (stage === "discover" ||
|
|
338
|
+
stage === "acquire" ||
|
|
339
|
+
stage === "analyze" ||
|
|
340
|
+
stage === "synthesize" ||
|
|
341
|
+
stage === "review" ||
|
|
342
|
+
stage === "doctor") {
|
|
343
|
+
schema = schemaForStage(stage);
|
|
318
344
|
}
|
|
319
|
-
|
|
320
|
-
stage !== "acquire" &&
|
|
321
|
-
stage !== "analyze" &&
|
|
322
|
-
stage !== "synthesize" &&
|
|
323
|
-
stage !== "review" &&
|
|
324
|
-
stage !== "doctor") {
|
|
345
|
+
else {
|
|
325
346
|
throw new CliError(`Unsupported research schema stage: ${stage}`, {
|
|
326
347
|
code: "RESEARCH_SCHEMA_INVALID",
|
|
327
348
|
exitCode: 2,
|
|
328
349
|
});
|
|
329
350
|
}
|
|
330
|
-
writeJson(io,
|
|
351
|
+
writeJson(io, compatibleSchema(schema, strictString(args, "compatibility")), args);
|
|
331
352
|
return 0;
|
|
332
353
|
}
|
|
354
|
+
function compatibleSchema(schema, compatibility) {
|
|
355
|
+
if (!compatibility)
|
|
356
|
+
return schema;
|
|
357
|
+
if (compatibility !== "claude-code") {
|
|
358
|
+
throw new CliError(`Unsupported schema compatibility target: ${compatibility}`, {
|
|
359
|
+
code: "RESEARCH_SCHEMA_COMPATIBILITY_INVALID",
|
|
360
|
+
exitCode: 2,
|
|
361
|
+
details: { supported: ["claude-code"] },
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
const compatible = structuredClone(schema);
|
|
365
|
+
const stripMetadata = (value) => {
|
|
366
|
+
if (Array.isArray(value)) {
|
|
367
|
+
for (const item of value)
|
|
368
|
+
stripMetadata(item);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (!value || typeof value !== "object")
|
|
372
|
+
return;
|
|
373
|
+
const record = value;
|
|
374
|
+
delete record.$schema;
|
|
375
|
+
delete record.$id;
|
|
376
|
+
for (const item of Object.values(record))
|
|
377
|
+
stripMetadata(item);
|
|
378
|
+
};
|
|
379
|
+
stripMetadata(compatible);
|
|
380
|
+
return compatible;
|
|
381
|
+
}
|
|
333
382
|
async function runContext(argv, io) {
|
|
334
383
|
const [action, ...rest] = argv;
|
|
335
384
|
if (!action || action === "--help" || action === "-h")
|
|
@@ -520,6 +569,41 @@ async function runProject(argv, io) {
|
|
|
520
569
|
const [action, ...rest] = argv;
|
|
521
570
|
if (!action || action === "--help" || action === "-h")
|
|
522
571
|
return writeHelp(io);
|
|
572
|
+
if (action === "audit") {
|
|
573
|
+
const [auditAction, ...auditRest] = rest;
|
|
574
|
+
if (auditAction === "export") {
|
|
575
|
+
const args = parseStrictArgs(auditRest, { ...WORKSPACE_OPTIONS, output: "string" }, "research project audit export");
|
|
576
|
+
if (strictBoolean(args, "help"))
|
|
577
|
+
return writeHelp(io);
|
|
578
|
+
const projectId = onePositional(args.positionals, "research project audit export");
|
|
579
|
+
const destination = strictString(args, "output");
|
|
580
|
+
if (!destination) {
|
|
581
|
+
throw new CliError("Audit export requires --output.", {
|
|
582
|
+
code: "RESEARCH_AUDIT_BUNDLE_PATH_INVALID",
|
|
583
|
+
exitCode: 2,
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
const root = await workspaceFromArgs(args);
|
|
587
|
+
writeJson(io, await exportProjectAuditBundle({ root, projectId, destination }), args);
|
|
588
|
+
return 0;
|
|
589
|
+
}
|
|
590
|
+
if (auditAction === "verify") {
|
|
591
|
+
const args = parseStrictArgs(auditRest, { ...COMMON_OPTIONS, bundle: "string" }, "research project audit verify");
|
|
592
|
+
if (strictBoolean(args, "help"))
|
|
593
|
+
return writeHelp(io);
|
|
594
|
+
rejectPositionals(args.positionals, "research project audit verify");
|
|
595
|
+
const bundlePath = strictString(args, "bundle");
|
|
596
|
+
if (!bundlePath) {
|
|
597
|
+
throw new CliError("Audit verification requires --bundle.", {
|
|
598
|
+
code: "RESEARCH_AUDIT_BUNDLE_PATH_INVALID",
|
|
599
|
+
exitCode: 2,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
writeJson(io, await verifyProjectAuditBundle(bundlePath), args);
|
|
603
|
+
return 0;
|
|
604
|
+
}
|
|
605
|
+
throw unknownAction("research project audit", auditAction ?? "");
|
|
606
|
+
}
|
|
523
607
|
if (action === "handoff") {
|
|
524
608
|
const [handoffAction, ...handoffRest] = rest;
|
|
525
609
|
if (handoffAction === "request") {
|
|
@@ -559,6 +643,71 @@ async function runProject(argv, io) {
|
|
|
559
643
|
}
|
|
560
644
|
throw unknownAction("research project handoff", handoffAction ?? "");
|
|
561
645
|
}
|
|
646
|
+
if (action === "scientific") {
|
|
647
|
+
const [scientificAction, ...scientificRest] = rest;
|
|
648
|
+
if (scientificAction === "status") {
|
|
649
|
+
const args = parseStrictArgs(scientificRest, WORKSPACE_OPTIONS, "research project scientific status");
|
|
650
|
+
if (strictBoolean(args, "help"))
|
|
651
|
+
return writeHelp(io);
|
|
652
|
+
const projectId = onePositional(args.positionals, "research project scientific status");
|
|
653
|
+
const root = await workspaceFromArgs(args);
|
|
654
|
+
writeJson(io, await inspectScientificReviewStatus(root, projectId), args);
|
|
655
|
+
return 0;
|
|
656
|
+
}
|
|
657
|
+
if (scientificAction !== "review") {
|
|
658
|
+
throw unknownAction("research project scientific", scientificAction ?? "");
|
|
659
|
+
}
|
|
660
|
+
const [reviewAction, ...reviewRest] = scientificRest;
|
|
661
|
+
if (reviewAction === "prepare") {
|
|
662
|
+
const args = parseStrictArgs(reviewRest, {
|
|
663
|
+
...WORKSPACE_OPTIONS,
|
|
664
|
+
role: "string",
|
|
665
|
+
assessment: "string",
|
|
666
|
+
"reviewer-agent": "string",
|
|
667
|
+
"reviewer-session": "string",
|
|
668
|
+
}, "research project scientific review prepare");
|
|
669
|
+
if (strictBoolean(args, "help"))
|
|
670
|
+
return writeHelp(io);
|
|
671
|
+
const projectId = onePositional(args.positionals, "research project scientific review prepare");
|
|
672
|
+
const assessmentPath = strictString(args, "assessment");
|
|
673
|
+
const reviewerSessionId = strictString(args, "reviewer-session");
|
|
674
|
+
if (!assessmentPath || !reviewerSessionId) {
|
|
675
|
+
throw new CliError("Scientific review prepare requires --assessment, --reviewer-agent, and --reviewer-session.", { code: "RESEARCH_SCIENTIFIC_REVIEW_ARGUMENT_REQUIRED", exitCode: 2 });
|
|
676
|
+
}
|
|
677
|
+
const root = await workspaceFromArgs(args);
|
|
678
|
+
writeJson(io, await prepareScientificReview({
|
|
679
|
+
root,
|
|
680
|
+
projectId,
|
|
681
|
+
role: scientificReviewRole(strictString(args, "role")),
|
|
682
|
+
assessmentPath,
|
|
683
|
+
reviewerAgent: publicationAgent(strictString(args, "reviewer-agent"), "reviewer"),
|
|
684
|
+
reviewerSessionId,
|
|
685
|
+
}), args);
|
|
686
|
+
return 0;
|
|
687
|
+
}
|
|
688
|
+
if (reviewAction === "submit") {
|
|
689
|
+
const args = parseStrictArgs(reviewRest, { ...WORKSPACE_OPTIONS, role: "string", review: "string" }, "research project scientific review submit");
|
|
690
|
+
if (strictBoolean(args, "help"))
|
|
691
|
+
return writeHelp(io);
|
|
692
|
+
const projectId = onePositional(args.positionals, "research project scientific review submit");
|
|
693
|
+
const reviewPath = strictString(args, "review");
|
|
694
|
+
if (!reviewPath) {
|
|
695
|
+
throw new CliError("Scientific review submit requires --review.", {
|
|
696
|
+
code: "RESEARCH_SCIENTIFIC_REVIEW_ARGUMENT_REQUIRED",
|
|
697
|
+
exitCode: 2,
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
const root = await workspaceFromArgs(args);
|
|
701
|
+
writeJson(io, await submitScientificReview({
|
|
702
|
+
root,
|
|
703
|
+
projectId,
|
|
704
|
+
role: scientificReviewRole(strictString(args, "role")),
|
|
705
|
+
reviewPath,
|
|
706
|
+
}), args);
|
|
707
|
+
return 0;
|
|
708
|
+
}
|
|
709
|
+
throw unknownAction("research project scientific review", reviewAction ?? "");
|
|
710
|
+
}
|
|
562
711
|
if (action === "stage") {
|
|
563
712
|
const [stageAction, ...stageRest] = rest;
|
|
564
713
|
if (stageAction === "prepare") {
|
|
@@ -800,6 +949,9 @@ async function runProject(argv, io) {
|
|
|
800
949
|
goal: "string",
|
|
801
950
|
requirements: "string",
|
|
802
951
|
"input-plan": "string",
|
|
952
|
+
design: "string",
|
|
953
|
+
"design-producer-agent": "string",
|
|
954
|
+
"design-producer-session": "string",
|
|
803
955
|
"confirm-budget": "boolean",
|
|
804
956
|
}, "research project init");
|
|
805
957
|
if (strictBoolean(args, "help"))
|
|
@@ -816,7 +968,26 @@ async function runProject(argv, io) {
|
|
|
816
968
|
const goal = researchGoal(strictString(args, "goal"));
|
|
817
969
|
const requirementsPath = strictString(args, "requirements");
|
|
818
970
|
const inputPlanPath = strictString(args, "input-plan");
|
|
819
|
-
const
|
|
971
|
+
const publicationPolicy = goal === "top-journal" ? await loadApprovedResearchPolicy(root, projectId) : undefined;
|
|
972
|
+
const designPath = strictString(args, "design");
|
|
973
|
+
const designProducerAgent = strictString(args, "design-producer-agent");
|
|
974
|
+
const designProducerSession = strictString(args, "design-producer-session");
|
|
975
|
+
if (goal === "top-journal" && (!designPath || !designProducerAgent || !designProducerSession)) {
|
|
976
|
+
throw new CliError("Top-journal init requires --design, --design-producer-agent, and --design-producer-session.", { code: "RESEARCH_SCIENTIFIC_DESIGN_REQUIRED", exitCode: 2 });
|
|
977
|
+
}
|
|
978
|
+
if (goal !== "top-journal" && (designPath || designProducerAgent || designProducerSession)) {
|
|
979
|
+
throw new CliError("Scientific design options are only valid with --goal top-journal.", {
|
|
980
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_POLICY_REQUIRED",
|
|
981
|
+
exitCode: 2,
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
const project = await initializeProject(root, projectId, question, requirementsPath ? await readEvidenceRequirements(requirementsPath) : undefined, strictBoolean(args, "confirm-budget"), inputPlanPath ? await readAndVerifyProjectInputPlan(inputPlanPath) : undefined, publicationPolicy, designPath && designProducerAgent && designProducerSession
|
|
985
|
+
? {
|
|
986
|
+
design: await readAndVerifyScientificDesign(designPath, projectId),
|
|
987
|
+
producerAgent: publicationAgent(designProducerAgent, "design producer"),
|
|
988
|
+
producerSessionId: designProducerSession,
|
|
989
|
+
}
|
|
990
|
+
: undefined);
|
|
820
991
|
writeJson(io, project, args);
|
|
821
992
|
return 0;
|
|
822
993
|
}
|
|
@@ -828,6 +999,7 @@ async function runProject(argv, io) {
|
|
|
828
999
|
"policy-project": "string",
|
|
829
1000
|
requirements: "string",
|
|
830
1001
|
"input-plan": "string",
|
|
1002
|
+
design: "string",
|
|
831
1003
|
}, "research project preflight");
|
|
832
1004
|
if (strictBoolean(args, "help"))
|
|
833
1005
|
return writeHelp(io);
|
|
@@ -842,6 +1014,7 @@ async function runProject(argv, io) {
|
|
|
842
1014
|
const root = await workspaceFromArgs(args);
|
|
843
1015
|
const goal = researchGoal(strictString(args, "goal"));
|
|
844
1016
|
const policyProject = strictString(args, "policy-project");
|
|
1017
|
+
let publicationPolicy = null;
|
|
845
1018
|
if (goal === "top-journal") {
|
|
846
1019
|
if (!policyProject) {
|
|
847
1020
|
throw new CliError("Top-journal preflight requires --policy-project.", {
|
|
@@ -849,7 +1022,7 @@ async function runProject(argv, io) {
|
|
|
849
1022
|
exitCode: 2,
|
|
850
1023
|
});
|
|
851
1024
|
}
|
|
852
|
-
await loadApprovedResearchPolicy(root, policyProject);
|
|
1025
|
+
publicationPolicy = await loadApprovedResearchPolicy(root, policyProject);
|
|
853
1026
|
}
|
|
854
1027
|
else if (policyProject) {
|
|
855
1028
|
throw new CliError("--policy-project is only valid with --goal top-journal.", {
|
|
@@ -861,7 +1034,26 @@ async function runProject(argv, io) {
|
|
|
861
1034
|
const requirements = requirementsPath ? await readEvidenceRequirements(requirementsPath) : null;
|
|
862
1035
|
const inputPlanPath = strictString(args, "input-plan");
|
|
863
1036
|
const inputPlan = inputPlanPath ? await readAndVerifyProjectInputPlan(inputPlanPath) : null;
|
|
864
|
-
const
|
|
1037
|
+
const designPath = strictString(args, "design");
|
|
1038
|
+
if (goal === "top-journal" && !designPath) {
|
|
1039
|
+
throw new CliError("Top-journal preflight requires --design.", {
|
|
1040
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_REQUIRED",
|
|
1041
|
+
exitCode: 2,
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
if (goal !== "top-journal" && designPath) {
|
|
1045
|
+
throw new CliError("--design is only valid with --goal top-journal.", {
|
|
1046
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_POLICY_REQUIRED",
|
|
1047
|
+
exitCode: 2,
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
const scientificDesign = designPath
|
|
1051
|
+
? await readAndVerifyScientificDesign(designPath, policyProject)
|
|
1052
|
+
: null;
|
|
1053
|
+
const result = await evaluateProjectPreflight(root, question, requirements, inputPlan, {
|
|
1054
|
+
publicationPolicy,
|
|
1055
|
+
scientificDesign,
|
|
1056
|
+
});
|
|
865
1057
|
writeJson(io, result, args);
|
|
866
1058
|
return result.readyToInitialize ? 0 : 3;
|
|
867
1059
|
}
|
|
@@ -909,7 +1101,14 @@ async function runProject(argv, io) {
|
|
|
909
1101
|
return 0;
|
|
910
1102
|
}
|
|
911
1103
|
if (action === "fork") {
|
|
912
|
-
const args = parseStrictArgs(rest, {
|
|
1104
|
+
const args = parseStrictArgs(rest, {
|
|
1105
|
+
...WORKSPACE_OPTIONS,
|
|
1106
|
+
to: "string",
|
|
1107
|
+
"resume-through": "string",
|
|
1108
|
+
design: "string",
|
|
1109
|
+
"design-producer-agent": "string",
|
|
1110
|
+
"design-producer-session": "string",
|
|
1111
|
+
}, "research project fork");
|
|
913
1112
|
if (strictBoolean(args, "help"))
|
|
914
1113
|
return writeHelp(io);
|
|
915
1114
|
const sourceProjectId = onePositional(args.positionals, "research project fork");
|
|
@@ -921,12 +1120,41 @@ async function runProject(argv, io) {
|
|
|
921
1120
|
});
|
|
922
1121
|
}
|
|
923
1122
|
const root = await workspaceFromArgs(args);
|
|
924
|
-
const
|
|
1123
|
+
const source = await loadProject(root, sourceProjectId);
|
|
1124
|
+
const designPath = strictString(args, "design");
|
|
1125
|
+
const designProducerAgent = strictString(args, "design-producer-agent");
|
|
1126
|
+
const designProducerSession = strictString(args, "design-producer-session");
|
|
1127
|
+
if (source.publicationPolicy &&
|
|
1128
|
+
(!designPath || !designProducerAgent || !designProducerSession)) {
|
|
1129
|
+
throw new CliError("A top-journal fork requires a target-specific approved policy plus --design, --design-producer-agent, and --design-producer-session.", { code: "RESEARCH_SCIENTIFIC_DESIGN_REAPPROVAL_REQUIRED", exitCode: 3 });
|
|
1130
|
+
}
|
|
1131
|
+
if (!source.publicationPolicy && (designPath || designProducerAgent || designProducerSession)) {
|
|
1132
|
+
throw new CliError("Scientific reapproval options require a top-journal source project.", {
|
|
1133
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_REAPPROVAL_INVALID",
|
|
1134
|
+
exitCode: 2,
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
const project = await forkProject(root, sourceProjectId, targetProjectId, resumeStage(strictString(args, "resume-through")), designPath && designProducerAgent && designProducerSession
|
|
1138
|
+
? {
|
|
1139
|
+
publicationPolicy: await loadApprovedResearchPolicy(root, targetProjectId),
|
|
1140
|
+
scientificDesign: {
|
|
1141
|
+
design: await readAndVerifyScientificDesign(designPath, targetProjectId),
|
|
1142
|
+
producerAgent: publicationAgent(designProducerAgent, "design producer"),
|
|
1143
|
+
producerSessionId: designProducerSession,
|
|
1144
|
+
},
|
|
1145
|
+
}
|
|
1146
|
+
: undefined);
|
|
925
1147
|
writeJson(io, project, args);
|
|
926
1148
|
return 0;
|
|
927
1149
|
}
|
|
928
1150
|
if (action === "addendum") {
|
|
929
|
-
const args = parseStrictArgs(rest, {
|
|
1151
|
+
const args = parseStrictArgs(rest, {
|
|
1152
|
+
...WORKSPACE_OPTIONS,
|
|
1153
|
+
to: "string",
|
|
1154
|
+
design: "string",
|
|
1155
|
+
"design-producer-agent": "string",
|
|
1156
|
+
"design-producer-session": "string",
|
|
1157
|
+
}, "research project addendum");
|
|
930
1158
|
if (strictBoolean(args, "help"))
|
|
931
1159
|
return writeHelp(io);
|
|
932
1160
|
const sourceProjectId = onePositional(args.positionals, "research project addendum");
|
|
@@ -938,7 +1166,30 @@ async function runProject(argv, io) {
|
|
|
938
1166
|
});
|
|
939
1167
|
}
|
|
940
1168
|
const root = await workspaceFromArgs(args);
|
|
941
|
-
const
|
|
1169
|
+
const source = await loadProject(root, sourceProjectId);
|
|
1170
|
+
const designPath = strictString(args, "design");
|
|
1171
|
+
const designProducerAgent = strictString(args, "design-producer-agent");
|
|
1172
|
+
const designProducerSession = strictString(args, "design-producer-session");
|
|
1173
|
+
if (source.publicationPolicy &&
|
|
1174
|
+
(!designPath || !designProducerAgent || !designProducerSession)) {
|
|
1175
|
+
throw new CliError("A top-journal addendum requires a target-specific approved policy plus --design, --design-producer-agent, and --design-producer-session.", { code: "RESEARCH_SCIENTIFIC_DESIGN_REAPPROVAL_REQUIRED", exitCode: 3 });
|
|
1176
|
+
}
|
|
1177
|
+
if (!source.publicationPolicy && (designPath || designProducerAgent || designProducerSession)) {
|
|
1178
|
+
throw new CliError("Scientific reapproval options require a top-journal source project.", {
|
|
1179
|
+
code: "RESEARCH_SCIENTIFIC_DESIGN_REAPPROVAL_INVALID",
|
|
1180
|
+
exitCode: 2,
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
const project = await createProjectAddendum(root, sourceProjectId, targetProjectId, designPath && designProducerAgent && designProducerSession
|
|
1184
|
+
? {
|
|
1185
|
+
publicationPolicy: await loadApprovedResearchPolicy(root, targetProjectId),
|
|
1186
|
+
scientificDesign: {
|
|
1187
|
+
design: await readAndVerifyScientificDesign(designPath, targetProjectId),
|
|
1188
|
+
producerAgent: publicationAgent(designProducerAgent, "design producer"),
|
|
1189
|
+
producerSessionId: designProducerSession,
|
|
1190
|
+
},
|
|
1191
|
+
}
|
|
1192
|
+
: undefined);
|
|
942
1193
|
writeJson(io, project, args);
|
|
943
1194
|
return 0;
|
|
944
1195
|
}
|
|
@@ -996,6 +1247,7 @@ async function runStatus(argv, io) {
|
|
|
996
1247
|
const nativeStage = await inspectNativeResearchStage(root, current);
|
|
997
1248
|
const snapshot = await inspectSnapshotForStatus(root, current.id);
|
|
998
1249
|
const readyPackage = nextReadyPackage(current)?.id ?? null;
|
|
1250
|
+
const scientificReview = await inspectScientificReviewStatus(root, current.id);
|
|
999
1251
|
const publication = current.publicationPolicy
|
|
1000
1252
|
? await inspectPublicationForStatus(root, current.id)
|
|
1001
1253
|
: null;
|
|
@@ -1009,9 +1261,10 @@ async function runStatus(argv, io) {
|
|
|
1009
1261
|
evidenceState: current.evidenceState,
|
|
1010
1262
|
snapshot,
|
|
1011
1263
|
nativeStage,
|
|
1264
|
+
scientificReview,
|
|
1012
1265
|
publication,
|
|
1013
1266
|
readyPackage,
|
|
1014
|
-
recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage, publication),
|
|
1267
|
+
recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage, scientificReview, publication),
|
|
1015
1268
|
usage: current.usage,
|
|
1016
1269
|
inputs: current.inputs,
|
|
1017
1270
|
packages: current.packages,
|
|
@@ -1079,7 +1332,7 @@ async function inspectPublicationForStatus(root, projectId) {
|
|
|
1079
1332
|
};
|
|
1080
1333
|
}
|
|
1081
1334
|
}
|
|
1082
|
-
function projectRecommendedAction(root, project, readyPackage, nativeStage, publication) {
|
|
1335
|
+
function projectRecommendedAction(root, project, readyPackage, nativeStage, scientificReview, publication) {
|
|
1083
1336
|
if (project.lineage.supersededBy) {
|
|
1084
1337
|
return `Continue with superseding project ${project.lineage.supersededBy}.`;
|
|
1085
1338
|
}
|
|
@@ -1098,6 +1351,14 @@ function projectRecommendedAction(root, project, readyPackage, nativeStage, publ
|
|
|
1098
1351
|
if (nativeStage.status === "active") {
|
|
1099
1352
|
return nativeStage.recommendedAction ?? "Resume the active native stage.";
|
|
1100
1353
|
}
|
|
1354
|
+
if (scientificReview.nextGate) {
|
|
1355
|
+
const gate = scientificReview.nextGate;
|
|
1356
|
+
if (gate.status === "stopped") {
|
|
1357
|
+
return `Scientific ${gate.role} review stopped the project; inspect the frozen review and request user or external action instead of continuing.`;
|
|
1358
|
+
}
|
|
1359
|
+
const schema = `scientific-assessment-${gate.role}`;
|
|
1360
|
+
return `Use the native producer App to create a bounded ${gate.role} assessment from schema ${schema}, then prepare an independent review: tiangong-ai research project scientific review prepare ${project.id} --role ${gate.role} --assessment <absolute-json> --reviewer-agent <codex|claude> --reviewer-session <fresh-opaque-id> --workspace ${root}`;
|
|
1361
|
+
}
|
|
1101
1362
|
if (project.status === "complete") {
|
|
1102
1363
|
if (project.publicationPolicy) {
|
|
1103
1364
|
if (publication && "code" in publication) {
|
|
@@ -1349,6 +1610,15 @@ function publicationReviewRole(value) {
|
|
|
1349
1610
|
}
|
|
1350
1611
|
throw new CliError("--role must be evidence, methods-reproducibility, domain-novelty, or journal-editor.", { code: "RESEARCH_PUBLICATION_REVIEW_ROLE_INVALID", exitCode: 2 });
|
|
1351
1612
|
}
|
|
1613
|
+
function scientificReviewRole(value) {
|
|
1614
|
+
if (value === "research-design" || value === "evidence-construct" || value === "pilot-methods") {
|
|
1615
|
+
return value;
|
|
1616
|
+
}
|
|
1617
|
+
throw new CliError("--role must be research-design, evidence-construct, or pilot-methods.", {
|
|
1618
|
+
code: "RESEARCH_SCIENTIFIC_REVIEW_ROLE_INVALID",
|
|
1619
|
+
exitCode: 2,
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1352
1622
|
function publicationAgent(value, label) {
|
|
1353
1623
|
if (value === "codex" || value === "claude")
|
|
1354
1624
|
return value;
|