@tiangong-ai/cli 0.0.34 → 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 (49) hide show
  1. package/AGENTS.md +18 -6
  2. package/README.md +147 -2
  3. package/dist/research/orchestration.js +651 -24
  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/input-plan.js +27 -1
  9. package/dist/research/workspace/input-plan.js.map +1 -1
  10. package/dist/research/workspace/preflight.d.ts +34 -2
  11. package/dist/research/workspace/preflight.js +145 -1
  12. package/dist/research/workspace/preflight.js.map +1 -1
  13. package/dist/research/workspace/projects.d.ts +32 -5
  14. package/dist/research/workspace/projects.js +263 -7
  15. package/dist/research/workspace/projects.js.map +1 -1
  16. package/dist/research/workspace/publication-workflow.d.ts +146 -0
  17. package/dist/research/workspace/publication-workflow.js +994 -0
  18. package/dist/research/workspace/publication-workflow.js.map +1 -0
  19. package/dist/research/workspace/publication.d.ts +110 -0
  20. package/dist/research/workspace/publication.js +246 -0
  21. package/dist/research/workspace/publication.js.map +1 -0
  22. package/dist/research/workspace/research-policy-wizard.d.ts +40 -0
  23. package/dist/research/workspace/research-policy-wizard.js +167 -0
  24. package/dist/research/workspace/research-policy-wizard.js.map +1 -0
  25. package/dist/research/workspace/research-policy.d.ts +70 -0
  26. package/dist/research/workspace/research-policy.js +886 -0
  27. package/dist/research/workspace/research-policy.js.map +1 -0
  28. package/dist/research/workspace/runtime.d.ts +17 -0
  29. package/dist/research/workspace/runtime.js +84 -2
  30. package/dist/research/workspace/runtime.js.map +1 -1
  31. package/dist/research/workspace/sanitization.js +9 -3
  32. package/dist/research/workspace/sanitization.js.map +1 -1
  33. package/dist/research/workspace/scientific-design.d.ts +336 -0
  34. package/dist/research/workspace/scientific-design.js +1845 -0
  35. package/dist/research/workspace/scientific-design.js.map +1 -0
  36. package/dist/research/workspace/scientific-review.d.ts +101 -0
  37. package/dist/research/workspace/scientific-review.js +1167 -0
  38. package/dist/research/workspace/scientific-review.js.map +1 -0
  39. package/dist/research/workspace/setup-catalog.js +2 -2
  40. package/dist/research/workspace/setup-wizard.d.ts +18 -1
  41. package/dist/research/workspace/setup-wizard.js +1 -1
  42. package/dist/research/workspace/setup-wizard.js.map +1 -1
  43. package/dist/research/workspace/setup.d.ts +1 -0
  44. package/dist/research/workspace/setup.js +64 -0
  45. package/dist/research/workspace/setup.js.map +1 -1
  46. package/dist/research/workspace/types.d.ts +58 -0
  47. package/dist/research/workspace/workspace.js +25 -7
  48. package/dist/research/workspace/workspace.js.map +1 -1
  49. package/package.json +4 -2
@@ -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";
@@ -20,8 +21,13 @@ import { recordNativeResearchActivity } from "./workspace/native-activity.js";
20
21
  import { readAndVerifyProjectInputPlan } from "./workspace/input-plan.js";
21
22
  import { addProjectInput, createProjectAddendum, initializeProject, forkProject, listProjects, loadProject, nextReadyPackage, normalizeEvidenceRequirements, refreshProject, retryProjectPackage, setProjectDisposition, } from "./workspace/projects.js";
22
23
  import { evaluateProjectPreflight } from "./workspace/preflight.js";
24
+ import { closePublication, freezePublicationManuscript, inspectPublicationStatus, preparePublicationReview, publicationAssessmentSchema, publicationReviewSchema, submitPublicationReview, } from "./workspace/publication-workflow.js";
25
+ import { approveResearchPolicy, initializeResearchPolicy, inspectResearchPolicyCatalog, inspectResearchPolicyStatus, loadApprovedResearchPolicy, } from "./workspace/research-policy.js";
26
+ import { resolveInstalledResearchPolicySource, runInteractiveResearchPolicyWizard, } from "./workspace/research-policy-wizard.js";
23
27
  import { abortNativeResearchStage, inspectNativeResearchStage, prepareNativeResearchStage, requestResearchHandoff, resolveResearchHandoff, runResearchWorkspace, submitNativeResearchStage, } from "./workspace/runtime.js";
