@tiangong-ai/cli 0.0.59 → 0.0.60

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 (61) hide show
  1. package/AGENTS.md +2 -2
  2. package/README.md +126 -21
  3. package/dist/research/orchestration.js +101 -44
  4. package/dist/research/orchestration.js.map +1 -1
  5. package/dist/research/workspace/acquisition-forecast.d.ts +3 -0
  6. package/dist/research/workspace/acquisition-forecast.js +28 -3
  7. package/dist/research/workspace/acquisition-forecast.js.map +1 -1
  8. package/dist/research/workspace/acquisition-revision.d.ts +13 -0
  9. package/dist/research/workspace/acquisition-revision.js +142 -0
  10. package/dist/research/workspace/acquisition-revision.js.map +1 -0
  11. package/dist/research/workspace/acquisition.d.ts +3 -1
  12. package/dist/research/workspace/acquisition.js +67 -35
  13. package/dist/research/workspace/acquisition.js.map +1 -1
  14. package/dist/research/workspace/analysis-run.d.ts +2 -0
  15. package/dist/research/workspace/analysis-run.js +28 -0
  16. package/dist/research/workspace/analysis-run.js.map +1 -0
  17. package/dist/research/workspace/artifacts.d.ts +3 -0
  18. package/dist/research/workspace/artifacts.js +13 -0
  19. package/dist/research/workspace/artifacts.js.map +1 -1
  20. package/dist/research/workspace/audit-bundle.d.ts +5 -0
  21. package/dist/research/workspace/audit-bundle.js +17 -0
  22. package/dist/research/workspace/audit-bundle.js.map +1 -1
  23. package/dist/research/workspace/content-evidence.d.ts +3 -0
  24. package/dist/research/workspace/content-evidence.js +149 -22
  25. package/dist/research/workspace/content-evidence.js.map +1 -1
  26. package/dist/research/workspace/project-authority.d.ts +21 -0
  27. package/dist/research/workspace/project-authority.js +186 -0
  28. package/dist/research/workspace/project-authority.js.map +1 -0
  29. package/dist/research/workspace/project-mutations.d.ts +26 -0
  30. package/dist/research/workspace/project-mutations.js +330 -0
  31. package/dist/research/workspace/project-mutations.js.map +1 -0
  32. package/dist/research/workspace/projects.d.ts +2 -1
  33. package/dist/research/workspace/projects.js +132 -91
  34. package/dist/research/workspace/projects.js.map +1 -1
  35. package/dist/research/workspace/publication-workflow.d.ts +3 -0
  36. package/dist/research/workspace/publication-workflow.js +15 -3
  37. package/dist/research/workspace/publication-workflow.js.map +1 -1
  38. package/dist/research/workspace/runtime.d.ts +5 -0
  39. package/dist/research/workspace/runtime.js +83 -54
  40. package/dist/research/workspace/runtime.js.map +1 -1
  41. package/dist/research/workspace/schemas.d.ts +6 -0
  42. package/dist/research/workspace/schemas.js +13 -2
  43. package/dist/research/workspace/schemas.js.map +1 -1
  44. package/dist/research/workspace/scientific-review-execution.js +2 -0
  45. package/dist/research/workspace/scientific-review-execution.js.map +1 -1
  46. package/dist/research/workspace/scientific-review.d.ts +3 -1
  47. package/dist/research/workspace/scientific-review.js +9 -2
  48. package/dist/research/workspace/scientific-review.js.map +1 -1
  49. package/dist/research/workspace/setup-catalog.js +2 -2
  50. package/dist/research/workspace/task-acceptance.d.ts +126 -0
  51. package/dist/research/workspace/task-acceptance.js +480 -0
  52. package/dist/research/workspace/task-acceptance.js.map +1 -0
  53. package/dist/research/workspace/task-audit.d.ts +12 -0
  54. package/dist/research/workspace/task-audit.js +181 -0
  55. package/dist/research/workspace/task-audit.js.map +1 -0
  56. package/dist/research/workspace/task-contract.d.ts +139 -0
  57. package/dist/research/workspace/task-contract.js +485 -0
  58. package/dist/research/workspace/task-contract.js.map +1 -0
  59. package/dist/research/workspace/workspace.js +11 -8
  60. package/dist/research/workspace/workspace.js.map +1 -1
  61. package/package.json +1 -1
