codex-workflow-v2 2.0.0-beta.12.17 → 2.0.0-beta.12.19

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.
@@ -2401,15 +2401,22 @@ export class WorkflowService {
2401
2401
  const downstreamProofCarryover = step.status === 'planned' && dirty.length > 0
2402
2402
  ? assessDownstreamProofCarryover(this.store, context.identity.repositoryRoot, task, stepId)
2403
2403
  : null;
2404
+ const planIntegrityDirtyCarryover = step.status === 'planned' && dirty.length > 0
2405
+ ? this.assessPostRebindCheckSupportDirtyCarryover(context.identity.repositoryRoot, task, stepId)
2406
+ : null;
2404
2407
  if (step.status === 'planned'
2405
2408
  && dirty.length > 0
2406
2409
  && !resumesCorrectiveReplan
2407
- && !downstreamProofCarryover?.eligible) {
2410
+ && !downstreamProofCarryover?.eligible
2411
+ && !planIntegrityDirtyCarryover?.eligible) {
2408
2412
  throw new WorkflowError('GIT_PRECONDITION_FAILED', `${stepId} must start from a clean checkout.`, {
2409
2413
  changedFiles: dirty,
2410
2414
  ...(downstreamProofCarryover?.applicable
2411
2415
  ? { downstreamProofCarryoverBlockers: downstreamProofCarryover.blockers }
2412
2416
  : {}),
2417
+ ...(planIntegrityDirtyCarryover?.applicable
2418
+ ? { planIntegrityDirtyCarryoverBlockers: planIntegrityDirtyCarryover.blockers }
2419
+ : {}),
2413
2420
  });
2414
2421
  }
2415
2422
  const dirtyOutsideStep = dirty.filter((file) => !pathAllowed(file, allowedWrites));
@@ -3640,6 +3647,20 @@ export class WorkflowService {
3640
3647
  .map((step) => assessPlanIntegrityRecovery(this.store, snapshot.identity.repositoryRoot, task, step.id))
3641
3648
  .filter((assessment) => assessment.applicable);
3642
3649
  const assessment = assessments.length === 1 ? assessments[0] : null;
3650
+ const adoptedCompatibility = assessment
3651
+ ? null
3652
+ : this.postRebindCheckSupportCompatibility(snapshot, task);
3653
+ const dirtyCarryoverAssessment = assessment || adoptedCompatibility
3654
+ ? null
3655
+ : this.assessPostRebindCheckSupportDirtyCarryover(snapshot.identity.repositoryRoot, task);
3656
+ const dirtyCarryoverCompatibility = dirtyCarryoverAssessment?.eligible
3657
+ && dirtyCarryoverAssessment.compatibility
3658
+ ? {
3659
+ ...dirtyCarryoverAssessment.compatibility,
3660
+ planHash: task.planHash,
3661
+ mode: 'post-rebind-dirty-carryover',
3662
+ }
3663
+ : null;
3643
3664
  const versions = inspectWorkflowVersions(snapshot.identity.repositoryRoot, [task.baseBranch]);
3644
3665
  const versionSurfaces = {
3645
3666
  declared: versions.declared,
@@ -3655,7 +3676,11 @@ export class WorkflowService {
3655
3676
  const targetPatchOrdinal = targetPatch === null ? null : Number(targetPatch);
3656
3677
  const blockers = [
3657
3678
  ...(assessments.length > 1 ? ['Exactly one failed Step may expose a Plan-integrity conflict.'] : []),
3658
- ...(assessment?.blockers ?? ['No eligible Plan-integrity conflict was found.']),
3679
+ ...(assessment?.blockers ?? (adoptedCompatibility || dirtyCarryoverCompatibility
3680
+ ? []
3681
+ : (dirtyCarryoverAssessment?.blockers.length
3682
+ ? dirtyCarryoverAssessment.blockers
3683
+ : ['No eligible Plan-integrity conflict, post-rebind scope adoption, or dirty carryover was found.']))),
3659
3684
  ...(sourcePatchOrdinal === null
3660
3685
  || targetPatchOrdinal === null
3661
3686
  || sourcePatchOrdinal < 11
@@ -3670,18 +3695,29 @@ export class WorkflowService {
3670
3695
  ];
3671
3696
  return {
3672
3697
  readOnly: true,
3673
- eligible: Boolean(assessment?.eligible) && blockers.length === 0,
3674
- action: assessment?.eligible && blockers.length === 0 ? 'update plan-integrity-transport' : null,
3698
+ eligible: Boolean(assessment?.eligible || adoptedCompatibility || dirtyCarryoverCompatibility) && blockers.length === 0,
3699
+ action: (assessment?.eligible || adoptedCompatibility || dirtyCarryoverCompatibility) && blockers.length === 0
3700
+ ? 'update plan-integrity-transport'
3701
+ : null,
3675
3702
  projectId: task.projectId,
3676
3703
  taskId: task.id,
3677
3704
  taskRevision: task.revision,
3678
- stepId: assessment?.stepId ?? null,
3705
+ stepId: assessment?.stepId ?? adoptedCompatibility?.stepId ?? dirtyCarryoverCompatibility?.stepId ?? null,
3679
3706
  sourceVersion,
3680
3707
  targetVersion: PACKAGE_VERSION,
3681
3708
  taskBranch: task.taskBranch,
3682
3709
  baseBranch: task.baseBranch,
3683
- dirtyFiles: assessment?.evidence?.changedFiles ?? [],
3684
- conflictHash: assessment?.evidence?.conflictHash ?? null,
3710
+ dirtyFiles: assessment?.evidence?.changedFiles
3711
+ ?? adoptedCompatibility?.dirtyFiles
3712
+ ?? dirtyCarryoverCompatibility?.dirtyFiles
3713
+ ?? [],
3714
+ conflictHash: assessment?.evidence?.conflictHash
3715
+ ?? adoptedCompatibility?.conflictHash
3716
+ ?? dirtyCarryoverCompatibility?.conflictHash
3717
+ ?? null,
3718
+ compatibilityMode: adoptedCompatibility?.mode
3719
+ ?? dirtyCarryoverCompatibility?.mode
3720
+ ?? 'failed-step-conflict',
3685
3721
  requiredTransport: [
3686
3722
  'Commit only package.json and package-lock.json on the active Milestone base.',
3687
3723
  'Fast-forward or apply only that dependency commit to the Task branch while preserving all product bytes.',
@@ -3717,10 +3753,19 @@ export class WorkflowService {
3717
3753
  })
3718
3754
  .filter((assessment) => assessment.applicable && assessment.eligible && assessment.evidence);
3719
3755
  const assessment = assessments.length === 1 ? assessments[0] : null;
3756
+ const adoptedCompatibility = assessment?.evidence
3757
+ ? null
3758
+ : this.postRebindCheckSupportCompatibility(snapshot, task);
3759
+ const dirtyCarryoverCompatibility = assessment?.evidence || adoptedCompatibility
3760
+ ? null
3761
+ : this.carriedPostRebindCheckSupportDirtyCarryover(snapshot, task);
3720
3762
  const carriedCompatibility = !assessment?.evidence && task.planHash
3721
3763
  ? this.carriedPlanIntegrityUpdateCompatibility(snapshot, task)
3722
3764
  : null;
3723
- if ((!assessment?.evidence && !carriedCompatibility) || !task.planHash) {
3765
+ if ((!assessment?.evidence
3766
+ && !carriedCompatibility
3767
+ && !adoptedCompatibility
3768
+ && !dirtyCarryoverCompatibility) || !task.planHash) {
3724
3769
  const ordinary = this.inspectDependencyProvenanceCandidate(snapshot, task);
3725
3770
  return {
3726
3771
  ...ordinary,
@@ -3734,16 +3779,19 @@ export class WorkflowService {
3734
3779
  ])],
3735
3780
  };
3736
3781
  }
3737
- const compatibility = carriedCompatibility ?? {
3738
- stepId: assessment.stepId,
3739
- planHash: task.planHash,
3740
- conflictHash: assessment.evidence.conflictHash,
3741
- dirtyFiles: [...assessment.evidence.changedFiles],
3742
- dirtyWorktreeHash: hashDirtyWorktree(snapshot.identity.repositoryRoot, assessment.evidence.changedFiles),
3743
- remediationEventIds: assessment.evidence.kind === 'missing-npm-script-outside-allowed-writes'
3744
- ? [assessment.evidence.remediationEventId]
3745
- : assessment.evidence.remediationEvents.map((event) => event.eventId),
3746
- };
3782
+ const compatibility = carriedCompatibility
3783
+ ?? adoptedCompatibility
3784
+ ?? dirtyCarryoverCompatibility
3785
+ ?? {
3786
+ stepId: assessment.stepId,
3787
+ planHash: task.planHash,
3788
+ conflictHash: assessment.evidence.conflictHash,
3789
+ dirtyFiles: [...assessment.evidence.changedFiles],
3790
+ dirtyWorktreeHash: hashDirtyWorktree(snapshot.identity.repositoryRoot, assessment.evidence.changedFiles),
3791
+ remediationEventIds: assessment.evidence.kind === 'missing-npm-script-outside-allowed-writes'
3792
+ ? [assessment.evidence.remediationEventId]
3793
+ : assessment.evidence.remediationEvents.map((event) => event.eventId),
3794
+ };
3747
3795
  return this.inspectDependencyProvenanceCandidate(snapshot, task, null, null, null, compatibility);
3748
3796
  }
3749
3797
  downstreamProofReplanDependencyRecoveryPreflight(repository, taskId) {
@@ -5174,6 +5222,11 @@ export class WorkflowService {
5174
5222
  && changedFiles(repositoryRoot).length > 0
5175
5223
  ? assessDownstreamProofCarryover(this.store, repositoryRoot, task, nextStepId)
5176
5224
  : null;
5225
+ const planIntegrityDirtyCarryover = next.action === 'task run'
5226
+ && nextStepId
5227
+ && changedFiles(repositoryRoot).length > 0
5228
+ ? this.assessPostRebindCheckSupportDirtyCarryover(repositoryRoot, task, nextStepId)
5229
+ : null;
5177
5230
  let mechanicalFeasibilityOverlay = {};
5178
5231
  if (task.status === 'awaiting_execution_authorization' && task.planHash) {
5179
5232
  try {
@@ -5725,6 +5778,29 @@ export class WorkflowService {
5725
5778
  },
5726
5779
  }
5727
5780
  : {}),
5781
+ ...(planIntegrityDirtyCarryover?.applicable
5782
+ ? planIntegrityDirtyCarryover.eligible
5783
+ ? {
5784
+ planIntegrityDirtyCarryover: {
5785
+ state: 'validated',
5786
+ stepId: nextStepId,
5787
+ dependencyRecoveryCommit: planIntegrityDirtyCarryover.dependencyRecoveryCommit,
5788
+ dirtyFiles: planIntegrityDirtyCarryover.compatibility?.dirtyFiles ?? [],
5789
+ dirtyWorktreeHash: planIntegrityDirtyCarryover.compatibility?.dirtyWorktreeHash ?? null,
5790
+ scopeExtensions: planIntegrityDirtyCarryover.compatibility?.scopeExtensions ?? [],
5791
+ meaning: 'The current Step may adopt exactly these hash-bound check-support bytes on task run.',
5792
+ },
5793
+ }
5794
+ : {
5795
+ action: 'doctor',
5796
+ blockedAction: 'task run',
5797
+ planIntegrityDirtyCarryover: {
5798
+ state: 'blocked',
5799
+ stepId: nextStepId,
5800
+ blockers: planIntegrityDirtyCarryover.blockers,
5801
+ },
5802
+ }
5803
+ : {}),
5728
5804
  ...(delegatedApprovalOptions.length > 0 ? { delegatedApprovalOptions } : {}),
5729
5805
  ...(correctiveDerivedAuthority && correctiveDerivedActor
5730
5806
  ? {
@@ -6086,6 +6162,180 @@ export class WorkflowService {
6086
6162
  });
6087
6163
  return candidates.length === 1 ? structuredClone(candidates[0].planIntegrity) : null;
6088
6164
  }
6165
+ postRebindCheckSupportCompatibility(snapshot, task) {
6166
+ if (task.status !== 'awaiting_execution_authorization'
6167
+ || !task.planHash
6168
+ || task.steps.some((step) => step.status === 'in_progress')
6169
+ || readTaskC1Posture(this.store, task).state !== 'none')
6170
+ return null;
6171
+ const dirtyFiles = changedFiles(snapshot.identity.repositoryRoot).sort();
6172
+ const candidates = readCorrectiveDecisionEvents(this.store, task).flatMap((event) => {
6173
+ const evidence = event.planIntegrityEvidence;
6174
+ if (event.planHash === task.planHash
6175
+ || event.decision !== 'continue-fix'
6176
+ || event.recoveryBasis !== 'plan-integrity'
6177
+ || evidence?.kind !== 'check-support-anchor-outside-allowed-writes'
6178
+ || !task.systemCommits.includes(evidence.gitHead)
6179
+ || canonicalJsonStringify([...evidence.changedFiles].sort()) !== canonicalJsonStringify(dirtyFiles)) {
6180
+ return [];
6181
+ }
6182
+ const step = task.steps.find((candidate) => candidate.id === event.stepId) ?? null;
6183
+ if (!step
6184
+ || step.status !== 'planned'
6185
+ || evidence.scopeExtensions.some((scopePath) => !pathAllowed(scopePath, step.allowedWrites)
6186
+ || !step.expectedOutputs.some((declared) => (declared.startsWith('path:') ? declared.slice(5) : declared) === scopePath)))
6187
+ return [];
6188
+ return [{
6189
+ stepId: step.id,
6190
+ planHash: task.planHash,
6191
+ conflictHash: evidence.conflictHash,
6192
+ dirtyFiles: [...dirtyFiles],
6193
+ dirtyWorktreeHash: hashDirtyWorktree(snapshot.identity.repositoryRoot, dirtyFiles),
6194
+ remediationEventIds: evidence.remediationEvents.map((binding) => binding.eventId),
6195
+ mode: 'post-rebind-scope-adoption',
6196
+ scopeExtensions: [...evidence.scopeExtensions],
6197
+ }];
6198
+ });
6199
+ return candidates.length === 1 ? candidates[0] : null;
6200
+ }
6201
+ carriedPostRebindCheckSupportDirtyCarryover(snapshot, task) {
6202
+ let boundaryCommit = headCommit(snapshot.identity.repositoryRoot);
6203
+ let dependencyCommitCount = 0;
6204
+ while (!task.systemCommits.includes(boundaryCommit)) {
6205
+ const dependencyCommit = inspectBoundedWorkflowDependencyCommit(snapshot.identity.repositoryRoot, boundaryCommit);
6206
+ if (!dependencyCommit)
6207
+ return null;
6208
+ boundaryCommit = dependencyCommit.parentCommitSha;
6209
+ dependencyCommitCount += 1;
6210
+ }
6211
+ if (dependencyCommitCount === 0)
6212
+ return null;
6213
+ const assessment = this.assessPostRebindCheckSupportDirtyCarryover(snapshot.identity.repositoryRoot, task, undefined, boundaryCommit);
6214
+ if (!assessment.eligible || !assessment.compatibility || !task.planHash)
6215
+ return null;
6216
+ return {
6217
+ ...assessment.compatibility,
6218
+ planHash: task.planHash,
6219
+ mode: 'post-rebind-dirty-carryover',
6220
+ };
6221
+ }
6222
+ assessPostRebindCheckSupportDirtyCarryover(repositoryRoot, task, stepId, historyBoundary = headCommit(repositoryRoot)) {
6223
+ const relevantRecords = [...(task.dependencyProvenanceRecoveries ?? [])]
6224
+ .reverse()
6225
+ .filter((record) => {
6226
+ const compatibility = record.planIntegrity;
6227
+ return compatibility
6228
+ && ['post-rebind-scope-adoption', 'post-rebind-dirty-carryover'].includes(compatibility.mode ?? '')
6229
+ && (!stepId || compatibility.stepId === stepId);
6230
+ });
6231
+ if (relevantRecords.length === 0) {
6232
+ return {
6233
+ applicable: false,
6234
+ eligible: false,
6235
+ compatibility: null,
6236
+ dependencyRecoveryCommit: null,
6237
+ blockers: [],
6238
+ };
6239
+ }
6240
+ const blockers = [];
6241
+ const record = relevantRecords[0];
6242
+ const compatibility = record.planIntegrity;
6243
+ const step = task.steps.find((candidate) => candidate.id === compatibility.stepId) ?? null;
6244
+ const dirtyFiles = changedFiles(repositoryRoot).sort();
6245
+ let dirtyWorktreeHash = null;
6246
+ try {
6247
+ dirtyWorktreeHash = hashDirtyWorktree(repositoryRoot, dirtyFiles);
6248
+ }
6249
+ catch (error) {
6250
+ blockers.push(error instanceof Error ? error.message : String(error));
6251
+ }
6252
+ if (record.commitSha !== historyBoundary || !task.systemCommits.includes(record.commitSha)) {
6253
+ blockers.push('The latest check-support compatibility record must bind the exact registered history boundary.');
6254
+ }
6255
+ if (task.workspaceOwner !== 'local') {
6256
+ blockers.push('Check-support dirty carryover requires the recorded local Task workspace.');
6257
+ }
6258
+ if (!task.taskBranch || currentBranch(repositoryRoot) !== task.taskBranch) {
6259
+ blockers.push('Current branch must equal the recorded Task branch.');
6260
+ }
6261
+ if (!['ready', 'in_progress'].includes(task.status)) {
6262
+ blockers.push('The Task must retain an execution-authorized posture before dirty carryover.');
6263
+ }
6264
+ if (!task.planHash) {
6265
+ blockers.push('The Task must retain a current Plan hash.');
6266
+ }
6267
+ if (!step || step.status !== 'planned' || step.evidence !== null) {
6268
+ blockers.push(`The bound Step ${compatibility.stepId} must remain planned with no evidence.`);
6269
+ }
6270
+ if (task.steps.some((candidate) => candidate.status === 'in_progress')) {
6271
+ blockers.push('No Step may already be in progress before dirty carryover.');
6272
+ }
6273
+ if (canonicalJsonStringify(dirtyFiles)
6274
+ !== canonicalJsonStringify([...compatibility.dirtyFiles].sort())) {
6275
+ blockers.push('Dirty files changed after the registered check-support compatibility boundary.');
6276
+ }
6277
+ if (dirtyWorktreeHash !== compatibility.dirtyWorktreeHash) {
6278
+ blockers.push('Dirty file content changed after the registered check-support compatibility boundary.');
6279
+ }
6280
+ if (!compatibility.scopeExtensions?.length) {
6281
+ blockers.push('The compatibility record must retain at least one machine-derived scope extension.');
6282
+ }
6283
+ if (step) {
6284
+ if (dirtyFiles.some((dirtyPath) => !pathAllowed(dirtyPath, step.allowedWrites))) {
6285
+ blockers.push('The current Step must explicitly own every preserved dirty file.');
6286
+ }
6287
+ if ((compatibility.scopeExtensions ?? []).some((scopePath) => !pathAllowed(scopePath, step.allowedWrites)
6288
+ || !step.expectedOutputs.some((declared) => (declared.startsWith('path:') ? declared.slice(5) : declared) === scopePath))) {
6289
+ blockers.push('The current Step must explicitly own every check-support scope extension as allowedWrites and expectedOutputs.');
6290
+ }
6291
+ }
6292
+ let reboundPlanHash = compatibility.planHash;
6293
+ for (const rebind of task.knowledgeRebinds) {
6294
+ if (rebind.previousPlanHash === reboundPlanHash)
6295
+ reboundPlanHash = rebind.reboundPlanHash;
6296
+ }
6297
+ if (!task.planHash || reboundPlanHash !== task.planHash) {
6298
+ blockers.push('The current Plan must equal the compatibility Plan or its exact Knowledge-rebind descendant.');
6299
+ }
6300
+ const matchingDecisions = readCorrectiveDecisionEvents(this.store, task).filter((event) => {
6301
+ const evidence = event.planIntegrityEvidence;
6302
+ return event.stepId === compatibility.stepId
6303
+ && event.decision === 'continue-fix'
6304
+ && event.recoveryBasis === 'plan-integrity'
6305
+ && evidence?.kind === 'check-support-anchor-outside-allowed-writes'
6306
+ && evidence.conflictHash === compatibility.conflictHash
6307
+ && canonicalJsonStringify([...evidence.changedFiles].sort())
6308
+ === canonicalJsonStringify([...compatibility.dirtyFiles].sort())
6309
+ && canonicalJsonStringify(evidence.remediationEvents.map((binding) => binding.eventId))
6310
+ === canonicalJsonStringify(compatibility.remediationEventIds)
6311
+ && canonicalJsonStringify([...evidence.scopeExtensions].sort())
6312
+ === canonicalJsonStringify([...(compatibility.scopeExtensions ?? [])].sort());
6313
+ });
6314
+ if (matchingDecisions.length !== 1) {
6315
+ blockers.push('Exactly one validated historical check-support decision must bind the preserved dirty set and scope.');
6316
+ }
6317
+ try {
6318
+ validateTaskHistory(repositoryRoot, task, historyBoundary);
6319
+ }
6320
+ catch (error) {
6321
+ blockers.push(error instanceof Error ? error.message : String(error));
6322
+ }
6323
+ return {
6324
+ applicable: true,
6325
+ eligible: blockers.length === 0,
6326
+ compatibility: blockers.length === 0
6327
+ ? {
6328
+ ...structuredClone(compatibility),
6329
+ planHash: task.planHash,
6330
+ dirtyFiles,
6331
+ dirtyWorktreeHash: dirtyWorktreeHash,
6332
+ mode: 'post-rebind-dirty-carryover',
6333
+ }
6334
+ : null,
6335
+ dependencyRecoveryCommit: record.commitSha,
6336
+ blockers,
6337
+ };
6338
+ }
6089
6339
  inspectDependencyProvenanceCandidate(snapshot, task, activeDownstreamProof = null, downstreamProofReplan = null, historicalStepProvenance = null, planIntegrity = null) {
6090
6340
  const versions = inspectWorkflowVersions(snapshot.identity.repositoryRoot, [task.baseBranch]);
6091
6341
  const now = this.now().getTime();