codex-workflow-v2 2.0.0-beta.12 → 2.0.0-beta.12.10

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 (62) hide show
  1. package/README.md +65 -5
  2. package/dist/src/alpha6/downstream-proof.d.ts +68 -0
  3. package/dist/src/alpha6/downstream-proof.js +604 -0
  4. package/dist/src/alpha6/downstream-proof.js.map +1 -0
  5. package/dist/src/alpha6/milestone.d.ts +6 -2
  6. package/dist/src/alpha6/milestone.js +164 -10
  7. package/dist/src/alpha6/milestone.js.map +1 -1
  8. package/dist/src/alpha6/plan-risk.js +2 -2
  9. package/dist/src/alpha6/plan-risk.js.map +1 -1
  10. package/dist/src/alpha6/remediation.d.ts +8 -3
  11. package/dist/src/alpha6/remediation.js +374 -9
  12. package/dist/src/alpha6/remediation.js.map +1 -1
  13. package/dist/src/alpha6/review.d.ts +6 -0
  14. package/dist/src/alpha6/review.js +12 -0
  15. package/dist/src/alpha6/review.js.map +1 -1
  16. package/dist/src/alpha7/corrective-recovery.d.ts +8 -1
  17. package/dist/src/alpha7/corrective-recovery.js +406 -26
  18. package/dist/src/alpha7/corrective-recovery.js.map +1 -1
  19. package/dist/src/cli.js +135 -5
  20. package/dist/src/cli.js.map +1 -1
  21. package/dist/src/contracts.d.ts +154 -3
  22. package/dist/src/dependency-provenance.d.ts +9 -2
  23. package/dist/src/dependency-provenance.js +70 -8
  24. package/dist/src/dependency-provenance.js.map +1 -1
  25. package/dist/src/git.d.ts +1 -1
  26. package/dist/src/git.js +6 -2
  27. package/dist/src/git.js.map +1 -1
  28. package/dist/src/historical-step-provenance.d.ts +20 -0
  29. package/dist/src/historical-step-provenance.js +111 -0
  30. package/dist/src/historical-step-provenance.js.map +1 -0
  31. package/dist/src/state/corrective-replan-executor.js +5 -0
  32. package/dist/src/state/corrective-replan-executor.js.map +1 -1
  33. package/dist/src/version.d.ts +1 -1
  34. package/dist/src/version.js +1 -1
  35. package/dist/src/version.js.map +1 -1
  36. package/dist/src/workflow.d.ts +40 -2
  37. package/dist/src/workflow.js +1440 -65
  38. package/dist/src/workflow.js.map +1 -1
  39. package/docs/autonomy-guardrails.md +91 -1
  40. package/docs/decisions.md +14 -0
  41. package/docs/delegated-approval.md +6 -1
  42. package/docs/development-flow.md +60 -5
  43. package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
  44. package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
  45. package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
  46. package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +97 -7
  47. package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +68 -9
  48. package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +150 -33
  49. package/docs/release.md +51 -8
  50. package/docs/updating-existing-project.md +155 -2
  51. package/docs/validation-report.md +101 -94
  52. package/package.json +7 -2
  53. package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +1 -1
  54. package/plugins/codex-workflow-gateway/references/protocol.md +69 -1
  55. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +156 -1
  56. package/schemas/authorization-event.schema.json +8 -1
  57. package/schemas/downstream-proof-invalidation-event.schema.json +71 -0
  58. package/schemas/downstream-proof-replan-recovery-event.schema.json +61 -0
  59. package/schemas/milestone-scope-change-event.schema.json +25 -1
  60. package/schemas/stop-escalate-override-event.schema.json +45 -0
  61. package/schemas/task.schema.json +104 -0
  62. package/scripts/generate-pdf-docs.py +13 -2
@@ -1,4 +1,4 @@
1
- import type { AuthorizationEvent, MilestoneMembership, MilestonePlanInput, MilestonePlanMembershipInput, MilestoneScopeSnapshot, MilestoneState, TaskState } from '../contracts.js';
1
+ import type { AuditedRemediationEvidence, AuthorizationEvent, MilestoneMembership, MilestonePlanInput, MilestonePlanMembershipInput, MilestoneScopeChangeEvent, MilestoneScopeSnapshot, MilestoneState, TaskState } from '../contracts.js';
2
2
  import type { FileStateStore } from '../state/store.js';
3
3
  type ReadTask = (taskId: string) => TaskState;
4
4
  type ListTasks = () => TaskState[];
