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.
- package/README.md +65 -5
- package/dist/src/alpha6/downstream-proof.d.ts +68 -0
- package/dist/src/alpha6/downstream-proof.js +604 -0
- package/dist/src/alpha6/downstream-proof.js.map +1 -0
- package/dist/src/alpha6/milestone.d.ts +6 -2
- package/dist/src/alpha6/milestone.js +164 -10
- package/dist/src/alpha6/milestone.js.map +1 -1
- package/dist/src/alpha6/plan-risk.js +2 -2
- package/dist/src/alpha6/plan-risk.js.map +1 -1
- package/dist/src/alpha6/remediation.d.ts +8 -3
- package/dist/src/alpha6/remediation.js +374 -9
- package/dist/src/alpha6/remediation.js.map +1 -1
- package/dist/src/alpha6/review.d.ts +6 -0
- package/dist/src/alpha6/review.js +12 -0
- package/dist/src/alpha6/review.js.map +1 -1
- package/dist/src/alpha7/corrective-recovery.d.ts +8 -1
- package/dist/src/alpha7/corrective-recovery.js +406 -26
- package/dist/src/alpha7/corrective-recovery.js.map +1 -1
- package/dist/src/cli.js +135 -5
- package/dist/src/cli.js.map +1 -1
- package/dist/src/contracts.d.ts +154 -3
- package/dist/src/dependency-provenance.d.ts +9 -2
- package/dist/src/dependency-provenance.js +70 -8
- package/dist/src/dependency-provenance.js.map +1 -1
- package/dist/src/git.d.ts +1 -1
- package/dist/src/git.js +6 -2
- package/dist/src/git.js.map +1 -1
- package/dist/src/historical-step-provenance.d.ts +20 -0
- package/dist/src/historical-step-provenance.js +111 -0
- package/dist/src/historical-step-provenance.js.map +1 -0
- package/dist/src/state/corrective-replan-executor.js +5 -0
- 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 +40 -2
- package/dist/src/workflow.js +1440 -65
- package/dist/src/workflow.js.map +1 -1
- package/docs/autonomy-guardrails.md +91 -1
- package/docs/decisions.md +14 -0
- package/docs/delegated-approval.md +6 -1
- package/docs/development-flow.md +60 -5
- 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 +97 -7
- package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +68 -9
- package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +150 -33
- package/docs/release.md +51 -8
- package/docs/updating-existing-project.md +155 -2
- package/docs/validation-report.md +101 -94
- package/package.json +7 -2
- package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +1 -1
- package/plugins/codex-workflow-gateway/references/protocol.md +69 -1
- package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +156 -1
- package/schemas/authorization-event.schema.json +8 -1
- package/schemas/downstream-proof-invalidation-event.schema.json +71 -0
- package/schemas/downstream-proof-replan-recovery-event.schema.json +61 -0
- package/schemas/milestone-scope-change-event.schema.json +25 -1
- package/schemas/stop-escalate-override-event.schema.json +45 -0
- package/schemas/task.schema.json +104 -0
- 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:
|
|
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:')
|
|
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
|
-
|
|
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:
|
|
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:')
|
|
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 = {}) {
|