package/AGENTS.md CHANGED
@@ -17,8 +17,8 @@ checkPaths:
17
17
  - .docpact/config.yaml
18
18
  - docs/agents/**
19
19
  - src/**
20
- lastReviewedAt: 2026-09-02
21
- lastReviewedCommit: 8a18ba69f432d2c639517549157ef29545722cff
20
+ lastReviewedAt: 2026-09-03
21
+ lastReviewedCommit: 06dadef170ab79c19eef9a35663e86fdee22479b
22
22
  ---
23
23
 
24
24
  # Tiangong AI CLI Contract
package/README.md CHANGED
@@ -12,8 +12,8 @@ checkPaths:
12
12
  - package.json
13
13
  - bin/**
14
14
  - src/**
15
- lastReviewedAt: 2026-09-02
16
- lastReviewedCommit: 8a18ba69f432d2c639517549157ef29545722cff
15
+ lastReviewedAt: 2026-09-03
16
+ lastReviewedCommit: 06dadef170ab79c19eef9a35663e86fdee22479b
17
17
  ---
18
18
 
19
19
  # Tiangong AI CLI
@@ -712,9 +712,15 @@ submission manifest must bind distinct absolute files for cover letter, title
712
712
  page, reporting checklist, data availability, code availability, and source
713
713
  data; figure/table index, extended data, and supplementary methods are optional.
714
714
  `research publication freeze` then content-addresses the Policy, scientific
715
- design and early reviews, acquisition/content/inference snapshots, reproduced
715
+ design and early reviews, acquisition/content/inference snapshots, mode-bound
716
716
  analysis, Claim-Evidence Graph, base outputs, manuscript, assessment,
717
717
  supplements, role-complete submission files, and reproducibility manifest.
718
+ Computational/mixed analysis still requires reproduced metadata with exact
719
+ implementation/environment bindings. Qualitative analysis uses
720
+ `status: not-applicable`, null command/seed and empty implementation/environment
721
+ lists; it must not invent a computation. Both paths retain the same evidence,
722
+ graph, Policy and independent-review checks. Metadata alone is not proof that
723
+ a computation was executed.
718
724
  Exactly four fresh independent sessions review that frozen generation:
719
725
  evidence, methods/reproducibility, domain/novelty, and journal-editor. A revised
720
726
  manuscript invalidates prior reviews. Every reviewer must use the configured
@@ -1016,6 +1022,14 @@ deficits; exit `0` means only potential eligibility, never successful atom
1016
1022
  registration, content freeze, independence certification, or review. Pending
1017
1023
  input materialization, decomposition, and exact atom assignments remain
1018
1024
  explicit. Re-run after material acquisition changes, not after every atom.
1025
+ `submissionGate` separately reports deterministic acquire-submit blockers;
1026
+ `potentially-ready` is not an inference/content pass. An accepted binary without
1027
+ required readable content stops before submission, while an honest incomplete
1028
+ audit may still freeze its limitations and separate inference-stop decision.
1029
+ An admitted local PDF/Office file alone is not producer-readable. Forecast uses
1030
+ the same media rules as artifact registration and reports a missing readable
1031
+ derivative unless a verified readable artifact or admitted text/context input
1032
+ is available. This classification does not reread or decode the binary again.
1019
1033
  Flat `sourceTypeRequirements` arrays mean **all-of**. A design may instead use
1020
1034
  `{"allOf":["academic-paper"],"anyOf":["government","industry"],"atLeast":{"count":2,"from":["academic-paper","government","industry"]}}`;
1021
1035
  every present group applies, and counts use distinct types. Forecast, typed
@@ -1370,27 +1384,118 @@ read-only under `outputs/revisions/synthesize/<sha256>/report.md`; review and
1370
1384
  closure are invalidated, while unchanged discovery, acquisition, content,
1371
1385
  inference, analysis, and graph objects remain bound.
1372
1386
 
1373
- Recovery fork writes are rollback-protected. If inherited output, content,
1374
- inference, or graph validation fails, the target directory is removed and
1375
- source authority is restored. A source typed-content snapshot is revalidated
1387
+ Recovery forks have one journal commit point. Before `project.forked` commits,
1388
+ their target is staging, not an authoritative project; the original source
1389
+ remains authoritative. Status, run and native/reviewer admission share that
1390
+ same verified lineage view. A source typed-content snapshot is revalidated
1376
1391
  and re-signed for the target generation instead of copying its project-bound
1377
1392
  hash. Top-journal generations may resume only through `acquire`, because the
1378
1393
  target-specific scientific design must complete fresh evidence-construct and
1379
- pilot-methods reviews before analysis. `research status --all` marks any legacy
1380
- fork directory without a `project.forked` commit marker as
1381
- `authority.state = "invalid"` rather than authoritative.
1382
-
1383
- To repair completed acquisition before analysis, use
1384
- `research project fork SOURCE --to TARGET --resume-through discover`. This
1385
- preserves the original frozen audit, inherits verified discovery/receipts and
1386
- exact acquired artifacts, and opens TARGET's acquire stage. Reuse the returned
1387
- artifact IDs from the source acquisition audit, run
1388
- `project evidence content forecast TARGET --input AUDIT`,
1389
- then prepare/submit TARGET's acquire stage and rebuild typed content. There is
1390
- no need to repeat paid searches or download unchanged files. Top-journal
1391
- recovery additionally requires a Policy approved for TARGET and `--design`,
1392
- `--design-producer-agent`, and `--design-producer-session`; new generation
1393
- reviews cannot inherit the source generation's scientific approval.
1394
+ pilot-methods reviews before analysis.
1395
+
1396
+ After process interruption, repeat the same explicit fork request. The next
1397
+ lease holder settles only hash-bound CLI-owned pending operations: before
1398
+ commit it retains the interrupted target under
1399
+ `lineage/interrupted-project-mutations/<operation-id>/target`, outside the
1400
+ project namespace; after commit it finishes source/ledger projections. Exact
1401
+ committed replay returns the existing target without recopying evidence or
1402
+ repeating provider work. A different request for that target is a conflict.
1403
+ An unchanged completed retry request is likewise acknowledged without another
1404
+ revision or attempt. A subsequent real failure can still be retried normally.
1405
+
1406
+ Unknown targets, symlink replacements, modified source state and invalid
1407
+ recovery metadata are not overwritten or deleted. Resolve the reported
1408
+ `RESEARCH_PROJECT_RECOVERY_REQUIRED` conflict or restore a trusted backup before
1409
+ retrying; do not forge control files. Read-only status ignores never-committed
1410
+ directories without project state and reports existing uncommitted derived
1411
+ states as invalid. Missing state for a committed project is still an error.
1412
+ Recovery does not truncate a corrupt journal or claim power-loss durability.
1413
+
1414
+ To repair completed acquisition before any analysis attempt, prefer an explicit
1415
+ same-project revision when question, Policy, design, and requirements are unchanged:
1416
+
1417
+ ```bash
1418
+ tiangong-ai research project evidence acquisition revise PROJECT \
1419
+ --expected-snapshot <current-acquisition-sha256> \
1420
+ --reason "Add the verified readable derivative before analysis" \
1421
+ --workspace /absolute/workspace --json
1422
+ ```
1423
+
1424
+ This reopens acquire, preserves discovery, exact artifacts/receipts, historical
1425
+ snapshot bytes, budgets already spent, and the research-design approval. It
1426
+ invalidates evidence-construct and pilot-methods approvals because their evidence
1427
+ changed. Add `--include-discovery` only when new sources must be discovered or
1428
+ admitted; that explicitly reopens discover then acquire without a new project or
1429
+ automatic provider call. Prepare the returned stage, reuse unchanged objects,
1430
+ forecast once per meaningful batch, submit the complete revised audit, and rebuild
1431
+ typed content. Exact revision replay is idempotent. Stale snapshots, active native
1432
+ sessions, unresolved handoffs, inference freezes, and later attempts are refused.
1433
+
1434
+ Changed failed/limited decomposition records can be superseded under a descendant
1435
+ acquisition snapshot; unchanged records are reused. Single and batch intake share
1436
+ this rule. Historical records remain immutable, and atoms from deselected artifacts
1437
+ cannot fill current coverage. Same-snapshot conflicting extraction still fails.
1438
+
1439
+ Actual question/Policy/design changes or post-analysis work require the existing
1440
+ fork/addendum flow. Pre-feature snapshots without immutable evidence records cannot
1441
+ be repaired in place; use `research project fork SOURCE --to TARGET
1442
+ --resume-through discover` to reuse discovery/receipts/artifacts, or explicitly
1443
+ start a new generation. There is no automatic migration. A top-journal successor
1444
+ requires a Policy approved for TARGET and `--design`, `--design-producer-agent`,
1445
+ and `--design-producer-session`; it cannot inherit scientific approval.
1446
+
1447
+ ### Original task, current scope, and actual checks
1448
+
1449
+ For a new research project, record a small original-requirement checklist after
1450
+ project initialization and before any producer execution or scientific review.
1451
+ The native host authors the content; the CLI owns its schema and hash bindings:
1452
+
1453
+ ```bash
1454
+ tiangong-ai research schema show task-contract --json
1455
+ tiangong-ai research project task define PROJECT \
1456
+ --input /absolute/task.json --workspace /absolute/workspace --json
1457
+ tiangong-ai research project task status PROJECT --workspace /absolute/workspace --json
1458
+ ```
1459
+
1460
+ Each requirement has a stable ID, acceptance condition, `checkKind` (`evidence`,
1461
+ `computation`, or `proof`), and optional bindings to existing design claims and
1462
+ coverage dimensions. Original wording cannot be overwritten. Old projects without
1463
+ a task remain explicitly unassessed rather than retrospectively accepted.
1464
+
1465
+ Before analysis, use `research schema show task-scope-change` and
1466
+ `project task scope propose PROJECT --input FILE --expected-contract SHA` to
1467
+ propose a change. Review the returned `changes.details` before/after values, then
1468
+ approve only with both `--proposal SHA` and `--confirm-change SHA` on
1469
+ `project task scope approve`. A generic continue instruction is not scope consent.
1470
+ The record is an **operator confirmation**, not authenticated proof of a human's
1471
+ identity. Scope approval does not rewrite scientific design or evidence floors;
1472
+ it invalidates prior scoped scientific reviews. Withdrawn original requirements
1473
+ remain visible and a fork/addendum retains original history without inheriting
1474
+ task completion.
1475
+
1476
+ Use `research schema show task-acceptance` and
1477
+ `project task acceptance record PROJECT --input FILE` after acquisition and
1478
+ between native stages. Records bind the exact requirement version, source/atom/
1479
+ finding IDs, and explicitly selected bounded UTF-8 result files. The declared
1480
+ command is stored only by hash and is **not executed by this command**. Raw result
1481
+ bytes are copied into immutable hash-addressed objects; secrets and control-store
1482
+ sources are rejected. Positive/negative computational outcomes need a command and
1483
+ results; failed, inconclusive, and not-run checks remain honest without invented
1484
+ results. All records say `trust=native-observation`, `executionCertified=false`.
1485
+
1486
+ One unchanged result blob is stored and included once per reviewer context. The
1487
+ existing independent review receives the original request, original/current
1488
+ requirements, exact checks and results, and returns a bound `taskAssessment`;
1489
+ there is no additional default paid review round. Missing current checks stop
1490
+ before review, and stale/failed/inconclusive checks cannot be promoted to answered.
1491
+ Publication packets and portable audit verification retain these relationships;
1492
+ hash integrity does not prove execution, scientific validity, or editorial acceptance.
1493
+
1494
+ `project task status` and each `research run` project summary report original and
1495
+ current task completion separately from workflow completion and publication verdict.
1496
+ A completed workflow or approved reduced scope must not be described as satisfying
1497
+ unanswered original requirements. Use the selected runtime's help/schema discovery
1498
+ once before adopting these commands; they are not an implicit runtime upgrade.
1394
1499
 
1395
1500
  ## Research Search
1396
1501
 
@@ -1,6 +1,7 @@
1
1
  import { lstat, readFile } from "node:fs/promises";
2
2
  import { isAbsolute, join, resolve } from "node:path";
3
3
  import { CliError } from "../errors.js";
4
+ import { projectAuthority, projectWithEffectiveAuthority, readProjectAuthorityIndex, } from "./workspace/project-authority.js";
4
5
  import { stringifyJson, write } from "../io.js";
5
6
  import { parseStrictArgs, strictBoolean, strictString } from "../strict-args.js";
6
7
  import { researchSetupHelp, runResearchSetupCommand } from "./setup-command.js";
@@ -9,13 +10,16 @@ import { inspectResearchContext } from "./workspace/context.js";
9
10
  import { packageVersion } from "./workspace/constants.js";
10
11
  import { researchDataCredentialIds, setCapabilityCredentialValue, } from "./workspace/credentials.js";
11
12
  import { configureExternalSkillProfile, doctorExternalCapabilities, EXTERNAL_SKILL_CONTEXT_PROFILE, EXTERNAL_SKILL_MEDIA_PROFILE, EXTERNAL_SKILL_PROFILE, importExternalCapability, inspectExternalSkillCatalog, } from "./workspace/external-skills.js";
12
- import { appendJournalEvent, readJournal } from "./workspace/journal.js";
13
+ import { appendJournalEvent } from "./workspace/journal.js";
13
14
  import { fetchNativeCandidateSource } from "./workspace/broker.js";
14
15
  import { preflightEvidenceArtifact, registerEvidenceArtifact } from "./workspace/artifacts.js";
15
16
  import { executeResearchDataCapability } from "./workspace/data-evidence-adapter.js";
16
17
  import { exportProjectAuditBundle, verifyProjectAuditBundle } from "./workspace/audit-bundle.js";
17
18
  import { loadCurrentEvidenceSnapshot } from "./workspace/acquisition.js";
18
19
  import { inspectAcquisitionForecast } from "./workspace/acquisition-forecast.js";
20
+ import { reviseProjectAcquisition } from "./workspace/acquisition-revision.js";
21
+ import { approveProjectTaskScope, defineProjectTask, isTaskSchemaName, proposeProjectTaskScope, taskInputSchema, } from "./workspace/task-contract.js";
22
+ import { inspectProjectTask, recordProjectTaskAcceptance, taskAcceptanceInputSchema, } from "./workspace/task-acceptance.js";
19
23
  import { freezeEvidenceContentSnapshot, loadCurrentEvidenceContentSnapshot, recordArtifactDecomposition, registerEvidenceAtom, registerEvidenceContentBatch, } from "./workspace/content-evidence.js";
20
24
  import { inspectDiscoveryProgress } from "./workspace/discovery-status.js";
21
25
  import { EVIDENCE_CONTENT_LIMITS, EVIDENCE_CONTENT_SCHEMA_NAMES, evidenceContentInputSchema, isEvidenceContentSchemaName, } from "./workspace/evidence-content-schema.js";
@@ -104,6 +108,11 @@ export function researchOrchestrationHelp() {
104
108
  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]
105
109
  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]
106
110
  tiangong-ai research project retry <project-id> [--package <package-id>] [--workspace <path>] [--json]
111
+ tiangong-ai research project task define <project-id> --input <absolute-json> [--workspace <path>] [--json]
112
+ tiangong-ai research project task status <project-id> [--workspace <path>] [--json]
113
+ tiangong-ai research project task acceptance record <project-id> --input <absolute-json> [--workspace <path>] [--json]
114
+ tiangong-ai research project task scope propose <project-id> --input <absolute-json> --expected-contract <sha256> [--workspace <path>] [--json]
115
+ tiangong-ai research project task scope approve <project-id> --proposal <sha256> --confirm-change <sha256> [--workspace <path>] [--json]
107
116
  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]
108
117
  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]
109
118
  tiangong-ai research project archive <project-id> --reason <text> [--workspace <path>] [--json]
@@ -134,8 +143,9 @@ export function researchOrchestrationHelp() {
134
143
  tiangong-ai research project evidence atom register <project-id> --record <absolute-json> [--workspace <path>] [--json]
135
144
  tiangong-ai research project evidence content freeze <project-id> [--workspace <path>] [--json]
136
145
  tiangong-ai research project evidence content forecast <project-id> --input <absolute-acquisition-audit.json> [--workspace <path>] [--json]
146
+ tiangong-ai research project evidence acquisition revise <project-id> --expected-snapshot <sha256> --reason <text> [--include-discovery] [--workspace <path>] [--json]
137
147
  tiangong-ai research project evidence content status <project-id> [--workspace <path>] [--json]
138
- 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]
148
+ tiangong-ai research schema show <discover|acquire|analyze|synthesize|review|doctor|task-contract|task-scope-change|task-acceptance|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]
139
149
  tiangong-ai research status [--project <project-id>] [--all] [--workspace <absolute-path>] [--json]
140
150
  tiangong-ai research run [--project <project-id>] [--max-parallel <1-8>] [--max-cycles <1-100>] [--dry-run] [--progress-jsonl] [--workspace <absolute-path>] [--json]
141
151
 
@@ -506,7 +516,13 @@ async function runSchema(argv, io) {
506
516
  return writeHelp(io);
507
517
  const stage = onePositional(args.positionals, "research schema show");
508
518
  let schema;
509
- if (isEvidenceContentSchemaName(stage)) {
519
+ if (stage === "task-acceptance") {
520
+ schema = taskAcceptanceInputSchema();
521
+ }
522
+ else if (isTaskSchemaName(stage)) {
523
+ schema = taskInputSchema(stage);
524
+ }
525
+ else if (isEvidenceContentSchemaName(stage)) {
510
526
  schema = evidenceContentInputSchema(stage);
511
527
  }
512
528
  else if (stage === "scientific-design") {
@@ -1025,8 +1041,79 @@ async function runProject(argv, io) {
1025
1041
  }
1026
1042
  throw unknownAction("research project stage", stageAction ?? "");
1027
1043
  }
1044
+ if (action === "task") {
1045
+ const [taskAction, ...taskRest] = rest;
1046
+ if (taskAction === "acceptance") {
1047
+ const [acceptanceAction, ...acceptanceRest] = taskRest;
1048
+ if (acceptanceAction !== "record")
1049
+ throw unknownAction("research project task acceptance", acceptanceAction ?? "");
1050
+ const args = parseStrictArgs(acceptanceRest, { ...WORKSPACE_OPTIONS, input: "string" }, "research project task acceptance record");
1051
+ if (strictBoolean(args, "help"))
1052
+ return writeHelp(io);
1053
+ const projectId = onePositional(args.positionals, "research project task acceptance record");
1054
+ const result = await recordProjectTaskAcceptance(await workspaceFromArgs(args), projectId, await readBoundedJsonRecord(strictString(args, "input") ?? "", "--input", "RESEARCH_TASK_ACCEPTANCE_INVALID"));
1055
+ writeJson(io, result, args);
1056
+ return 0;
1057
+ }
1058
+ if (taskAction === "scope") {
1059
+ const [scopeAction, ...scopeRest] = taskRest;
1060
+ if (!["propose", "approve"].includes(scopeAction ?? ""))
1061
+ throw unknownAction("research project task scope", scopeAction ?? "");
1062
+ const args = parseStrictArgs(scopeRest, {
1063
+ ...WORKSPACE_OPTIONS,
1064
+ input: "string",
1065
+ "expected-contract": "string",
1066
+ proposal: "string",
1067
+ "confirm-change": "string",
1068
+ }, `research project task scope ${scopeAction}`);
1069
+ if (strictBoolean(args, "help"))
1070
+ return writeHelp(io);
1071
+ const projectId = onePositional(args.positionals, `research project task scope ${scopeAction}`);
1072
+ const root = await workspaceFromArgs(args);
1073
+ const result = scopeAction === "approve"
1074
+ ? await approveProjectTaskScope(root, projectId, strictString(args, "proposal") ?? "", strictString(args, "confirm-change"))
1075
+ : await proposeProjectTaskScope(root, projectId, strictString(args, "expected-contract") ?? "", await readBoundedJsonRecord(strictString(args, "input") ?? "", "--input", "RESEARCH_TASK_INVALID"));
1076
+ writeJson(io, result, args);
1077
+ return 0;
1078
+ }
1079
+ if (taskAction !== "define" && taskAction !== "status")
1080
+ throw unknownAction("research project task", taskAction ?? "");
1081
+ const args = parseStrictArgs(taskRest, { ...WORKSPACE_OPTIONS, input: "string" }, `research project task ${taskAction}`);
1082
+ if (strictBoolean(args, "help"))
1083
+ return writeHelp(io);
1084
+ const projectId = onePositional(args.positionals, `research project task ${taskAction}`);
1085
+ const root = await workspaceFromArgs(args);
1086
+ const result = taskAction === "status"
1087
+ ? await inspectProjectTask(root, projectId)
1088
+ : await defineProjectTask(root, projectId, await readBoundedJsonRecord(strictString(args, "input") ?? "", "--input", "RESEARCH_TASK_INVALID"));
1089
+ writeJson(io, result, args);
1090
+ return 0;
1091
+ }
1028
1092
  if (action === "evidence") {
1029
1093
  const [evidenceAction, ...evidenceRest] = rest;
1094
+ if (evidenceAction === "acquisition") {
1095
+ const [revisionAction, ...revisionRest] = evidenceRest;
1096
+ if (revisionAction !== "revise")
1097
+ throw unknownAction("research project evidence acquisition", revisionAction ?? "");
1098
+ const args = parseStrictArgs(revisionRest, {
1099
+ ...WORKSPACE_OPTIONS,
1100
+ "expected-snapshot": "string",
1101
+ reason: "string",
1102
+ "include-discovery": "boolean",
1103
+ }, "research project evidence acquisition revise");
1104
+ if (strictBoolean(args, "help"))
1105
+ return writeHelp(io);
1106
+ const projectId = onePositional(args.positionals, "research project evidence acquisition revise");
1107
+ const result = await reviseProjectAcquisition({
1108
+ root: await workspaceFromArgs(args),
1109
+ projectId,
1110
+ expectedSnapshotSha256: strictString(args, "expected-snapshot") ?? "",
1111
+ reason: strictString(args, "reason") ?? "",
1112
+ includeDiscovery: strictBoolean(args, "include-discovery"),
1113
+ });
1114
+ writeJson(io, result, args);
1115
+ return 0;
1116
+ }
1030
1117
  if (evidenceAction === "data") {
1031
1118
  const [dataAction, ...dataRest] = evidenceRest;
1032
1119
  if (dataAction !== "run") {
@@ -1287,7 +1374,9 @@ async function runProject(argv, io) {
1287
1374
  const value = await readBoundedJsonRecord(inputPath, "--input", "RESEARCH_ACQUISITION_FORECAST_INVALID");
1288
1375
  const result = await inspectAcquisitionForecast(root, projectId, value);
1289
1376
  writeJson(io, result, args);
1290
- return result.acquisitionGate.decision === "pass" && !result.knownRoleDeficits.length
1377
+ return result.submissionGate.decision !== "stop" &&
1378
+ result.acquisitionGate.decision === "pass" &&
1379
+ !result.knownRoleDeficits.length
1291
1380
  ? 0
1292
1381
  : 3;
1293
1382
  }
@@ -1603,12 +1692,8 @@ async function runStatus(argv, io) {
1603
1692
  const root = await workspaceFromArgs(args);
1604
1693
  const config = await loadWorkspaceConfig(root);
1605
1694
  const selectedProject = strictString(args, "project");
1606
- const workspaceProjects = await listProjects(root);
1607
- const committedForkTargets = new Set((await readJournal(workspacePaths(root).journal))
1608
- .filter((event) => event.type === "project.forked" &&
1609
- isObject(event.payload) &&
1610
- typeof event.payload.targetProjectId === "string")
1611
- .map((event) => String(event.payload.targetProjectId)));
1695
+ const authorityIndex = await readProjectAuthorityIndex(root);
1696
+ const workspaceProjects = await listProjects(root, authorityIndex);
1612
1697
  const allProjects = selectedProject
1613
1698
  ? [
1614
1699
  workspaceProjects.find((project) => project.id === selectedProject) ??
@@ -1617,9 +1702,7 @@ async function runStatus(argv, io) {
1617
1702
  : workspaceProjects;
1618
1703
  const projects = selectedProject || strictBoolean(args, "all")
1619
1704
  ? allProjects
1620
- : allProjects.filter((project) => project.lineage.supersededBy === null &&
1621
- project.status !== "archived" &&
1622
- project.status !== "abandoned");
1705
+ : allProjects.filter((project) => ["authoritative", "invalid"].includes(projectAuthority(project, authorityIndex).state));
1623
1706
  const result = {
1624
1707
  workspace: root,
1625
1708
  execution: {
@@ -1638,7 +1721,7 @@ async function runStatus(argv, io) {
1638
1721
  },
1639
1722
  hiddenSupersededProjects: selectedProject || strictBoolean(args, "all")
1640
1723
  ? 0
1641
- : workspaceProjects.filter((project) => project.lineage.supersededBy !== null).length,
1724
+ : workspaceProjects.filter((project) => authorityIndex.successors.has(project.id)).length,
1642
1725
  hiddenArchivedProjects: selectedProject || strictBoolean(args, "all")
1643
1726
  ? 0
1644
1727
  : workspaceProjects.filter((project) => project.status === "archived").length,
@@ -1646,13 +1729,13 @@ async function runStatus(argv, io) {
1646
1729
  ? 0
1647
1730
  : workspaceProjects.filter((project) => project.status === "abandoned").length,
1648
1731
  projects: await Promise.all(projects.map(async (project) => {
1649
- const current = refreshProject(project);
1650
- const authority = projectAuthority(current, workspaceProjects, committedForkTargets);
1732
+ const current = refreshProject(projectWithEffectiveAuthority(project, authorityIndex));
1733
+ const authority = projectAuthority(current, authorityIndex);
1651
1734
  const nativeStage = await inspectNativeResearchStage(root, current);
1652
1735
  const evidencePipeline = await inspectEvidencePipelineForStatus(root, current);
1653
1736
  const snapshot = evidencePipeline.acquisition;
1654
1737
  const readyPackage = nextReadyPackage(current)?.id ?? null;
1655
- const scientificReview = await inspectScientificReviewStatus(root, current.id);
1738
+ const scientificReview = await inspectScientificReviewStatus(root, current.id, current);
1656
1739
  const evidenceAccess = current.scientificDesign
1657
1740
  ? await inspectEvidenceAccessForStatus(root, current.id)
1658
1741
  : null;
@@ -1671,6 +1754,7 @@ async function runStatus(argv, io) {
1671
1754
  evidencePipeline,
1672
1755
  nativeStage,
1673
1756
  scientificReview,
1757
+ task: await inspectProjectTask(root, current.id, authorityIndex.taskEvents.get(current.id) ?? []),
1674
1758
  evidenceAccess,
1675
1759
  publication,
1676
1760
  readyPackage,
@@ -1786,33 +1870,6 @@ async function inspectEvidenceAccessForStatus(root, projectId) {
1786
1870
  };
1787
1871
  }
1788
1872
  }
1789
- function projectAuthority(project, projects, committedForkTargets) {
1790
- if (project.lineage.kind === "fork" && !committedForkTargets.has(project.id)) {
1791
- return { state: "invalid", projectId: project.id };
1792
- }
1793
- const byId = new Map(projects.map((candidate) => [candidate.id, candidate]));
1794
- const visited = new Set([project.id]);
1795
- let current = project;
1796
- while (current.lineage.supersededBy) {
1797
- if (visited.has(current.lineage.supersededBy))
1798
- break;
1799
- visited.add(current.lineage.supersededBy);
1800
- const next = byId.get(current.lineage.supersededBy);
1801
- if (!next)
1802
- return { state: "superseded", projectId: current.lineage.supersededBy };
1803
- if (next.lineage.kind === "fork" && !committedForkTargets.has(next.id))
1804
- break;
1805
- current = next;
1806
- }
1807
- if (project.status === "archived")
1808
- return { state: "archived", projectId: current.id };
1809
- if (project.status === "abandoned")
1810
- return { state: "abandoned", projectId: current.id };
1811
- return {
1812
- state: project.id === current.id ? "authoritative" : "superseded",
1813
- projectId: current.id,
1814
- };
1815
- }
1816
1873
  async function inspectSnapshotForStatus(root, projectId) {
1817
1874
  const currentPath = join(workspacePaths(root).projects, projectId, "outputs", "evidence-snapshot.json");
1818
1875
  if (!(await pathExists(currentPath)))