codex-workflow-v2 2.0.0-beta.2 → 2.0.0-beta.5

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 (135) hide show
  1. package/README.md +35 -2
  2. package/dist/src/alpha6/adoption.js +13 -3
  3. package/dist/src/alpha6/adoption.js.map +1 -1
  4. package/dist/src/alpha6/handoff.d.ts +23 -0
  5. package/dist/src/alpha6/handoff.js +221 -28
  6. package/dist/src/alpha6/handoff.js.map +1 -1
  7. package/dist/src/alpha6/milestone.d.ts +2 -0
  8. package/dist/src/alpha6/milestone.js +15 -0
  9. package/dist/src/alpha6/milestone.js.map +1 -1
  10. package/dist/src/alpha6/plan-risk.d.ts +1 -0
  11. package/dist/src/alpha6/plan-risk.js +15 -7
  12. package/dist/src/alpha6/plan-risk.js.map +1 -1
  13. package/dist/src/alpha6/review.d.ts +1 -0
  14. package/dist/src/alpha6/review.js +9 -0
  15. package/dist/src/alpha6/review.js.map +1 -1
  16. package/dist/src/cli.js +253 -67
  17. package/dist/src/cli.js.map +1 -1
  18. package/dist/src/contracts.d.ts +111 -5
  19. package/dist/src/contracts.js +1 -1
  20. package/dist/src/credential-output.d.ts +10 -0
  21. package/dist/src/credential-output.js +73 -0
  22. package/dist/src/credential-output.js.map +1 -0
  23. package/dist/src/errors.d.ts +1 -1
  24. package/dist/src/errors.js.map +1 -1
  25. package/dist/src/index.d.ts +1 -1
  26. package/dist/src/index.js +1 -1
  27. package/dist/src/index.js.map +1 -1
  28. package/dist/src/lifecycle/canonical-hash.d.ts +6 -0
  29. package/dist/src/lifecycle/canonical-hash.js +24 -0
  30. package/dist/src/lifecycle/canonical-hash.js.map +1 -0
  31. package/dist/src/lifecycle/catalog.d.ts +26 -0
  32. package/dist/src/lifecycle/catalog.js +208 -0
  33. package/dist/src/lifecycle/catalog.js.map +1 -0
  34. package/dist/src/lifecycle/compiler-inspection.d.ts +48 -0
  35. package/dist/src/lifecycle/compiler-inspection.js +29 -0
  36. package/dist/src/lifecycle/compiler-inspection.js.map +1 -0
  37. package/dist/src/lifecycle/core-static-readiness.d.ts +6 -0
  38. package/dist/src/lifecycle/core-static-readiness.js +130 -0
  39. package/dist/src/lifecycle/core-static-readiness.js.map +1 -0
  40. package/dist/src/lifecycle/corrective-replan-authority.d.ts +53 -0
  41. package/dist/src/lifecycle/corrective-replan-authority.js +139 -0
  42. package/dist/src/lifecycle/corrective-replan-authority.js.map +1 -0
  43. package/dist/src/lifecycle/corrective-replan-binding-manifest.d.ts +21 -0
  44. package/dist/src/lifecycle/corrective-replan-binding-manifest.js +35 -0
  45. package/dist/src/lifecycle/corrective-replan-binding-manifest.js.map +1 -0
  46. package/dist/src/lifecycle/corrective-replan-credential-core.d.ts +86 -0
  47. package/dist/src/lifecycle/corrective-replan-credential-core.js +563 -0
  48. package/dist/src/lifecycle/corrective-replan-credential-core.js.map +1 -0
  49. package/dist/src/lifecycle/corrective-replan-credential-schema.d.ts +17 -0
  50. package/dist/src/lifecycle/corrective-replan-credential-schema.js +44 -0
  51. package/dist/src/lifecycle/corrective-replan-credential-schema.js.map +1 -0
  52. package/dist/src/lifecycle/corrective-replan-credentials.d.ts +47 -0
  53. package/dist/src/lifecycle/corrective-replan-credentials.js +103 -0
  54. package/dist/src/lifecycle/corrective-replan-credentials.js.map +1 -0
  55. package/dist/src/lifecycle/corrective-replan.d.ts +64 -0
  56. package/dist/src/lifecycle/corrective-replan.js +548 -0
  57. package/dist/src/lifecycle/corrective-replan.js.map +1 -0
  58. package/dist/src/lifecycle/evaluator.d.ts +2 -0
  59. package/dist/src/lifecycle/evaluator.js +35 -0
  60. package/dist/src/lifecycle/evaluator.js.map +1 -0
  61. package/dist/src/lifecycle/fingerprint.d.ts +31 -0
  62. package/dist/src/lifecycle/fingerprint.js +388 -0
  63. package/dist/src/lifecycle/fingerprint.js.map +1 -0
  64. package/dist/src/lifecycle/immutable.d.ts +1 -0
  65. package/dist/src/lifecycle/immutable.js +10 -0
  66. package/dist/src/lifecycle/immutable.js.map +1 -0
  67. package/dist/src/lifecycle/implementation-table.d.ts +29 -0
  68. package/dist/src/lifecycle/implementation-table.js +87 -0
  69. package/dist/src/lifecycle/implementation-table.js.map +1 -0
  70. package/dist/src/lifecycle/index.d.ts +4 -0
  71. package/dist/src/lifecycle/index.js +5 -0
  72. package/dist/src/lifecycle/index.js.map +1 -0
  73. package/dist/src/lifecycle/schema-artifact.d.ts +12 -0
  74. package/dist/src/lifecycle/schema-artifact.js +214 -0
  75. package/dist/src/lifecycle/schema-artifact.js.map +1 -0
  76. package/dist/src/lifecycle/semantic-registry.d.ts +6 -0
  77. package/dist/src/lifecycle/semantic-registry.js +489 -0
  78. package/dist/src/lifecycle/semantic-registry.js.map +1 -0
  79. package/dist/src/lifecycle/types.d.ts +718 -0
  80. package/dist/src/lifecycle/types.js +4 -0
  81. package/dist/src/lifecycle/types.js.map +1 -0
  82. package/dist/src/observation.d.ts +20 -0
  83. package/dist/src/observation.js +171 -0
  84. package/dist/src/observation.js.map +1 -0
  85. package/dist/src/reviewer.d.ts +39 -1
  86. package/dist/src/reviewer.js +179 -43
  87. package/dist/src/reviewer.js.map +1 -1
  88. package/dist/src/state/corrective-replan-executor.d.ts +64 -0
  89. package/dist/src/state/corrective-replan-executor.js +608 -0
  90. package/dist/src/state/corrective-replan-executor.js.map +1 -0
  91. package/dist/src/state/corrective-replan-public-schema.d.ts +3 -0
  92. package/dist/src/state/corrective-replan-public-schema.js +43 -0
  93. package/dist/src/state/corrective-replan-public-schema.js.map +1 -0
  94. package/dist/src/state/corrective-replan-public.d.ts +95 -0
  95. package/dist/src/state/corrective-replan-public.js +665 -0
  96. package/dist/src/state/corrective-replan-public.js.map +1 -0
  97. package/dist/src/state/corrective-replan-transaction.d.ts +160 -0
  98. package/dist/src/state/corrective-replan-transaction.js +730 -0
  99. package/dist/src/state/corrective-replan-transaction.js.map +1 -0
  100. package/dist/src/state/corrective-yield-executor.d.ts +31 -0
  101. package/dist/src/state/corrective-yield-executor.js +236 -0
  102. package/dist/src/state/corrective-yield-executor.js.map +1 -0
  103. package/dist/src/state/corrective-yield-transaction.d.ts +97 -0
  104. package/dist/src/state/corrective-yield-transaction.js +591 -0
  105. package/dist/src/state/corrective-yield-transaction.js.map +1 -0
  106. package/dist/src/state/lock.d.ts +76 -3
  107. package/dist/src/state/lock.js +611 -30
  108. package/dist/src/state/lock.js.map +1 -1
  109. package/dist/src/state/store.d.ts +3 -0
  110. package/dist/src/state/store.js +65 -12
  111. package/dist/src/state/store.js.map +1 -1
  112. package/dist/src/transition-core.d.ts +67 -0
  113. package/dist/src/transition-core.js +318 -0
  114. package/dist/src/transition-core.js.map +1 -0
  115. package/dist/src/version.d.ts +1 -1
  116. package/dist/src/version.js +1 -1
  117. package/dist/src/workflow.d.ts +62 -25
  118. package/dist/src/workflow.js +892 -183
  119. package/dist/src/workflow.js.map +1 -1
  120. package/docs/lifecycle/state-machine-stabilization.md +641 -0
  121. package/docs/release.md +6 -2
  122. package/docs/updating-existing-project.md +9 -0
  123. package/package.json +4 -1
  124. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +36 -0
  125. package/schemas/adoption-posture-event.schema.json +1 -1
  126. package/schemas/context-envelope.schema.json +1 -1
  127. package/schemas/corrective-replan-credentials.private.schema.json +98 -0
  128. package/schemas/corrective-replan-execution-event.private.schema.json +53 -0
  129. package/schemas/corrective-replan-public.schema.json +142 -0
  130. package/schemas/corrective-replan-task-transaction.private.schema.json +151 -0
  131. package/schemas/corrective-yield-task-transaction.private.schema.json +98 -0
  132. package/schemas/remediation-event.schema.json +11 -1
  133. package/schemas/task-handoff-event.schema.json +1 -1
  134. package/schemas/transition-definition.schema.json +2609 -0
  135. package/schemas/transition-payloads.schema.json +140 -0
