codex-workflow-v2 2.0.0-beta.13.1 → 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.
- package/README.md +1 -1
- package/dist/reviewer-runtime-build.json +15 -11
- package/dist/src/cli-actions.d.ts +1 -1
- package/dist/src/cli-actions.js +3 -0
- package/dist/src/cli-actions.js.map +1 -1
- package/dist/src/cli.js +15 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/gateway-handshake.js +1 -0
- package/dist/src/gateway-handshake.js.map +1 -1
- package/dist/src/observed-routes.js +1 -0
- package/dist/src/observed-routes.js.map +1 -1
- package/dist/src/pending-review-update.d.ts +49 -0
- package/dist/src/pending-review-update.js +132 -0
- package/dist/src/pending-review-update.js.map +1 -0
- package/dist/src/repository.js +16 -4
- package/dist/src/repository.js.map +1 -1
- package/dist/src/reviewer.js +4 -1
- package/dist/src/reviewer.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/workflow.d.ts +10 -0
- package/dist/src/workflow.js +155 -2
- package/dist/src/workflow.js.map +1 -1
- package/docs/beta13.2-signal-review-recovery.md +38 -0
- package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
- package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
- package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
- package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +1 -1
- package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +2 -2
- package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +2 -2
- package/docs/pending-review-update.md +15 -0
- package/docs/release.md +11 -0
- package/package.json +1 -1
- package/plugins/codex-workflow-gateway/references/protocol.md +7 -0
- package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +36 -13
package/dist/src/workflow.js
CHANGED
|
@@ -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';
|
|
@@ -2970,7 +2971,15 @@ export class WorkflowService {
|
|
|
2970
2971
|
throw new WorkflowError('TRANSITION_BLOCKED', `External Strict Step Review requires ${stepId} in progress with canonical evidence.`);
|
|
2971
2972
|
}
|
|
2972
2973
|
validateTaskHistory(identity.repositoryRoot, task);
|
|
2973
|
-
|
|
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)) {
|
|
2974
2983
|
throw new WorkflowError('GIT_PRECONDITION_FAILED', 'External Strict Step Review requires a clean checkout at the completion commit.');
|
|
2975
2984
|
}
|
|
2976
2985
|
const envelope = this.taskContext(repository, taskId, 'independent-reviewer');
|
|
@@ -3657,6 +3666,137 @@ export class WorkflowService {
|
|
|
3657
3666
|
now: this.now(),
|
|
3658
3667
|
}, actor, confirmationCode);
|
|
3659
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
|
+
}
|
|
3660
3800
|
updatePreflight(repository) {
|
|
3661
3801
|
const snapshot = this.observationSnapshot(repository);
|
|
3662
3802
|
const tasks = snapshot.tasks;
|
|
@@ -4748,6 +4888,19 @@ export class WorkflowService {
|
|
|
4748
4888
|
?? prioritizedRemediationTask
|
|
4749
4889
|
?? status.tasks.find((task) => taskIsActionableFromRepositoryNext(task, milestonesById, tasksById));
|
|
4750
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
|
+
}
|
|
4751
4904
|
const historicalStepProvenanceRecovery = this.historicalStepProvenanceRecoveryPreflight(repositoryRoot, activeTask.id);
|
|
4752
4905
|
if (historicalStepProvenanceRecovery.eligible) {
|
|
4753
4906
|
return withAdoption({
|
|
@@ -5092,7 +5245,7 @@ export class WorkflowService {
|
|
|
5092
5245
|
|| taskNext?.action === 'task downstream-proof-recover'
|
|
5093
5246
|
|| taskNext?.action === 'task remediation-mode-recover'
|
|
5094
5247
|
|| (taskNext?.action === 'task writer-credential-replace'
|
|
5095
|
-
&&
|
|
5248
|
+
&& ['task step-complete', 'task step-review', 'task remediation-mode-recover'].includes(String(taskNext?.blockedAction)))
|
|
5096
5249
|
|| taskNext?.action === 'task plan-integrity-recover'
|
|
5097
5250
|
|| taskNext?.blockedAction === 'task plan-integrity-recover'
|
|
5098
5251
|
|| taskNext?.action === 'task corrective-yield'
|