@tiangong-ai/cli 0.0.42 → 0.0.44

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 (57) hide show
  1. package/AGENTS.md +3 -2
  2. package/README.md +87 -24
  3. package/dist/research/orchestration.js +318 -14
  4. package/dist/research/orchestration.js.map +1 -1
  5. package/dist/research/setup-command.js +13 -1
  6. package/dist/research/setup-command.js.map +1 -1
  7. package/dist/research/workspace/acquisition.d.ts +7 -0
  8. package/dist/research/workspace/acquisition.js +37 -10
  9. package/dist/research/workspace/acquisition.js.map +1 -1
  10. package/dist/research/workspace/audit-bundle.d.ts +18 -0
  11. package/dist/research/workspace/audit-bundle.js +100 -3
  12. package/dist/research/workspace/audit-bundle.js.map +1 -1
  13. package/dist/research/workspace/content-evidence.d.ts +98 -0
  14. package/dist/research/workspace/content-evidence.js +715 -0
  15. package/dist/research/workspace/content-evidence.js.map +1 -0
  16. package/dist/research/workspace/evidence-ledger.d.ts +1 -1
  17. package/dist/research/workspace/evidence-ledger.js +2 -0
  18. package/dist/research/workspace/evidence-ledger.js.map +1 -1
  19. package/dist/research/workspace/executor.d.ts +9 -0
  20. package/dist/research/workspace/executor.js +161 -4
  21. package/dist/research/workspace/executor.js.map +1 -1
  22. package/dist/research/workspace/inference.d.ts +69 -0
  23. package/dist/research/workspace/inference.js +360 -0
  24. package/dist/research/workspace/inference.js.map +1 -0
  25. package/dist/research/workspace/native-activity.js +14 -5
  26. package/dist/research/workspace/native-activity.js.map +1 -1
  27. package/dist/research/workspace/preflight.d.ts +2 -0
  28. package/dist/research/workspace/preflight.js +6 -2
  29. package/dist/research/workspace/preflight.js.map +1 -1
  30. package/dist/research/workspace/projects.js +17 -1
  31. package/dist/research/workspace/projects.js.map +1 -1
  32. package/dist/research/workspace/publication-workflow.d.ts +26 -0
  33. package/dist/research/workspace/publication-workflow.js +314 -3
  34. package/dist/research/workspace/publication-workflow.js.map +1 -1
  35. package/dist/research/workspace/review-executor.d.ts +50 -0
  36. package/dist/research/workspace/review-executor.js +854 -0
  37. package/dist/research/workspace/review-executor.js.map +1 -0
  38. package/dist/research/workspace/runtime.js +194 -45
  39. package/dist/research/workspace/runtime.js.map +1 -1
  40. package/dist/research/workspace/schemas.js +83 -4
  41. package/dist/research/workspace/schemas.js.map +1 -1
  42. package/dist/research/workspace/scientific-review.js +57 -0
  43. package/dist/research/workspace/scientific-review.js.map +1 -1
  44. package/dist/research/workspace/setup-catalog.js +22 -2
  45. package/dist/research/workspace/setup-catalog.js.map +1 -1
  46. package/dist/research/workspace/setup-declarative.d.ts +4 -0
  47. package/dist/research/workspace/setup-declarative.js +16 -1
  48. package/dist/research/workspace/setup-declarative.js.map +1 -1
  49. package/dist/research/workspace/setup-wizard.js +46 -3
  50. package/dist/research/workspace/setup-wizard.js.map +1 -1
  51. package/dist/research/workspace/setup.d.ts +3 -1
  52. package/dist/research/workspace/setup.js +77 -19
  53. package/dist/research/workspace/setup.js.map +1 -1
  54. package/dist/research/workspace/types.d.ts +43 -1
  55. package/dist/research/workspace/workspace.js +62 -5
  56. package/dist/research/workspace/workspace.js.map +1 -1
  57. package/package.json +1 -1
@@ -6,6 +6,7 @@ import { parseStrictArgs, strictBoolean, strictString } from "../strict-args.js"
6
6
  import { researchSetupHelp, runResearchSetupCommand } from "./setup-command.js";
7
7
  import { loadCapabilityDeclarations, lockCapabilities, verifyCapabilities, } from "./workspace/capabilities.js";
8
8
  import { inspectResearchContext } from "./workspace/context.js";
9
+ import { packageVersion } from "./workspace/constants.js";
9
10
  import { setCapabilityCredentialFromEnvironment } from "./workspace/credentials.js";
10
11
  import { configureExternalSkillProfile, doctorExternalCapabilities, EXTERNAL_SKILL_CONTEXT_PROFILE, EXTERNAL_SKILL_MEDIA_PROFILE, EXTERNAL_SKILL_PROFILE, importExternalCapability, inspectExternalSkillCatalog, } from "./workspace/external-skills.js";