package/dist/src/cli.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { createHash } from 'node:crypto';
3
2
  import { readFileSync } from 'node:fs';
4
3
  import path from 'node:path';
5
4
  import process from 'node:process';
6
5
  import { defaultTaskWorkerActor } from './alpha6/handoff.js';
6
+ import { assertCliSuccessCredentialSafety, projectCliErrorMessage, projectCliErrorValue, UnsafeCredentialOutputError, } from './credential-output.js';
7
7
  import { integrationDiagnostics } from './diagnostics.js';
8
8
  import { WorkflowError } from './errors.js';
9
9
  import { prepareGraphView } from './graph.js';
10
10
  import { migrateState } from './migration.js';
11
- import { changedFiles, currentBranch, isClean } from './repository.js';
11
+ import { changedFiles, currentBranch, isClean, resolveRepositoryIdentity } from './repository.js';
12
12
  import { launchStrictReviewer } from './reviewer.js';
13
13
  import { WorkflowService } from './workflow.js';
14
14
  import { PACKAGE_NAME, PACKAGE_VERSION } from './version.js';
@@ -35,14 +35,26 @@ const CLI_ACTIONS = {
35
35
  'submit',
36
36
  'review-record',
37
37
  'review-launch',
38
+ 'review-packet',
39
+ 'review-sealed-record',
38
40
  'step-review',
41
+ 'step-review-packet',
42
+ 'step-review-record',
39
43
  'corrective-decision-recover',
40
44
  'remediation-mode-recover',
41
45
  'corrective-decision',
46
+ 'corrective-yield',
47
+ 'corrective-yield-recover',
48
+ 'corrective-replan-prepare',
49
+ 'corrective-replan-confirm',
50
+ 'corrective-replan-execute',
51
+ 'corrective-replan-recover',
42
52
  'handoff',
43
53
  'handoff-prepare',
54
+ 'handoff-replace',
44
55
  'handoff-show',
45
56
  'claim',
57
+ 'writer-credential-replace',
46
58
  'handback',
47
59
  'handback-create',
48
60
  'result-set',
@@ -84,6 +96,38 @@ const CLI_HELP_SPECS = {
84
96
  requiredOptions: ['--id', '--expected-revision', '--file', '--risk-audit-file'],
85
97
  optionalOptions: ['--corrective-audit-file', '--repo', '--json'],
86
98
  },
99
+ 'task handoff-replace': {
100
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--target-actor', '--reason'],
101
+ optionalOptions: ['--delegation-grant', '--expires-at', '--repo', '--json'],
102
+ },
103
+ 'task writer-credential-replace': {
104
+ requiredOptions: ['--id', '--expected-revision', '--actor'],
105
+ optionalOptions: ['--repo', '--json'],
106
+ },
107
+ 'task corrective-yield': {
108
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--writer-token', '--reason'],
109
+ optionalOptions: ['--repo', '--json'],
110
+ },
111
+ 'task corrective-yield-recover': {
112
+ requiredOptions: ['--id', '--transaction-id'],
113
+ optionalOptions: ['--repo', '--json'],
114
+ },
115
+ 'task corrective-replan-prepare': {
116
+ requiredOptions: ['--id', '--file'],
117
+ optionalOptions: ['--repo', '--json'],
118
+ },
119
+ 'task corrective-replan-confirm': {
120
+ requiredOptions: ['--id', '--file', '--gate-file', '--confirmation-code'],
121
+ optionalOptions: ['--repo', '--json'],
122
+ },
123
+ 'task corrective-replan-execute': {
124
+ requiredOptions: ['--id', '--file', '--credential-file'],
125
+ optionalOptions: ['--repo', '--json'],
126
+ },
127
+ 'task corrective-replan-recover': {
128
+ requiredOptions: ['--id', '--transaction-id'],
129
+ optionalOptions: ['--repo', '--json'],
130
+ },
87
131
  'task authorize': {
88
132
  requiredOptions: ['--id', '--expected-revision'],
89
133
  optionalOptions: ['--actor', '--reason', '--delegation-grant', '--repo', '--json'],
@@ -108,6 +152,22 @@ const CLI_HELP_SPECS = {
108
152
  requiredOptions: ['--id', '--expected-revision'],
109
153
  optionalOptions: ['--actor', '--writer-token', '--repo', '--json'],
110
154
  },
155
+ 'task review-packet': {
156
+ requiredOptions: ['--id', '--expected-revision'],
157
+ optionalOptions: ['--repo', '--json'],
158
+ },
159
+ 'task review-sealed-record': {
160
+ requiredOptions: ['--id', '--expected-revision', '--actor', '--file'],
161
+ optionalOptions: ['--writer-token', '--repo', '--json'],
162
+ },
163
+ 'task step-review-packet': {
164
+ requiredOptions: ['--id', '--step', '--expected-revision'],
165
+ optionalOptions: ['--repo', '--json'],
166
+ },
167
+ 'task step-review-record': {
168
+ requiredOptions: ['--id', '--step', '--expected-revision', '--actor', '--file'],
169
+ optionalOptions: ['--writer-token', '--repo', '--json'],
170
+ },
111
171
  'task result-set': {
112
172
  requiredOptions: ['--id', '--expected-revision', '--summary'],
113
173
  optionalOptions: ['--limitation', '--repo', '--json'],
@@ -163,14 +223,17 @@ function cliHelp(args) {
163
223
  }
164
224
  if (!action) {
165
225
  if (['doctor', 'status', 'next'].includes(noun)) {
226
+ const taskSelector = noun === 'next' ? ['--task'] : [];
166
227
  return {
167
228
  kind: 'cli-help',
168
229
  readOnly: true,
169
230
  scope: 'command',
170
231
  command: noun,
171
- usage: `codex-workflow ${noun} [--repo <path>] [--json]`,
232
+ usage: noun === 'next'
233
+ ? 'codex-workflow next [--task <task-id>] [--repo <path>] [--json]'
234
+ : `codex-workflow ${noun} [--repo <path>] [--json]`,
172
235
  requiredOptions: [],
173
- optionalOptions: ['--repo', '--json'],
236
+ optionalOptions: [...taskSelector, '--repo', '--json'],
174
237
  exactOptionContractAvailable: true,
175
238
  };
176
239
  }
@@ -308,6 +371,10 @@ function workflowCapabilities() {
308
371
  'plan-proof-obligations-v1',
309
372
  'terminal-task-replacement-transaction-v1',
310
373
  'project-composite-transaction-journal-v1',
374
+ 'corrective-yield-journal-v1',
375
+ 'corrective-replan-journal-v1',
376
+ 'corrective-replan-human-gate-v1',
377
+ 'corrective-replan-protocol-v2',
311
378
  ];
312
379
  }
313
380
  async function dispatch(args) {
@@ -326,19 +393,19 @@ async function dispatch(args) {
326
393
  }
327
394
  const workflow = new WorkflowService();
328
395
  if (noun === 'doctor') {
329
- const context = workflow.context(repo);
330
- const integration = integrationDiagnostics(context.identity.repositoryRoot);
396
+ const identity = resolveRepositoryIdentity(repo);
397
+ const integration = integrationDiagnostics(identity.repositoryRoot);
331
398
  const nodeSupported = Number(process.versions.node.split('.')[0]) >= 22;
332
399
  return {
333
400
  ok: nodeSupported && integration.localDependency.current,
334
401
  node: process.versions.node,
335
402
  requiredNode: '>=22',
336
- repositoryRoot: context.identity.repositoryRoot,
337
- projectId: context.identity.projectId,
338
- stateRoot: context.store.root,
339
- branch: currentBranch(context.identity.repositoryRoot),
340
- clean: isClean(context.identity.repositoryRoot),
341
- changedFiles: changedFiles(context.identity.repositoryRoot),
403
+ repositoryRoot: identity.repositoryRoot,
404
+ projectId: identity.projectId,
405
+ stateRoot: workflow.store.root,
406
+ branch: currentBranch(identity.repositoryRoot),
407
+ clean: isClean(identity.repositoryRoot),
408
+ changedFiles: changedFiles(identity.repositoryRoot),
342
409
  integration,
343
410
  limitations: [
344
411
  'CLI diagnostics cannot prove that Codex enforced a role, model, sandbox, or read-only reviewer.',
@@ -349,7 +416,7 @@ async function dispatch(args) {
349
416
  if (noun === 'status')
350
417
  return workflow.status(repo);
351
418
  if (noun === 'next')
352
- return workflow.next(repo);
419
+ return workflow.next(repo, option(args, 'task') ?? null);
353
420
  if (noun === 'update' && action === 'preflight')
354
421
  return workflow.updatePreflight(repo);
355
422
  if (noun === 'update' && action === 'rescue-preflight') {
@@ -362,12 +429,12 @@ async function dispatch(args) {
362
429
  return workflow.grantDelegation(repo, jsonFile(args), required(args, 'confirmation-code'));
363
430
  }
364
431
  if (noun === 'delegation' && action === 'list') {
365
- const context = workflow.context(repo);
366
- return context.store.listDelegations(context.identity.projectId);
432
+ const identity = resolveRepositoryIdentity(repo);
433
+ return workflow.store.listDelegations(identity.projectId);
367
434
  }
368
435
  if (noun === 'delegation' && action === 'show') {
369
- const context = workflow.context(repo);
370
- return context.store.readDelegation(context.identity.projectId, required(args, 'id'));
436
+ const identity = resolveRepositoryIdentity(repo);
437
+ return workflow.store.readDelegation(identity.projectId, required(args, 'id'));
371
438
  }
372
439
  if (noun === 'delegation' && action === 'revoke') {
373
440
  return workflow.revokeDelegation(repo, required(args, 'id'), revision(args), required(args, 'actor'), required(args, 'reason'));
@@ -470,8 +537,8 @@ async function dispatch(args) {
470
537
  return workflow.updateDiscovery(repo, required(args, 'id'), revision(args), patch);
471
538
  }
472
539
  if (noun === 'discovery' && action === 'show') {
473
- const context = workflow.context(repo);
474
- return context.store.readDiscovery(context.identity.projectId, required(args, 'id'));
540
+ const identity = resolveRepositoryIdentity(repo);
541
+ return workflow.store.readDiscovery(identity.projectId, required(args, 'id'));
475
542
  }
476
543
  if (noun === 'discovery' && action === 'materialize') {
477
544
  const kind = option(args, 'kind') ?? 'task';
@@ -484,8 +551,8 @@ async function dispatch(args) {
484
551
  throw new WorkflowError('INVALID_ARGUMENT', '--kind must be task or milestone.');
485
552
  }
486
553
  if (noun === 'task' && action === 'show') {
487
- const context = workflow.context(repo);
488
- return context.store.readTask(context.identity.projectId, required(args, 'id'));
554
+ const identity = resolveRepositoryIdentity(repo);
555
+ return workflow.store.readTask(identity.projectId, required(args, 'id'));
489
556
  }
490
557
  if (noun === 'task' && action === 'replacement-materialize') {
491
558
  return workflow.replaceSplitRequiredTask(repo, required(args, 'id'), revision(args), required(args, 'discovery'), positiveIntegerOption(args, 'expected-discovery-revision'), positiveIntegerOption(args, 'expected-milestone-revision'), required(args, 'title'), option(args, 'actor') ?? 'user');
@@ -516,21 +583,29 @@ async function dispatch(args) {
516
583
  }
517
584
  if (noun === 'task' && action === 'run') {
518
585
  const outcome = workflow.runStep(repo, required(args, 'id'), required(args, 'step'), revision(args), option(args, 'owner') ?? 'worker', option(args, 'writer-token') ?? null);
519
- const { token, ...lease } = outcome.lease;
520
- const { writerToken: _writerToken, ...envelope } = outcome.envelope;
586
+ const { writerToken, ...envelope } = outcome.envelope;
587
+ if (outcome.writerCredential.disposition === 'issued' && !writerToken) {
588
+ throw new WorkflowError('STATE_CORRUPT', 'Fresh task run credential issuance did not return its protocol-v1 writer token.');
589
+ }
590
+ if (outcome.writerCredential.disposition === 'renewed' && writerToken !== null) {
591
+ throw new WorkflowError('STATE_CORRUPT', 'Renewed task run credential unexpectedly returned a writer token.');
592
+ }
521
593
  return {
522
- writerLeaseReceipt: {
523
- token,
524
- kind: 'writer-lease',
525
- owner: outcome.lease.owner,
526
- action: 'task step-complete',
527
- option: '--writer-token',
528
- source: 'task run',
529
- sensitive: true,
530
- oneTime: false,
531
- },
594
+ writerCredential: { disposition: outcome.writerCredential.disposition },
595
+ ...(writerToken ? {
596
+ writerLeaseReceipt: {
597
+ token: writerToken,
598
+ kind: 'writer-lease',
599
+ owner: outcome.lease.owner,
600
+ action: 'task step-complete',
601
+ option: '--writer-token',
602
+ source: 'task run',
603
+ sensitive: true,
604
+ oneTime: false,
605
+ },
606
+ } : {}),
532
607
  task: outcome.task,
533
- lease,
608
+ lease: outcome.lease,
534
609
  envelope,
535
610
  };
536
611
  }
@@ -545,10 +620,10 @@ async function dispatch(args) {
545
620
  return workflow.recordReview(repo, required(args, 'id'), revision(args), review, option(args, 'actor'), option(args, 'writer-token') ?? null);
546
621
  }
547
622
  if (noun === 'task' && action === 'review-launch') {
548
- const context = workflow.context(repo);
623
+ const identity = resolveRepositoryIdentity(repo);
549
624
  const id = required(args, 'id');
550
625
  const expectedRevision = revision(args);
551
- const task = context.store.readTask(context.identity.projectId, id);
626
+ const task = workflow.store.readTask(identity.projectId, id);
552
627
  if (task.revision !== expectedRevision) {
553
628
  throw new WorkflowError('STATE_CONFLICT', `State revision conflict for ${id}`, {
554
629
  expectedRevision,
@@ -556,17 +631,29 @@ async function dispatch(args) {
556
631
  });
557
632
  }
558
633
  const envelope = workflow.taskContext(repo, id, 'independent-reviewer');
559
- const launch = launchStrictReviewer(context.identity.repositoryRoot, task, context.store.taskRoot(task.projectId, task.id), envelope);
634
+ const launch = launchStrictReviewer(identity.repositoryRoot, task, workflow.store.taskRoot(task.projectId, task.id), envelope);
560
635
  // The independent reviewer identity belongs to the strict reviewer payload. CLI --actor
561
636
  // remains the lifecycle mutation actor, such as the current C1 claimant.
562
637
  const saved = workflow.recordReview(repo, id, expectedRevision, launch.review, option(args, 'actor'), option(args, 'writer-token') ?? null);
563
638
  return { launch, task: saved };
564
639
  }
640
+ if (noun === 'task' && action === 'review-packet') {
641
+ return workflow.prepareExternalTaskReview(repo, required(args, 'id'), revision(args));
642
+ }
643
+ if (noun === 'task' && action === 'review-sealed-record') {
644
+ return workflow.recordExternalTaskReview(repo, required(args, 'id'), revision(args), jsonFile(args), required(args, 'actor'), option(args, 'writer-token') ?? null);
645
+ }
565
646
  if (noun === 'task' && action === 'step-review') {
566
647
  // Strict Step Review launches and records the isolated reviewer inside Core. CLI --actor
567
648
  // remains the lifecycle actor from next, typically the active C1 claimant.
568
649
  return workflow.reviewStep(repo, required(args, 'id'), required(args, 'step'), revision(args), undefined, option(args, 'actor'), option(args, 'writer-token') ?? null);
569
650
  }
651
+ if (noun === 'task' && action === 'step-review-packet') {
652
+ return workflow.prepareExternalStepReview(repo, required(args, 'id'), required(args, 'step'), revision(args));
653
+ }
654
+ if (noun === 'task' && action === 'step-review-record') {
655
+ return workflow.recordExternalStepReview(repo, required(args, 'id'), required(args, 'step'), revision(args), jsonFile(args), required(args, 'actor'), option(args, 'writer-token') ?? null);
656
+ }
570
657
  if (noun === 'task' && action === 'corrective-decision-recover') {
571
658
  return workflow.recoverStepCorrectiveDecision(repo, required(args, 'id'), required(args, 'step'), revision(args), required(args, 'actor'), option(args, 'writer-token') ?? null);
572
659
  }
@@ -576,8 +663,29 @@ async function dispatch(args) {
576
663
  if (noun === 'task' && action === 'corrective-decision') {
577
664
  return workflow.recordStepCorrectiveDecision(repo, required(args, 'id'), required(args, 'step'), revision(args), jsonFile(args), option(args, 'actor'), option(args, 'writer-token') ?? null);
578
665
  }
579
- if (noun === 'task' && (action === 'handoff' || action === 'handoff-prepare')) {
580
- const outcome = workflow.handoffTask(repo, required(args, 'id'), revision(args), required(args, 'actor'), option(args, 'target-actor') ?? defaultTaskWorkerActor(required(args, 'id')), required(args, 'reason'), option(args, 'writer-token') ?? null, option(args, 'delegation-grant') ?? null, option(args, 'expires-at') ?? null);
666
+ if (noun === 'task' && action === 'corrective-yield') {
667
+ return workflow.executeCorrectiveYield(repo, required(args, 'id'), revision(args), required(args, 'actor'), required(args, 'writer-token'), required(args, 'reason'));
668
+ }
669
+ if (noun === 'task' && action === 'corrective-yield-recover') {
670
+ return workflow.recoverCorrectiveYield(repo, required(args, 'id'), required(args, 'transaction-id'));
671
+ }
672
+ if (noun === 'task' && action === 'corrective-replan-prepare') {
673
+ return workflow.prepareCorrectiveReplan(repo, required(args, 'id'), jsonFile(args));
674
+ }
675
+ if (noun === 'task' && action === 'corrective-replan-confirm') {
676
+ return workflow.confirmCorrectiveReplan(repo, required(args, 'id'), jsonFile(args), jsonFile(args, 'gate-file'), required(args, 'confirmation-code'));
677
+ }
678
+ if (noun === 'task' && action === 'corrective-replan-execute') {
679
+ return workflow.executeCorrectiveReplan(repo, required(args, 'id'), jsonFile(args), jsonFile(args, 'credential-file'));
680
+ }
681
+ if (noun === 'task' && action === 'corrective-replan-recover') {
682
+ return workflow.recoverCorrectiveReplan(repo, required(args, 'id'), required(args, 'transaction-id'));
683
+ }
684
+ if (noun === 'task' && (action === 'handoff' || action === 'handoff-prepare' || action === 'handoff-replace')) {
685
+ const replacePending = action === 'handoff-replace';
686
+ const outcome = workflow.handoffTask(repo, required(args, 'id'), revision(args), required(args, 'actor'), replacePending
687
+ ? required(args, 'target-actor')
688
+ : option(args, 'target-actor') ?? defaultTaskWorkerActor(required(args, 'id')), required(args, 'reason'), option(args, 'writer-token') ?? null, option(args, 'delegation-grant') ?? null, option(args, 'expires-at') ?? null, replacePending);
581
689
  const { claimToken, ...bundle } = outcome.bundle;
582
690
  return {
583
691
  credentialHandoff: {
@@ -586,7 +694,7 @@ async function dispatch(args) {
586
694
  targetActor: outcome.bundle.delegate,
587
695
  action: 'task claim',
588
696
  option: '--claim-token',
589
- source: 'task handoff-prepare',
697
+ source: replacePending ? 'task handoff-replace' : 'task handoff-prepare',
590
698
  sensitive: true,
591
699
  oneTime: true,
592
700
  },
@@ -600,21 +708,38 @@ async function dispatch(args) {
600
708
  }
601
709
  if (noun === 'task' && action === 'claim') {
602
710
  const outcome = workflow.claimTask(repo, required(args, 'id'), revision(args), required(args, 'actor'), required(args, 'claim-token'), option(args, 'writer-token') ?? null);
603
- const { token, ...lease } = outcome.lease;
604
711
  return {
712
+ writerCredential: { disposition: outcome.writerCredential.disposition },
713
+ ...(outcome.writerCredential.disposition === 'issued' ? {
714
+ writerLeaseReceipt: {
715
+ token: outcome.writerCredential.token,
716
+ kind: 'writer-lease',
717
+ owner: outcome.lease.owner,
718
+ action: 'task run',
719
+ option: '--writer-token',
720
+ source: 'task claim',
721
+ sensitive: true,
722
+ oneTime: false,
723
+ },
724
+ } : {}),
725
+ task: outcome.task,
726
+ event: outcome.event,
727
+ lease: outcome.lease,
728
+ };
729
+ }
730
+ if (noun === 'task' && action === 'writer-credential-replace') {
731
+ const outcome = workflow.replaceClaimedWriterCredential(repo, required(args, 'id'), revision(args), required(args, 'actor'));
732
+ return {
733
+ writerCredential: { disposition: outcome.writerCredential.disposition },
605
734
  writerLeaseReceipt: {
606
- token,
735
+ token: outcome.writerCredential.token,
607
736
  kind: 'writer-lease',
608
- owner: outcome.lease.owner,
609
- action: 'task run',
610
- option: '--writer-token',
611
- source: 'task claim',
737
+ source: 'task writer-credential-replace',
612
738
  sensitive: true,
613
739
  oneTime: false,
614
740
  },
615
741
  task: outcome.task,
616
- event: outcome.event,
617
- lease,
742
+ lease: outcome.lease,
618
743
  };
619
744
  }
620
745
  if (noun === 'task' && (action === 'handback' || action === 'handback-create')) {
@@ -638,8 +763,8 @@ async function dispatch(args) {
638
763
  return workflow.confirmExternalMerge(repo, required(args, 'id'), revision(args), required(args, 'writer-token'), option(args, 'actor'));
639
764
  }
640
765
  if (noun === 'milestone' && action === 'show') {
641
- const context = workflow.context(repo);
642
- return context.store.readMilestone(context.identity.projectId, required(args, 'id'));
766
+ const identity = resolveRepositoryIdentity(repo);
767
+ return workflow.store.readMilestone(identity.projectId, required(args, 'id'));
643
768
  }
644
769
  if (noun === 'milestone' && action === 'plan-set') {
645
770
  return workflow.setMilestonePlan(repo, required(args, 'id'), revision(args), jsonFile(args));
@@ -672,39 +797,100 @@ async function dispatch(args) {
672
797
  return workflow.cancelMilestone(repo, required(args, 'id'), revision(args), required(args, 'reason'));
673
798
  }
674
799
  if (noun === 'locks' && (action === 'inspect' || action === 'repair')) {
675
- const context = workflow.context(repo);
676
800
  const id = required(args, 'id');
677
- const lease = action === 'inspect' ? context.locks.inspect(id) : context.locks.repairStale(id);
678
- return lease ? {
679
- schemaVersion: lease.schemaVersion,
680
- entityId: lease.entityId,
681
- tokenFingerprint: createHash('sha256').update(lease.token).digest('hex').slice(0, 16),
682
- owner: lease.owner,
683
- pid: lease.pid,
684
- hostname: lease.hostname,
685
- acquiredAt: lease.acquiredAt,
686
- heartbeatAt: lease.heartbeatAt,
687
- expiresAt: lease.expiresAt,
688
- repaired: action === 'repair',
689
- } : null;
801
+ if (action === 'repair')
802
+ return workflow.repairStaleWriterLease(repo, id);
803
+ const inspection = workflow.inspectWriterLease(repo, id);
804
+ return inspection ? { ...inspection, repaired: false } : null;
690
805
  }
691
806
  throw new WorkflowError('INVALID_ARGUMENT', 'Unknown command.', {
692
807
  command: args.positionals.join(' '),
693
808
  hint: 'Use doctor, status, next, update preflight, state <action>, delegation <action>, project-memory <action>, discovery <action>, task <action>, milestone <action>, or locks <action>.',
694
809
  });
695
810
  }
811
+ function credentialAllowancesForResult(args, value) {
812
+ const [noun, action] = args.positionals;
813
+ if (!isRecord(value))
814
+ return [];
815
+ if (noun === 'task' && (action === 'run' || action === 'claim' || action === 'writer-credential-replace')) {
816
+ const disposition = isRecord(value.writerCredential) ? value.writerCredential.disposition : null;
817
+ const receipt = isRecord(value.writerLeaseReceipt) ? value.writerLeaseReceipt : null;
818
+ if (disposition === 'issued'
819
+ && receipt?.kind === 'writer-lease'
820
+ && receipt.source === `task ${action}`
821
+ && receipt.sensitive === true) {
822
+ return [{ path: '$.writerLeaseReceipt.token' }];
823
+ }
824
+ return [];
825
+ }
826
+ if (noun === 'task' && (action === 'handoff' || action === 'handoff-prepare' || action === 'handoff-replace')) {
827
+ const receipt = isRecord(value.credentialHandoff) ? value.credentialHandoff : null;
828
+ if (receipt?.kind === 'claim-token'
829
+ && receipt.source === (action === 'handoff-replace' ? 'task handoff-replace' : 'task handoff-prepare')
830
+ && receipt.sensitive === true
831
+ && receipt.oneTime === true) {
832
+ return [{ path: '$.credentialHandoff.token' }];
833
+ }
834
+ }
835
+ return [];
836
+ }
837
+ function rawCredentialInputs(argv) {
838
+ const values = [];
839
+ for (let index = 0; index < argv.length; index += 1) {
840
+ const argument = argv[index];
841
+ for (const optionName of ['--writer-token', '--claim-token', '--confirmation-code']) {
842
+ if (argument === optionName && argv[index + 1])
843
+ values.push(argv[index + 1]);
844
+ if (argument.startsWith(`${optionName}=`))
845
+ values.push(argument.slice(optionName.length + 1));
846
+ }
847
+ }
848
+ return [...new Set(values.filter(Boolean))];
849
+ }
850
+ function isRecord(value) {
851
+ return typeof value === 'object' && value !== null;
852
+ }
696
853
  async function main() {
854
+ const argv = process.argv.slice(2);
855
+ const sensitiveInputs = rawCredentialInputs(argv);
697
856
  try {
698
- const value = await dispatch(parseArguments(process.argv.slice(2)));
857
+ const args = parseArguments(argv);
858
+ const value = await dispatch(args);
859
+ assertCliSuccessCredentialSafety(value, credentialAllowancesForResult(args, value), sensitiveInputs);
699
860
  process.stdout.write(`${JSON.stringify({ ok: true, value }, null, 2)}\n`);
700
861
  }
701
862
  catch (error) {
863
+ if (error instanceof UnsafeCredentialOutputError) {
864
+ process.stderr.write(`${JSON.stringify({
865
+ ok: false,
866
+ error: {
867
+ code: 'STATE_CORRUPT',
868
+ message: error.message,
869
+ details: { credentialPaths: error.paths },
870
+ },
871
+ }, null, 2)}\n`);
872
+ process.exitCode = 2;
873
+ return;
874
+ }
702
875
  if (error instanceof WorkflowError) {
703
- process.stderr.write(`${JSON.stringify({ ok: false, error: { code: error.code, message: error.message, details: error.details } }, null, 2)}\n`);
876
+ process.stderr.write(`${JSON.stringify({
877
+ ok: false,
878
+ error: {
879
+ code: error.code,
880
+ message: projectCliErrorMessage(error.message, sensitiveInputs),
881
+ details: projectCliErrorValue(error.details, sensitiveInputs),
882
+ },
883
+ }, null, 2)}\n`);
704
884
  process.exitCode = 2;
705
885
  return;
706
886
  }
707
- process.stderr.write(`${JSON.stringify({ ok: false, error: { code: 'UNEXPECTED', message: error instanceof Error ? error.message : String(error) } }, null, 2)}\n`);
887
+ process.stderr.write(`${JSON.stringify({
888
+ ok: false,
889
+ error: {
890
+ code: 'UNEXPECTED',
891
+ message: projectCliErrorMessage(error instanceof Error ? error.message : String(error), sensitiveInputs),
892
+ },
893
+ }, null, 2)}\n`);
708
894
  process.exitCode = 1;
709
895
  }
710
896
  }