codex-workflow-v2 2.0.0-beta.13.14 → 2.0.0-beta.13.16

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 (125) hide show
  1. package/README.md +17 -0
  2. package/dist/reviewer-runtime-build.json +20 -8
  3. package/dist/src/alpha6/captured-check-evidence.d.ts +49 -0
  4. package/dist/src/alpha6/captured-check-evidence.js +146 -0
  5. package/dist/src/alpha6/captured-check-evidence.js.map +1 -0
  6. package/dist/src/alpha6/corrective-decision-boundary.d.ts +4 -0
  7. package/dist/src/alpha6/corrective-decision-boundary.js +47 -0
  8. package/dist/src/alpha6/corrective-decision-boundary.js.map +1 -0
  9. package/dist/src/alpha6/root-cause-replan-carryover.d.ts +3 -1
  10. package/dist/src/alpha6/root-cause-replan-carryover.js +4 -0
  11. package/dist/src/alpha6/root-cause-replan-carryover.js.map +1 -1
  12. package/dist/src/contracts.d.ts +5 -1
  13. package/dist/src/dependency-provenance.js +10 -0
  14. package/dist/src/dependency-provenance.js.map +1 -1
  15. package/dist/src/graph.js +25 -3
  16. package/dist/src/graph.js.map +1 -1
  17. package/dist/src/version.d.ts +1 -1
  18. package/dist/src/version.js +1 -1
  19. package/dist/src/workflow-blocker-route.d.ts +8 -0
  20. package/dist/src/workflow-blocker-route.js +106 -0
  21. package/dist/src/workflow-blocker-route.js.map +1 -0
  22. package/dist/src/workflow.d.ts +1 -0
  23. package/dist/src/workflow.js +139 -19
  24. package/dist/src/workflow.js.map +1 -1
  25. package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
  26. package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
  27. package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
  28. package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +1 -1
  29. package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +2 -2
  30. package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +2 -2
  31. package/docs/release.md +49 -0
  32. package/package.json +2 -1
  33. package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +1 -1
  34. package/plugins/codex-workflow-gateway/references/chat-dispatch.md +40 -11
  35. package/plugins/codex-workflow-gateway/references/codebase-memory-routing.md +55 -0
  36. package/plugins/codex-workflow-gateway/scripts/chat-dispatch.mjs +7 -1
  37. package/plugins/codex-workflow-gateway/scripts/chat-model-policy.mjs +10 -8
  38. package/plugins/codex-workflow-gateway/scripts/chat-registry.mjs +23 -5
  39. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +78 -12
  40. package/src/alpha6/adoption.ts +1105 -0
  41. package/src/alpha6/captured-check-evidence.ts +126 -0
  42. package/src/alpha6/check-support-anchor.ts +128 -0
  43. package/src/alpha6/component-owner.ts +168 -0
  44. package/src/alpha6/corrective-decision-boundary.ts +39 -0
  45. package/src/alpha6/downstream-proof.ts +732 -0
  46. package/src/alpha6/failed-step-planning-recovery.ts +88 -0
  47. package/src/alpha6/handoff.ts +1443 -0
  48. package/src/alpha6/journal.ts +473 -0
  49. package/src/alpha6/literal-test-invocation.ts +84 -0
  50. package/src/alpha6/mechanical-feasibility.ts +488 -0
  51. package/src/alpha6/milestone.ts +2182 -0
  52. package/src/alpha6/npm-check-contract.ts +47 -0
  53. package/src/alpha6/plan-integrity-update.ts +20 -0
  54. package/src/alpha6/plan-integrity.ts +298 -0
  55. package/src/alpha6/plan-risk.ts +1480 -0
  56. package/src/alpha6/preexecution-replan.ts +187 -0
  57. package/src/alpha6/remediation-cause.ts +98 -0
  58. package/src/alpha6/remediation.ts +2965 -0
  59. package/src/alpha6/review.ts +1198 -0
  60. package/src/alpha6/root-cause-replan-carryover.ts +471 -0
  61. package/src/alpha6/store-sidecars.ts +335 -0
  62. package/src/alpha7/autonomy.ts +411 -0
  63. package/src/alpha7/corrective-recovery.ts +1332 -0
  64. package/src/artifacts.ts +130 -0
  65. package/src/beta1/project-transaction.ts +351 -0
  66. package/src/change-explanation.ts +153 -0
  67. package/src/cli-actions.ts +119 -0
  68. package/src/cli.ts +1583 -0
  69. package/src/contracts.ts +1510 -0
  70. package/src/credential-output.ts +89 -0
  71. package/src/credential-transport.ts +215 -0
  72. package/src/delegation.ts +190 -0
  73. package/src/dependency-provenance.ts +538 -0
  74. package/src/diagnostics.ts +93 -0
  75. package/src/domain/completed-step-carryover.ts +76 -0
  76. package/src/domain/discovery.ts +27 -0
  77. package/src/domain/plan-semantics.ts +58 -0
  78. package/src/domain/validation.ts +177 -0
  79. package/src/errors.ts +23 -0
  80. package/src/fs-utils.ts +61 -0
  81. package/src/gateway-handshake.ts +103 -0
  82. package/src/git.ts +158 -0
  83. package/src/graph.ts +342 -0
  84. package/src/historical-step-provenance.ts +136 -0
  85. package/src/index.ts +21 -0
  86. package/src/lifecycle/canonical-hash.ts +28 -0
  87. package/src/lifecycle/catalog.ts +202 -0
  88. package/src/lifecycle/compiler-inspection.ts +29 -0
  89. package/src/lifecycle/core-static-readiness.ts +132 -0
  90. package/src/lifecycle/corrective-replan-authority.ts +136 -0
  91. package/src/lifecycle/corrective-replan-binding-manifest.ts +51 -0
  92. package/src/lifecycle/corrective-replan-credential-core.ts +408 -0
  93. package/src/lifecycle/corrective-replan-credential-schema.ts +54 -0
  94. package/src/lifecycle/corrective-replan-credentials.ts +48 -0
  95. package/src/lifecycle/corrective-replan.ts +843 -0
  96. package/src/lifecycle/evaluator.ts +48 -0
  97. package/src/lifecycle/fingerprint.ts +488 -0
  98. package/src/lifecycle/immutable.ts +8 -0
  99. package/src/lifecycle/implementation-table.ts +118 -0
  100. package/src/lifecycle/index.ts +8 -0
  101. package/src/lifecycle/schema-artifact.ts +263 -0
  102. package/src/lifecycle/semantic-registry.ts +572 -0
  103. package/src/lifecycle/types.ts +838 -0
  104. package/src/memory.ts +273 -0
  105. package/src/migration.ts +161 -0
  106. package/src/navigation-actions.ts +70 -0
  107. package/src/observation.ts +198 -0
  108. package/src/observed-routes.ts +661 -0
  109. package/src/operational-contract.ts +125 -0
  110. package/src/pending-review-update.ts +191 -0
  111. package/src/repository.ts +99 -0
  112. package/src/reviewer.ts +1876 -0
  113. package/src/state/corrective-replan-executor.ts +818 -0
  114. package/src/state/corrective-replan-public-schema.ts +83 -0
  115. package/src/state/corrective-replan-public.ts +908 -0
  116. package/src/state/corrective-replan-transaction.ts +947 -0
  117. package/src/state/corrective-yield-executor.ts +327 -0
  118. package/src/state/corrective-yield-transaction.ts +728 -0
  119. package/src/state/lock.ts +880 -0
  120. package/src/state/store.ts +567 -0
  121. package/src/transition-core.ts +330 -0
  122. package/src/ulid.ts +24 -0
  123. package/src/version.ts +2 -0
  124. package/src/workflow-blocker-route.ts +109 -0
  125. package/src/workflow.ts +11436 -0