11
12
  import { appendJournalEvent } from "./workspace/journal.js";
@@ -13,13 +14,16 @@ import { fetchNativeCandidateSource } from "./workspace/broker.js";
13
14
  import { registerEvidenceArtifact } from "./workspace/artifacts.js";
14
15
  import { exportProjectAuditBundle, verifyProjectAuditBundle } from "./workspace/audit-bundle.js";
15
16
  import { loadCurrentEvidenceSnapshot } from "./workspace/acquisition.js";
17
+ import { freezeEvidenceContentSnapshot, loadCurrentEvidenceContentSnapshot, recordArtifactDecomposition, registerEvidenceAtom, } from "./workspace/content-evidence.js";
16
18
  import { inspectDiscoveryProgress } from "./workspace/discovery-status.js";
17
19
  import { inspectEvidenceAccessStatus } from "./workspace/evidence-exhaustion.js";
18
20
  import { recordDiscoveryAssessmentBatch } from "./workspace/discovery.js";
19
21
  import { bindEvidenceDownload } from "./workspace/downloads.js";
20
22
  import { registerNativeDiscoveryCandidate } from "./workspace/evidence-ledger.js";
21
23
  import { recordNativeResearchActivity } from "./workspace/native-activity.js";
24
+ import { inspectReviewerBridgeStatus, startReviewerBridgeSidecar, } from "./workspace/review-executor.js";
22
25
  import { readAndVerifyProjectInputPlan } from "./workspace/input-plan.js";
26
+ import { loadCurrentClaimEvidenceGraph, loadCurrentInferenceSnapshot, } from "./workspace/inference.js";
23
27
  import { addProjectInput, createProjectAddendum, initializeProject, forkProject, listProjects, loadProject, nextReadyPackage, normalizeEvidenceRequirements, refreshProject, retryProjectPackage, setProjectDisposition, } from "./workspace/projects.js";
24
28
  import { evaluateProjectPreflight } from "./workspace/preflight.js";
25
29
  import { closePublication, freezePublicationManuscript, inspectPublicationStatus, preparePublicationReview, publicationAssessmentSchema, publicationReviewSchema, submitPublicationReview, } from "./workspace/publication-workflow.js";
