codex-workflow-v2 2.0.0-beta.13 → 2.0.0-beta.13.2

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 (45) hide show
  1. package/README.md +1 -1
  2. package/dist/reviewer-runtime-build.json +17 -13
  3. package/dist/src/alpha6/plan-risk.d.ts +1 -0
  4. package/dist/src/alpha6/plan-risk.js +34 -7
  5. package/dist/src/alpha6/plan-risk.js.map +1 -1
  6. package/dist/src/cli-actions.d.ts +1 -1
  7. package/dist/src/cli-actions.js +3 -0
  8. package/dist/src/cli-actions.js.map +1 -1
  9. package/dist/src/cli.js +15 -0
  10. package/dist/src/cli.js.map +1 -1
  11. package/dist/src/contracts.d.ts +2 -0
  12. package/dist/src/gateway-handshake.js +1 -0
  13. package/dist/src/gateway-handshake.js.map +1 -1
  14. package/dist/src/observed-routes.js +1 -0
  15. package/dist/src/observed-routes.js.map +1 -1
  16. package/dist/src/pending-review-update.d.ts +49 -0
  17. package/dist/src/pending-review-update.js +132 -0
  18. package/dist/src/pending-review-update.js.map +1 -0
  19. package/dist/src/repository.js +16 -4
  20. package/dist/src/repository.js.map +1 -1
  21. package/dist/src/reviewer.js +4 -1
  22. package/dist/src/reviewer.js.map +1 -1
  23. package/dist/src/state/corrective-replan-executor.js +3 -1
  24. package/dist/src/state/corrective-replan-executor.js.map +1 -1
  25. package/dist/src/version.d.ts +1 -1
  26. package/dist/src/version.js +1 -1
  27. package/dist/src/version.js.map +1 -1
  28. package/dist/src/workflow.d.ts +10 -0
  29. package/dist/src/workflow.js +161 -2
  30. package/dist/src/workflow.js.map +1 -1
  31. package/docs/beta13.2-signal-review-recovery.md +38 -0
  32. package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
  33. package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
  34. package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
  35. package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +9 -1
  36. package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +9 -3
  37. package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +9 -2
  38. package/docs/pending-review-update.md +15 -0
  39. package/docs/release.md +20 -0
  40. package/docs/updating-existing-project.md +26 -4
  41. package/docs/validation-report.md +30 -9
  42. package/package.json +1 -1
  43. package/plugins/codex-workflow-gateway/references/protocol.md +7 -0
  44. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +36 -13
  45. package/schemas/plan-risk-audit-event.schema.json +1 -0
@@ -1,3 +1,4 @@
1
+ import { pendingReviewRunnerFingerprint, PENDING_REVIEW_UPDATE_SIDECAR, preparePendingReviewUpdateEvent, PENDING_REVIEW_SOURCE_VERSION, pendingReviewSourceBinding, pendingReviewBindingHash, assertPendingReviewTransport, verifiedPendingReviewTransport } from './pending-review-update.js';
1
2
  import { appendFileSync, existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
2
3
  import { createHash } from 'node:crypto';
3
4
  import path from 'node:path';
@@ -1930,6 +1931,12 @@ export class WorkflowService {
1930
1931
  knowledgeMapHash,
1931
1932
  graphUse: task.graphUse,
1932
1933
  steps: task.steps.map(stripStepRuntime),
1934
+ ...(Object.hasOwn(task, 'productionEdges')
1935
+ ? { productionEdges: structuredClone(task.productionEdges ?? []) }
1936
+ : {}),
1937
+ ...(Object.hasOwn(task, 'requiresProductionEdges')
1938
+ ? { requiresProductionEdges: task.requiresProductionEdges === true }
1939
+ : {}),
1933
1940
  };
1934
1941
  validatePlan(reboundPlan);
1935
1942
  this.validateKnowledgeTargets(knowledgeMap, reboundPlan);
@@ -2964,7 +2971,15 @@ export class WorkflowService {
2964
2971
  throw new WorkflowError('TRANSITION_BLOCKED', `External Strict Step Review requires ${stepId} in progress with canonical evidence.`);
2965
2972
  }
2966
2973
  validateTaskHistory(identity.repositoryRoot, task);
2967
- if (!isClean(identity.repositoryRoot) || headCommit(identity.repositoryRoot) !== step.evidence.commitSha) {
2974
+ const transport = verifiedPendingReviewTransport(identity.repositoryRoot, task, stepId, this.store.taskRoot(task.projectId, task.id));
2975
+ if (transport) {
2976
+ const cycle = buildCurrentStrictStepReviewCycle(this.store, task, stepId);
2977
+ if (cycle.resolution !== 'pending' || cycle.pendingEvent?.eventHash !== transport.source.pendingEventHash
2978
+ || cycle.binding.evidenceHash !== transport.source.evidenceHash) {
2979
+ throw new WorkflowError('TRANSITION_BLOCKED', 'The transported strict review must retain its exact pending chain.');
2980
+ }
2981
+ }
2982
+ if (!isClean(identity.repositoryRoot) || (headCommit(identity.repositoryRoot) !== step.evidence.commitSha && !transport)) {
2968
2983
  throw new WorkflowError('GIT_PRECONDITION_FAILED', 'External Strict Step Review requires a clean checkout at the completion commit.');
2969
2984
  }
2970
2985
  const envelope = this.taskContext(repository, taskId, 'independent-reviewer');
@@ -3651,6 +3666,137 @@ export class WorkflowService {
3651
3666
  now: this.now(),
3652
3667
  }, actor, confirmationCode);
3653
3668
  }
