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.
- package/README.md +1 -1
- package/dist/reviewer-runtime-build.json +17 -13
- package/dist/src/alpha6/plan-risk.d.ts +1 -0
- package/dist/src/alpha6/plan-risk.js +34 -7
- package/dist/src/alpha6/plan-risk.js.map +1 -1
- 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/contracts.d.ts +2 -0
- 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/state/corrective-replan-executor.js +3 -1
- package/dist/src/state/corrective-replan-executor.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/src/workflow.d.ts +10 -0
- package/dist/src/workflow.js +161 -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 +9 -1
- package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +9 -3
- package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +9 -2
- package/docs/pending-review-update.md +15 -0
- package/docs/release.md +20 -0
- package/docs/updating-existing-project.md +26 -4
- package/docs/validation-report.md +30 -9
- 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/schemas/plan-risk-audit-event.schema.json +1 -0
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';
|
|
@@ -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
|
-
|
|
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
|
-
&&
|
|
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'
|