@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.
Files changed (32) hide show
  1. package/AGENTS.md +15 -5
  2. package/README.md +90 -8
  3. package/dist/research/orchestration.js +298 -28
  4. package/dist/research/orchestration.js.map +1 -1
  5. package/dist/research/workspace/audit-bundle.d.ts +44 -0
  6. package/dist/research/workspace/audit-bundle.js +357 -0
  7. package/dist/research/workspace/audit-bundle.js.map +1 -0
  8. package/dist/research/workspace/preflight.d.ts +34 -2
  9. package/dist/research/workspace/preflight.js +145 -1
  10. package/dist/research/workspace/preflight.js.map +1 -1
  11. package/dist/research/workspace/projects.d.ts +28 -4
  12. package/dist/research/workspace/projects.js +249 -12
  13. package/dist/research/workspace/projects.js.map +1 -1
  14. package/dist/research/workspace/publication-workflow.js +2 -0
  15. package/dist/research/workspace/publication-workflow.js.map +1 -1
  16. package/dist/research/workspace/runtime.js +4 -1
  17. package/dist/research/workspace/runtime.js.map +1 -1
  18. package/dist/research/workspace/sanitization.js +9 -3
  19. package/dist/research/workspace/sanitization.js.map +1 -1
  20. package/dist/research/workspace/scientific-design.d.ts +336 -0
  21. package/dist/research/workspace/scientific-design.js +1845 -0
  22. package/dist/research/workspace/scientific-design.js.map +1 -0
  23. package/dist/research/workspace/scientific-review.d.ts +101 -0
  24. package/dist/research/workspace/scientific-review.js +1167 -0
  25. package/dist/research/workspace/scientific-review.js.map +1 -0
  26. package/dist/research/workspace/setup-catalog.js +2 -2
  27. package/dist/research/workspace/setup.js +12 -3
  28. package/dist/research/workspace/setup.js.map +1 -1
  29. package/dist/research/workspace/types.d.ts +27 -0
  30. package/dist/research/workspace/workspace.js +25 -7
  31. package/dist/research/workspace/workspace.js.map +1 -1
  32. 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
- if (stage === "publication-assessment") {
311
- writeJson(io, publicationAssessmentSchema(), args);
312
- return 0;
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
- writeJson(io, publicationReviewSchema(role), args);
317
- return 0;
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
- if (stage !== "discover" &&
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, schemaForStage(stage), args);
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 project = await initializeProject(root, projectId, question, requirementsPath ? await readEvidenceRequirements(requirementsPath) : undefined, strictBoolean(args, "confirm-budget"), inputPlanPath ? await readAndVerifyProjectInputPlan(inputPlanPath) : undefined, goal === "top-journal" ? await loadApprovedResearchPolicy(root, projectId) : undefined);
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 result = await evaluateProjectPreflight(root, question, requirements, inputPlan);
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, { ...WORKSPACE_OPTIONS, to: "string", "resume-through": "string" }, "research project fork");
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 project = await forkProject(root, sourceProjectId, targetProjectId, resumeStage(strictString(args, "resume-through")));
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, { ...WORKSPACE_OPTIONS, to: "string" }, "research project addendum");
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 project = await createProjectAddendum(root, sourceProjectId, targetProjectId);
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;