3669
+ pendingReviewUpdatePreflight(repository, taskId) {
3670
+ const snapshot = this.observationSnapshot(repository);
3671
+ const blockers = observationPreflightBlockers(snapshot.observation);
3672
+ try {
3673
+ pendingReviewRunnerFingerprint();
3674
+ }
3675
+ catch (error) {
3676
+ blockers.push(error instanceof Error ? error.message : String(error));
3677
+ }
3678
+ const task = snapshot.tasks.find((candidate) => candidate.id === taskId);
3679
+ let binding = null;
3680
+ if (PACKAGE_VERSION === PENDING_REVIEW_SOURCE_VERSION)
3681
+ blockers.push('The exact target runner must be newer than the supported source.');
3682
+ if (!task)
3683
+ blockers.push('Task not found.');
3684
+ if (task) {
3685
+ try {
3686
+ binding = pendingReviewSourceBinding(snapshot.identity.repositoryRoot, this.store, task);
3687
+ }
3688
+ catch (error) {
3689
+ blockers.push(error instanceof Error ? error.message : String(error));
3690
+ }
3691
+ const versions = inspectWorkflowVersions(snapshot.identity.repositoryRoot, [task.baseBranch]);
3692
+ if ([versions.declared, versions.locked, versions.installed, versions.currentBranch,
3693
+ ...versions.milestoneBases.map((base) => base.version)].some((version) => version !== PENDING_REVIEW_SOURCE_VERSION)) {
3694
+ blockers.push('All source version surfaces must equal the exact supported beta.13.1.');
3695
+ }
3696
+ }
3697
+ const otherRunningSteps = snapshot.tasks.flatMap((candidate) => candidate.steps.filter((step) => step.status === 'in_progress' && (candidate.id !== taskId || step.id !== binding?.stepId)));
3698
+ if (otherRunningSteps.length)
3699
+ blockers.push('No other Step may be running.');
3700
+ const stale = snapshot.leases.filter((lease) => Date.parse(lease.expiresAt) <= this.now().getTime());
3701
+ if (snapshot.leases.some((lease) => !stale.includes(lease)))
3702
+ blockers.push('No live writer lease may exist.');
3703
+ if (stale.some((lease) => lease.entityId !== taskId))
3704
+ blockers.push('No unrelated stale writer lease may exist.');
3705
+ const staleLeaseRepair = blockers.length === 0 && binding
3706
+ ? stale.map((lease) => ({ action: 'locks repair', entityId: lease.entityId })) : [];
3707
+ if (stale.length)
3708
+ blockers.push('Repair the exact stale Task lease with the installed source locks repair, then rerun source preflight.');
3709
+ return { readOnly: true, eligible: blockers.length === 0 && binding !== null, binding,
3710
+ bindingHash: binding ? pendingReviewBindingHash(binding) : null, blockers, staleLeaseRepair };
3711
+ }
3712
+ pendingReviewDependencyPreflight(repository, taskId, source) {
3713
+ const snapshot = this.observationSnapshot(repository);
3714
+ const blockers = observationPreflightBlockers(snapshot.observation);
3715
+ try {
3716
+ pendingReviewRunnerFingerprint();
3717
+ }
3718
+ catch (error) {
3719
+ blockers.push(error instanceof Error ? error.message : String(error));
3720
+ }
3721
+ const task = snapshot.tasks.find((candidate) => candidate.id === taskId);
3722
+ const taskHead = headCommit(snapshot.identity.repositoryRoot);
3723
+ let baseTransportCommit = null;
3724
+ if (snapshot.leases.length)
3725
+ blockers.push('No live or stale writer lease may exist.');
3726
+ if (!task)
3727
+ blockers.push('Task not found.');
3728
+ if (task && existsSync(path.join(this.store.taskRoot(task.projectId, task.id), PENDING_REVIEW_UPDATE_SIDECAR)))
3729
+ blockers.push('A pending review update is already recorded.');
3730
+ if (task) {
3731
+ try {
3732
+ // Receipt fields are claims, not authority. Reconstruct every source binding from
3733
+ // canonical Task/sidecars and the exact parents of the candidate Git commits.
3734
+ const parent = runGit(repository, ['rev-parse', 'HEAD^']);
3735
+ baseTransportCommit = runGit(repository, ['rev-parse', task.baseBranch]);
3736
+ const baseParent = runGit(repository, ['rev-parse', `${baseTransportCommit}^`]);
3737
+ const reconstructed = pendingReviewSourceBinding(repository, this.store, task, parent, baseParent);
3738
+ if (canonicalJsonStringify(reconstructed) !== canonicalJsonStringify(source)) {
3739
+ throw new Error('Source receipt does not match reconstructed Task/revision/Plan/HEAD/pending bindings.');
3740
+ }
3741
+ assertPendingReviewTransport(repository, reconstructed, taskHead, baseTransportCommit);
3742
+ if (snapshot.tasks.some((candidate) => candidate.steps.some((step) => step.status === 'in_progress' && (candidate.id !== taskId || step.id !== source.stepId)))) {
3743
+ throw new Error('No other Step may be running.');
3744
+ }
3745
+ const versions = inspectWorkflowVersions(repository, [task.baseBranch]);
3746
+ if ([versions.runtime, versions.declared, versions.locked, versions.installed, versions.currentBranch,
3747
+ ...versions.milestoneBases.map((base) => base.version)].some((version) => version !== PACKAGE_VERSION)) {
3748
+ throw new Error('All target version surfaces must equal the exact target runner.');
3749
+ }
3750
+ }
3751
+ catch (error) {
3752
+ blockers.push(error instanceof Error ? error.message : String(error));
3753
+ }
3754
+ }
3755
+ return { readOnly: true, eligible: blockers.length === 0, blockers, taskHead, baseTransportCommit };
3756
+ }
3757
+ recoverPendingReviewDependency(repository, taskId, expectedRevision, source, actor, reason) {
3758
+ if (!actor.trim() || !reason.trim())
3759
+ throw new WorkflowError('INVALID_ARGUMENT', 'Actor and reason are required.');
3760
+ const initial = this.pendingReviewDependencyPreflight(repository, taskId, source);
3761
+ if (!initial.eligible)
3762
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Pending review dependency preflight failed.', initial);
3763
+ const context = this.#mutationContext(repository);
3764
+ const task = this.store.readTask(context.identity.projectId, taskId);
3765
+ assertExpectedRevision(task, expectedRevision);
3766
+ const preflight = this.pendingReviewDependencyPreflight(repository, taskId, source);
3767
+ if (!preflight.eligible || !preflight.baseTransportCommit) {
3768
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Pending review dependency preflight changed.', preflight);
3769
+ }
3770
+ const now = this.now();
3771
+ const nextTask = { ...task, revision: task.revision + 1, updatedAt: now.toISOString(),
3772
+ systemCommits: [...task.systemCommits, preflight.taskHead],
3773
+ dependencyProvenanceRecoveries: [...(task.dependencyProvenanceRecoveries ?? []), {
3774
+ commitSha: preflight.taskHead, parentCommitSha: source.completionCommit,
3775
+ packageName: PACKAGE_NAME, packageVersion: PACKAGE_VERSION, files: ['package-lock.json', 'package.json'],
3776
+ actor: actor.trim(), reason: reason.trim(), recordedAt: now.toISOString(),
3777
+ }] };
3778
+ const projectRoot = this.store.projectRoot(task.projectId);
3779
+ const event = preparePendingReviewUpdateEvent({ source, baseTransportCommit: preflight.baseTransportCommit }, preflight.taskHead, now.toISOString());
3780
+ applyProjectTransaction({ projectRoot, projectId: task.projectId,
3781
+ transactionId: `tx-${createEntityId('TASK', now.getTime()).toLowerCase()}`,
3782
+ kind: 'pending-review-dependency-recovery', now,
3783
+ targets: [{ path: path.relative(projectRoot, path.join(this.store.taskRoot(task.projectId, task.id), 'state.json')).split(path.sep).join('/'),
3784
+ content: `${JSON.stringify(nextTask, null, 2)}\n` },
3785
+ { path: path.relative(projectRoot, path.join(this.store.taskRoot(task.projectId, task.id), PENDING_REVIEW_UPDATE_SIDECAR)).split(path.sep).join('/'),
3786
+ content: `${canonicalJsonStringify(event)}\n` }],
3787
+ validate: () => {
3788
+ const saved = this.store.readTask(task.projectId, task.id);
3789
+ if (saved.revision !== nextTask.revision || !verifiedPendingReviewTransport(repository, saved, source.stepId, this.store.taskRoot(task.projectId, task.id))) {
3790
+ throw new WorkflowError('STATE_CORRUPT', 'Pending review dependency recovery readback failed.');
3791
+ }
3792
+ const cycle = buildCurrentStrictStepReviewCycle(this.store, saved, source.stepId);
3793
+ if (cycle.resolution !== 'pending' || cycle.pendingEvent?.eventHash !== source.pendingEventHash) {
3794
+ throw new WorkflowError('STATE_CORRUPT', 'Pending review changed during dependency recovery.');
3795
+ }
3796
+ },
3797
+ });
3798
+ return this.store.readTask(task.projectId, task.id);
3799
+ }
3654
3800
  updatePreflight(repository) {
3655
3801
  const snapshot = this.observationSnapshot(repository);
3656
3802
  const tasks = snapshot.tasks;
@@ -4742,6 +4888,19 @@ export class WorkflowService {
4742
4888
  ?? prioritizedRemediationTask
4743
4889
  ?? status.tasks.find((task) => taskIsActionableFromRepositoryNext(task, milestonesById, tasksById));
4744
4890
  if (activeTask) {
4891
+ let pendingReviewDependencyRecovery = null;
4892
+ try {
4893
+ const reconstructed = pendingReviewSourceBinding(repositoryRoot, this.store, activeTask, runGit(repositoryRoot, ['rev-parse', 'HEAD^']), runGit(repositoryRoot, ['rev-parse', `${activeTask.baseBranch}^`]));
4894
+ pendingReviewDependencyRecovery = this.pendingReviewDependencyPreflight(repositoryRoot, activeTask.id, reconstructed);
4895
+ }
4896
+ catch { /* A nonmatching shape follows ordinary navigation without a compatibility bypass. */ }
4897
+ if (pendingReviewDependencyRecovery?.eligible) {
4898
+ return withAdoption({ scope: 'task', id: activeTask.id, status: activeTask.status,
4899
+ action: 'update pending-review-dependency-recover', revision: activeTask.revision,
4900
+ pendingReviewDependencyRecovery,
4901
+ sourceReceiptRequired: true,
4902
+ });
4903
+ }
4745
4904
  const historicalStepProvenanceRecovery = this.historicalStepProvenanceRecoveryPreflight(repositoryRoot, activeTask.id);
4746
4905
  if (historicalStepProvenanceRecovery.eligible) {
4747
4906
  return withAdoption({
@@ -5086,7 +5245,7 @@ export class WorkflowService {
5086
5245
  || taskNext?.action === 'task downstream-proof-recover'
5087
5246
  || taskNext?.action === 'task remediation-mode-recover'
5088
5247
  || (taskNext?.action === 'task writer-credential-replace'
5089
- && taskNext?.blockedAction === 'task remediation-mode-recover')
5248
+ && ['task step-complete', 'task step-review', 'task remediation-mode-recover'].includes(String(taskNext?.blockedAction)))
5090
5249
  || taskNext?.action === 'task plan-integrity-recover'
5091
5250
  || taskNext?.blockedAction === 'task plan-integrity-recover'
5092
5251
  || taskNext?.action === 'task corrective-yield'