package/src/cli.ts ADDED
@@ -0,0 +1,1583 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import process from 'node:process';
5
+ import type {
6
+ CorrectivePlanAuditInput,
7
+ DelegationPolicyInput,
8
+ GraphKind,
9
+ GraphMode,
10
+ GraphRefreshEvidence,
11
+ GraphRefreshRequest,
12
+ KnowledgeSelection,
13
+ MilestonePlanInput,
14
+ ReviewInput,
15
+ TaskPlanInput,
16
+ WorkspaceOwner,
17
+ } from './contracts.js';
18
+ import type { RecordPlanRiskAuditInput } from './alpha6/plan-risk.js';
19
+ import { defaultTaskWorkerActor } from './alpha6/handoff.js';
20
+ import {
21
+ assertCliSuccessCredentialSafety,
22
+ projectCliErrorMessage,
23
+ projectCliErrorValue,
24
+ UnsafeCredentialOutputError,
25
+ } from './credential-output.js';
26
+ import {
27
+ assertNoLegacyCredentialArguments,
28
+ consumeCredentialReference,
29
+ issueCredentialReference,
30
+ resolveCredentialReference,
31
+ } from './credential-transport.js';
32
+ import { integrationDiagnostics } from './diagnostics.js';
33
+ import { WorkflowError } from './errors.js';
34
+ import { prepareGraphView } from './graph.js';
35
+ import { migrateState } from './migration.js';
36
+ import { changedFiles, currentBranch, isClean, resolveRepositoryIdentity } from './repository.js';
37
+ import { launchStrictReviewer } from './reviewer.js';
38
+ import type { ExternalStrictStepReviewInput, ExternalStrictTaskReviewInput, ReviewMode } from './reviewer.js';
39
+ import {
40
+ WorkflowService,
41
+ type AuditedRemediationMaterializationInput,
42
+ type DiscoveryInput,
43
+ type DiscoveryPatch,
44
+ } from './workflow.js';
45
+ import { gatewayHandshake } from './gateway-handshake.js';
46
+ import type {
47
+ CorrectiveReplanHumanGate,
48
+ CorrectiveReplanPublicCandidate,
49
+ CorrectiveReplanPublicCredential,
50
+ } from './state/corrective-replan-public.js';
51
+ import { CLI_ACTIONS } from './cli-actions.js';
52
+
53
+ interface ParsedArguments {
54
+ positionals: string[];
55
+ options: Map<string, string[]>;
56
+ }
57
+
58
+ interface CliHelpSpec {
59
+ requiredOptions: string[];
60
+ optionalOptions: string[];
61
+ fileInput?: {
62
+ description: string;
63
+ requiredTopLevelFields: string[];
64
+ reviewMode: "ordinary | security";
65
+ nestedReviewRequiredFields: string[];
66
+ example: Record<string, unknown>;
67
+ };
68
+ }
69
+
70
+ const CLI_HELP_SPECS: Record<string, CliHelpSpec> = {
71
+ 'change explain': { requiredOptions: ['--task', '--file'], optionalOptions: ['--repo', '--json'] },
72
+ 'milestone remediation-materialize': {
73
+ requiredOptions: ['--id', '--expected-revision', '--task', '--expected-task-revision', '--file'],
74
+ optionalOptions: ['--repo', '--json'],
75
+ },
76
+ 'update pending-review-source-preflight': { requiredOptions: ['--id'], optionalOptions: ['--repo', '--json'] },
77
+ 'update pending-review-dependency-preflight': { requiredOptions: ['--id', '--file'], optionalOptions: ['--repo', '--json'] },
78
+ 'update pending-review-dependency-recover': { requiredOptions: ['--id', '--file', '--expected-revision', '--actor', '--reason'], optionalOptions: ['--repo', '--json'] },
79
+ 'update dependency-provenance-preflight': {
80
+ requiredOptions: ['--id'],
81
+ optionalOptions: ['--repo', '--json'],
82
+ },
83
+ 'update corrective-carryover-preflight': {
84
+ requiredOptions: ['--id'],
85
+ optionalOptions: ['--repo', '--json', '--file', '--risk-audit-file'],
86
+ },
87
+ 'task historical-step-provenance-preflight': {
88
+ requiredOptions: ['--id'],
89
+ optionalOptions: ['--repo', '--json'],
90
+ },
91
+ 'task historical-step-provenance-recover': {
92
+ requiredOptions: ['--id', '--expected-revision', '--commit', '--actor', '--reason'],
93
+ optionalOptions: ['--repo', '--json'],
94
+ },
95
+ 'update dependency-provenance-recover': {
96
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--reason'],
97
+ optionalOptions: ['--repo', '--json'],
98
+ },
99
+ 'update historical-step-dependency-preflight': {
100
+ requiredOptions: ['--id'],
101
+ optionalOptions: ['--repo', '--json'],
102
+ },
103
+ 'update historical-step-dependency-recover': {
104
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--reason'],
105
+ optionalOptions: ['--repo', '--json'],
106
+ },
107
+ 'update downstream-proof-dependency-preflight': {
108
+ requiredOptions: ['--id'],
109
+ optionalOptions: ['--repo', '--json'],
110
+ },
111
+ 'update downstream-proof-dependency-recover': {
112
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--reason'],
113
+ optionalOptions: ['--repo', '--json'],
114
+ },
115
+ 'update downstream-proof-replan-update-preflight': {
116
+ requiredOptions: ['--id'],
117
+ optionalOptions: ['--repo', '--json'],
118
+ },
119
+ 'update downstream-proof-replan-dependency-preflight': {
120
+ requiredOptions: ['--id'],
121
+ optionalOptions: ['--repo', '--json'],
122
+ },
123
+ 'update downstream-proof-replan-dependency-recover': {
124
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--reason'],
125
+ optionalOptions: ['--repo', '--json'],
126
+ },
127
+ 'update plan-integrity-update-preflight': {
128
+ requiredOptions: ['--id'],
129
+ optionalOptions: ['--repo', '--json'],
130
+ },
131
+ 'update plan-integrity-dependency-preflight': {
132
+ requiredOptions: ['--id'],
133
+ optionalOptions: ['--repo', '--json'],
134
+ },
135
+ 'update plan-integrity-dependency-recover': {
136
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--reason'],
137
+ optionalOptions: ['--repo', '--json'],
138
+ },
139
+ 'milestone progress': {
140
+ requiredOptions: ['--id'],
141
+ optionalOptions: ['--repo', '--json'],
142
+ },
143
+ 'milestone authorize': {
144
+ requiredOptions: ['--id', '--expected-revision'],
145
+ optionalOptions: ['--actor', '--reason', '--delegation-grant', '--repo', '--json'],
146
+ },
147
+ 'milestone validate': {
148
+ requiredOptions: ['--id', '--expected-revision'],
149
+ optionalOptions: ['--repo', '--json'],
150
+ },
151
+ 'milestone accept': {
152
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--confirmation-code'],
153
+ optionalOptions: ['--delegation-grant', '--repo', '--json'],
154
+ },
155
+ 'task plan-set': {
156
+ requiredOptions: ['--id', '--expected-revision', '--file', '--risk-audit-file'],
157
+ optionalOptions: ['--corrective-audit-file', '--repo', '--json'],
158
+ },
159
+ 'task preexecution-replan': {
160
+ requiredOptions: [
161
+ '--id', '--expected-revision', '--actor', '--reason', '--file', '--expected-brief-hash',
162
+ '--expected-plan-hash', '--expected-plan-risk-audit-event-hash', '--expected-head-commit',
163
+ ],
164
+ optionalOptions: ['--repo', '--json'],
165
+ },
166
+ 'task handoff-replace': {
167
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--target-actor', '--reason'],
168
+ optionalOptions: ['--writer-credential-ref', '--delegation-grant', '--expires-at', '--repo', '--json'],
169
+ },
170
+ 'task handoff': {
171
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--reason'],
172
+ optionalOptions: ['--target-actor', '--writer-credential-ref', '--delegation-grant', '--expires-at', '--repo', '--json'],
173
+ },
174
+ 'task handoff-prepare': {
175
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--reason'],
176
+ optionalOptions: ['--target-actor', '--writer-credential-ref', '--delegation-grant', '--expires-at', '--repo', '--json'],
177
+ },
178
+ 'task claim': {
179
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--claim-credential-ref'],
180
+ optionalOptions: ['--writer-credential-ref', '--repo', '--json'],
181
+ },
182
+ 'task writer-credential-replace': {
183
+ requiredOptions: ['--id', '--expected-revision', '--actor'],
184
+ optionalOptions: ['--repo', '--json'],
185
+ },
186
+ 'task corrective-yield': {
187
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--writer-credential-ref', '--reason'],
188
+ optionalOptions: ['--repo', '--json'],
189
+ },
190
+ 'task plan-integrity-recover': {
191
+ requiredOptions: ['--id', '--step', '--expected-revision', '--actor'],
192
+ optionalOptions: ['--writer-credential-ref', '--repo', '--json'],
193
+ },
194
+ 'task downstream-proof-recover': {
195
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--writer-credential-ref', '--reason'],
196
+ optionalOptions: ['--repo', '--json'],
197
+ },
198
+ 'task downstream-proof-replan-recover': {
199
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--reason'],
200
+ optionalOptions: ['--repo', '--json'],
201
+ },
202
+ 'task stop-override-prepare': {
203
+ requiredOptions: ['--id', '--step', '--expected-revision', '--actor', '--reason'],
204
+ optionalOptions: ['--repo', '--json'],
205
+ },
206
+ 'task stop-override-apply': {
207
+ requiredOptions: ['--id', '--step', '--expected-revision', '--actor', '--reason', '--confirmation-code'],
208
+ optionalOptions: ['--repo', '--json'],
209
+ },
210
+ 'task corrective-yield-recover': {
211
+ requiredOptions: ['--id', '--transaction-id'],
212
+ optionalOptions: ['--repo', '--json'],
213
+ },
214
+ 'task corrective-replan-prepare': {
215
+ requiredOptions: ['--id', '--file'],
216
+ optionalOptions: ['--repo', '--json'],
217
+ },
218
+ 'task corrective-replan-confirm': {
219
+ requiredOptions: ['--id', '--file', '--gate-file', '--confirmation-code'],
220
+ optionalOptions: ['--repo', '--json'],
221
+ },
222
+ 'task corrective-replan-execute': {
223
+ requiredOptions: ['--id', '--file', '--credential-file'],
224
+ optionalOptions: ['--repo', '--json'],
225
+ },
226
+ 'task corrective-replan-recover': {
227
+ requiredOptions: ['--id', '--transaction-id'],
228
+ optionalOptions: ['--repo', '--json'],
229
+ },
230
+ 'task authorize': {
231
+ requiredOptions: ['--id', '--expected-revision'],
232
+ optionalOptions: ['--actor', '--reason', '--delegation-grant', '--repo', '--json'],
233
+ },
234
+ 'task start': {
235
+ requiredOptions: ['--id', '--expected-revision', '--workspace-owner'],
236
+ optionalOptions: ['--repo', '--json'],
237
+ },
238
+ 'task run': {
239
+ requiredOptions: ['--id', '--step', '--expected-revision'],
240
+ optionalOptions: ['--owner', '--writer-credential-ref', '--repo', '--json'],
241
+ },
242
+ 'task step-complete': {
243
+ requiredOptions: ['--id', '--step', '--expected-revision', '--writer-credential-ref'],
244
+ optionalOptions: ['--note', '--actor', '--repo', '--json'],
245
+ },
246
+ 'task submit': {
247
+ requiredOptions: ['--id', '--expected-revision', '--writer-credential-ref'],
248
+ optionalOptions: ['--actor', '--repo', '--json'],
249
+ },
250
+ 'task review-launch': {
251
+ requiredOptions: ['--id', '--expected-revision'],
252
+ optionalOptions: ['--actor', '--writer-credential-ref', '--repo', '--json'],
253
+ },
254
+ 'task review-packet': {
255
+ requiredOptions: ['--id', '--expected-revision', '--review-mode'],
256
+ optionalOptions: ['--repo', '--json'],
257
+ },
258
+ 'task review-sealed-record': {
259
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--file'],
260
+ optionalOptions: ['--writer-credential-ref', '--repo', '--json'],
261
+ fileInput: {
262
+ description: 'Full ExternalStrictTaskReviewInput envelope; reviewMode is a top-level JSON field.',
263
+ requiredTopLevelFields: ['protocol', 'packetHash', 'repositorySealHash', 'reviewerThreadId', 'reviewMode', 'review'],
264
+ reviewMode: 'ordinary | security',
265
+ nestedReviewRequiredFields: ['status', 'reviewer', 'summary', 'findings'],
266
+ example: {
267
+ protocol: 'codex-workflow-external-strict-task-review-v1',
268
+ packetHash: '<packet.packetHash>',
269
+ repositorySealHash: '<packet.repositorySealHash>',
270
+ reviewerThreadId: '<separate-reviewer-thread-id>',
271
+ reviewMode: 'ordinary',
272
+ review: { status: 'passed', reviewer: '<reviewer>', summary: '<summary>', findings: [] },
273
+ },
274
+ },
275
+ },
276
+ 'task step-review-packet': {
277
+ requiredOptions: ['--id', '--step', '--expected-revision', '--review-mode'],
278
+ optionalOptions: ['--repo', '--json'],
279
+ },
280
+ 'task step-review-record': {
281
+ requiredOptions: ['--id', '--step', '--expected-revision', '--actor', '--file'],
282
+ optionalOptions: ['--writer-credential-ref', '--repo', '--json'],
283
+ fileInput: {
284
+ description: 'Full ExternalStrictStepReviewInput envelope; reviewMode is a top-level JSON field.',
285
+ requiredTopLevelFields: ['protocol', 'packetHash', 'repositorySealHash', 'reviewerThreadId', 'reviewMode', 'review'],
286
+ reviewMode: 'ordinary | security',
287
+ nestedReviewRequiredFields: ['status', 'reviewer', 'summary', 'findings'],
288
+ example: {
289
+ protocol: 'codex-workflow-external-strict-step-review-v1',
290
+ packetHash: '<packet.packetHash>',
291
+ repositorySealHash: '<packet.repositorySealHash>',
292
+ reviewerThreadId: '<separate-reviewer-thread-id>',
293
+ reviewMode: 'ordinary',
294
+ review: { status: 'passed', reviewer: '<reviewer>', summary: '<summary>', findings: [] },
295
+ },
296
+ },
297
+ },
298
+ 'task result-set': {
299
+ requiredOptions: ['--id', '--expected-revision', '--summary', '--writer-credential-ref'],
300
+ optionalOptions: ['--actor', '--limitation', '--repo', '--json'],
301
+ },
302
+ 'task accept': {
303
+ requiredOptions: ['--id', '--expected-revision'],
304
+ optionalOptions: ['--actor', '--delegation-grant', '--repo', '--json'],
305
+ },
306
+ 'task merge': {
307
+ requiredOptions: ['--id', '--expected-revision', '--writer-credential-ref'],
308
+ optionalOptions: ['--actor', '--repo', '--json'],
309
+ },
310
+ 'task merge-confirm': {
311
+ requiredOptions: ['--id', '--expected-revision', '--writer-credential-ref'],
312
+ optionalOptions: ['--actor', '--repo', '--json'],
313
+ },
314
+ };
315
+
316
+ function parseArguments(argv: string[]): ParsedArguments {
317
+ const positionals: string[] = [];
318
+ const options = new Map<string, string[]>();
319
+ for (let index = 0; index < argv.length; index += 1) {
320
+ const argument = argv[index];
321
+ if (!argument) continue;
322
+ if (!argument.startsWith('--')) {
323
+ positionals.push(argument);
324
+ continue;
325
+ }
326
+ const equals = argument.indexOf('=');
327
+ const name = argument.slice(2, equals === -1 ? undefined : equals);
328
+ const inlineValue = equals === -1 ? null : argument.slice(equals + 1);
329
+ const next = argv[index + 1];
330
+ const value = inlineValue ?? (next && !next.startsWith('--') ? next : 'true');
331
+ if (inlineValue === null && next && !next.startsWith('--')) index += 1;
332
+ options.set(name, [...(options.get(name) ?? []), value]);
333
+ }
334
+ return { positionals, options };
335
+ }
336
+
337
+ function option(args: ParsedArguments, name: string): string | undefined {
338
+ return args.options.get(name)?.at(-1);
339
+ }
340
+
341
+ function reviewMode(args: ParsedArguments): ReviewMode {
342
+ const value = required(args, 'review-mode');
343
+ if (value !== 'ordinary' && value !== 'security') {
344
+ throw new WorkflowError('INVALID_ARGUMENT', '--review-mode must be ordinary or security.');
345
+ }
346
+ return value;
347
+ }
348
+
349
+ function cliHelp(args: ParsedArguments): Record<string, unknown> {
350
+ const [noun, action] = args.positionals;
351
+ if (!noun) {
352
+ return {
353
+ kind: 'cli-help',
354
+ readOnly: true,
355
+ scope: 'top-level',
356
+ usage: 'codex-workflow <command> [action] [options]',
357
+ commands: ['doctor', 'status', 'next', ...Object.keys(CLI_ACTIONS)],
358
+ };
359
+ }
360
+ if (!action) {
361
+ if (['doctor', 'status', 'next'].includes(noun)) {
362
+ const taskSelector = noun === 'next' ? ['--task'] : [];
363
+ return {
364
+ kind: 'cli-help',
365
+ readOnly: true,
366
+ scope: 'command',
367
+ command: noun,
368
+ usage: noun === 'next'
369
+ ? 'codex-workflow next [--task <task-id>] [--repo <path>] [--json]'
370
+ : noun === 'status'
371
+ ? 'codex-workflow status [--full] [--repo <path>] [--json]'
372
+ : `codex-workflow ${noun} [--repo <path>] [--json]`,
373
+ requiredOptions: [],
374
+ optionalOptions: [...taskSelector, ...(noun === 'status' ? ['--full'] : []), '--repo', '--json'],
375
+ exactOptionContractAvailable: true,
376
+ };
377
+ }
378
+ return {
379
+ kind: 'cli-help',
380
+ readOnly: true,
381
+ scope: 'command',
382
+ command: noun,
383
+ usage: `codex-workflow ${noun} <action> [options]`,
384
+ actions: CLI_ACTIONS[noun] ?? [],
385
+ };
386
+ }
387
+ const command = `${noun} ${action}`;
388
+ const spec = CLI_HELP_SPECS[command];
389
+ return {
390
+ kind: 'cli-help',
391
+ readOnly: true,
392
+ scope: 'action',
393
+ command,
394
+ usage: spec
395
+ ? ['codex-workflow', noun, action, ...spec.requiredOptions.map((name) => `${name} <value>`), '[options]'].join(' ')
396
+ : `codex-workflow ${noun} ${action} [options]`,
397
+ requiredOptions: spec?.requiredOptions ?? null,
398
+ optionalOptions: spec?.optionalOptions ?? null,
399
+ exactOptionContractAvailable: Boolean(spec),
400
+ ...(spec?.fileInput ? { fileInput: spec.fileInput } : {}),
401
+ ...(spec ? {} : { note: 'Consult the packaged protocol for this action option contract.' }),
402
+ };
403
+ }
404
+
405
+ function required(args: ParsedArguments, name: string): string {
406
+ const value = option(args, name)?.trim();
407
+ if (!value) throw new WorkflowError('INVALID_ARGUMENT', `Missing required option --${name}.`);
408
+ return value;
409
+ }
410
+
411
+ function repeated(args: ParsedArguments, name: string): string[] {
412
+ return (args.options.get(name) ?? []).map((value) => value.trim()).filter(Boolean);
413
+ }
414
+
415
+ function revision(args: ParsedArguments): number {
416
+ const value = Number(required(args, 'expected-revision'));
417
+ if (!Number.isInteger(value) || value < 1) {
418
+ throw new WorkflowError('INVALID_ARGUMENT', '--expected-revision must be a positive integer.');
419
+ }
420
+ return value;
421
+ }
422
+
423
+ function positiveIntegerOption(args: ParsedArguments, name: string): number {
424
+ const value = Number(required(args, name));
425
+ if (!Number.isInteger(value) || value < 1) {
426
+ throw new WorkflowError('INVALID_ARGUMENT', `--${name} must be a positive integer.`);
427
+ }
428
+ return value;
429
+ }
430
+
431
+ function optionalRevision(args: ParsedArguments): number | null {
432
+ const raw = option(args, 'expected-revision');
433
+ if (raw === undefined) return null;
434
+ const value = Number(raw);
435
+ if (!Number.isInteger(value) || value < 1) {
436
+ throw new WorkflowError('INVALID_ARGUMENT', '--expected-revision must be a positive integer.');
437
+ }
438
+ return value;
439
+ }
440
+
441
+ function graphKind(args: ParsedArguments): GraphKind {
442
+ const value = required(args, 'kind');
443
+ if (value !== 'product' && value !== 'workflow') {
444
+ throw new WorkflowError('INVALID_ARGUMENT', '--kind must be product or workflow.');
445
+ }
446
+ return value;
447
+ }
448
+
449
+ function graphMode(args: ParsedArguments): GraphMode {
450
+ const value = option(args, 'mode') ?? 'moderate';
451
+ if (value !== 'moderate' && value !== 'full') {
452
+ throw new WorkflowError('INVALID_ARGUMENT', '--mode must be moderate or full.');
453
+ }
454
+ return value;
455
+ }
456
+
457
+ function jsonFile<T>(args: ParsedArguments, optionName = 'file'): T {
458
+ const file = path.resolve(required(args, optionName));
459
+ try {
460
+ return JSON.parse(readFileSync(file, 'utf8')) as T;
461
+ } catch (error) {
462
+ throw new WorkflowError('INVALID_ARGUMENT', `Cannot read JSON from ${file}.`, {
463
+ cause: error instanceof Error ? error.message : String(error),
464
+ });
465
+ }
466
+ }
467
+
468
+ function stdinJson<T>(): T {
469
+ try {
470
+ return JSON.parse(readFileSync(0, 'utf8')) as T;
471
+ } catch (error) {
472
+ throw new WorkflowError('INVALID_ARGUMENT', 'Cannot read JSON from stdin.', {
473
+ cause: error instanceof Error ? error.message : String(error),
474
+ });
475
+ }
476
+ }
477
+
478
+ function repository(args: ParsedArguments): string {
479
+ return path.resolve(option(args, 'repo') ?? '.');
480
+ }
481
+
482
+ const WRITER_CREDENTIAL_ACTIONS = [
483
+ 'task run',
484
+ 'task step-complete',
485
+ 'task submit',
486
+ 'task review-record',
487
+ 'task review-launch',
488
+ 'task review-sealed-record',
489
+ 'task step-review',
490
+ 'task step-review-record',
491
+ 'task corrective-decision-recover',
492
+ 'task remediation-mode-recover',
493
+ 'task plan-integrity-recover',
494
+ 'task downstream-proof-recover',
495
+ 'task corrective-decision',
496
+ 'task corrective-yield',
497
+ 'task handoff',
498
+ 'task handoff-prepare',
499
+ 'task handoff-replace',
500
+ 'task claim',
501
+ 'task handback',
502
+ 'task handback-create',
503
+ 'task result-set',
504
+ 'task merge',
505
+ 'task merge-confirm',
506
+ ] as const;
507
+
508
+ function credentialToken(
509
+ workflow: WorkflowService,
510
+ repo: string,
511
+ args: ParsedArguments,
512
+ kind: 'writer-lease' | 'claim-token',
513
+ action: string,
514
+ entityId: string,
515
+ actor: string | null,
516
+ requiredReference: boolean,
517
+ ): string | null {
518
+ const optionName = kind === 'writer-lease' ? 'writer-credential-ref' : 'claim-credential-ref';
519
+ const reference = option(args, optionName);
520
+ if (!reference) {
521
+ if (requiredReference) {
522
+ throw new WorkflowError('INVALID_ARGUMENT', `Missing required option --${optionName}.`);
523
+ }
524
+ return null;
525
+ }
526
+ const identity = resolveRepositoryIdentity(repo);
527
+ return resolveCredentialReference({
528
+ stateRoot: workflow.store.root,
529
+ projectId: identity.projectId,
530
+ reference,
531
+ kind,
532
+ entityId,
533
+ actor,
534
+ action,
535
+ }).token;
536
+ }
537
+
538
+ function writerCredentialReceipt(
539
+ workflow: WorkflowService,
540
+ repo: string,
541
+ taskId: string,
542
+ actor: string,
543
+ token: string,
544
+ expiresAt: string,
545
+ ) {
546
+ const identity = resolveRepositoryIdentity(repo);
547
+ return issueCredentialReference({
548
+ stateRoot: workflow.store.root,
549
+ projectId: identity.projectId,
550
+ entityId: taskId,
551
+ actor,
552
+ kind: 'writer-lease',
553
+ token,
554
+ allowedActions: WRITER_CREDENTIAL_ACTIONS,
555
+ expiresAt,
556
+ oneTime: false,
557
+ });
558
+ }
559
+
560
+ function consumeCredentialOption(
561
+ workflow: WorkflowService,
562
+ repo: string,
563
+ args: ParsedArguments,
564
+ optionName: 'writer-credential-ref' | 'claim-credential-ref',
565
+ ): void {
566
+ const reference = option(args, optionName);
567
+ if (!reference) return;
568
+ consumeCredentialReference(workflow.store.root, resolveRepositoryIdentity(repo).projectId, reference);
569
+ }
570
+
571
+ async function dispatch(args: ParsedArguments): Promise<unknown> {
572
+ const [noun, action] = args.positionals;
573
+ if (args.options.has('help')) return cliHelp(args);
574
+ const repo = repository(args);
575
+
576
+ if (noun === 'gateway' && action === 'handshake') {
577
+ return gatewayHandshake();
578
+ }
579
+
580
+ const workflow = new WorkflowService();
581
+
582
+ if (noun === 'doctor') {
583
+ const identity = resolveRepositoryIdentity(repo);
584
+ const integration = integrationDiagnostics(identity.repositoryRoot);
585
+ const nodeSupported = Number(process.versions.node.split('.')[0]) >= 22;
586
+ return {
587
+ ok: nodeSupported && integration.localDependency.current,
588
+ node: process.versions.node,
589
+ requiredNode: '>=22',
590
+ repositoryRoot: identity.repositoryRoot,
591
+ projectId: identity.projectId,
592
+ stateRoot: workflow.store.root,
593
+ branch: currentBranch(identity.repositoryRoot),
594
+ clean: isClean(identity.repositoryRoot),
595
+ changedFiles: changedFiles(identity.repositoryRoot),
596
+ integration,
597
+ limitations: [
598
+ 'CLI diagnostics cannot prove that Codex enforced a role, model, sandbox, or read-only reviewer.',
599
+ 'MCP refresh execution is performed by the personal gateway; the core verifies returned binding evidence.',
600
+ ],
601
+ };
602
+ }
603
+ if (noun === 'status') return args.options.has('full') ? workflow.status(repo) : workflow.compactStatus(repo);
604
+ if (noun === 'next') return workflow.next(repo, option(args, 'task') ?? null);
605
+ if (noun === 'change' && action === 'explain') return workflow.explainChange(repo, required(args, 'task'), jsonFile(args));
606
+ if (noun === 'update' && action === 'pending-review-source-preflight') {
607
+ return workflow.pendingReviewUpdatePreflight(repo, required(args, 'id'));
608
+ }
609
+ if (noun === 'update' && (action === 'pending-review-dependency-preflight' || action === 'pending-review-dependency-recover')) {
610
+ const receipt = JSON.parse(readFileSync(required(args, 'file'), 'utf8')) as import('./pending-review-update.js').PendingReviewUpdatePreflight | { ok: true; value: import('./pending-review-update.js').PendingReviewUpdatePreflight };
611
+ const source = 'value' in receipt && receipt.ok === true ? receipt.value : receipt as import('./pending-review-update.js').PendingReviewUpdatePreflight;
612
+ if (!source.eligible || !source.binding) throw new WorkflowError('INVALID_ARGUMENT', 'An eligible source preflight receipt is required.');
613
+ return action === 'pending-review-dependency-preflight'
614
+ ? workflow.pendingReviewDependencyPreflight(repo, required(args, 'id'), source.binding)
615
+ : workflow.recoverPendingReviewDependency(repo, required(args, 'id'), revision(args), source.binding, required(args, 'actor'), required(args, 'reason'));
616
+ }
617
+ if (noun === 'update' && action === 'preflight') return workflow.updatePreflight(repo);
618
+ if (noun === 'update' && action === 'rescue-preflight') {
619
+ return workflow.alpha6StrictReviewRescuePreflight(repo);
620
+ }
621
+ if (noun === 'update' && action === 'dependency-provenance-preflight') {
622
+ return workflow.dependencyProvenanceRecoveryPreflight(repo, required(args, 'id'));
623
+ }
624
+ if (noun === 'update' && action === 'corrective-carryover-preflight') {
625
+ return workflow.correctiveCarryoverUpdatePreflight(
626
+ repo,
627
+ required(args, 'id'),
628
+ option(args, 'file') || option(args, 'risk-audit-file') ? {
629
+ plan: jsonFile<TaskPlanInput>(args),
630
+ planRiskAudit: jsonFile<RecordPlanRiskAuditInput>(args, 'risk-audit-file'),
631
+ } : null,
632
+ );
633
+ }
634
+ if (noun === 'task' && action === 'historical-step-provenance-preflight') {
635
+ return workflow.historicalStepProvenanceRecoveryPreflight(repo, required(args, 'id'));
636
+ }
637
+ if (noun === 'task' && action === 'historical-step-provenance-recover') {
638
+ return workflow.recoverHistoricalStepProvenance(
639
+ repo,
640
+ required(args, 'id'),
641
+ revision(args),
642
+ required(args, 'commit'),
643
+ required(args, 'actor'),
644
+ required(args, 'reason'),
645
+ );
646
+ }
647
+ if (noun === 'update' && action === 'dependency-provenance-recover') {
648
+ return workflow.recoverDependencyProvenance(
649
+ repo,
650
+ required(args, 'id'),
651
+ revision(args),
652
+ required(args, 'actor'),
653
+ required(args, 'reason'),
654
+ );
655
+ }
656
+ if (noun === 'update' && action === 'historical-step-dependency-preflight') {
657
+ return workflow.historicalStepDependencyRecoveryPreflight(repo, required(args, 'id'));
658
+ }
659
+ if (noun === 'update' && action === 'historical-step-dependency-recover') {
660
+ return workflow.recoverHistoricalStepDependencyProvenance(
661
+ repo,
662
+ required(args, 'id'),
663
+ revision(args),
664
+ required(args, 'actor'),
665
+ required(args, 'reason'),
666
+ );
667
+ }
668
+ if (noun === 'update' && action === 'downstream-proof-dependency-preflight') {
669
+ return workflow.activeDownstreamProofDependencyRecoveryPreflight(repo, required(args, 'id'));
670
+ }
671
+ if (noun === 'update' && action === 'downstream-proof-dependency-recover') {
672
+ return workflow.recoverActiveDownstreamProofDependencyProvenance(
673
+ repo,
674
+ required(args, 'id'),
675
+ revision(args),
676
+ required(args, 'actor'),
677
+ required(args, 'reason'),
678
+ );
679
+ }
680
+ if (noun === 'update' && action === 'downstream-proof-replan-update-preflight') {
681
+ return workflow.downstreamProofReplanUpdatePreflight(repo, required(args, 'id'));
682
+ }
683
+ if (noun === 'update' && action === 'downstream-proof-replan-dependency-preflight') {
684
+ return workflow.downstreamProofReplanDependencyRecoveryPreflight(repo, required(args, 'id'));
685
+ }
686
+ if (noun === 'update' && action === 'downstream-proof-replan-dependency-recover') {
687
+ return workflow.recoverDownstreamProofReplanDependencyProvenance(
688
+ repo,
689
+ required(args, 'id'),
690
+ revision(args),
691
+ required(args, 'actor'),
692
+ required(args, 'reason'),
693
+ );
694
+ }
695
+ if (noun === 'update' && action === 'plan-integrity-update-preflight') {
696
+ return workflow.planIntegrityUpdatePreflight(repo, required(args, 'id'));
697
+ }
698
+ if (noun === 'update' && action === 'plan-integrity-dependency-preflight') {
699
+ return workflow.planIntegrityDependencyRecoveryPreflight(repo, required(args, 'id'));
700
+ }
701
+ if (noun === 'update' && action === 'plan-integrity-dependency-recover') {
702
+ return workflow.recoverPlanIntegrityDependencyProvenance(
703
+ repo,
704
+ required(args, 'id'),
705
+ revision(args),
706
+ required(args, 'actor'),
707
+ required(args, 'reason'),
708
+ );
709
+ }
710
+ if (noun === 'delegation' && action === 'prepare') {
711
+ return workflow.prepareDelegation(repo, jsonFile<DelegationPolicyInput>(args));
712
+ }
713
+ if (noun === 'delegation' && action === 'grant') {
714
+ return workflow.grantDelegation(
715
+ repo,
716
+ jsonFile<DelegationPolicyInput>(args),
717
+ required(args, 'confirmation-code'),
718
+ );
719
+ }
720
+ if (noun === 'delegation' && action === 'list') {
721
+ const identity = resolveRepositoryIdentity(repo);
722
+ return workflow.listDelegations(identity.projectId);
723
+ }
724
+ if (noun === 'delegation' && action === 'show') {
725
+ const identity = resolveRepositoryIdentity(repo);
726
+ return workflow.store.readDelegation(identity.projectId, required(args, 'id'));
727
+ }
728
+ if (noun === 'delegation' && action === 'revoke') {
729
+ return workflow.revokeDelegation(
730
+ repo,
731
+ required(args, 'id'),
732
+ revision(args),
733
+ required(args, 'actor'),
734
+ required(args, 'reason'),
735
+ );
736
+ }
737
+ if (noun === 'project-memory' && action === 'scan') return workflow.scanKnowledgeMap(repo);
738
+ if (noun === 'project-memory' && action === 'show') return workflow.showKnowledgeMap(repo);
739
+ if (noun === 'project-memory' && action === 'status') return workflow.knowledgeMapStatus(repo);
740
+ if (noun === 'project-memory' && action === 'approve') {
741
+ const input = jsonFile<{ selections: KnowledgeSelection[] }>(args);
742
+ if (!Array.isArray(input.selections)) {
743
+ throw new WorkflowError('INVALID_ARGUMENT', 'Knowledge approval file requires a selections array.');
744
+ }
745
+ return workflow.approveKnowledgeMap(
746
+ repo,
747
+ revision(args),
748
+ input.selections,
749
+ option(args, 'actor') ?? 'user',
750
+ option(args, 'delegation-grant') ?? null,
751
+ );
752
+ }
753
+ if (noun === 'project-memory' && action === 'reconcile') {
754
+ return workflow.reconcileKnowledgeMap(repo, revision(args));
755
+ }
756
+ if (noun === 'state' && action === 'migrate') {
757
+ const dryRun = option(args, 'dry-run') === 'true';
758
+ const snapshot = option(args, 'snapshot') === 'true';
759
+ if (dryRun === snapshot) {
760
+ throw new WorkflowError('INVALID_ARGUMENT', 'state migrate requires exactly one of --dry-run or --snapshot.');
761
+ }
762
+ return migrateState(repo, workflow.store, dryRun, snapshot);
763
+ }
764
+ if (noun === 'state' && action === 'adoption-prepare') {
765
+ return workflow.prepareAdoption(repo);
766
+ }
767
+ if (noun === 'state' && action === 'adoption-apply') {
768
+ return workflow.applyAdoption(
769
+ repo,
770
+ required(args, 'actor'),
771
+ required(args, 'confirmation-code'),
772
+ );
773
+ }
774
+ if (noun === 'milestone' && action === 'recover') {
775
+ return workflow.recoverMilestoneTransactions(repo);
776
+ }
777
+ if (noun === 'graph' && action === 'prepare') {
778
+ return prepareGraphView(option(args, 'target') ? path.resolve(required(args, 'target')) : undefined);
779
+ }
780
+ if (noun === 'graph' && action === 'status') {
781
+ return workflow.graphStatus(repo, graphKind(args), graphMode(args));
782
+ }
783
+ if (noun === 'graph' && action === 'refresh-request') {
784
+ return workflow.graphRefreshRequest(repo, graphKind(args), graphMode(args));
785
+ }
786
+ if (noun === 'graph' && action === 'bind') {
787
+ const input = stdinJson<{ request: GraphRefreshRequest; evidence: GraphRefreshEvidence }>();
788
+ return workflow.bindGraph(repo, optionalRevision(args), input.request, input.evidence);
789
+ }
790
+ if (noun === 'graph' && action === 'fallback') {
791
+ return workflow.recordGraphFallback(
792
+ repo,
793
+ graphKind(args),
794
+ graphMode(args),
795
+ optionalRevision(args),
796
+ required(args, 'reason'),
797
+ );
798
+ }
799
+ if (noun === 'context') {
800
+ const role = required(args, 'role');
801
+ if (role === 'worker' || role === 'scope-lead') {
802
+ throw new WorkflowError(
803
+ 'INVALID_ARGUMENT',
804
+ role === 'worker'
805
+ ? 'Worker context is issued by task run together with a writer lease.'
806
+ : 'Scope Lead works on Discovery before Task materialization.',
807
+ );
808
+ }
809
+ if (!['technical-planner', 'delivery-coordinator', 'independent-reviewer'].includes(role)) {
810
+ throw new WorkflowError('INVALID_ARGUMENT', 'Unknown context role.');
811
+ }
812
+ return workflow.taskContext(
813
+ repo,
814
+ required(args, 'task'),
815
+ role as 'technical-planner' | 'delivery-coordinator' | 'independent-reviewer',
816
+ );
817
+ }
818
+
819
+ if (noun === 'discovery' && action === 'start') {
820
+ const outcome = option(args, 'outcome')?.trim();
821
+ const input: DiscoveryInput = {
822
+ goal: required(args, 'goal'),
823
+ ...(outcome ? { outcome } : {}),
824
+ facts: repeated(args, 'fact'),
825
+ decisions: repeated(args, 'decision'),
826
+ assumptions: repeated(args, 'assumption'),
827
+ unknowns: repeated(args, 'unknown'),
828
+ blockingUnknowns: repeated(args, 'blocking-unknown'),
829
+ scope: repeated(args, 'scope'),
830
+ outOfScope: repeated(args, 'out-of-scope'),
831
+ constraints: repeated(args, 'constraint'),
832
+ acceptance: repeated(args, 'acceptance'),
833
+ };
834
+ return workflow.startDiscovery(repo, input);
835
+ }
836
+ if (noun === 'discovery' && action === 'update') {
837
+ const goal = option(args, 'goal')?.trim();
838
+ const outcome = option(args, 'outcome')?.trim();
839
+ const patch: DiscoveryPatch = {
840
+ ...(goal ? { goal } : {}),
841
+ ...(outcome ? { outcome } : {}),
842
+ facts: repeated(args, 'fact'),
843
+ decisions: repeated(args, 'decision'),
844
+ assumptions: repeated(args, 'assumption'),
845
+ unknowns: repeated(args, 'unknown'),
846
+ blockingUnknowns: repeated(args, 'blocking-unknown'),
847
+ resolveUnknowns: repeated(args, 'resolve-unknown'),
848
+ scope: repeated(args, 'scope'),
849
+ outOfScope: repeated(args, 'out-of-scope'),
850
+ constraints: repeated(args, 'constraint'),
851
+ acceptance: repeated(args, 'acceptance'),
852
+ };
853
+ return workflow.updateDiscovery(repo, required(args, 'id'), revision(args), patch);
854
+ }
855
+ if (noun === 'discovery' && action === 'show') {
856
+ const identity = resolveRepositoryIdentity(repo);
857
+ return workflow.store.readDiscovery(identity.projectId, required(args, 'id'));
858
+ }
859
+ if (noun === 'discovery' && action === 'materialize') {
860
+ const kind = option(args, 'kind') ?? 'task';
861
+ if (kind === 'task') {
862
+ return workflow.materializeTask(
863
+ repo,
864
+ required(args, 'id'),
865
+ revision(args),
866
+ required(args, 'title'),
867
+ option(args, 'base') ?? 'main',
868
+ option(args, 'milestone') ?? null,
869
+ );
870
+ }
871
+ if (kind === 'milestone') {
872
+ return workflow.materializeMilestone(
873
+ repo,
874
+ required(args, 'id'),
875
+ revision(args),
876
+ required(args, 'title'),
877
+ option(args, 'base') ?? 'main',
878
+ );
879
+ }
880
+ throw new WorkflowError('INVALID_ARGUMENT', '--kind must be task or milestone.');
881
+ }
882
+
883
+ if (noun === 'task' && action === 'show') {
884
+ const identity = resolveRepositoryIdentity(repo);
885
+ return workflow.store.readTask(identity.projectId, required(args, 'id'));
886
+ }
887
+ if (noun === 'task' && action === 'replacement-materialize') {
888
+ return workflow.replaceSplitRequiredTask(
889
+ repo,
890
+ required(args, 'id'),
891
+ revision(args),
892
+ required(args, 'discovery'),
893
+ positiveIntegerOption(args, 'expected-discovery-revision'),
894
+ positiveIntegerOption(args, 'expected-milestone-revision'),
895
+ required(args, 'title'),
896
+ option(args, 'actor') ?? 'user',
897
+ );
898
+ }
899
+ if (noun === 'task' && action === 'preexecution-replan') {
900
+ return workflow.recordPreExecutionReplan(
901
+ repo,
902
+ required(args, 'id'),
903
+ revision(args),
904
+ required(args, 'actor'),
905
+ required(args, 'reason'),
906
+ {
907
+ expectedBriefHash: required(args, 'expected-brief-hash'),
908
+ expectedPlanHash: required(args, 'expected-plan-hash'),
909
+ expectedPlanRiskAuditEventHash: required(args, 'expected-plan-risk-audit-event-hash'),
910
+ expectedHeadCommit: required(args, 'expected-head-commit'),
911
+ },
912
+ jsonFile<RecordPlanRiskAuditInput>(args),
913
+ );
914
+ }
915
+ if (noun === 'task' && action === 'plan-set') {
916
+ return workflow.setTaskPlan(
917
+ repo,
918
+ required(args, 'id'),
919
+ revision(args),
920
+ jsonFile<TaskPlanInput>(args),
921
+ option(args, 'corrective-audit-file')
922
+ ? jsonFile<CorrectivePlanAuditInput>(args, 'corrective-audit-file')
923
+ : null,
924
+ jsonFile<RecordPlanRiskAuditInput>(args, 'risk-audit-file'),
925
+ );
926
+ }
927
+ if (noun === 'task' && action === 'knowledge-rebind') {
928
+ return workflow.rebindTaskKnowledge(repo, required(args, 'id'), revision(args));
929
+ }
930
+ if (noun === 'task' && action === 'context-refresh') {
931
+ return workflow.refreshTaskContext(
932
+ repo,
933
+ required(args, 'id'),
934
+ positiveIntegerOption(args, 'expected-task-revision'),
935
+ positiveIntegerOption(args, 'expected-map-revision'),
936
+ required(args, 'actor'),
937
+ required(args, 'delegation-grant'),
938
+ );
939
+ }
940
+ if (noun === 'task' && action === 'authorize') {
941
+ return workflow.authorizeTask(
942
+ repo,
943
+ required(args, 'id'),
944
+ revision(args),
945
+ option(args, 'actor') ?? 'user',
946
+ option(args, 'reason') ?? '',
947
+ option(args, 'delegation-grant') ?? null,
948
+ );
949
+ }
950
+ if (noun === 'task' && action === 'plan-risk-audit') {
951
+ return workflow.recordPlanRiskAudit(
952
+ repo,
953
+ required(args, 'id'),
954
+ revision(args),
955
+ jsonFile<RecordPlanRiskAuditInput>(args),
956
+ );
957
+ }
958
+ if (noun === 'task' && action === 'start') {
959
+ const owner = required(args, 'workspace-owner');
960
+ if (owner !== 'local' && owner !== 'external') {
961
+ throw new WorkflowError('INVALID_ARGUMENT', '--workspace-owner must be local or external.');
962
+ }
963
+ return workflow.startTask(repo, required(args, 'id'), revision(args), owner as WorkspaceOwner);
964
+ }
965
+ if (noun === 'task' && action === 'run') {
966
+ const taskId = required(args, 'id');
967
+ const owner = option(args, 'owner') ?? 'worker';
968
+ const outcome = workflow.runStep(
969
+ repo,
970
+ taskId,
971
+ required(args, 'step'),
972
+ revision(args),
973
+ owner,
974
+ credentialToken(workflow, repo, args, 'writer-lease', 'task run', taskId, owner, false),
975
+ );
976
+ const { writerToken, ...envelope } = outcome.envelope;
977
+ if (outcome.writerCredential.disposition === 'issued' && !writerToken) {
978
+ throw new WorkflowError('STATE_CORRUPT', 'Fresh task run credential issuance did not return its protocol-v1 writer token.');
979
+ }
980
+ if (outcome.writerCredential.disposition === 'renewed' && writerToken !== null) {
981
+ throw new WorkflowError('STATE_CORRUPT', 'Renewed task run credential unexpectedly returned a writer token.');
982
+ }
983
+ return {
984
+ writerCredential: { disposition: outcome.writerCredential.disposition },
985
+ ...(writerToken ? {
986
+ writerCredentialReference: writerCredentialReceipt(
987
+ workflow, repo, taskId, outcome.lease.owner, writerToken, outcome.lease.expiresAt,
988
+ ),
989
+ } : {}),
990
+ task: outcome.task,
991
+ lease: outcome.lease,
992
+ envelope,
993
+ };
994
+ }
995
+ if (noun === 'task' && action === 'step-complete') {
996
+ const taskId = required(args, 'id');
997
+ return workflow.completeStep(
998
+ repo,
999
+ taskId,
1000
+ required(args, 'step'),
1001
+ revision(args),
1002
+ credentialToken(workflow, repo, args, 'writer-lease', 'task step-complete', taskId, option(args, 'actor') ?? null, true)!,
1003
+ repeated(args, 'note'),
1004
+ option(args, 'actor'),
1005
+ );
1006
+ }
1007
+ if (noun === 'task' && action === 'submit') {
1008
+ const taskId = required(args, 'id');
1009
+ return workflow.submitTask(
1010
+ repo,
1011
+ taskId,
1012
+ revision(args),
1013
+ credentialToken(workflow, repo, args, 'writer-lease', 'task submit', taskId, option(args, 'actor') ?? null, true)!,
1014
+ option(args, 'actor'),
1015
+ );
1016
+ }
1017
+ if (noun === 'task' && action === 'review-record') {
1018
+ const review = jsonFile<ReviewInput>(args);
1019
+ return workflow.recordReview(
1020
+ repo,
1021
+ required(args, 'id'),
1022
+ revision(args),
1023
+ review,
1024
+ option(args, 'actor'),
1025
+ credentialToken(workflow, repo, args, 'writer-lease', 'task review-record', required(args, 'id'), option(args, 'actor') ?? null, false),
1026
+ );
1027
+ }
1028
+ if (noun === 'task' && action === 'review-launch') {
1029
+ const identity = resolveRepositoryIdentity(repo);
1030
+ const id = required(args, 'id');
1031
+ const expectedRevision = revision(args);
1032
+ const task = workflow.store.readTask(identity.projectId, id);
1033
+ if (task.revision !== expectedRevision) {
1034
+ throw new WorkflowError('STATE_CONFLICT', `State revision conflict for ${id}`, {
1035
+ expectedRevision,
1036
+ actualRevision: task.revision,
1037
+ });
1038
+ }
1039
+ const envelope = workflow.taskContext(repo, id, 'independent-reviewer');
1040
+ const launch = launchStrictReviewer(
1041
+ identity.repositoryRoot,
1042
+ task,
1043
+ workflow.store.taskRoot(task.projectId, task.id),
1044
+ envelope,
1045
+ );
1046
+ // The independent reviewer identity belongs to the strict reviewer payload. CLI --actor
1047
+ // remains the lifecycle mutation actor, such as the current C1 claimant.
1048
+ const saved = workflow.recordReview(
1049
+ repo,
1050
+ id,
1051
+ expectedRevision,
1052
+ launch.review,
1053
+ option(args, 'actor'),
1054
+ credentialToken(workflow, repo, args, 'writer-lease', 'task review-launch', id, option(args, 'actor') ?? null, false),
1055
+ );
1056
+ return { launch, task: saved };
1057
+ }
1058
+ if (noun === 'task' && action === 'review-packet') {
1059
+ return workflow.prepareExternalTaskReview(
1060
+ repo,
1061
+ required(args, 'id'),
1062
+ revision(args),
1063
+ reviewMode(args),
1064
+ );
1065
+ }
1066
+ if (noun === 'task' && action === 'review-sealed-record') {
1067
+ return workflow.recordExternalTaskReview(
1068
+ repo,
1069
+ required(args, 'id'),
1070
+ revision(args),
1071
+ jsonFile<ExternalStrictTaskReviewInput>(args),
1072
+ required(args, 'actor'),
1073
+ credentialToken(workflow, repo, args, 'writer-lease', 'task review-sealed-record', required(args, 'id'), required(args, 'actor'), false),
1074
+ );
1075
+ }
1076
+ if (noun === 'task' && action === 'step-review') {
1077
+ // Strict Step Review launches and records the isolated reviewer inside Core. CLI --actor
1078
+ // remains the lifecycle actor from next, typically the active C1 claimant.
1079
+ return workflow.reviewStep(
1080
+ repo,
1081
+ required(args, 'id'),
1082
+ required(args, 'step'),
1083
+ revision(args),
1084
+ undefined,
1085
+ option(args, 'actor'),
1086
+ credentialToken(workflow, repo, args, 'writer-lease', 'task step-review', required(args, 'id'), option(args, 'actor') ?? null, false),
1087
+ );
1088
+ }
1089
+ if (noun === 'task' && action === 'step-review-packet') {
1090
+ return workflow.prepareExternalStepReview(
1091
+ repo,
1092
+ required(args, 'id'),
1093
+ required(args, 'step'),
1094
+ revision(args),
1095
+ reviewMode(args),
1096
+ );
1097
+ }
1098
+ if (noun === 'task' && action === 'step-review-record') {
1099
+ return workflow.recordExternalStepReview(
1100
+ repo,
1101
+ required(args, 'id'),
1102
+ required(args, 'step'),
1103
+ revision(args),
1104
+ jsonFile<ExternalStrictStepReviewInput>(args),
1105
+ required(args, 'actor'),
1106
+ credentialToken(workflow, repo, args, 'writer-lease', 'task step-review-record', required(args, 'id'), required(args, 'actor'), false),
1107
+ );
1108
+ }
1109
+ if (noun === 'task' && action === 'corrective-decision-recover') {
1110
+ const taskId = required(args, 'id');
1111
+ const actor = required(args, 'actor');
1112
+ return workflow.recoverStepCorrectiveDecision(
1113
+ repo,
1114
+ taskId,
1115
+ required(args, 'step'),
1116
+ revision(args),
1117
+ actor,
1118
+ credentialToken(workflow, repo, args, 'writer-lease', 'task corrective-decision-recover', taskId, actor, false),
1119
+ );
1120
+ }
1121
+ if (noun === 'task' && action === 'remediation-mode-recover') {
1122
+ const taskId = required(args, 'id');
1123
+ const actor = required(args, 'actor');
1124
+ return workflow.recoverRemediationMode(
1125
+ repo,
1126
+ taskId,
1127
+ required(args, 'step'),
1128
+ revision(args),
1129
+ actor,
1130
+ credentialToken(workflow, repo, args, 'writer-lease', 'task remediation-mode-recover', taskId, actor, false),
1131
+ );
1132
+ }
1133
+ if (noun === 'task' && action === 'plan-integrity-recover') {
1134
+ const taskId = required(args, 'id');
1135
+ const actor = required(args, 'actor');
1136
+ return workflow.recoverPlanIntegrity(
1137
+ repo,
1138
+ taskId,
1139
+ required(args, 'step'),
1140
+ revision(args),
1141
+ actor,
1142
+ credentialToken(workflow, repo, args, 'writer-lease', 'task plan-integrity-recover', taskId, actor, false),
1143
+ );
1144
+ }
1145
+ if (noun === 'task' && action === 'downstream-proof-recover') {
1146
+ const taskId = required(args, 'id');
1147
+ const actor = required(args, 'actor');
1148
+ return workflow.recoverDownstreamProof(
1149
+ repo,
1150
+ taskId,
1151
+ revision(args),
1152
+ actor,
1153
+ credentialToken(workflow, repo, args, 'writer-lease', 'task downstream-proof-recover', taskId, actor, true)!,
1154
+ required(args, 'reason'),
1155
+ );
1156
+ }
1157
+ if (noun === 'task' && action === 'downstream-proof-replan-recover') {
1158
+ return workflow.recoverDownstreamProofReplan(
1159
+ repo,
1160
+ required(args, 'id'),
1161
+ revision(args),
1162
+ required(args, 'actor'),
1163
+ required(args, 'reason'),
1164
+ );
1165
+ }
1166
+ if (noun === 'task' && action === 'stop-override-prepare') {
1167
+ return workflow.prepareTaskStopOverride(
1168
+ repo,
1169
+ required(args, 'id'),
1170
+ required(args, 'step'),
1171
+ revision(args),
1172
+ required(args, 'actor'),
1173
+ required(args, 'reason'),
1174
+ );
1175
+ }
1176
+ if (noun === 'task' && action === 'stop-override-apply') {
1177
+ return workflow.applyTaskStopOverride(
1178
+ repo,
1179
+ required(args, 'id'),
1180
+ required(args, 'step'),
1181
+ revision(args),
1182
+ required(args, 'actor'),
1183
+ required(args, 'reason'),
1184
+ required(args, 'confirmation-code'),
1185
+ );
1186
+ }
1187
+ if (noun === 'task' && action === 'corrective-decision') {
1188
+ const taskId = required(args, 'id');
1189
+ return workflow.recordStepCorrectiveDecision(
1190
+ repo,
1191
+ taskId,
1192
+ required(args, 'step'),
1193
+ revision(args),
1194
+ jsonFile<CorrectivePlanAuditInput>(args),
1195
+ option(args, 'actor'),
1196
+ credentialToken(workflow, repo, args, 'writer-lease', 'task corrective-decision', taskId, option(args, 'actor') ?? null, false),
1197
+ );
1198
+ }
1199
+ if (noun === 'task' && action === 'corrective-yield') {
1200
+ const taskId = required(args, 'id');
1201
+ const actor = required(args, 'actor');
1202
+ const outcome = workflow.executeCorrectiveYield(
1203
+ repo,
1204
+ taskId,
1205
+ revision(args),
1206
+ actor,
1207
+ credentialToken(workflow, repo, args, 'writer-lease', 'task corrective-yield', taskId, actor, true)!,
1208
+ required(args, 'reason'),
1209
+ );
1210
+ consumeCredentialOption(workflow, repo, args, 'writer-credential-ref');
1211
+ return outcome;
1212
+ }
1213
+ if (noun === 'task' && action === 'corrective-yield-recover') {
1214
+ return workflow.recoverCorrectiveYield(repo, required(args, 'id'), required(args, 'transaction-id'));
1215
+ }
1216
+ if (noun === 'task' && action === 'corrective-replan-prepare') {
1217
+ return workflow.prepareCorrectiveReplan(
1218
+ repo,
1219
+ required(args, 'id'),
1220
+ jsonFile<CorrectiveReplanPublicCandidate>(args),
1221
+ );
1222
+ }
1223
+ if (noun === 'task' && action === 'corrective-replan-confirm') {
1224
+ return workflow.confirmCorrectiveReplan(
1225
+ repo,
1226
+ required(args, 'id'),
1227
+ jsonFile<CorrectiveReplanPublicCandidate>(args),
1228
+ jsonFile<CorrectiveReplanHumanGate>(args, 'gate-file'),
1229
+ required(args, 'confirmation-code'),
1230
+ );
1231
+ }
1232
+ if (noun === 'task' && action === 'corrective-replan-execute') {
1233
+ return workflow.executeCorrectiveReplan(
1234
+ repo,
1235
+ required(args, 'id'),
1236
+ jsonFile<CorrectiveReplanPublicCandidate>(args),
1237
+ jsonFile<CorrectiveReplanPublicCredential>(args, 'credential-file'),
1238
+ );
1239
+ }
1240
+ if (noun === 'task' && action === 'corrective-replan-recover') {
1241
+ return workflow.recoverCorrectiveReplan(repo, required(args, 'id'), required(args, 'transaction-id'));
1242
+ }
1243
+ if (noun === 'task' && (action === 'handoff' || action === 'handoff-prepare' || action === 'handoff-replace')) {
1244
+ const replacePending = action === 'handoff-replace';
1245
+ const taskId = required(args, 'id');
1246
+ const actor = required(args, 'actor');
1247
+ const targetActor = replacePending
1248
+ ? required(args, 'target-actor')
1249
+ : option(args, 'target-actor') ?? defaultTaskWorkerActor(taskId);
1250
+ const outcome = workflow.handoffTask(
1251
+ repo,
1252
+ taskId,
1253
+ revision(args),
1254
+ actor,
1255
+ targetActor,
1256
+ required(args, 'reason'),
1257
+ credentialToken(workflow, repo, args, 'writer-lease', `task ${action}`, taskId, actor, false),
1258
+ option(args, 'delegation-grant') ?? null,
1259
+ option(args, 'expires-at') ?? null,
1260
+ replacePending,
1261
+ );
1262
+ consumeCredentialOption(workflow, repo, args, 'writer-credential-ref');
1263
+ const { claimToken, ...bundle } = outcome.bundle;
1264
+ const identity = resolveRepositoryIdentity(repo);
1265
+ return {
1266
+ claimCredentialReference: issueCredentialReference({
1267
+ stateRoot: workflow.store.root,
1268
+ projectId: identity.projectId,
1269
+ entityId: taskId,
1270
+ actor: outcome.bundle.delegate,
1271
+ kind: 'claim-token',
1272
+ token: claimToken,
1273
+ allowedActions: ['task claim'],
1274
+ expiresAt: outcome.bundle.expiresAt,
1275
+ oneTime: true,
1276
+ }),
1277
+ task: outcome.task,
1278
+ event: outcome.event,
1279
+ bundle,
1280
+ };
1281
+ }
1282
+ if (noun === 'task' && action === 'handoff-show') {
1283
+ return workflow.showTaskHandoff(repo, required(args, 'id'));
1284
+ }
1285
+ if (noun === 'task' && action === 'claim') {
1286
+ const taskId = required(args, 'id');
1287
+ const actor = required(args, 'actor');
1288
+ const claimToken = credentialToken(
1289
+ workflow, repo, args, 'claim-token', 'task claim', taskId, actor, true,
1290
+ )!;
1291
+ const outcome = workflow.claimTask(
1292
+ repo,
1293
+ taskId,
1294
+ revision(args),
1295
+ actor,
1296
+ claimToken,
1297
+ credentialToken(workflow, repo, args, 'writer-lease', 'task claim', taskId, actor, false),
1298
+ );
1299
+ consumeCredentialOption(workflow, repo, args, 'claim-credential-ref');
1300
+ return {
1301
+ writerCredential: { disposition: outcome.writerCredential.disposition },
1302
+ ...(outcome.writerCredential.disposition === 'issued' ? {
1303
+ writerCredentialReference: writerCredentialReceipt(
1304
+ workflow, repo, taskId, outcome.lease.owner, outcome.writerCredential.token, outcome.lease.expiresAt,
1305
+ ),
1306
+ } : {}),
1307
+ task: outcome.task,
1308
+ event: outcome.event,
1309
+ lease: outcome.lease,
1310
+ };
1311
+ }
1312
+ if (noun === 'task' && action === 'writer-credential-replace') {
1313
+ const taskId = required(args, 'id');
1314
+ const actor = required(args, 'actor');
1315
+ const outcome = workflow.replaceClaimedWriterCredential(
1316
+ repo,
1317
+ taskId,
1318
+ revision(args),
1319
+ actor,
1320
+ );
1321
+ return {
1322
+ writerCredential: { disposition: outcome.writerCredential.disposition },
1323
+ writerCredentialReference: writerCredentialReceipt(
1324
+ workflow, repo, taskId, outcome.lease.owner, outcome.writerCredential.token, outcome.lease.expiresAt,
1325
+ ),
1326
+ task: outcome.task,
1327
+ lease: outcome.lease,
1328
+ };
1329
+ }
1330
+ if (noun === 'task' && (action === 'handback' || action === 'handback-create')) {
1331
+ const taskId = required(args, 'id');
1332
+ const actor = required(args, 'actor');
1333
+ const outcome = workflow.handbackTask(
1334
+ repo,
1335
+ taskId,
1336
+ revision(args),
1337
+ actor,
1338
+ required(args, 'reason'),
1339
+ credentialToken(workflow, repo, args, 'writer-lease', `task ${action}`, taskId, actor, false),
1340
+ repeated(args, 'limitation'),
1341
+ action === 'handback-create'
1342
+ ? required(args, 'recommended-next')
1343
+ : option(args, 'recommended-next') ?? 'Coordinator reviews the terminal Task result and recorded evidence.',
1344
+ );
1345
+ consumeCredentialOption(workflow, repo, args, 'writer-credential-ref');
1346
+ return outcome;
1347
+ }
1348
+ if (noun === 'task' && action === 'result-set') {
1349
+ const taskId = required(args, 'id');
1350
+ const outcome = workflow.setTaskResult(
1351
+ repo,
1352
+ taskId,
1353
+ revision(args),
1354
+ required(args, 'summary'),
1355
+ repeated(args, 'limitation'),
1356
+ credentialToken(workflow, repo, args, 'writer-lease', 'task result-set', taskId, option(args, 'actor') ?? null, true)!,
1357
+ option(args, 'actor'),
1358
+ );
1359
+ consumeCredentialOption(workflow, repo, args, 'writer-credential-ref');
1360
+ return outcome;
1361
+ }
1362
+ if (noun === 'task' && action === 'accept') {
1363
+ return workflow.acceptTask(
1364
+ repo,
1365
+ required(args, 'id'),
1366
+ revision(args),
1367
+ option(args, 'actor') ?? 'user',
1368
+ option(args, 'delegation-grant') ?? null,
1369
+ );
1370
+ }
1371
+ if (noun === 'task' && action === 'sync-base') {
1372
+ return workflow.syncTaskBase(repo, required(args, 'id'), revision(args));
1373
+ }
1374
+ if (noun === 'task' && action === 'merge') {
1375
+ const taskId = required(args, 'id');
1376
+ const outcome = workflow.mergeTask(
1377
+ repo,
1378
+ taskId,
1379
+ revision(args),
1380
+ credentialToken(workflow, repo, args, 'writer-lease', 'task merge', taskId, option(args, 'actor') ?? null, true)!,
1381
+ option(args, 'actor'),
1382
+ );
1383
+ consumeCredentialOption(workflow, repo, args, 'writer-credential-ref');
1384
+ return outcome;
1385
+ }
1386
+ if (noun === 'task' && action === 'merge-confirm') {
1387
+ const taskId = required(args, 'id');
1388
+ const outcome = workflow.confirmExternalMerge(
1389
+ repo,
1390
+ taskId,
1391
+ revision(args),
1392
+ credentialToken(workflow, repo, args, 'writer-lease', 'task merge-confirm', taskId, option(args, 'actor') ?? null, true)!,
1393
+ option(args, 'actor'),
1394
+ );
1395
+ consumeCredentialOption(workflow, repo, args, 'writer-credential-ref');
1396
+ return outcome;
1397
+ }
1398
+
1399
+ if (noun === 'milestone' && action === 'show') {
1400
+ const identity = resolveRepositoryIdentity(repo);
1401
+ return workflow.store.readMilestone(identity.projectId, required(args, 'id'));
1402
+ }
1403
+ if (noun === 'milestone' && action === 'progress') {
1404
+ return workflow.milestoneProgress(repo, required(args, 'id'));
1405
+ }
1406
+ if (noun === 'milestone' && action === 'plan-set') {
1407
+ return workflow.setMilestonePlan(repo, required(args, 'id'), revision(args), jsonFile<MilestonePlanInput>(args));
1408
+ }
1409
+ if (noun === 'milestone' && action === 'remediation-materialize') {
1410
+ return workflow.materializeAuditedRemediationTask(
1411
+ repo,
1412
+ required(args, 'id'),
1413
+ revision(args),
1414
+ required(args, 'task'),
1415
+ positiveIntegerOption(args, 'expected-task-revision'),
1416
+ jsonFile<AuditedRemediationMaterializationInput>(args),
1417
+ );
1418
+ }
1419
+ if (noun === 'milestone' && action === 'scope-change-prepare') {
1420
+ return workflow.prepareMilestoneScopeChange(
1421
+ repo,
1422
+ required(args, 'id'),
1423
+ revision(args),
1424
+ jsonFile<MilestonePlanInput>(args),
1425
+ required(args, 'actor'),
1426
+ );
1427
+ }
1428
+ if (noun === 'milestone' && action === 'scope-change-apply') {
1429
+ return workflow.applyMilestoneScopeChange(
1430
+ repo,
1431
+ required(args, 'id'),
1432
+ revision(args),
1433
+ jsonFile<MilestonePlanInput>(args),
1434
+ required(args, 'actor'),
1435
+ required(args, 'confirmation-code'),
1436
+ );
1437
+ }
1438
+ if (noun === 'milestone' && action === 'autonomy-prepare') {
1439
+ return workflow.prepareMilestoneAutonomy(
1440
+ repo,
1441
+ required(args, 'id'),
1442
+ revision(args),
1443
+ required(args, 'principal'),
1444
+ required(args, 'delegate'),
1445
+ required(args, 'expires-at'),
1446
+ );
1447
+ }
1448
+ if (noun === 'milestone' && action === 'autonomy-grant') {
1449
+ return workflow.grantMilestoneAutonomy(
1450
+ repo,
1451
+ required(args, 'id'),
1452
+ revision(args),
1453
+ required(args, 'principal'),
1454
+ required(args, 'delegate'),
1455
+ required(args, 'expires-at'),
1456
+ required(args, 'confirmation-code'),
1457
+ );
1458
+ }
1459
+ if (noun === 'milestone' && action === 'autonomy-evolve') {
1460
+ return workflow.evolveMilestonePlanAutonomously(
1461
+ repo,
1462
+ required(args, 'id'),
1463
+ revision(args),
1464
+ jsonFile<MilestonePlanInput>(args),
1465
+ required(args, 'actor'),
1466
+ );
1467
+ }
1468
+ if (noun === 'milestone' && action === 'authorize') {
1469
+ return workflow.authorizeMilestone(
1470
+ repo,
1471
+ required(args, 'id'),
1472
+ revision(args),
1473
+ option(args, 'actor') ?? 'user',
1474
+ option(args, 'reason') ?? '',
1475
+ option(args, 'delegation-grant') ?? null,
1476
+ );
1477
+ }
1478
+ if (noun === 'milestone' && action === 'validate') {
1479
+ return workflow.validateMilestone(repo, required(args, 'id'), revision(args));
1480
+ }
1481
+ if (noun === 'milestone' && action === 'accept') {
1482
+ return workflow.acceptMilestone(
1483
+ repo,
1484
+ required(args, 'id'),
1485
+ revision(args),
1486
+ required(args, 'actor'),
1487
+ required(args, 'confirmation-code'),
1488
+ option(args, 'delegation-grant') ?? null,
1489
+ );
1490
+ }
1491
+ if (noun === 'milestone' && action === 'cancel') {
1492
+ return workflow.cancelMilestone(repo, required(args, 'id'), revision(args), required(args, 'reason'));
1493
+ }
1494
+
1495
+ if (noun === 'locks' && (action === 'inspect' || action === 'repair')) {
1496
+ const id = required(args, 'id');
1497
+ if (action === 'repair') return workflow.repairStaleWriterLease(repo, id);
1498
+ const inspection = workflow.inspectWriterLease(repo, id);
1499
+ return inspection ? { ...inspection, repaired: false } : null;
1500
+ }
1501
+
1502
+ throw new WorkflowError('INVALID_ARGUMENT', 'Unknown command.', {
1503
+ command: args.positionals.join(' '),
1504
+ hint: 'Use doctor, status, next, update preflight, state <action>, delegation <action>, project-memory <action>, discovery <action>, task <action>, milestone <action>, or locks <action>.',
1505
+ });
1506
+ }
1507
+
1508
+ function credentialAllowancesForResult(
1509
+ _args: ParsedArguments,
1510
+ _value: unknown,
1511
+ ): [] {
1512
+ // Beta 13 has no CLI issuance allowance: the host receives reference metadata only.
1513
+ return [];
1514
+ }
1515
+
1516
+ function rawCredentialInputs(argv: readonly string[]): string[] {
1517
+ const values: string[] = [];
1518
+ for (let index = 0; index < argv.length; index += 1) {
1519
+ const argument = argv[index]!;
1520
+ for (const optionName of ['--writer-token', '--claim-token', '--confirmation-code']) {
1521
+ if (argument === optionName && argv[index + 1]) values.push(argv[index + 1]!);
1522
+ if (argument.startsWith(`${optionName}=`)) values.push(argument.slice(optionName.length + 1));
1523
+ }
1524
+ }
1525
+ return [...new Set(values.filter(Boolean))];
1526
+ }
1527
+
1528
+ function isRecord(value: unknown): value is Record<string, unknown> {
1529
+ return typeof value === 'object' && value !== null;
1530
+ }
1531
+
1532
+ async function main(): Promise<void> {
1533
+ const argv = process.argv.slice(2);
1534
+ const sensitiveInputs = rawCredentialInputs(argv);
1535
+ try {
1536
+ const args = parseArguments(argv);
1537
+ assertNoLegacyCredentialArguments(new Set(args.options.keys()));
1538
+ const value = await dispatch(args);
1539
+ assertCliSuccessCredentialSafety(value, credentialAllowancesForResult(args, value), sensitiveInputs);
1540
+ process.stdout.write(`${JSON.stringify({ ok: true, value }, null, 2)}\n`);
1541
+ } catch (error) {
1542
+ if (error instanceof UnsafeCredentialOutputError) {
1543
+ process.stderr.write(
1544
+ `${JSON.stringify({
1545
+ ok: false,
1546
+ error: {
1547
+ code: 'STATE_CORRUPT',
1548
+ message: error.message,
1549
+ details: { credentialPaths: error.paths },
1550
+ },
1551
+ }, null, 2)}\n`,
1552
+ );
1553
+ process.exitCode = 2;
1554
+ return;
1555
+ }
1556
+ if (error instanceof WorkflowError) {
1557
+ process.stderr.write(
1558
+ `${JSON.stringify({
1559
+ ok: false,
1560
+ error: {
1561
+ code: error.code,
1562
+ message: projectCliErrorMessage(error.message, sensitiveInputs),
1563
+ details: projectCliErrorValue(error.details, sensitiveInputs),
1564
+ },
1565
+ }, null, 2)}\n`,
1566
+ );
1567
+ process.exitCode = 2;
1568
+ return;
1569
+ }
1570
+ process.stderr.write(
1571
+ `${JSON.stringify({
1572
+ ok: false,
1573
+ error: {
1574
+ code: 'UNEXPECTED',
1575
+ message: projectCliErrorMessage(error instanceof Error ? error.message : String(error), sensitiveInputs),
1576
+ },
1577
+ }, null, 2)}\n`,
1578
+ );
1579
+ process.exitCode = 1;
1580
+ }
1581
+ }
1582
+
1583
+ await main();