24
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";
25
31
  import { pathExists, sha256Text, workspacePaths } from "./workspace/storage.js";
26
32
  import { doctorResearchWorkspace, initializeResearchWorkspace, requireResearchWorkspace, withWorkspaceLock, } from "./workspace/workspace.js";
27
33
  const COMMON_OPTIONS = { help: "boolean", json: "boolean" };
@@ -35,6 +41,10 @@ export async function runResearchOrchestrationCommand(subcommand, argv, io) {
35
41
  return runWorkspace(argv, io);
36
42
  if (subcommand === "capability")
37
43
  return runCapability(argv, io);
44
+ if (subcommand === "policy")
45
+ return runPolicy(argv, io);
46
+ if (subcommand === "publication")
47
+ return runPublication(argv, io);
38
48
  if (subcommand === "project")
39
49
  return runProject(argv, io);
40
50
  if (subcommand === "schema")
@@ -59,16 +69,33 @@ export function researchOrchestrationHelp() {
59
69
  tiangong-ai research capability credential set --id <logical-id> --from-env <name> [--workspace <absolute-path>] [--json]
60
70
  tiangong-ai research capability lock [--workspace <absolute-path>] [--json]
61
71
  tiangong-ai research capability verify [--workspace <absolute-path>] [--json]
62
- tiangong-ai research project init <project-id> --question <question> [--requirements <absolute-json>] [--input-plan <absolute-json>] [--confirm-budget] [--workspace <path>] [--json]
63
- tiangong-ai research project preflight --question <question> [--requirements <absolute-json>] [--input-plan <absolute-json>] [--workspace <path>] [--json]
64
- tiangong-ai research project input add <project-id> --path <absolute-file> [--role primary|reference|replication] [--workspace <path>] [--json]
72
+ tiangong-ai research policy wizard <project-id> [--workspace <path>] [--json]
73
+ tiangong-ai research policy catalog [--source-root <absolute-installed-skill-root>] [--workspace <path>] [--json]
74
+ tiangong-ai research policy init <project-id> [--source-root <absolute-installed-skill-root>] --article-type <id> --field <id> --journal-class <id> [--include-exact-journal-template] [--workspace <path>] [--json]
75
+ tiangong-ai research policy status <project-id> [--workspace <path>] [--json]
76
+ tiangong-ai research policy validate <project-id> [--workspace <path>] [--json]
77
+ tiangong-ai research policy approve <project-id> --confirm [--acknowledge-defaults] [--workspace <path>] [--json]
78
+ tiangong-ai research policy resolve <project-id> [--workspace <path>] [--json]
79
+ tiangong-ai research publication freeze <project-id> --manuscript <absolute-file> --assessment <absolute-json> --producer-agent codex|claude --producer-session <opaque-id> [--supplements <absolute-json-array>] [--workspace <path>] [--json]
80
+ tiangong-ai research publication review prepare <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --reviewer-agent codex|claude --reviewer-session <opaque-id> [--workspace <path>] [--json]
81
+ tiangong-ai research publication review submit <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --review <absolute-json> [--workspace <path>] [--json]
82
+ tiangong-ai research publication status <project-id> [--workspace <path>] [--json]
83
+ tiangong-ai research publication close <project-id> [--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]
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]
65
87
  tiangong-ai research project retry <project-id> [--package <package-id>] [--workspace <path>] [--json]
66
- tiangong-ai research project fork <source-project-id> --to <target-project-id> [--resume-through discover|acquire|analyze|synthesize] [--workspace <path>] [--json]
67
- 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]
68
90
  tiangong-ai research project archive <project-id> --reason <text> [--workspace <path>] [--json]
69
91
  tiangong-ai research project abandon <project-id> --reason <text> [--workspace <path>] [--json]
70
92
  tiangong-ai research project handoff request <project-id> --record <absolute-json> [--workspace <path>] [--json]
71
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]
72
99
  tiangong-ai research project stage prepare <project-id> --stage discover|acquire|analyze|synthesize --host-agent codex|claude [--workspace <path>] [--json]
73
100
  tiangong-ai research project stage submit <project-id> --session <id> --output <absolute-json> [--confirm-model <id>] [--workspace <path>] [--json]