@@ -40,8 +40,10 @@ export interface PreparedMilestoneScopeChange {
40
40
  after: MilestoneScopeSnapshot;
41
41
  confirmationCodeBindingHash: string;
42
42
  confirmationCode: string;
43
- authorizationMode: 'human' | 'milestone-autonomy';
43
+ authorizationMode: 'human' | 'milestone-autonomy' | 'audited-remediation';
44
44
  autonomyContractEventHash: string | null;
45
+ remediationEvidence: AuditedRemediationEvidence | null;
46
+ resultingStatus: MilestoneState['status'];
45
47
  }
46
48
  export interface PreparedMilestoneScopeChangeBundle {
47
49
  state: MilestoneState;
@@ -83,6 +85,7 @@ export declare function prepareMilestoneScopeChangeCandidate(options: {
83
85
  readTask: ReadTask;
84
86
  actor: string;
85
87
  autonomyContractEventHash?: string;
88
+ auditedRemediationEvidence?: AuditedRemediationEvidence;
86
89
  }): PreparedMilestoneScopeChange;
87
90
  export declare function applyMilestoneScopeChangeTransaction(options: {
88
91
  store: FileStateStore;
@@ -101,4 +104,5 @@ export declare function applyInitialMilestonePlanTransaction(options: {
101
104
  readTask: ReadTask;
102
105
  listTasks?: ListTasks;
103
106
  }): MilestoneState;
107
+ export declare function readMilestoneScopeChangeEvents(store: FileStateStore, projectId: string, milestoneId: string): MilestoneScopeChangeEvent[];
104
108
  export {};
@@ -40,12 +40,13 @@ export function prepareMilestoneScopeChangeBundle(options) {
40
40
  confirmationCodeBindingHash: prepared.confirmationCodeBindingHash,
41
41
  authorizationMode: prepared.authorizationMode,
42
42
  autonomyContractEventHash: prepared.autonomyContractEventHash,
43
+ remediationEvidence: prepared.remediationEvidence,
43
44
  }), { validateEvent: (candidate) => validateMilestoneScopeChangeEvent(candidate, milestone.id) });
44
45
  const state = {
45
46
  ...milestone,
46
47
  revision: milestone.revision + 1,
47
48
  updatedAt: now.toISOString(),
48
- status: 'awaiting_execution_authorization',
49
+ status: prepared.resultingStatus,
49
50
  outcome: prepared.normalizedPlan.outcome,
50
51
  successSignal: prepared.normalizedPlan.successSignal,
51
52
  acceptance: prepared.normalizedPlan.acceptance,
@@ -492,7 +493,9 @@ export function prepareMilestoneScopeChangeCandidate(options) {
492
493
  if (!trimmedActor) {
493
494
  throw new WorkflowError('INVALID_ARGUMENT', 'Milestone scope change requires an explicit actor.');
494
495
  }
495
- if (trimmedActor.startsWith('agent:') && !options.autonomyContractEventHash) {
496
+ if (trimmedActor.startsWith('agent:')
497
+ && !options.autonomyContractEventHash
498
+ && !options.auditedRemediationEvidence) {
496
499
  throw new WorkflowError('TRANSITION_BLOCKED', 'Milestone scope change is human-only and rejects delegated agent actors.', {
497
500
  actor: trimmedActor,
498
501
  });
@@ -500,8 +503,19 @@ export function prepareMilestoneScopeChangeCandidate(options) {
500
503
  if (options.autonomyContractEventHash) {
501
504
  assertHash64(options.autonomyContractEventHash, 'Milestone autonomy contract event hash');
502
505
  }
503
- const authorizationMode = options.autonomyContractEventHash ? 'milestone-autonomy' : 'human';
506
+ if (options.autonomyContractEventHash && options.auditedRemediationEvidence) {
507
+ throw new WorkflowError('INVALID_ARGUMENT', 'Milestone scope change cannot combine autonomy and audited-remediation authority.');
508
+ }
509
+ if (options.auditedRemediationEvidence && !trimmedActor.startsWith('agent:')) {
510
+ throw new WorkflowError('INVALID_ARGUMENT', 'Audited remediation requires an agent actor.');
511
+ }
512
+ const authorizationMode = options.auditedRemediationEvidence
513
+ ? 'audited-remediation'
514
+ : options.autonomyContractEventHash
515
+ ? 'milestone-autonomy'
516
+ : 'human';
504
517
  const autonomyContractEventHash = options.autonomyContractEventHash ?? null;
518
+ const remediationEvidence = options.auditedRemediationEvidence ?? null;
505
519
  const normalizedPlan = normalizeMilestonePlan(plan);
506
520
  const before = milestoneScopeSnapshotFromState(milestone);
507
521
  const after = milestoneScopeSnapshotFromPlan(normalizedPlan);
@@ -558,6 +572,7 @@ export function prepareMilestoneScopeChangeCandidate(options) {
558
572
  after,
559
573
  authorizationMode,
560
574
  autonomyContractEventHash,
575
+ remediationEvidence,
561
576
  });
562
577
  return {
563
578
  milestoneId: milestone.id,
@@ -572,6 +587,10 @@ export function prepareMilestoneScopeChangeCandidate(options) {
572
587
  confirmationCode: milestoneScopeChangeConfirmationCode(confirmationCodeBindingHash),
573
588
  authorizationMode,
574
589
  autonomyContractEventHash,
590
+ remediationEvidence,
591
+ resultingStatus: authorizationMode === 'audited-remediation'
592
+ ? 'active'
593
+ : 'awaiting_execution_authorization',
575
594
  };
576
595
  }
577
596
  export function applyMilestoneScopeChangeTransaction(options) {
@@ -598,6 +617,7 @@ export function applyMilestoneScopeChangeTransaction(options) {
598
617
  confirmationCodeBindingHash: prepared.confirmationCodeBindingHash,
599
618
  authorizationMode: prepared.authorizationMode,
600
619
  autonomyContractEventHash: prepared.autonomyContractEventHash,
620
+ remediationEvidence: prepared.remediationEvidence,
601
621
  }), { validateEvent: (candidate) => validateMilestoneScopeChangeEvent(candidate, milestone.id) });
602
622
  if (preparedSidecar.event.actor !== prepared.actor) {
603
623
  throw new WorkflowError('STATE_CORRUPT', 'Prepared Milestone scope-change actor drifted before journal apply.', {
@@ -616,7 +636,7 @@ export function applyMilestoneScopeChangeTransaction(options) {
616
636
  ...milestone,
617
637
  revision: milestone.revision + 1,
618
638
  updatedAt: now.toISOString(),
619
- status: 'awaiting_execution_authorization',
639
+ status: prepared.resultingStatus,
620
640
  outcome: prepared.normalizedPlan.outcome,
621
641
  successSignal: prepared.normalizedPlan.successSignal,
622
642
  acceptance: prepared.normalizedPlan.acceptance,
@@ -843,7 +863,7 @@ function assertMilestonePlanAgreement(store, milestone) {
843
863
  });
844
864
  }
845
865
  }
846
- function readMilestoneScopeChangeEvents(store, projectId, milestoneId) {
866
+ export function readMilestoneScopeChangeEvents(store, projectId, milestoneId) {
847
867
  const sidecarPath = path.join(resolveAlpha6MilestoneRoot(store, projectId, milestoneId), SCOPE_CHANGE_SIDECAR);
848
868
  assertNoSymlinkPathComponents(sidecarPath, { label: 'Milestone scope-change sidecar path', requireLeaf: true });
849
869
  if (!existsSync(sidecarPath))
@@ -886,7 +906,9 @@ function validateMilestoneScopeChangeEvent(event, expectedMilestoneId) {
886
906
  if (!event.actor.trim()) {
887
907
  throw new WorkflowError('STATE_CORRUPT', 'Milestone scope-change actor is required.', { eventId: event.eventId });
888
908
  }
889
- if (event.actor.startsWith('agent:') && event.authorizationMode !== 'milestone-autonomy') {
909
+ if (event.actor.startsWith('agent:')
910
+ && event.authorizationMode !== 'milestone-autonomy'
911
+ && event.authorizationMode !== 'audited-remediation') {
890
912
  throw new WorkflowError('STATE_CORRUPT', 'Milestone scope-change actor must be human-only.', {
891
913
  eventId: event.eventId,
892
914
  actor: event.actor,
@@ -903,6 +925,20 @@ function validateMilestoneScopeChangeEvent(event, expectedMilestoneId) {
903
925
  else if (event.autonomyContractEventHash !== undefined && event.autonomyContractEventHash !== null) {
904
926
  throw new WorkflowError('STATE_CORRUPT', 'Human Milestone scope change cannot carry autonomy contract evidence.');
905
927
  }
928
+ if (event.authorizationMode === 'audited-remediation') {
929
+ if (event.actor !== `agent:milestone-remediation:${event.milestoneId}` || !event.remediationEvidence) {
930
+ throw new WorkflowError('STATE_CORRUPT', 'Audited remediation requires an agent actor and bound remediation evidence.', {
931
+ eventId: event.eventId,
932
+ });
933
+ }
934
+ validateAuditedRemediationEvidence(event.remediationEvidence, event.eventId);
935
+ validateAuditedRemediationTopologyEvent(event);
936
+ }
937
+ else if (event.remediationEvidence !== undefined && event.remediationEvidence !== null) {
938
+ throw new WorkflowError('STATE_CORRUPT', 'Non-remediation scope change cannot carry remediation evidence.', {
939
+ eventId: event.eventId,
940
+ });
941
+ }
906
942
  if (!event.confirmationCodeBindingHash.trim()) {
907
943
  throw new WorkflowError('STATE_CORRUPT', 'Milestone scope-change confirmationCodeBindingHash is required.', {
908
944
  eventId: event.eventId,
@@ -922,6 +958,9 @@ function validateMilestoneScopeChangeEvent(event, expectedMilestoneId) {
922
958
  ...(event.autonomyContractEventHash === undefined
923
959
  ? {}
924
960
  : { autonomyContractEventHash: event.autonomyContractEventHash }),
961
+ ...(event.remediationEvidence === undefined
962
+ ? {}
963
+ : { remediationEvidence: event.remediationEvidence }),
925
964
  });
926
965
  if (event.confirmationCodeBindingHash !== expectedBindingHash) {
927
966
  throw new WorkflowError('STATE_CORRUPT', 'Milestone scope-change confirmationCodeBindingHash is inconsistent.', {
@@ -1213,7 +1252,7 @@ function parseMilestoneScopeChangeEvents(content, milestoneId) {
1213
1252
  return events;
1214
1253
  }
1215
1254
  function createMilestoneScopeChangeEvent(options) {
1216
- const { milestone, normalizedPlan, actor, now, previousEventHash, candidatePlanHash, candidateMembershipRevision, confirmationCodeBindingHash, authorizationMode, autonomyContractEventHash, } = options;
1255
+ const { milestone, normalizedPlan, actor, now, previousEventHash, candidatePlanHash, candidateMembershipRevision, confirmationCodeBindingHash, authorizationMode, autonomyContractEventHash, remediationEvidence, } = options;
1217
1256
  return {
1218
1257
  eventId: createEntityId('MS', now.getTime()),
1219
1258
  previousEventHash,
@@ -1230,7 +1269,9 @@ function createMilestoneScopeChangeEvent(options) {
1230
1269
  confirmationCodeBindingHash,
1231
1270
  ...(authorizationMode === 'milestone-autonomy'
1232
1271
  ? { authorizationMode, autonomyContractEventHash }
1233
- : {}),
1272
+ : authorizationMode === 'audited-remediation'
1273
+ ? { authorizationMode, remediationEvidence }
1274
+ : {}),
1234
1275
  };
1235
1276
  }
1236
1277
  function milestoneScopeSnapshotFromState(milestone) {
@@ -1264,6 +1305,7 @@ function parseMilestoneScopeChangeEventRecord(parsed, milestoneId, line) {
1264
1305
  'confirmationCodeBindingHash',
1265
1306
  'authorizationMode',
1266
1307
  'autonomyContractEventHash',
1308
+ 'remediationEvidence',
1267
1309
  'eventHash',
1268
1310
  ]);
1269
1311
  const extraKeys = Object.keys(record).filter((key) => !allowedKeys.has(key));
@@ -1292,6 +1334,9 @@ function parseMilestoneScopeChangeEventRecord(parsed, milestoneId, line) {
1292
1334
  const autonomyContractEventHash = record.autonomyContractEventHash === undefined
1293
1335
  ? undefined
1294
1336
  : requireNullableHash64(record.autonomyContractEventHash, `Milestone ${milestoneId} scope-change autonomyContractEventHash`, { line });
1337
+ const remediationEvidence = record.remediationEvidence === undefined
1338
+ ? undefined
1339
+ : parseAuditedRemediationEvidence(record.remediationEvidence, milestoneId, line);
1295
1340
  const eventHash = requireHash64(record.eventHash, `Milestone ${milestoneId} scope-change eventHash`, { line });
1296
1341
  return {
1297
1342
  eventId,
@@ -1309,11 +1354,12 @@ function parseMilestoneScopeChangeEventRecord(parsed, milestoneId, line) {
1309
1354
  confirmationCodeBindingHash,
1310
1355
  ...(authorizationMode === undefined ? {} : { authorizationMode }),
1311
1356
  ...(autonomyContractEventHash === undefined ? {} : { autonomyContractEventHash }),
1357
+ ...(remediationEvidence === undefined ? {} : { remediationEvidence }),
1312
1358
  eventHash,
1313
1359
  };
1314
1360
  }
1315
1361
  function requireMilestoneScopeChangeAuthorizationMode(value, milestoneId, line) {
1316
- if (value !== 'human' && value !== 'milestone-autonomy') {
1362
+ if (value !== 'human' && value !== 'milestone-autonomy' && value !== 'audited-remediation') {
1317
1363
  throw new WorkflowError('STATE_CORRUPT', `Milestone ${milestoneId} scope-change authorizationMode is invalid.`, {
1318
1364
  line,
1319
1365
  value,
@@ -1321,6 +1367,109 @@ function requireMilestoneScopeChangeAuthorizationMode(value, milestoneId, line)
1321
1367
  }
1322
1368
  return value;
1323
1369
  }
1370
+ function parseAuditedRemediationEvidence(value, milestoneId, line) {
1371
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
1372
+ throw new WorkflowError('STATE_CORRUPT', `Milestone ${milestoneId} remediation evidence must be an object.`, { line });
1373
+ }
1374
+ const record = value;
1375
+ const allowedKeys = new Set([
1376
+ 'blockedTaskId',
1377
+ 'blockedTaskRevision',
1378
+ 'blockedBriefHash',
1379
+ 'blockedPlanHash',
1380
+ 'planRiskAuditEventId',
1381
+ 'planRiskAuditEventHash',
1382
+ 'remediationTaskId',
1383
+ 'predecessorTaskIds',
1384
+ ]);
1385
+ const extraKeys = Object.keys(record).filter((key) => !allowedKeys.has(key));
1386
+ if (extraKeys.length > 0) {
1387
+ throw new WorkflowError('STATE_CORRUPT', `Milestone ${milestoneId} remediation evidence contains unexpected fields.`, {
1388
+ line,
1389
+ extraKeys,
1390
+ });
1391
+ }
1392
+ const evidence = {
1393
+ blockedTaskId: requireNonEmptyString(record.blockedTaskId, 'remediation blockedTaskId', { line }),
1394
+ blockedTaskRevision: requireInteger(record.blockedTaskRevision, 'remediation blockedTaskRevision', { line }),
1395
+ blockedBriefHash: requireHash64(record.blockedBriefHash, 'remediation blockedBriefHash', { line }),
1396
+ blockedPlanHash: requireHash64(record.blockedPlanHash, 'remediation blockedPlanHash', { line }),
1397
+ planRiskAuditEventId: requireNonEmptyString(record.planRiskAuditEventId, 'remediation planRiskAuditEventId', { line }),
1398
+ planRiskAuditEventHash: requireHash64(record.planRiskAuditEventHash, 'remediation planRiskAuditEventHash', { line }),
1399
+ remediationTaskId: requireNonEmptyString(record.remediationTaskId, 'remediation remediationTaskId', { line }),
1400
+ predecessorTaskIds: parseTaskIdArray(record.predecessorTaskIds, 'remediation predecessorTaskIds', { line }),
1401
+ };
1402
+ validateAuditedRemediationEvidence(evidence, evidence.planRiskAuditEventId);
1403
+ return evidence;
1404
+ }
1405
+ function validateAuditedRemediationEvidence(evidence, eventId) {
1406
+ if (!Number.isInteger(evidence.blockedTaskRevision) || evidence.blockedTaskRevision < 1) {
1407
+ throw new WorkflowError('STATE_CORRUPT', 'Audited remediation blocked Task revision is invalid.', {
1408
+ eventId,
1409
+ blockedTaskRevision: evidence.blockedTaskRevision,
1410
+ });
1411
+ }
1412
+ assertHash64(evidence.blockedBriefHash, 'Audited remediation blocked Brief hash');
1413
+ assertHash64(evidence.blockedPlanHash, 'Audited remediation blocked Plan hash');
1414
+ assertHash64(evidence.planRiskAuditEventHash, 'Audited remediation Plan Risk Audit event hash');
1415
+ if (!evidence.blockedTaskId.trim()
1416
+ || !evidence.planRiskAuditEventId.trim()
1417
+ || !evidence.remediationTaskId.trim()
1418
+ || evidence.predecessorTaskIds.length === 0
1419
+ || new Set(evidence.predecessorTaskIds).size !== evidence.predecessorTaskIds.length
1420
+ || evidence.predecessorTaskIds.includes(evidence.blockedTaskId)
1421
+ || evidence.predecessorTaskIds.includes(evidence.remediationTaskId)) {
1422
+ throw new WorkflowError('STATE_CORRUPT', 'Audited remediation evidence has invalid Task bindings.', {
1423
+ eventId,
1424
+ evidence,
1425
+ });
1426
+ }
1427
+ }
1428
+ function validateAuditedRemediationTopologyEvent(event) {
1429
+ const evidence = event.remediationEvidence;
1430
+ const semanticsBefore = {
1431
+ outcome: event.before.outcome,
1432
+ successSignal: event.before.successSignal,
1433
+ acceptance: event.before.acceptance,
1434
+ checks: event.before.checks,
1435
+ };
1436
+ const semanticsAfter = {
1437
+ outcome: event.after.outcome,
1438
+ successSignal: event.after.successSignal,
1439
+ acceptance: event.after.acceptance,
1440
+ checks: event.after.checks,
1441
+ };
1442
+ const expectedTaskIds = [...event.before.taskIds, evidence.remediationTaskId];
1443
+ const beforeBlocked = event.before.memberships.find((membership) => membership.taskId === evidence.blockedTaskId);
1444
+ const afterBlocked = event.after.memberships.find((membership) => membership.taskId === evidence.blockedTaskId);
1445
+ const remediation = event.after.memberships.find((membership) => membership.taskId === evidence.remediationTaskId);
1446
+ const expectedBlockedDependencies = [...new Set([
1447
+ ...(beforeBlocked?.dependsOnTaskIds ?? []),
1448
+ evidence.remediationTaskId,
1449
+ ])].sort();
1450
+ const unchangedMemberships = event.before.memberships.every((beforeMembership) => {
1451
+ if (beforeMembership.taskId === evidence.blockedTaskId)
1452
+ return true;
1453
+ const afterMembership = event.after.memberships.find((candidate) => candidate.taskId === beforeMembership.taskId);
1454
+ return canonicalJsonStringify(afterMembership) === canonicalJsonStringify(beforeMembership);
1455
+ });
1456
+ if (canonicalJsonStringify(semanticsBefore) !== canonicalJsonStringify(semanticsAfter)
1457
+ || canonicalJsonStringify(event.after.taskIds) !== canonicalJsonStringify(expectedTaskIds)
1458
+ || event.after.memberships.length !== event.before.memberships.length + 1
1459
+ || beforeBlocked?.disposition !== 'required'
1460
+ || afterBlocked?.disposition !== 'required'
1461
+ || canonicalJsonStringify(afterBlocked?.dependsOnTaskIds ?? [])
1462
+ !== canonicalJsonStringify(expectedBlockedDependencies)
1463
+ || remediation?.disposition !== 'required'
1464
+ || !remediation.reason.trim()
1465
+ || canonicalJsonStringify(remediation.dependsOnTaskIds ?? [])
1466
+ !== canonicalJsonStringify(evidence.predecessorTaskIds)
1467
+ || canonicalJsonStringify(beforeBlocked?.dependsOnTaskIds ?? [])
1468
+ !== canonicalJsonStringify(evidence.predecessorTaskIds)
1469
+ || !unchangedMemberships) {
1470
+ throw new WorkflowError('STATE_CORRUPT', 'Audited remediation scope-change event exceeds its additive topology authority.', { eventId: event.eventId, remediationEvidence: evidence });
1471
+ }
1472
+ }
1324
1473
  function parseMilestoneScopeSnapshot(value, milestoneId, line, label) {
1325
1474
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
1326
1475
  throw new WorkflowError('STATE_CORRUPT', `Milestone ${milestoneId} scope-change ${label} snapshot must be an object.`, { line });
@@ -1491,7 +1640,12 @@ function computeMilestoneScopeChangeBindingHash(input) {
1491
1640
  authorizationMode: input.authorizationMode,
1492
1641
  autonomyContractEventHash: input.autonomyContractEventHash,
1493
1642
  }
1494
- : {}),
1643
+ : input.authorizationMode === 'audited-remediation'
1644
+ ? {
1645
+ authorizationMode: input.authorizationMode,
1646
+ remediationEvidence: input.remediationEvidence,
1647
+ }
1648
+ : {}),
1495
1649
  }));
1496
1650
  }
1497
1651
  function assertSnapshotTaskIdsMatchMemberships(snapshot, milestoneId, details = {}) {