@@ -29,8 +33,8 @@ import { abortNativeResearchStage, inspectNativeResearchStage, prepareNativeRese
29
33
  import { schemaForStage } from "./workspace/schemas.js";
30
34
  import { readAndVerifyScientificDesign, scientificDesignSchema, } from "./workspace/scientific-design.js";
31
35
  import { inspectScientificReviewStatus, prepareScientificReview, scientificGateAssessmentSchema, scientificReviewSchema, submitScientificReview, } from "./workspace/scientific-review.js";
32
- import { pathExists, sha256Text, workspacePaths } from "./workspace/storage.js";
33
- import { doctorResearchWorkspace, initializeResearchWorkspace, requireResearchWorkspace, withWorkspaceLock, } from "./workspace/workspace.js";
36
+ import { isObject, pathExists, sha256Text, workspacePaths } from "./workspace/storage.js";
37
+ import { doctorResearchWorkspace, initializeResearchWorkspace, loadWorkspaceConfig, requireResearchWorkspace, withWorkspaceLock, } from "./workspace/workspace.js";
34
38
  const COMMON_OPTIONS = { help: "boolean", json: "boolean" };
35
39
  const WORKSPACE_OPTIONS = { ...COMMON_OPTIONS, workspace: "string" };
36
40
  export async function runResearchOrchestrationCommand(subcommand, argv, io) {
@@ -40,6 +44,8 @@ export async function runResearchOrchestrationCommand(subcommand, argv, io) {
40
44
  return runResearchSetupCommand(argv, io);
41
45
  if (subcommand === "workspace")
42
46
  return runWorkspace(argv, io);
47
+ if (subcommand === "reviewer")
48
+ return runReviewer(argv, io);
43
49
  if (subcommand === "capability")
44
50
  return runCapability(argv, io);
45
51
  if (subcommand === "policy")
@@ -63,6 +69,9 @@ export function researchOrchestrationHelp() {
63
69
  tiangong-ai research context inspect [--path <absolute-path>] [--json]
64
70
  tiangong-ai research workspace init <absolute-path> [--name <name>] [--mode smoke-test|production-research] [--json]
65
71
  tiangong-ai research workspace doctor [--workspace <absolute-path>] [--agent-smoke] [--capability-smoke] [--json]
72
+ tiangong-ai research reviewer serve --state-dir <absolute-private-directory> [--workspace <absolute-path>] [--json]
73
+ tiangong-ai research reviewer status [--workspace <absolute-path>] [--json]
74
+ tiangong-ai research reviewer doctor --confirm-agent-smoke-cost [--workspace <absolute-path>] [--json]
66
75
  tiangong-ai research capability catalog [--path <absolute-path>] [--workspace <absolute-path>] [--skill-root <absolute-path>] [--json]
67
76
  tiangong-ai research capability configure [--profile ${EXTERNAL_SKILL_PROFILE}|${EXTERNAL_SKILL_CONTEXT_PROFILE}|${EXTERNAL_SKILL_MEDIA_PROFILE}] [--skill-root <absolute-path>] [--workspace <absolute-path>] [--json]
68
77
  tiangong-ai research capability import --definition <absolute-json> [--workspace <absolute-path>] [--json]
@@ -77,7 +86,7 @@ export function researchOrchestrationHelp() {
77
86
  tiangong-ai research policy validate <project-id> [--workspace <path>] [--json]
78
87
  tiangong-ai research policy approve <project-id> --confirm [--acknowledge-defaults] [--workspace <path>] [--json]
79
88
  tiangong-ai research policy resolve <project-id> [--workspace <path>] [--json]
80
- 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]
89
+ tiangong-ai research publication freeze <project-id> --manuscript <absolute-file> --assessment <absolute-json> --submission <absolute-json> --producer-agent codex|claude|workbuddy|codebuddy --producer-session <opaque-id> [--supplements <absolute-json-array>] [--workspace <path>] [--json]
81
90
  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]
82
91
  tiangong-ai research publication review submit <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --review <absolute-json> [--workspace <path>] [--json]
83
92
  tiangong-ai research publication status <project-id> [--workspace <path>] [--json]
@@ -98,7 +107,7 @@ export function researchOrchestrationHelp() {
98
107
  tiangong-ai research project scientific status <project-id> [--workspace <path>] [--json]
99
108
  tiangong-ai research project audit export <project-id> --output <absolute-new-directory> [--workspace <path>] [--json]
100
109
  tiangong-ai research project audit verify --bundle <absolute-directory> [--json]
101
- tiangong-ai research project stage prepare <project-id> --stage discover|acquire|analyze|synthesize --host-agent codex|claude [--workspace <path>] [--json]
110
+ tiangong-ai research project stage prepare <project-id> --stage discover|acquire|analyze|synthesize --host-agent codex|claude|workbuddy|codebuddy [--workspace <path>] [--json]
102
111
  tiangong-ai research project stage submit <project-id> --session <id> --output <absolute-json> [--confirm-model <id>] [--workspace <path>] [--json]
103
112
  tiangong-ai research project stage abort <project-id> --session <id> [--workspace <path>] [--json]
104
113
  tiangong-ai research project evidence fetch <project-id> --request <absolute-json> [--workspace <path>] [--json]
@@ -107,6 +116,10 @@ export function researchOrchestrationHelp() {
107
116
  tiangong-ai research project evidence assessment record <project-id> --record <absolute-json> [--workspace <path>] [--json]
108
117
  tiangong-ai research project evidence download bind <project-id> --candidate <id> --record <absolute-json> [--workspace <path>] [--json]
109
118
  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]
119
+ tiangong-ai research project evidence decomposition record <project-id> --record <absolute-json> [--workspace <path>] [--json]
120
+ tiangong-ai research project evidence atom register <project-id> --record <absolute-json> [--workspace <path>] [--json]
121
+ tiangong-ai research project evidence content freeze <project-id> [--workspace <path>] [--json]
122
+ tiangong-ai research project evidence content status <project-id> [--workspace <path>] [--json]
110
123
  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]
111
124
  tiangong-ai research status [--project <project-id>] [--all] [--workspace <absolute-path>] [--json]
112
125
  tiangong-ai research run [--project <project-id>] [--max-parallel <1-8>] [--max-cycles <1-100>] [--dry-run] [--progress-jsonl] [--workspace <absolute-path>] [--json]
@@ -114,6 +127,92 @@ export function researchOrchestrationHelp() {
114
127
  ${researchSetupHelp()}
115
128
  `;
116
129
  }
130
+ async function runReviewer(argv, io) {
131
+ const [action, ...rest] = argv;
132
+ if (!action || action === "--help" || action === "-h")
133
+ return writeHelp(io);
134
+ if (action === "status") {
135
+ const args = parseStrictArgs(rest, WORKSPACE_OPTIONS, "research reviewer status");
136
+ if (strictBoolean(args, "help"))
137
+ return writeHelp(io);
138
+ if (args.positionals.length)
139
+ throw unknownAction("research reviewer status", args.positionals[0]);
140
+ const root = await workspaceFromArgs(args);
141
+ writeJson(io, await inspectReviewerBridgeStatus(root), args);
142
+ return 0;
143
+ }
144
+ if (action === "doctor") {
145
+ const args = parseStrictArgs(rest, { ...WORKSPACE_OPTIONS, "confirm-agent-smoke-cost": "boolean" }, "research reviewer doctor");
146
+ if (strictBoolean(args, "help"))
147
+ return writeHelp(io);
148
+ if (args.positionals.length)
149
+ throw unknownAction("research reviewer doctor", args.positionals[0]);
150
+ if (!strictBoolean(args, "confirm-agent-smoke-cost")) {
151
+ throw new CliError("Reviewer doctor requires explicit model-cost confirmation.", {
152
+ code: "RESEARCH_REVIEW_BRIDGE_CONFIRMATION_REQUIRED",
153
+ exitCode: 3,
154
+ details: {
155
+ minimumAction: "Rerun with --confirm-agent-smoke-cost after reviewing reviewer provider quota and cost.",
156
+ },
157
+ });
158
+ }
159
+ const root = await workspaceFromArgs(args);
160
+ const result = await doctorResearchWorkspace(root, {
161
+ agentSmoke: true,
162
+ environment: io.env,
163
+ });
164
+ writeJson(io, result, args);
165
+ return result.status === "ready" ? 0 : 3;
166
+ }
167
+ if (action === "serve") {
168
+ const args = parseStrictArgs(rest, { ...WORKSPACE_OPTIONS, "state-dir": "string" }, "research reviewer serve");
169
+ if (strictBoolean(args, "help"))
170
+ return writeHelp(io);
171
+ if (args.positionals.length)
172
+ throw unknownAction("research reviewer serve", args.positionals[0]);
173
+ const stateDirectory = strictString(args, "state-dir");
174
+ if (!stateDirectory ||
175
+ !isAbsolute(stateDirectory) ||
176
+ resolve(stateDirectory) !== stateDirectory) {
177
+ throw new CliError("Reviewer sidecar --state-dir must be an explicit absolute directory.", {
178
+ code: "RESEARCH_REVIEW_BRIDGE_STATE_INVALID",
179
+ exitCode: 2,
180
+ });
181
+ }
182
+ const root = await workspaceFromArgs(args);
183
+ const sidecar = await startReviewerBridgeSidecar({
184
+ root,
185
+ stateDirectory,
186
+ environment: io.env,
187
+ });
188
+ writeJson(io, {
189
+ status: "ready",
190
+ workspaceId: sidecar.workspaceId,
191
+ packageVersion: packageVersion(),
192
+ keyFingerprint: sidecar.keyFingerprint,
193
+ supportedActions: ["execute", "fingerprint", "status"],
194
+ }, args);
195
+ try {
196
+ await waitForReviewerSidecarTermination();
197
+ }
198
+ finally {
199
+ await sidecar.close();
200
+ }
201
+ return 0;
202
+ }
203
+ throw unknownAction("research reviewer", action);
204
+ }
205
+ async function waitForReviewerSidecarTermination() {
206
+ await new Promise((resolvePromise) => {
207
+ const stop = () => {
208
+ process.off("SIGINT", stop);
209
+ process.off("SIGTERM", stop);
210
+ resolvePromise();
211
+ };
212
+ process.once("SIGINT", stop);
213
+ process.once("SIGTERM", stop);
214
+ });
215
+ }
117
216
  async function runPublication(argv, io) {
118
217
  const [action, ...rest] = argv;
119
218
  if (!action || action === "--help" || action === "-h")
@@ -124,6 +223,7 @@ async function runPublication(argv, io) {
124
223
  manuscript: "string",
125
224
  assessment: "string",
126
225
  supplements: "string",
226
+ submission: "string",
127
227
  "producer-agent": "string",
128
228
  "producer-session": "string",
129
229
  }, "research publication freeze");
@@ -133,8 +233,9 @@ async function runPublication(argv, io) {
133
233
  const manuscriptPath = strictString(args, "manuscript");
134
234
  const assessmentPath = strictString(args, "assessment");
135
235
  const producerSessionId = strictString(args, "producer-session");
136
- if (!manuscriptPath || !assessmentPath || !producerSessionId) {
137
- throw new CliError("research publication freeze requires --manuscript, --assessment, --producer-agent, and --producer-session.", { code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED", exitCode: 2 });
236
+ const submissionPath = strictString(args, "submission");
237
+ if (!manuscriptPath || !assessmentPath || !submissionPath || !producerSessionId) {
238
+ throw new CliError("research publication freeze requires --manuscript, --assessment, --submission, --producer-agent, and --producer-session.", { code: "RESEARCH_PUBLICATION_ARGUMENT_REQUIRED", exitCode: 2 });
138
239
  }
139
240
  const root = await workspaceFromArgs(args);
140
241
  const supplementsPath = strictString(args, "supplements");
@@ -144,6 +245,7 @@ async function runPublication(argv, io) {
144
245
  manuscriptPath,
145
246
  assessmentPath,
146
247
  supplementPaths: supplementsPath ? await readAbsolutePathArray(supplementsPath) : [],
248
+ submissionFiles: await readSubmissionFiles(submissionPath),
147
249
  producerAgent: publicationAgent(strictString(args, "producer-agent"), "producer"),
148
250
  producerSessionId,
149
251
  }), args);
@@ -949,6 +1051,66 @@ async function runProject(argv, io) {
949
1051
  writeJson(io, result, args);
950
1052
  return 0;
951
1053
  }
1054
+ if (evidenceAction === "decomposition") {
1055
+ const [decompositionAction, ...decompositionRest] = evidenceRest;
1056
+ if (decompositionAction !== "record") {
1057
+ throw unknownAction("research project evidence decomposition", decompositionAction ?? "");
1058
+ }
1059
+ const args = parseStrictArgs(decompositionRest, { ...WORKSPACE_OPTIONS, record: "string" }, "research project evidence decomposition record");
1060
+ if (strictBoolean(args, "help"))
1061
+ return writeHelp(io);
1062
+ const projectId = onePositional(args.positionals, "research project evidence decomposition record");
1063
+ const recordPath = strictString(args, "record");
1064
+ if (!recordPath) {
1065
+ throw new CliError("decomposition record requires --record.", {
1066
+ code: "RESEARCH_DECOMPOSITION_INVALID",
1067
+ exitCode: 2,
1068
+ });
1069
+ }
1070
+ const root = await workspaceFromArgs(args);
1071
+ const record = await readBoundedJsonRecord(recordPath, "--record", "RESEARCH_DECOMPOSITION_INVALID");
1072
+ const result = await withWorkspaceLock(root, "research.decomposition.record", () => recordArtifactDecomposition({ root, projectId, value: record }));
1073
+ writeJson(io, result, args);
1074
+ return 0;
1075
+ }
1076
+ if (evidenceAction === "atom") {
1077
+ const [atomAction, ...atomRest] = evidenceRest;
1078
+ if (atomAction !== "register") {
1079
+ throw unknownAction("research project evidence atom", atomAction ?? "");
1080
+ }
1081
+ const args = parseStrictArgs(atomRest, { ...WORKSPACE_OPTIONS, record: "string" }, "research project evidence atom register");
1082
+ if (strictBoolean(args, "help"))
1083
+ return writeHelp(io);
1084
+ const projectId = onePositional(args.positionals, "research project evidence atom register");
1085
+ const recordPath = strictString(args, "record");
1086
+ if (!recordPath) {
1087
+ throw new CliError("atom register requires --record.", {
1088
+ code: "RESEARCH_EVIDENCE_ATOM_INVALID",
1089
+ exitCode: 2,
1090
+ });
1091
+ }
1092
+ const root = await workspaceFromArgs(args);
1093
+ const record = await readBoundedJsonRecord(recordPath, "--record", "RESEARCH_EVIDENCE_ATOM_INVALID");
1094
+ const result = await withWorkspaceLock(root, "research.evidence-atom.register", () => registerEvidenceAtom({ root, projectId, value: record }));
1095
+ writeJson(io, result, args);
1096
+ return 0;
1097
+ }
1098
+ if (evidenceAction === "content") {
1099
+ const [contentAction, ...contentRest] = evidenceRest;
1100
+ if (contentAction !== "freeze" && contentAction !== "status") {
1101
+ throw unknownAction("research project evidence content", contentAction ?? "");
1102
+ }
1103
+ const args = parseStrictArgs(contentRest, WORKSPACE_OPTIONS, `research project evidence content ${contentAction}`);
1104
+ if (strictBoolean(args, "help"))
1105
+ return writeHelp(io);
1106
+ const projectId = onePositional(args.positionals, `research project evidence content ${contentAction}`);
1107
+ const root = await workspaceFromArgs(args);
1108
+ const result = contentAction === "freeze"
1109
+ ? await withWorkspaceLock(root, "research.evidence-content.freeze", () => freezeEvidenceContentSnapshot(root, projectId))
1110
+ : await loadCurrentEvidenceContentSnapshot(root, projectId);
1111
+ writeJson(io, result, args);
1112
+ return 0;
1113
+ }
952
1114
  if (evidenceAction !== "fetch") {
953
1115
  throw unknownAction("research project evidence", evidenceAction ?? "");
954
1116
  }
@@ -1245,6 +1407,7 @@ async function runStatus(argv, io) {
1245
1407
  return writeHelp(io);
1246
1408
  rejectPositionals(args.positionals, "research status");
1247
1409
  const root = await workspaceFromArgs(args);
1410
+ const config = await loadWorkspaceConfig(root);
1248
1411
  const selectedProject = strictString(args, "project");
1249
1412
  const workspaceProjects = await listProjects(root);
1250
1413
  const allProjects = selectedProject
@@ -1260,6 +1423,20 @@ async function runStatus(argv, io) {
1260
1423
  project.status !== "abandoned");
1261
1424
  const result = {
1262
1425
  workspace: root,
1426
+ execution: {
1427
+ producer: {
1428
+ host: config.producer.agent,
1429
+ model: config.producer.model,
1430
+ mode: config.producer.executionMode,
1431
+ },
1432
+ reviewer: {
1433
+ agent: config.reviewer.agent,
1434
+ model: config.reviewer.model,
1435
+ mode: config.reviewer.executionMode,
1436
+ transport: config.reviewerExecution.transport,
1437
+ isolationProvider: config.reviewerExecution.isolationProvider,
1438
+ },
1439
+ },
1263
1440
  hiddenSupersededProjects: selectedProject || strictBoolean(args, "all")
1264
1441
  ? 0
1265
1442
  : workspaceProjects.filter((project) => project.lineage.supersededBy !== null).length,
@@ -1272,7 +1449,8 @@ async function runStatus(argv, io) {
1272
1449
  projects: await Promise.all(projects.map(async (project) => {
1273
1450
  const current = refreshProject(project);
1274
1451
  const nativeStage = await inspectNativeResearchStage(root, current);
1275
- const snapshot = await inspectSnapshotForStatus(root, current.id);
1452
+ const evidencePipeline = await inspectEvidencePipelineForStatus(root, current);
1453
+ const snapshot = evidencePipeline.acquisition;
1276
1454
  const readyPackage = nextReadyPackage(current)?.id ?? null;
1277
1455
  const scientificReview = await inspectScientificReviewStatus(root, current.id);
1278
1456
  const evidenceAccess = current.scientificDesign
@@ -1290,12 +1468,13 @@ async function runStatus(argv, io) {
1290
1468
  handoff: current.handoff,
1291
1469
  evidenceState: current.evidenceState,
1292
1470
  snapshot,
1471
+ evidencePipeline,
1293
1472
  nativeStage,
1294
1473
  scientificReview,
1295
1474
  evidenceAccess,
1296
1475
  publication,
1297
1476
  readyPackage,
1298
- recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage, scientificReview, publication),
1477
+ recommendedAction: projectRecommendedAction(root, current, readyPackage, nativeStage, scientificReview, evidencePipeline, publication),
1299
1478
  usage: current.usage,
1300
1479
  inputs: current.inputs,
1301
1480
  packages: current.packages,
@@ -1306,6 +1485,94 @@ async function runStatus(argv, io) {
1306
1485
  writeJson(io, result, args);
1307
1486
  return 0;
1308
1487
  }
1488
+ async function inspectEvidencePipelineForStatus(root, project) {
1489
+ const projectRoot = join(workspacePaths(root).projects, project.id);
1490
+ const acquisition = await inspectSnapshotForStatus(root, project.id);
1491
+ const contentPath = join(projectRoot, "outputs", "content-snapshot.json");
1492
+ let content = { status: "absent" };
1493
+ if (await pathExists(contentPath)) {
1494
+ try {
1495
+ const snapshot = await loadCurrentEvidenceContentSnapshot(root, project.id);
1496
+ content = {
1497
+ status: "verified",
1498
+ snapshotId: snapshot.snapshotId,
1499
+ snapshotSha256: snapshot.snapshotSha256,
1500
+ decompositionCount: snapshot.decompositions.length,
1501
+ atomCount: snapshot.atoms.length,
1502
+ sourceCount: snapshot.sourceCoverage.length,
1503
+ roleCount: snapshot.roleCoverage.length,
1504
+ insufficientRoleIds: snapshot.roleCoverage
1505
+ .filter((role) => role.decision === "insufficient")
1506
+ .map((role) => role.roleId),
1507
+ gate: snapshot.gate,
1508
+ };
1509
+ }
1510
+ catch (error) {
1511
+ content = {
1512
+ status: "invalid",
1513
+ code: error instanceof CliError ? error.code : "RESEARCH_EVIDENCE_CONTENT_SNAPSHOT_INVALID",
1514
+ };
1515
+ }
1516
+ }
1517
+ const inferencePath = join(projectRoot, "outputs", "inference-snapshot.json");
1518
+ let inference = { status: "absent" };
1519
+ if (await pathExists(inferencePath)) {
1520
+ try {
1521
+ const snapshot = await loadCurrentInferenceSnapshot(root, project.id);
1522
+ inference = {
1523
+ status: "verified",
1524
+ snapshotId: snapshot.snapshotId,
1525
+ snapshotSha256: snapshot.snapshotSha256,
1526
+ sourceCount: snapshot.sources.length,
1527
+ atomCount: snapshot.atoms.length,
1528
+ claimCount: snapshot.claims.length,
1529
+ artifactCount: snapshot.artifactSha256s.length,
1530
+ gate: snapshot.gate,
1531
+ };
1532
+ }
1533
+ catch (error) {
1534
+ inference = {
1535
+ status: "invalid",
1536
+ code: error instanceof CliError ? error.code : "RESEARCH_INFERENCE_SNAPSHOT_INVALID",
1537
+ };
1538
+ }
1539
+ }
1540
+ else if (acquisition.gate?.decision === "stop") {
1541
+ inference = { status: "blocked", gate: acquisition.gate };
1542
+ }
1543
+ else if (content.gate?.decision === "stop") {
1544
+ inference = { status: "blocked", gate: content.gate };
1545
+ }
1546
+ else if (project.scientificDesign && content.status !== "verified") {
1547
+ inference = {
1548
+ status: "blocked",
1549
+ code: "RESEARCH_EVIDENCE_CONTENT_SNAPSHOT_REQUIRED",
1550
+ };
1551
+ }
1552
+ const graphPath = join(projectRoot, "outputs", "claim-evidence-graph.json");
1553
+ let claimGraph = { status: "absent" };
1554
+ if (await pathExists(graphPath)) {
1555
+ try {
1556
+ const graph = await loadCurrentClaimEvidenceGraph(root, project.id);
1557
+ claimGraph = {
1558
+ status: "verified",
1559
+ graphId: graph.graphId,
1560
+ graphSha256: graph.graphSha256,
1561
+ analysisSha256: graph.analysisSha256,
1562
+ analysisRunId: graph.analysisRunId,
1563
+ nodeCount: graph.nodes.length,
1564
+ edgeCount: graph.edges.length,
1565
+ };
1566
+ }
1567
+ catch (error) {
1568
+ claimGraph = {
1569
+ status: "invalid",
1570
+ code: error instanceof CliError ? error.code : "RESEARCH_CLAIM_EVIDENCE_GRAPH_INVALID",
1571
+ };
1572
+ }
1573
+ }
1574
+ return { acquisition, content, inference, claimGraph };
1575
+ }
1309
1576
  async function inspectEvidenceAccessForStatus(root, projectId) {
1310
1577
  try {
1311
1578
  return await inspectEvidenceAccessStatus(root, projectId);
@@ -1352,6 +1619,8 @@ async function inspectSnapshotForStatus(root, projectId) {
1352
1619
  parentSnapshotId: snapshot.parentSnapshotId,
1353
1620
  sourceCount: snapshot.sources.length,
1354
1621
  artifactCount: snapshot.artifacts.length,
1622
+ gapCount: snapshot.gaps.length,
1623
+ gate: snapshot.inferenceGate,
1355
1624
  delta: snapshot.delta,
1356
1625
  };
1357
1626
  }
@@ -1374,7 +1643,7 @@ async function inspectPublicationForStatus(root, projectId) {
1374
1643
  };
1375
1644
  }
1376
1645
  }
1377
- function projectRecommendedAction(root, project, readyPackage, nativeStage, scientificReview, publication) {
1646
+ function projectRecommendedAction(root, project, readyPackage, nativeStage, scientificReview, evidencePipeline, publication) {
1378
1647
  if (project.lineage.supersededBy) {
1379
1648
  return `Continue with superseding project ${project.lineage.supersededBy}.`;
1380
1649
  }
@@ -1398,6 +1667,23 @@ function projectRecommendedAction(root, project, readyPackage, nativeStage, scie
1398
1667
  if (nativeStage.status === "active") {
1399
1668
  return nativeStage.recommendedAction ?? "Resume the active native stage.";
1400
1669
  }
1670
+ if (readyPackage === "analyze") {
1671
+ if (evidencePipeline.content.status === "absent") {
1672
+ return `Decompose every acquired full-text/data artifact, register exact evidence atoms, then freeze typed content: tiangong-ai research project evidence content freeze ${project.id} --workspace ${root}`;
1673
+ }
1674
+ if (evidencePipeline.content.status === "invalid") {
1675
+ return `Typed evidence content is invalid (${evidencePipeline.content.code ?? "unknown"}); repair exact decomposition/atom bindings before analysis.`;
1676
+ }
1677
+ if (evidencePipeline.content.gate?.decision === "stop") {
1678
+ return "Typed evidence coverage is insufficient; complete lawful gap filling or request a scope/access handoff instead of starting inference.";
1679
+ }
1680
+ if (evidencePipeline.acquisition.gate?.decision === "stop") {
1681
+ return "Frozen acquisition gaps block inference; request a scope/access handoff after all acquired content is decomposed instead of continuing substitute search.";
1682
+ }
1683
+ if (evidencePipeline.inference.status === "invalid") {
1684
+ return `Inference snapshot is invalid (${evidencePipeline.inference.code ?? "unknown"}); repair its frozen upstream bindings before analysis.`;
1685
+ }
1686
+ }
1401
1687
  if (scientificReview.nextGate) {
1402
1688
  const gate = scientificReview.nextGate;
1403
1689
  if (gate.status === "stopped") {
@@ -1432,7 +1718,7 @@ function projectRecommendedAction(root, project, readyPackage, nativeStage, scie
1432
1718
  : "Inspect the blocking package and use explicit retry or fork recovery.";
1433
1719
  }
1434
1720
  if (readyPackage && ["discover", "acquire", "analyze", "synthesize"].includes(readyPackage)) {
1435
- return `Prepare native ${readyPackage}: tiangong-ai research project stage prepare ${project.id} --stage ${readyPackage} --host-agent <codex|claude> --workspace ${root}`;
1721
+ return `Prepare native ${readyPackage}: tiangong-ai research project stage prepare ${project.id} --stage ${readyPackage} --host-agent <codex|claude|workbuddy|codebuddy> --workspace ${root}`;
1436
1722
  }
1437
1723
  return readyPackage === "review"
1438
1724
  ? `Run the independent reviewer package: tiangong-ai research run --project ${project.id} --workspace ${root}`
@@ -1553,9 +1839,10 @@ function nativeProducerStage(value) {
1553
1839
  });
1554
1840
  }
1555
1841
  function nativeHostAgent(value) {
1556
- if (value === "codex" || value === "claude")
1842
+ if (value === "codex" || value === "claude" || value === "workbuddy" || value === "codebuddy") {
1557
1843
  return value;
1558
- throw new CliError("--host-agent must be codex or claude.", {
1844
+ }
1845
+ throw new CliError("--host-agent must be codex, claude, workbuddy, or codebuddy.", {
1559
1846
  code: "RESEARCH_NATIVE_HOST_INVALID",
1560
1847
  exitCode: 2,
1561
1848
  });
@@ -1668,9 +1955,13 @@ function scientificReviewRole(value) {
1668
1955
  });
1669
1956
  }
1670
1957
  function publicationAgent(value, label) {
1671
- if (value === "codex" || value === "claude")
1958
+ const producer = label.includes("producer");
1959
+ if (value === "codex" ||
1960
+ value === "claude" ||
1961
+ (producer && (value === "workbuddy" || value === "codebuddy"))) {
1672
1962
  return value;
1673
- throw new CliError(`--${label}-agent must be codex or claude.`, {
1963
+ }
1964
+ throw new CliError(`--${label}-agent must be ${producer ? "codex, claude, workbuddy, or codebuddy" : "codex or claude"}.`, {
1674
1965
  code: "RESEARCH_PUBLICATION_AGENT_INVALID",
1675
1966
  exitCode: 2,
1676
1967
  });
@@ -1712,6 +2003,19 @@ async function readAbsolutePathArray(path, option = "supplements") {
1712
2003
  }
1713
2004
  return value;
1714
2005
  }
2006
+ async function readSubmissionFiles(path) {
2007
+ const value = await readBoundedJsonRecord(path, "--submission", "RESEARCH_PUBLICATION_SUBMISSION_PACKAGE_INVALID");
2008
+ if (value.schemaVersion !== 1 ||
2009
+ !Array.isArray(value.files) ||
2010
+ value.files.some((file) => !isObject(file) ||
2011
+ typeof file.role !== "string" ||
2012
+ typeof file.path !== "string" ||
2013
+ !isAbsolute(file.path) ||
2014
+ resolve(file.path) !== file.path)) {
2015
+ throw new CliError("--submission must declare schemaVersion 1 and role/path entries with absolute canonical paths.", { code: "RESEARCH_PUBLICATION_SUBMISSION_PACKAGE_INVALID", exitCode: 2 });
2016
+ }
2017
+ return value.files;
2018
+ }
1715
2019
  function integerOption(value, fallback, label) {
1716
2020
  if (!value)
1717
2021
  return fallback;