74
101
  tiangong-ai research project stage abort <project-id> --session <id> [--workspace <path>] [--json]
@@ -78,37 +105,280 @@ export function researchOrchestrationHelp() {
78
105
  tiangong-ai research project evidence assessment record <project-id> --record <absolute-json> [--workspace <path>] [--json]
79
106
  tiangong-ai research project evidence download bind <project-id> --candidate <id> --record <absolute-json> [--workspace <path>] [--json]
80
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]
81
- tiangong-ai research schema show <discover|acquire|analyze|synthesize|review|doctor> [--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]
82
109
  tiangong-ai research status [--project <project-id>] [--all] [--workspace <absolute-path>] [--json]
83
110
  tiangong-ai research run [--project <project-id>] [--max-parallel <1-8>] [--max-cycles <1-100>] [--dry-run] [--progress-jsonl] [--workspace <absolute-path>] [--json]
84
111
 
85
112
  ${researchSetupHelp()}
86
113
  `;
87
114
  }
115
+ async function runPublication(argv, io) {
116
+ const [action, ...rest] = argv;
117
+ if (!action || action === "--help" || action === "-h")
118
+ return writeHelp(io);
119
+ if (action === "freeze") {
120
+ const args = parseStrictArgs(rest, {
121
+ ...WORKSPACE_OPTIONS,
122
+ manuscript: "string",
123
+ assessment: "string",
124
+ supplements: "string",
125
+ "producer-agent": "string",
126
+ "producer-session": "string",
127
+ }, "research publication freeze");
128
+ if (strictBoolean(args, "help"))
129
+ return writeHelp(io);
130
+ const projectId = onePositional(args.positionals, "research publication freeze");
131
+ const manuscriptPath = strictString(args, "manuscript");
132
+ const assessmentPath = strictString(args, "assessment");
133
+ const producerSessionId = strictString(args, "producer-session");
134
+ if (!manuscriptPath || !assessmentPath || !producerSessionId) {
135
+ throw new CliError("research publication freeze requires --manuscript, --assessment, --producer-agent, and --producer-session.", { code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED", exitCode: 2 });
136
+ }
137
+ const root = await workspaceFromArgs(args);
138
+ const supplementsPath = strictString(args, "supplements");
139
+ writeJson(io, await freezePublicationManuscript({
140
+ root,
141
+ projectId,
142
+ manuscriptPath,
143
+ assessmentPath,
144
+ supplementPaths: supplementsPath ? await readAbsolutePathArray(supplementsPath) : [],
145
+ producerAgent: publicationAgent(strictString(args, "producer-agent"), "producer"),
146
+ producerSessionId,
147
+ }), args);
148
+ return 0;
149
+ }
150
+ if (action === "review") {
151
+ const [reviewAction, ...reviewRest] = rest;
152
+ if (reviewAction === "--help" || reviewAction === "-h")
153
+ return writeHelp(io);
154
+ if (reviewAction === "prepare") {
155
+ const args = parseStrictArgs(reviewRest, {
156
+ ...WORKSPACE_OPTIONS,
157
+ role: "string",
158
+ "reviewer-agent": "string",
159
+ "reviewer-session": "string",
160
+ }, "research publication review prepare");
161
+ if (strictBoolean(args, "help"))
162
+ return writeHelp(io);
163
+ const projectId = onePositional(args.positionals, "research publication review prepare");
164
+ const reviewerSessionId = strictString(args, "reviewer-session");
165
+ if (!reviewerSessionId) {
166
+ throw new CliError("publication review prepare requires --reviewer-session.", {
167
+ code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED",
168
+ exitCode: 2,
169
+ });
170
+ }
171
+ const root = await workspaceFromArgs(args);
172
+ writeJson(io, await preparePublicationReview({
173
+ root,
174
+ projectId,
175
+ role: publicationReviewRole(strictString(args, "role")),
176
+ reviewerAgent: publicationAgent(strictString(args, "reviewer-agent"), "reviewer"),
177
+ reviewerSessionId,
178
+ }), args);
179
+ return 0;
180
+ }
181
+ if (reviewAction === "submit") {
182
+ const args = parseStrictArgs(reviewRest, { ...WORKSPACE_OPTIONS, role: "string", review: "string" }, "research publication review submit");
183
+ if (strictBoolean(args, "help"))
184
+ return writeHelp(io);
185
+ const projectId = onePositional(args.positionals, "research publication review submit");
186
+ const reviewPath = strictString(args, "review");
187
+ if (!reviewPath) {
188
+ throw new CliError("publication review submit requires --review.", {
189
+ code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED",
190
+ exitCode: 2,
191
+ });
192
+ }
193
+ const root = await workspaceFromArgs(args);
194
+ writeJson(io, await submitPublicationReview({
195
+ root,
196
+ projectId,
197
+ role: publicationReviewRole(strictString(args, "role")),
198
+ reviewPath,
199
+ }), args);
200
+ return 0;
201
+ }
202
+ throw unknownAction("research publication review", reviewAction ?? "missing");
203
+ }
204
+ if (action === "status" || action === "close") {
205
+ const args = parseStrictArgs(rest, WORKSPACE_OPTIONS, `research publication ${action}`);
206
+ if (strictBoolean(args, "help"))
207
+ return writeHelp(io);
208
+ const projectId = onePositional(args.positionals, `research publication ${action}`);
209
+ const root = await workspaceFromArgs(args);
210
+ writeJson(io, action === "status"
211
+ ? await inspectPublicationStatus(root, projectId)
212
+ : await closePublication(root, projectId), args);
213
+ return 0;
214
+ }
215
+ throw unknownAction("research publication", action);
216
+ }
217
+ async function runPolicy(argv, io) {
218
+ const [action, ...rest] = argv;
219
+ if (!action || action === "--help" || action === "-h")
220
+ return writeHelp(io);
221
+ if (action === "wizard") {
222
+ const args = parseStrictArgs(rest, WORKSPACE_OPTIONS, "research policy wizard");
223
+ if (strictBoolean(args, "help"))
224
+ return writeHelp(io);
225
+ const projectId = onePositional(args.positionals, "research policy wizard");
226
+ const root = await workspaceFromArgs(args);
227
+ return runInteractiveResearchPolicyWizard({
228
+ root,
229
+ projectId,
230
+ io,
231
+ json: strictBoolean(args, "json"),
232
+ });
233
+ }
234
+ if (action === "catalog") {
235
+ const args = parseStrictArgs(rest, { ...WORKSPACE_OPTIONS, "source-root": "string" }, "research policy catalog");
236
+ if (strictBoolean(args, "help"))
237
+ return writeHelp(io);
238
+ rejectPositionals(args.positionals, "research policy catalog");
239
+ const explicitSourceRoot = strictString(args, "source-root");
240
+ const sourceRoot = explicitSourceRoot ??
241
+ (await resolveInstalledResearchPolicySource(await workspaceFromArgs(args), io.env));
242
+ writeJson(io, await inspectResearchPolicyCatalog(sourceRoot), args);
243
+ return 0;
244
+ }
245
+ if (action === "init") {
246
+ const args = parseStrictArgs(rest, {
247
+ ...WORKSPACE_OPTIONS,
248
+ "source-root": "string",
249
+ "article-type": "string",
250
+ field: "string",
251
+ "journal-class": "string",
252
+ "include-exact-journal-template": "boolean",
253
+ }, "research policy init");
254
+ if (strictBoolean(args, "help"))
255
+ return writeHelp(io);
256
+ const projectId = onePositional(args.positionals, "research policy init");
257
+ const articleType = strictString(args, "article-type");
258
+ const field = strictString(args, "field");
259
+ const journalClass = strictString(args, "journal-class");
260
+ if (!articleType || !field || !journalClass) {
261
+ throw new CliError("research policy init requires --article-type, --field, and --journal-class.", { code: "RESEARCH_POLICY_INVALID", exitCode: 2 });
262
+ }
263
+ const root = await workspaceFromArgs(args);
264
+ const sourceRoot = strictString(args, "source-root") ??
265
+ (await resolveInstalledResearchPolicySource(root, io.env));
266
+ writeJson(io, await initializeResearchPolicy({
267
+ root,
268
+ projectId,
269
+ sourceRoot,
270
+ articleType,
271
+ field,
272
+ journalClass,
273
+ includeExactJournalTemplate: strictBoolean(args, "include-exact-journal-template"),
274
+ }), args);
275
+ return 0;
276
+ }
277
+ if (["status", "validate", "approve", "resolve"].includes(action)) {
278
+ const args = parseStrictArgs(rest, {
279
+ ...WORKSPACE_OPTIONS,
280
+ confirm: "boolean",
281
+ "acknowledge-defaults": "boolean",
282
+ }, `research policy ${action}`);
283
+ if (strictBoolean(args, "help"))
284
+ return writeHelp(io);
285
+ const projectId = onePositional(args.positionals, `research policy ${action}`);
286
+ const root = await workspaceFromArgs(args);
287
+ if (action === "approve") {
288
+ const result = await approveResearchPolicy(root, projectId, {
289
+ confirm: strictBoolean(args, "confirm"),
290
+ acknowledgeDefaults: strictBoolean(args, "acknowledge-defaults"),
291
+ });
292
+ writeJson(io, result, args);
293
+ return 0;
294
+ }
295
+ if (action === "resolve") {
296
+ writeJson(io, await loadApprovedResearchPolicy(root, projectId), args);
297
+ return 0;
298
+ }
299
+ const result = await inspectResearchPolicyStatus(root, projectId);
300
+ writeJson(io, result, args);
301
+ return action === "validate" &&
302
+ ["missing", "invalid", "changed", "stale"].includes(result.status)
303
+ ? 3
304
+ : 0;
305
+ }
306
+ throw unknownAction("research policy", action);
307
+ }
88
308
  async function runSchema(argv, io) {
89
309
  const [action, ...rest] = argv;
90
310
  if (!action || action === "--help" || action === "-h")
91
311
  return writeHelp(io);
92
312
  if (action !== "show")
93
313
  throw unknownAction("research schema", action);
94
- const args = parseStrictArgs(rest, COMMON_OPTIONS, "research schema show");
314
+ const args = parseStrictArgs(rest, { ...COMMON_OPTIONS, compatibility: "string" }, "research schema show");
95
315
  if (strictBoolean(args, "help"))
96
316
  return writeHelp(io);
97
317
  const stage = onePositional(args.positionals, "research schema show");
98
- if (stage !== "discover" &&
99
- stage !== "acquire" &&
100
- stage !== "analyze" &&
101
- stage !== "synthesize" &&
102
- stage !== "review" &&
103
- stage !== "doctor") {
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();
332
+ }
333
+ else if (stage.startsWith("publication-review-")) {
334
+ const role = publicationReviewRole(stage.slice("publication-review-".length));
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);
344
+ }
345
+ else {
104
346
  throw new CliError(`Unsupported research schema stage: ${stage}`, {
105
347
  code: "RESEARCH_SCHEMA_INVALID",
106
348
  exitCode: 2,
107
349
  });
108
350
  }
109
- writeJson(io, schemaForStage(stage), args);
351
+ writeJson(io, compatibleSchema(schema, strictString(args, "compatibility")), args);
110
352
  return 0;
111
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
+ }
112
382
  async function runContext(argv, io) {
113
383
  const [action, ...rest] = argv;
114
384
  if (!action || action === "--help" || action === "-h")
@@ -299,6 +569,41 @@ async function runProject(argv, io) {
299
569
  const [action, ...rest] = argv;
300
570
  if (!action || action === "--help" || action === "-h")
301
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
+ }
302
607
  if (action === "handoff") {
303
608
  const [handoffAction, ...handoffRest] = rest;
304
609
  if (handoffAction === "request") {
@@ -338,6 +643,71 @@ async function runProject(argv, io) {
338
643
  }
339
644
  throw unknownAction("research project handoff", handoffAction ?? "");
340
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
+ }
341
711
  if (action === "stage") {
342
712
  const [stageAction, ...stageRest] = rest;
343
713
  if (stageAction === "prepare") {
@@ -576,8 +946,12 @@ async function runProject(argv, io) {
576
946
  const args = parseStrictArgs(rest, {
577
947
  ...WORKSPACE_OPTIONS,
578
948
  question: "string",
949
+ goal: "string",
579
950
  requirements: "string",
580
951
  "input-plan": "string",
952
+ design: "string",
953
+ "design-producer-agent": "string",
954
+ "design-producer-session": "string",
581
955
  "confirm-budget": "boolean",
582
956
  }, "research project init");
583
957
  if (strictBoolean(args, "help"))
@@ -591,9 +965,29 @@ async function runProject(argv, io) {
591
965
  });
592
966
  }
593
967
  const root = await workspaceFromArgs(args);
968
+ const goal = researchGoal(strictString(args, "goal"));
594
969
  const requirementsPath = strictString(args, "requirements");
595
970
  const inputPlanPath = strictString(args, "input-plan");
596
- const project = await initializeProject(root, projectId, question, requirementsPath ? await readEvidenceRequirements(requirementsPath) : undefined, strictBoolean(args, "confirm-budget"), inputPlanPath ? await readAndVerifyProjectInputPlan(inputPlanPath) : 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);
597
991
  writeJson(io, project, args);
598
992
  return 0;
599
993
  }
@@ -601,8 +995,11 @@ async function runProject(argv, io) {
601
995
  const args = parseStrictArgs(rest, {
602
996
  ...WORKSPACE_OPTIONS,
603
997
  question: "string",
998
+ goal: "string",
999
+ "policy-project": "string",
604
1000
  requirements: "string",
605
1001
  "input-plan": "string",
1002
+ design: "string",
606
1003
  }, "research project preflight");
607
1004
  if (strictBoolean(args, "help"))
608
1005
  return writeHelp(io);
@@ -615,11 +1012,48 @@ async function runProject(argv, io) {
615
1012
  });
616
1013
  }
617
1014
  const root = await workspaceFromArgs(args);
1015
+ const goal = researchGoal(strictString(args, "goal"));
1016
+ const policyProject = strictString(args, "policy-project");
1017
+ let publicationPolicy = null;
1018
+ if (goal === "top-journal") {
1019
+ if (!policyProject) {
1020
+ throw new CliError("Top-journal preflight requires --policy-project.", {
1021
+ code: "RESEARCH_POLICY_REQUIRED",
1022
+ exitCode: 2,
1023
+ });
1024
+ }
1025
+ publicationPolicy = await loadApprovedResearchPolicy(root, policyProject);
1026
+ }
1027
+ else if (policyProject) {
1028
+ throw new CliError("--policy-project is only valid with --goal top-journal.", {
1029
+ code: "RESEARCH_POLICY_INVALID",
1030
+ exitCode: 2,
1031
+ });
1032
+ }
618
1033
  const requirementsPath = strictString(args, "requirements");
619
1034
  const requirements = requirementsPath ? await readEvidenceRequirements(requirementsPath) : null;
620
1035
  const inputPlanPath = strictString(args, "input-plan");
621
1036
  const inputPlan = inputPlanPath ? await readAndVerifyProjectInputPlan(inputPlanPath) : null;
622
- 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
+ });
623
1057
  writeJson(io, result, args);
624
1058
  return result.readyToInitialize ? 0 : 3;
625
1059
  }
@@ -629,7 +1063,13 @@ async function runProject(argv, io) {
629
1063
  return writeHelp(io);
630
1064
  if (inputAction !== "add")
631
1065
  throw unknownAction("research project input", inputAction ?? "");
632
- const args = parseStrictArgs(inputRest, { ...WORKSPACE_OPTIONS, path: "string", role: "string" }, "research project input add");
1066
+ const args = parseStrictArgs(inputRest, {
1067
+ ...WORKSPACE_OPTIONS,
1068
+ path: "string",
1069
+ role: "string",
1070
+ "trust-status": "string",
1071
+ "independently-reproduced": "boolean",
1072
+ }, "research project input add");
633
1073
  if (strictBoolean(args, "help"))
634
1074
  return writeHelp(io);
635
1075
  const projectId = onePositional(args.positionals, "research project input add");
@@ -642,7 +1082,11 @@ async function runProject(argv, io) {
642
1082
  }
643
1083
  const role = inputRole(strictString(args, "role"));
644
1084
  const root = await workspaceFromArgs(args);
645
- const input = await addProjectInput(root, projectId, inputPath, role);
1085
+ const trustStatus = projectInputTrustStatus(strictString(args, "trust-status"), role);
1086
+ const input = await addProjectInput(root, projectId, inputPath, role, {
1087
+ ...(trustStatus ? { trustStatus } : {}),
1088
+ independentlyReproduced: strictBoolean(args, "independently-reproduced"),
1089
+ });
646
1090
  writeJson(io, input, args);
647
1091
  return 0;
648
1092
  }
@@ -657,7 +1101,14 @@ async function runProject(argv, io) {
657
1101
  return 0;
658
1102
  }
659
1103
  if (action === "fork") {
660
- 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");
661
1112
  if (strictBoolean(args, "help"))
662
1113
  return writeHelp(io);
663
1114
  const sourceProjectId = onePositional(args.positionals, "research project fork");
@@ -669,12 +1120,41 @@ async function runProject(argv, io) {
669
1120
  });
670
1121
  }
671
1122
  const root = await workspaceFromArgs(args);
672
- 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);
673
1147
  writeJson(io, project, args);
674
1148
  return 0;
675
1149
  }
676
1150
  if (action === "addendum") {
677
- 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");
678
1158
  if (strictBoolean(args, "help"))
679
1159
  return writeHelp(io);
680
1160
  const sourceProjectId = onePositional(args.positionals, "research project addendum");
@@ -686,7 +1166,30 @@ async function runProject(argv, io) {
686
1166
  });
687
1167
  }
688
1168
  const root = await workspaceFromArgs(args);
689
- 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);
690
1193
  writeJson(io, project, args);
691
1194
  return 0;
692
1195
  }
@@ -744,6 +1247,10 @@ async function runStatus(argv, io) {
744
1247
  const nativeStage = await inspectNativeResearchStage(root, current);
745
1248
  const snapshot = await inspectSnapshotForStatus(root, current.id);
746
1249
  const readyPackage = nextReadyPackage(current)?.id ?? null;
1250
+ const scientificReview = await inspectScientificReviewStatus(root, current.id);
1251
+ const publication = current.publicationPolicy
1252
+ ? await inspectPublicationForStatus(root, current.id)
1253
+ : null;
747
1254
  return {
748
1255
  id: current.id,
749
1256
  question: current.question,
@@ -754,8 +1261,10 @@ async function runStatus(argv, io) {
754
1261
  evidenceState: current.evidenceState,
755
1262
  snapshot,
756
1263
  nativeStage,
1264
+ scientificReview,
1265
+ publication,
757
1266
  readyPackage,
758
- recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage),
1267
+ recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage, scientificReview, publication),
759
1268
  usage: current.usage,
760
1269
  inputs: current.inputs,
761
1270
  packages: current.packages,
@@ -812,7 +1321,18 @@ async function inspectSnapshotForStatus(root, projectId) {
812
1321
  };
813
1322
  }
814
1323
  }
815
- function projectRecommendedAction(root, project, readyPackage, nativeStage) {
1324
+ async function inspectPublicationForStatus(root, projectId) {
1325
+ try {
1326
+ return await inspectPublicationStatus(root, projectId);
1327
+ }
1328
+ catch (error) {
1329
+ return {
1330
+ generationStatus: "invalid",
1331
+ code: error instanceof CliError ? error.code : "RESEARCH_PUBLICATION_STATE_INVALID",
1332
+ };
1333
+ }
1334
+ }
1335
+ function projectRecommendedAction(root, project, readyPackage, nativeStage, scientificReview, publication) {
816
1336
  if (project.lineage.supersededBy) {
817
1337
  return `Continue with superseding project ${project.lineage.supersededBy}.`;
818
1338
  }
@@ -831,7 +1351,30 @@ function projectRecommendedAction(root, project, readyPackage, nativeStage) {
831
1351
  if (nativeStage.status === "active") {
832
1352
  return nativeStage.recommendedAction ?? "Resume the active native stage.";
833
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
+ }
834
1362
  if (project.status === "complete") {
1363
+ if (project.publicationPolicy) {
1364
+ if (publication && "code" in publication) {
1365
+ return `Publication state is invalid (${publication.code}); inspect Research Policy and frozen object bindings before continuing.`;
1366
+ }
1367
+ if (!publication || publication.generationStatus === "not-started") {
1368
+ return `Freeze the final manuscript and publication assessment, then inspect publication status: tiangong-ai research publication freeze ${project.id} --help`;
1369
+ }
1370
+ if (publication.closureSha256) {
1371
+ return `Publication closure is complete at ${publication.readinessVerdict}; inspect the immutable closure before post-closure authoring.`;
1372
+ }
1373
+ if (publication.reviewState !== "complete") {
1374
+ return `Complete fresh independent publication reviews for ${publication.missingReviewRoles.join(", ")}; inspect: tiangong-ai research publication status ${project.id} --workspace ${root}`;
1375
+ }
1376
+ return `Mechanically close the reviewed frozen manuscript: tiangong-ai research publication close ${project.id} --workspace ${root}`;
1377
+ }
835
1378
  return `Create an immutable evidence addendum only when new evidence exists: tiangong-ai research project addendum ${project.id} --to <new-project-id> --workspace ${root}`;
836
1379
  }
837
1380
  if (project.status === "blocked") {
@@ -933,6 +1476,16 @@ function researchMode(value) {
933
1476
  exitCode: 2,
934
1477
  });
935
1478
  }
1479
+ function researchGoal(value) {
1480
+ if (!value || value === "evidence-report")
1481
+ return "evidence-report";
1482
+ if (value === "top-journal")
1483
+ return value;
1484
+ throw new CliError(`Unsupported research goal: ${value}`, {
1485
+ code: "RESEARCH_GOAL_INVALID",
1486
+ exitCode: 2,
1487
+ });
1488
+ }
936
1489
  function resumeStage(value) {
937
1490
  if (!value)
938
1491
  return undefined;
@@ -1034,6 +1587,80 @@ function inputRole(value) {
1034
1587
  exitCode: 2,
1035
1588
  });
1036
1589
  }
1590
+ function projectInputTrustStatus(value, role) {
1591
+ if (!value)
1592
+ return undefined;
1593
+ if (value === "verified-owner-input" ||
1594
+ value === "unverified-owner-input" ||
1595
+ value === "reference-only" ||
1596
+ value === "replication-candidate") {
1597
+ return value;
1598
+ }
1599
+ throw new CliError(`Unsupported trust status for ${role} input: ${value}`, {
1600
+ code: "RESEARCH_INPUT_TRUST_INVALID",
1601
+ exitCode: 2,
1602
+ });
1603
+ }
1604
+ function publicationReviewRole(value) {
1605
+ if (value === "evidence" ||
1606
+ value === "methods-reproducibility" ||
1607
+ value === "domain-novelty" ||
1608
+ value === "journal-editor") {
1609
+ return value;
1610
+ }
1611
+ throw new CliError("--role must be evidence, methods-reproducibility, domain-novelty, or journal-editor.", { code: "RESEARCH_PUBLICATION_REVIEW_ROLE_INVALID", exitCode: 2 });
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
+ }
1622
+ function publicationAgent(value, label) {
1623
+ if (value === "codex" || value === "claude")
1624
+ return value;
1625
+ throw new CliError(`--${label}-agent must be codex or claude.`, {
1626
+ code: "RESEARCH_PUBLICATION_AGENT_INVALID",
1627
+ exitCode: 2,
1628
+ });
1629
+ }
1630
+ async function readAbsolutePathArray(path) {
1631
+ if (!isAbsolute(path)) {
1632
+ throw new CliError("--supplements must be an absolute JSON file path.", {
1633
+ code: "RESEARCH_PUBLICATION_FILE_INVALID",
1634
+ exitCode: 2,
1635
+ });
1636
+ }
1637
+ const selected = resolve(path);
1638
+ const info = await lstat(selected).catch(() => undefined);
1639
+ if (!info?.isFile() || info.isSymbolicLink() || info.size > 1024 * 1024) {
1640
+ throw new CliError("--supplements must be a bounded regular non-symlink JSON file.", {
1641
+ code: "RESEARCH_PUBLICATION_FILE_INVALID",
1642
+ exitCode: 2,
1643
+ });
1644
+ }
1645
+ let value;
1646
+ try {
1647
+ value = JSON.parse(await readFile(selected, "utf8"));
1648
+ }
1649
+ catch {
1650
+ throw new CliError("--supplements contains invalid JSON.", {
1651
+ code: "RESEARCH_PUBLICATION_FILE_INVALID",
1652
+ exitCode: 2,
1653
+ });
1654
+ }
1655
+ if (!Array.isArray(value) ||
1656
+ value.some((item) => typeof item !== "string" || !isAbsolute(item) || resolve(item) !== item)) {
1657
+ throw new CliError("--supplements must contain an array of absolute canonical paths.", {
1658
+ code: "RESEARCH_PUBLICATION_FILE_INVALID",
1659
+ exitCode: 2,
1660
+ });
1661
+ }
1662
+ return value;
1663
+ }
1037
1664
  function integerOption(value, fallback, label) {
1038
1665
  if (!value)
1039
1666
  return fallback;