codex-workflow-v2 2.0.0-beta.12.9 → 2.0.0-beta.13
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 +58 -16
- package/dist/reviewer-runtime-build.json +1092 -0
- package/dist/src/alpha6/check-support-anchor.d.ts +24 -0
- package/dist/src/alpha6/check-support-anchor.js +102 -0
- package/dist/src/alpha6/check-support-anchor.js.map +1 -0
- package/dist/src/alpha6/component-owner.d.ts +28 -0
- package/dist/src/alpha6/component-owner.js +95 -0
- package/dist/src/alpha6/component-owner.js.map +1 -0
- package/dist/src/alpha6/downstream-proof.js +4 -2
- package/dist/src/alpha6/downstream-proof.js.map +1 -1
- package/dist/src/alpha6/mechanical-feasibility.js +42 -12
- package/dist/src/alpha6/mechanical-feasibility.js.map +1 -1
- 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/npm-check-contract.d.ts +11 -0
- package/dist/src/alpha6/npm-check-contract.js +36 -0
- package/dist/src/alpha6/npm-check-contract.js.map +1 -0
- package/dist/src/alpha6/plan-integrity.js +157 -27
- package/dist/src/alpha6/plan-integrity.js.map +1 -1
- package/dist/src/alpha6/plan-risk.js +3 -1
- package/dist/src/alpha6/plan-risk.js.map +1 -1
- package/dist/src/alpha6/remediation-cause.d.ts +13 -0
- package/dist/src/alpha6/remediation-cause.js +77 -0
- package/dist/src/alpha6/remediation-cause.js.map +1 -0
- package/dist/src/alpha6/remediation.d.ts +15 -4
- package/dist/src/alpha6/remediation.js +430 -67
- package/dist/src/alpha6/remediation.js.map +1 -1
- package/dist/src/alpha6/review.js +34 -4
- package/dist/src/alpha6/review.js.map +1 -1
- package/dist/src/artifacts.js +6 -0
- package/dist/src/artifacts.js.map +1 -1
- package/dist/src/cli-actions.d.ts +25 -0
- package/dist/src/cli-actions.js +109 -0
- package/dist/src/cli-actions.js.map +1 -0
- package/dist/src/cli.js +216 -258
- package/dist/src/cli.js.map +1 -1
- package/dist/src/contracts.d.ts +166 -5
- package/dist/src/credential-transport.d.ts +38 -0
- package/dist/src/credential-transport.js +156 -0
- package/dist/src/credential-transport.js.map +1 -0
- package/dist/src/dependency-provenance.d.ts +23 -2
- package/dist/src/dependency-provenance.js +221 -34
- package/dist/src/dependency-provenance.js.map +1 -1
- package/dist/src/domain/completed-step-carryover.d.ts +13 -0
- package/dist/src/domain/completed-step-carryover.js +71 -0
- package/dist/src/domain/completed-step-carryover.js.map +1 -0
- package/dist/src/domain/validation.d.ts +9 -1
- package/dist/src/domain/validation.js +86 -0
- package/dist/src/domain/validation.js.map +1 -1
- package/dist/src/gateway-handshake.d.ts +11 -0
- package/dist/src/gateway-handshake.js +92 -0
- package/dist/src/gateway-handshake.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/lifecycle/corrective-replan-binding-manifest.d.ts +5 -0
- package/dist/src/lifecycle/corrective-replan-binding-manifest.js +9 -0
- package/dist/src/lifecycle/corrective-replan-binding-manifest.js.map +1 -1
- package/dist/src/lifecycle/corrective-replan.js +1 -1
- package/dist/src/lifecycle/corrective-replan.js.map +1 -1
- package/dist/src/lifecycle/semantic-registry.js +1 -1
- package/dist/src/observation.js +1 -2
- package/dist/src/observation.js.map +1 -1
- package/dist/src/observed-routes.d.ts +114 -0
- package/dist/src/observed-routes.js +545 -0
- package/dist/src/observed-routes.js.map +1 -0
- package/dist/src/operational-contract.d.ts +26 -0
- package/dist/src/operational-contract.js +94 -0
- package/dist/src/operational-contract.js.map +1 -0
- package/dist/src/reviewer.d.ts +218 -8
- package/dist/src/reviewer.js +927 -25
- package/dist/src/reviewer.js.map +1 -1
- package/dist/src/state/corrective-replan-executor.js +18 -15
- package/dist/src/state/corrective-replan-executor.js.map +1 -1
- package/dist/src/state/corrective-replan-public.js +35 -10
- package/dist/src/state/corrective-replan-public.js.map +1 -1
- package/dist/src/state/corrective-replan-transaction.js +12 -4
- package/dist/src/state/corrective-replan-transaction.js.map +1 -1
- package/dist/src/state/corrective-yield-transaction.js +11 -2
- package/dist/src/state/corrective-yield-transaction.js.map +1 -1
- package/dist/src/state/lock.d.ts +1 -0
- package/dist/src/state/lock.js +24 -0
- package/dist/src/state/lock.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 +65 -4
- package/dist/src/workflow.js +1704 -155
- package/dist/src/workflow.js.map +1 -1
- package/docs/autonomy-guardrails.md +41 -14
- package/docs/delegated-approval.md +6 -1
- package/docs/development-flow.md +42 -12
- 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 +181 -22
- package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +148 -26
- package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +326 -52
- package/docs/problem-briefs/01-pre-implementation-integrity.md +11 -7
- package/docs/problem-briefs/04-task-dependency-and-structural-replacement-integrity.md +13 -6
- package/docs/problem-briefs/README.md +11 -11
- package/docs/release.md +83 -9
- package/docs/updating-existing-project.md +205 -0
- package/docs/validation-report.md +156 -99
- package/package.json +12 -3
- package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +42 -1
- package/schemas/authorization-event.schema.json +10 -3
- package/schemas/corrective-decision-event.schema.json +164 -9
- package/schemas/corrective-replan-credentials.private.schema.json +4 -2
- package/schemas/milestone-scope-change-event.schema.json +25 -1
- package/schemas/remediation-event.schema.json +17 -1
- package/schemas/review-result.schema.json +1 -0
- package/schemas/step-review-event.schema.json +15 -0
- package/schemas/task.schema.json +61 -1
- package/schemas/transition-definition.schema.json +8 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { WorkflowService } from './workflow.js';
|
|
2
|
+
import { type CliCommand } from './cli-actions.js';
|
|
3
|
+
export declare const OBSERVED_ROUTE_MATRIX_VERSION: 2;
|
|
4
|
+
export type ObservedRouteId = 'observed.observation-blocked' | 'observed.observation-repair' | 'observed.adoption' | 'observed.dependency-recovery' | 'observed.corrective-continuation' | 'observed.worker-authority' | 'observed.step-execution' | 'observed.strict-review' | 'observed.task-lifecycle' | 'observed.project-knowledge' | 'observed.discovery-remediation' | 'observed.milestone-lifecycle' | 'observed.manual-boundary';
|
|
5
|
+
type ExecutorRef = Extract<keyof WorkflowService, string>;
|
|
6
|
+
type InventorySupport = 'executable' | 'diagnostic' | 'unsupported';
|
|
7
|
+
type Support = 'executable-witnessed' | 'unwitnessed' | 'unsupported';
|
|
8
|
+
export interface ObservedActionBinding {
|
|
9
|
+
action: string;
|
|
10
|
+
support: InventorySupport;
|
|
11
|
+
cliBinding: CliCommand | null;
|
|
12
|
+
executorRefs: readonly ExecutorRef[];
|
|
13
|
+
}
|
|
14
|
+
export interface ObservedRouteDefinition {
|
|
15
|
+
routeId: ObservedRouteId;
|
|
16
|
+
priority: number;
|
|
17
|
+
predicateRef: string;
|
|
18
|
+
actions: readonly ObservedActionBinding[];
|
|
19
|
+
successorWitness: {
|
|
20
|
+
kind: 'reachable';
|
|
21
|
+
routeIds: readonly ObservedRouteId[];
|
|
22
|
+
evidence: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: 'terminal';
|
|
25
|
+
evidence: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface ObservedTransitionWitnessContract {
|
|
29
|
+
action: CliCommand;
|
|
30
|
+
executorRef: ExecutorRef;
|
|
31
|
+
successorActions: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
export type ObservedRouteClassWitnessContract = {
|
|
34
|
+
routeId: ObservedRouteId;
|
|
35
|
+
action: CliCommand;
|
|
36
|
+
kind: 'transition';
|
|
37
|
+
executorRef: ExecutorRef;
|
|
38
|
+
successorActions: readonly string[];
|
|
39
|
+
} | {
|
|
40
|
+
routeId: ObservedRouteId;
|
|
41
|
+
action: string;
|
|
42
|
+
kind: 'terminal';
|
|
43
|
+
executorRef: null;
|
|
44
|
+
successorActions: readonly [];
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* These are the only executable route claims in beta.13. The release E2E must
|
|
48
|
+
* execute every row as fresh next -> named WorkflowService method -> fresh next.
|
|
49
|
+
*/
|
|
50
|
+
export declare const OBSERVED_ROUTE_WITNESS_CONTRACTS: readonly ObservedTransitionWitnessContract[];
|
|
51
|
+
/**
|
|
52
|
+
* B13-A14 is a class-level claim, not a claim that every known action has an
|
|
53
|
+
* executable E2E. Each Signal-observed class has exactly one production
|
|
54
|
+
* transition witness or one real terminal observation. Other known actions
|
|
55
|
+
* remain explicit in OBSERVED_UNWITNESSED_ACTIONS/OBSERVED_UNSUPPORTED_ACTIONS.
|
|
56
|
+
*/
|
|
57
|
+
export declare const OBSERVED_ROUTE_CLASS_WITNESS_CONTRACTS: readonly ObservedRouteClassWitnessContract[];
|
|
58
|
+
/** Claimed route classes only; action-level coverage remains explicitly bounded below. */
|
|
59
|
+
export declare const OBSERVED_ROUTE_MATRIX: readonly ObservedRouteDefinition[];
|
|
60
|
+
export declare const OBSERVED_UNWITNESSED_ACTIONS: {
|
|
61
|
+
action: string;
|
|
62
|
+
routeId: ObservedRouteId;
|
|
63
|
+
reason: string;
|
|
64
|
+
}[];
|
|
65
|
+
export declare const OBSERVED_UNSUPPORTED_ACTIONS: {
|
|
66
|
+
action: string;
|
|
67
|
+
routeId: ObservedRouteId;
|
|
68
|
+
reason: string;
|
|
69
|
+
}[];
|
|
70
|
+
export declare const OBSERVED_ROUTE_MATRIX_FINGERPRINT: string;
|
|
71
|
+
export interface ObservedRouteMetadata {
|
|
72
|
+
matrixVersion: typeof OBSERVED_ROUTE_MATRIX_VERSION;
|
|
73
|
+
matrixFingerprint: string;
|
|
74
|
+
routeId: ObservedRouteId;
|
|
75
|
+
priority: number;
|
|
76
|
+
predicateRef: string;
|
|
77
|
+
routeFingerprint: string;
|
|
78
|
+
actionSupport: Support;
|
|
79
|
+
cliBinding: CliCommand | null;
|
|
80
|
+
executorRefs: readonly ExecutorRef[];
|
|
81
|
+
successorWitness: ObservedRouteDefinition['successorWitness'] | {
|
|
82
|
+
kind: 'unwitnessed';
|
|
83
|
+
evidence: string;
|
|
84
|
+
};
|
|
85
|
+
coverageBoundary: 'signal-observed-route-class-witnesses';
|
|
86
|
+
}
|
|
87
|
+
export declare function validateObservedRouteMatrix(): void;
|
|
88
|
+
export declare function observedRouteMetadataForAction(action: string): ObservedRouteMetadata;
|
|
89
|
+
export declare function verifyObservedRouteTransitionWitness(input: {
|
|
90
|
+
before: Record<string, unknown>;
|
|
91
|
+
executorRef: ExecutorRef;
|
|
92
|
+
after: Record<string, unknown>;
|
|
93
|
+
}): {
|
|
94
|
+
action: string;
|
|
95
|
+
successorAction: string;
|
|
96
|
+
witnessFingerprint: string;
|
|
97
|
+
};
|
|
98
|
+
export declare function verifyObservedRouteClassTransitionWitness(input: {
|
|
99
|
+
before: Record<string, unknown>;
|
|
100
|
+
executorRef: ExecutorRef;
|
|
101
|
+
after: Record<string, unknown>;
|
|
102
|
+
}): {
|
|
103
|
+
routeId: ObservedRouteId;
|
|
104
|
+
action: string;
|
|
105
|
+
successorAction: string;
|
|
106
|
+
witnessFingerprint: string;
|
|
107
|
+
};
|
|
108
|
+
export declare function verifyObservedRouteTerminalWitness(next: Record<string, unknown>): {
|
|
109
|
+
routeId: ObservedRouteId;
|
|
110
|
+
action: string;
|
|
111
|
+
witnessFingerprint: string;
|
|
112
|
+
};
|
|
113
|
+
export declare function withObservedRouteMetadata(next: Record<string, unknown>): Record<string, unknown>;
|
|
114
|
+
export {};
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { WorkflowError } from './errors.js';
|
|
2
|
+
import { isCliCommand } from './cli-actions.js';
|
|
3
|
+
import { hashCanonical } from './lifecycle/fingerprint.js';
|
|
4
|
+
export const OBSERVED_ROUTE_MATRIX_VERSION = 2;
|
|
5
|
+
const executable = (action, ...executorRefs) => ({ action, support: 'executable', cliBinding: action, executorRefs });
|
|
6
|
+
const diagnostic = (action) => ({ action, support: 'diagnostic', cliBinding: action, executorRefs: [] });
|
|
7
|
+
const unsupported = (action) => ({ action, support: 'unsupported', cliBinding: null, executorRefs: [] });
|
|
8
|
+
/** Signal-observed inventory only; entries are not release claims without an executable witness. */
|
|
9
|
+
const OBSERVED_ROUTE_INVENTORY = [
|
|
10
|
+
{
|
|
11
|
+
routeId: 'observed.observation-blocked', priority: 1300,
|
|
12
|
+
predicateRef: 'WorkflowService.next/status.observation.kind=blocked',
|
|
13
|
+
actions: [unsupported('blocked')],
|
|
14
|
+
successorWitness: { kind: 'terminal', evidence: 'No safe Core transition is advertised.' },
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
routeId: 'observed.observation-repair', priority: 1200,
|
|
18
|
+
predicateRef: 'WorkflowService.next/status.observation.kind=repair',
|
|
19
|
+
actions: [
|
|
20
|
+
executable('milestone recover', 'recoverMilestoneTransactions'),
|
|
21
|
+
executable('locks repair', 'repairStaleWriterLease'),
|
|
22
|
+
executable('task corrective-replan-recover', 'recoverCorrectiveReplan'),
|
|
23
|
+
executable('task corrective-yield-recover', 'recoverCorrectiveYield'),
|
|
24
|
+
],
|
|
25
|
+
successorWitness: {
|
|
26
|
+
kind: 'reachable', routeIds: ['observed.dependency-recovery', 'observed.task-lifecycle'],
|
|
27
|
+
evidence: 'Successful scoped repair is followed by fresh next.',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
routeId: 'observed.adoption', priority: 1100,
|
|
32
|
+
predicateRef: 'WorkflowService.next/status.adoption.status!=applied',
|
|
33
|
+
actions: [
|
|
34
|
+
executable('state adoption-apply', 'applyAdoption'),
|
|
35
|
+
unsupported('restore safe boundary, then state adoption-prepare'),
|
|
36
|
+
],
|
|
37
|
+
successorWitness: {
|
|
38
|
+
kind: 'reachable', routeIds: ['observed.project-knowledge'],
|
|
39
|
+
evidence: 'Applied adoption exposes Project Knowledge posture.',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
routeId: 'observed.dependency-recovery', priority: 1000,
|
|
44
|
+
predicateRef: 'WorkflowService.next/dependency recovery preflights',
|
|
45
|
+
actions: [
|
|
46
|
+
executable('task historical-step-provenance-recover', 'recoverHistoricalStepProvenance'),
|
|
47
|
+
executable('update historical-step-dependency-recover', 'recoverHistoricalStepDependencyProvenance'),
|
|
48
|
+
executable('update plan-integrity-dependency-recover', 'recoverPlanIntegrityDependencyProvenance'),
|
|
49
|
+
executable('update dependency-provenance-recover', 'recoverDependencyProvenance'),
|
|
50
|
+
executable('update downstream-proof-dependency-recover', 'recoverActiveDownstreamProofDependencyProvenance'),
|
|
51
|
+
executable('update downstream-proof-replan-dependency-recover', 'recoverDownstreamProofReplanDependencyProvenance'),
|
|
52
|
+
],
|
|
53
|
+
successorWitness: {
|
|
54
|
+
kind: 'reachable', routeIds: ['observed.corrective-continuation'],
|
|
55
|
+
evidence: 'Compatibility recovery preserves the Task and requires fresh next.',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
routeId: 'observed.corrective-continuation', priority: 900,
|
|
60
|
+
predicateRef: 'WorkflowService.nextForTaskWithDelegation/corrective overlays',
|
|
61
|
+
actions: [
|
|
62
|
+
executable('task downstream-proof-replan-recover', 'recoverDownstreamProofReplan'),
|
|
63
|
+
executable('task plan-integrity-recover', 'recoverPlanIntegrity'),
|
|
64
|
+
executable('task remediation-mode-recover', 'recoverRemediationMode'),
|
|
65
|
+
executable('task corrective-decision-recover', 'recoverStepCorrectiveDecision'),
|
|
66
|
+
executable('task downstream-proof-recover', 'recoverDownstreamProof'),
|
|
67
|
+
executable('task corrective-decision', 'recordStepCorrectiveDecision'),
|
|
68
|
+
executable('task corrective-yield', 'executeCorrectiveYield'),
|
|
69
|
+
executable('task corrective-replan-prepare', 'prepareCorrectiveReplan'),
|
|
70
|
+
executable('task stop-override-prepare', 'prepareTaskStopOverride'),
|
|
71
|
+
],
|
|
72
|
+
successorWitness: {
|
|
73
|
+
kind: 'reachable', routeIds: ['observed.task-lifecycle'],
|
|
74
|
+
evidence: 'Signal recovery replays reached replacement planning or Task continuation.',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
routeId: 'observed.worker-authority', priority: 800,
|
|
79
|
+
predicateRef: 'WorkflowService.nextForTaskWithDelegation/C1 and credential overlays',
|
|
80
|
+
actions: [
|
|
81
|
+
executable('task handoff-prepare', 'handoffTask'),
|
|
82
|
+
executable('task handoff-replace', 'handoffTask'),
|
|
83
|
+
executable('task claim', 'claimTask'),
|
|
84
|
+
executable('task writer-credential-replace', 'replaceClaimedWriterCredential'),
|
|
85
|
+
],
|
|
86
|
+
successorWitness: {
|
|
87
|
+
kind: 'reachable', routeIds: ['observed.step-execution'],
|
|
88
|
+
evidence: 'Usable claimed writer authority makes the Step executable.',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
routeId: 'observed.step-execution', priority: 700,
|
|
93
|
+
predicateRef: 'nextForTask/active or runnable Step',
|
|
94
|
+
actions: [executable('task run', 'runStep'), executable('task step-complete', 'completeStep')],
|
|
95
|
+
successorWitness: {
|
|
96
|
+
kind: 'reachable', routeIds: ['observed.strict-review'],
|
|
97
|
+
evidence: 'Completion reaches strict review when required, otherwise Task lifecycle.',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
routeId: 'observed.strict-review', priority: 600,
|
|
102
|
+
predicateRef: 'WorkflowService.nextForTaskWithDelegation/strict review overlays',
|
|
103
|
+
actions: [
|
|
104
|
+
executable('task step-review', 'reviewStep'),
|
|
105
|
+
executable('task review-launch', 'recordReview'),
|
|
106
|
+
],
|
|
107
|
+
successorWitness: {
|
|
108
|
+
kind: 'reachable', routeIds: ['observed.task-lifecycle'],
|
|
109
|
+
evidence: 'Approval advances; findings return the same Task through corrective routing.',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
routeId: 'observed.task-lifecycle', priority: 500,
|
|
114
|
+
predicateRef: 'nextForTask/task.status',
|
|
115
|
+
actions: [
|
|
116
|
+
executable('task plan-set', 'setTaskPlan'),
|
|
117
|
+
executable('task plan-risk-audit', 'recordPlanRiskAudit'),
|
|
118
|
+
executable('task authorize', 'authorizeTask'),
|
|
119
|
+
executable('task start', 'startTask'),
|
|
120
|
+
executable('task submit', 'submitTask'),
|
|
121
|
+
executable('task result-set', 'setTaskResult'),
|
|
122
|
+
executable('task accept', 'acceptTask'),
|
|
123
|
+
executable('task sync-base', 'syncTaskBase'),
|
|
124
|
+
executable('task merge', 'mergeTask'),
|
|
125
|
+
executable('task merge-confirm', 'confirmExternalMerge'),
|
|
126
|
+
],
|
|
127
|
+
successorWitness: {
|
|
128
|
+
kind: 'reachable', routeIds: ['observed.milestone-lifecycle'],
|
|
129
|
+
evidence: 'A merged Task is consumed by Milestone dependency/progress navigation.',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
routeId: 'observed.project-knowledge', priority: 400,
|
|
134
|
+
predicateRef: 'WorkflowService.next/Project Knowledge posture',
|
|
135
|
+
actions: [
|
|
136
|
+
executable('project-memory scan', 'scanKnowledgeMap'),
|
|
137
|
+
executable('project-memory reconcile', 'reconcileKnowledgeMap'),
|
|
138
|
+
executable('project-memory approve', 'approveKnowledgeMap'),
|
|
139
|
+
executable('task context-refresh', 'refreshTaskContext'),
|
|
140
|
+
executable('task knowledge-rebind', 'rebindTaskKnowledge'),
|
|
141
|
+
],
|
|
142
|
+
successorWitness: {
|
|
143
|
+
kind: 'reachable', routeIds: ['observed.discovery-remediation'],
|
|
144
|
+
evidence: 'Current approved knowledge permits Discovery or bound-Task navigation.',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
routeId: 'observed.discovery-remediation', priority: 300,
|
|
149
|
+
predicateRef: 'WorkflowService.next/active Discovery or audited remediation',
|
|
150
|
+
actions: [
|
|
151
|
+
executable('discovery start', 'startDiscovery'),
|
|
152
|
+
executable('discovery update', 'updateDiscovery'),
|
|
153
|
+
executable('discovery materialize', 'materializeTask', 'materializeMilestone'),
|
|
154
|
+
executable('milestone remediation-materialize', 'materializeAuditedRemediationTask'),
|
|
155
|
+
],
|
|
156
|
+
successorWitness: {
|
|
157
|
+
kind: 'reachable', routeIds: ['observed.task-lifecycle'],
|
|
158
|
+
evidence: 'Materialization creates an entity selected by fresh next.',
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
routeId: 'observed.milestone-lifecycle', priority: 200,
|
|
163
|
+
predicateRef: 'WorkflowService.next/active Milestone status',
|
|
164
|
+
actions: [
|
|
165
|
+
executable('milestone plan-set', 'setMilestonePlan'),
|
|
166
|
+
executable('milestone authorize', 'authorizeMilestone'),
|
|
167
|
+
executable('milestone validate', 'validateMilestone'),
|
|
168
|
+
],
|
|
169
|
+
successorWitness: {
|
|
170
|
+
kind: 'reachable', routeIds: ['observed.manual-boundary'],
|
|
171
|
+
evidence: 'Validation reaches the explicit final acceptance boundary.',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
routeId: 'observed.manual-boundary', priority: 100,
|
|
176
|
+
predicateRef: 'WorkflowService.next/non-executable instruction',
|
|
177
|
+
actions: [
|
|
178
|
+
diagnostic('doctor'),
|
|
179
|
+
unsupported('milestone initial-assembly'),
|
|
180
|
+
unsupported('restore clean base, then milestone validate'),
|
|
181
|
+
unsupported('milestone accept using delegated approval or await explicit human final acceptance'),
|
|
182
|
+
unsupported('await explicit human final acceptance'),
|
|
183
|
+
unsupported('complete required Tasks, then milestone validate'),
|
|
184
|
+
unsupported('milestone plan-set or milestone cancel'),
|
|
185
|
+
unsupported('none'),
|
|
186
|
+
],
|
|
187
|
+
successorWitness: { kind: 'terminal', evidence: 'Automation stops or reads structured options.' },
|
|
188
|
+
},
|
|
189
|
+
];
|
|
190
|
+
/**
|
|
191
|
+
* These are the only executable route claims in beta.13. The release E2E must
|
|
192
|
+
* execute every row as fresh next -> named WorkflowService method -> fresh next.
|
|
193
|
+
*/
|
|
194
|
+
export const OBSERVED_ROUTE_WITNESS_CONTRACTS = [
|
|
195
|
+
{ action: 'milestone recover', executorRef: 'recoverMilestoneTransactions', successorActions: ['milestone authorize'] },
|
|
196
|
+
{ action: 'state adoption-apply', executorRef: 'applyAdoption', successorActions: ['discovery start'] },
|
|
197
|
+
{ action: 'task historical-step-provenance-recover', executorRef: 'recoverHistoricalStepProvenance', successorActions: ['task step-complete'] },
|
|
198
|
+
{ action: 'task plan-integrity-recover', executorRef: 'recoverPlanIntegrity', successorActions: ['task run'] },
|
|
199
|
+
{ action: 'project-memory reconcile', executorRef: 'reconcileKnowledgeMap', successorActions: ['project-memory approve'] },
|
|
200
|
+
{ action: 'milestone authorize', executorRef: 'authorizeMilestone', successorActions: ['task plan-set'] },
|
|
201
|
+
{ action: 'discovery start', executorRef: 'startDiscovery', successorActions: ['discovery update'] },
|
|
202
|
+
{ action: 'discovery update', executorRef: 'updateDiscovery', successorActions: ['discovery materialize'] },
|
|
203
|
+
{ action: 'discovery materialize', executorRef: 'materializeTask', successorActions: ['task plan-set'] },
|
|
204
|
+
{ action: 'task plan-set', executorRef: 'setTaskPlan', successorActions: ['task plan-risk-audit'] },
|
|
205
|
+
{ action: 'task plan-risk-audit', executorRef: 'recordPlanRiskAudit', successorActions: ['task authorize'] },
|
|
206
|
+
{ action: 'task authorize', executorRef: 'authorizeTask', successorActions: ['task start'] },
|
|
207
|
+
{ action: 'task start', executorRef: 'startTask', successorActions: ['task run'] },
|
|
208
|
+
{ action: 'task run', executorRef: 'runStep', successorActions: ['task step-complete'] },
|
|
209
|
+
{ action: 'task step-complete', executorRef: 'completeStep', successorActions: ['task submit'] },
|
|
210
|
+
{ action: 'task submit', executorRef: 'submitTask', successorActions: ['task review-launch'] },
|
|
211
|
+
{ action: 'task review-launch', executorRef: 'recordReview', successorActions: ['task result-set'] },
|
|
212
|
+
{ action: 'task result-set', executorRef: 'setTaskResult', successorActions: ['task accept'] },
|
|
213
|
+
{ action: 'task accept', executorRef: 'acceptTask', successorActions: ['task writer-credential-replace'] },
|
|
214
|
+
{ action: 'task writer-credential-replace', executorRef: 'replaceClaimedWriterCredential', successorActions: ['task merge'] },
|
|
215
|
+
{ action: 'task merge', executorRef: 'mergeTask', successorActions: ['none', 'discovery start'] },
|
|
216
|
+
];
|
|
217
|
+
/**
|
|
218
|
+
* B13-A14 is a class-level claim, not a claim that every known action has an
|
|
219
|
+
* executable E2E. Each Signal-observed class has exactly one production
|
|
220
|
+
* transition witness or one real terminal observation. Other known actions
|
|
221
|
+
* remain explicit in OBSERVED_UNWITNESSED_ACTIONS/OBSERVED_UNSUPPORTED_ACTIONS.
|
|
222
|
+
*/
|
|
223
|
+
export const OBSERVED_ROUTE_CLASS_WITNESS_CONTRACTS = [
|
|
224
|
+
{
|
|
225
|
+
routeId: 'observed.observation-blocked', action: 'blocked', kind: 'terminal',
|
|
226
|
+
executorRef: null, successorActions: [],
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
routeId: 'observed.observation-repair', action: 'milestone recover', kind: 'transition',
|
|
230
|
+
executorRef: 'recoverMilestoneTransactions', successorActions: ['milestone authorize'],
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
routeId: 'observed.adoption', action: 'state adoption-apply', kind: 'transition',
|
|
234
|
+
executorRef: 'applyAdoption', successorActions: ['discovery start'],
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
routeId: 'observed.dependency-recovery', action: 'task historical-step-provenance-recover', kind: 'transition',
|
|
238
|
+
executorRef: 'recoverHistoricalStepProvenance', successorActions: ['task step-complete'],
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
routeId: 'observed.corrective-continuation', action: 'task plan-integrity-recover', kind: 'transition',
|
|
242
|
+
executorRef: 'recoverPlanIntegrity', successorActions: ['task run'],
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
routeId: 'observed.worker-authority', action: 'task writer-credential-replace', kind: 'transition',
|
|
246
|
+
executorRef: 'replaceClaimedWriterCredential', successorActions: ['task merge'],
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
routeId: 'observed.step-execution', action: 'task run', kind: 'transition',
|
|
250
|
+
executorRef: 'runStep', successorActions: ['task step-complete'],
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
routeId: 'observed.strict-review', action: 'task review-launch', kind: 'transition',
|
|
254
|
+
executorRef: 'recordReview', successorActions: ['task result-set'],
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
routeId: 'observed.task-lifecycle', action: 'task plan-set', kind: 'transition',
|
|
258
|
+
executorRef: 'setTaskPlan', successorActions: ['task plan-risk-audit'],
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
routeId: 'observed.project-knowledge', action: 'project-memory reconcile', kind: 'transition',
|
|
262
|
+
executorRef: 'reconcileKnowledgeMap', successorActions: ['project-memory approve'],
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
routeId: 'observed.discovery-remediation', action: 'discovery start', kind: 'transition',
|
|
266
|
+
executorRef: 'startDiscovery', successorActions: ['discovery update'],
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
routeId: 'observed.milestone-lifecycle', action: 'milestone authorize', kind: 'transition',
|
|
270
|
+
executorRef: 'authorizeMilestone', successorActions: ['task plan-set'],
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
routeId: 'observed.manual-boundary', action: 'milestone initial-assembly', kind: 'terminal',
|
|
274
|
+
executorRef: null, successorActions: [],
|
|
275
|
+
},
|
|
276
|
+
];
|
|
277
|
+
const WITNESS_CONTRACT_BY_ACTION = new Map(OBSERVED_ROUTE_WITNESS_CONTRACTS.map((contract) => [contract.action, contract]));
|
|
278
|
+
const CLASS_WITNESS_BY_ROUTE = new Map(OBSERVED_ROUTE_CLASS_WITNESS_CONTRACTS.map((contract) => [contract.routeId, contract]));
|
|
279
|
+
function inventoryRouteIdForAction(action) {
|
|
280
|
+
return OBSERVED_ROUTE_INVENTORY.find((route) => route.actions.some((binding) => binding.action === action))?.routeId;
|
|
281
|
+
}
|
|
282
|
+
/** Claimed route classes only; action-level coverage remains explicitly bounded below. */
|
|
283
|
+
export const OBSERVED_ROUTE_MATRIX = OBSERVED_ROUTE_INVENTORY
|
|
284
|
+
.map((route) => {
|
|
285
|
+
const classWitness = CLASS_WITNESS_BY_ROUTE.get(route.routeId);
|
|
286
|
+
return {
|
|
287
|
+
...route,
|
|
288
|
+
predicateRef: `WorkflowService.next.action===${JSON.stringify(classWitness?.action)}`,
|
|
289
|
+
actions: route.actions.filter((binding) => binding.action === classWitness?.action),
|
|
290
|
+
successorWitness: classWitness?.kind === 'terminal'
|
|
291
|
+
? {
|
|
292
|
+
kind: 'terminal',
|
|
293
|
+
evidence: 'Fresh production next returned the exact non-executable terminal observation.',
|
|
294
|
+
}
|
|
295
|
+
: {
|
|
296
|
+
kind: 'reachable',
|
|
297
|
+
routeIds: [...new Set(classWitness?.successorActions
|
|
298
|
+
.map((action) => inventoryRouteIdForAction(action))
|
|
299
|
+
.filter((routeId) => Boolean(routeId)) ?? [])],
|
|
300
|
+
evidence: classWitness
|
|
301
|
+
? `Fresh production next -> WorkflowService.${classWitness.executorRef} -> fresh production next.`
|
|
302
|
+
: 'Missing class witness.',
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
})
|
|
306
|
+
.filter((route) => route.actions.length > 0);
|
|
307
|
+
export const OBSERVED_UNWITNESSED_ACTIONS = OBSERVED_ROUTE_INVENTORY.flatMap((route) => route.actions
|
|
308
|
+
.filter((binding) => binding.support !== 'unsupported' && !WITNESS_CONTRACT_BY_ACTION.has(binding.action))
|
|
309
|
+
.map((binding) => ({
|
|
310
|
+
action: binding.action,
|
|
311
|
+
routeId: route.routeId,
|
|
312
|
+
reason: 'Known Signal-observed route has no executable next/executor/successor witness in beta.13.',
|
|
313
|
+
})));
|
|
314
|
+
export const OBSERVED_UNSUPPORTED_ACTIONS = OBSERVED_ROUTE_INVENTORY.flatMap((route) => route.actions.filter((binding) => binding.support === 'unsupported').map((binding) => ({
|
|
315
|
+
action: binding.action,
|
|
316
|
+
routeId: route.routeId,
|
|
317
|
+
reason: binding.action === 'blocked'
|
|
318
|
+
? 'No safe Core transition is advertised for this observation.'
|
|
319
|
+
: binding.action === 'milestone initial-assembly'
|
|
320
|
+
? 'This is an option-bearing navigation node, not a CLI command.'
|
|
321
|
+
: 'This is a manual instruction or terminal marker, not one executable CLI binding.',
|
|
322
|
+
})));
|
|
323
|
+
export const OBSERVED_ROUTE_MATRIX_FINGERPRINT = hashCanonical({
|
|
324
|
+
version: OBSERVED_ROUTE_MATRIX_VERSION,
|
|
325
|
+
routes: OBSERVED_ROUTE_MATRIX,
|
|
326
|
+
witnessContracts: OBSERVED_ROUTE_WITNESS_CONTRACTS,
|
|
327
|
+
classWitnessContracts: OBSERVED_ROUTE_CLASS_WITNESS_CONTRACTS,
|
|
328
|
+
unwitnessed: OBSERVED_UNWITNESSED_ACTIONS,
|
|
329
|
+
unsupported: OBSERVED_UNSUPPORTED_ACTIONS,
|
|
330
|
+
});
|
|
331
|
+
export function validateObservedRouteMatrix() {
|
|
332
|
+
const routeIds = new Set();
|
|
333
|
+
const priorities = new Set();
|
|
334
|
+
const actions = new Set();
|
|
335
|
+
let previousPriority = Number.POSITIVE_INFINITY;
|
|
336
|
+
for (const route of OBSERVED_ROUTE_MATRIX) {
|
|
337
|
+
if (routeIds.has(route.routeId))
|
|
338
|
+
throw new TypeError(`Duplicate observed route id: ${route.routeId}`);
|
|
339
|
+
routeIds.add(route.routeId);
|
|
340
|
+
if (!Number.isInteger(route.priority) || route.priority < 1 || priorities.has(route.priority)) {
|
|
341
|
+
throw new TypeError(`Observed route priority must be a unique positive integer: ${route.priority}`);
|
|
342
|
+
}
|
|
343
|
+
if (route.priority >= previousPriority) {
|
|
344
|
+
throw new TypeError('Observed route matrix must be ordered by strict descending priority.');
|
|
345
|
+
}
|
|
346
|
+
priorities.add(route.priority);
|
|
347
|
+
previousPriority = route.priority;
|
|
348
|
+
if (route.actions.length !== 1)
|
|
349
|
+
throw new TypeError(`Observed route class ${route.routeId} must have one representative action.`);
|
|
350
|
+
const classWitness = CLASS_WITNESS_BY_ROUTE.get(route.routeId);
|
|
351
|
+
if (!classWitness || route.actions[0]?.action !== classWitness.action) {
|
|
352
|
+
throw new TypeError(`Observed route class has no exact witness contract: ${route.routeId}`);
|
|
353
|
+
}
|
|
354
|
+
for (const binding of route.actions) {
|
|
355
|
+
if (actions.has(binding.action))
|
|
356
|
+
throw new TypeError(`Advertised action has multiple routes: ${binding.action}`);
|
|
357
|
+
actions.add(binding.action);
|
|
358
|
+
if (classWitness.kind === 'terminal') {
|
|
359
|
+
if (binding.support !== 'unsupported' || binding.cliBinding !== null
|
|
360
|
+
|| binding.executorRefs.length > 0 || route.successorWitness.kind !== 'terminal') {
|
|
361
|
+
throw new TypeError(`Terminal route class claims an executable transition: ${binding.action}`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
const witness = WITNESS_CONTRACT_BY_ACTION.get(binding.action);
|
|
366
|
+
if (!witness || binding.support !== 'executable' || binding.cliBinding !== witness.action
|
|
367
|
+
|| !isCliCommand(witness.action) || !binding.executorRefs.includes(witness.executorRef)
|
|
368
|
+
|| witness.executorRef !== classWitness.executorRef
|
|
369
|
+
|| witness.successorActions.length === 0
|
|
370
|
+
|| witness.successorActions.some((action) => !classWitness.successorActions.includes(action))) {
|
|
371
|
+
throw new TypeError(`Witnessed route has no exact CLI/executor/successor contract: ${binding.action}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (routeIds.size !== OBSERVED_ROUTE_CLASS_WITNESS_CONTRACTS.length
|
|
377
|
+
|| routeIds.size !== OBSERVED_ROUTE_INVENTORY.length) {
|
|
378
|
+
throw new TypeError('Every Signal-observed route class must have exactly one class-level witness.');
|
|
379
|
+
}
|
|
380
|
+
const knownActions = new Set(OBSERVED_ROUTE_INVENTORY.flatMap((route) => route.actions.map((binding) => binding.action)));
|
|
381
|
+
for (const witness of OBSERVED_ROUTE_WITNESS_CONTRACTS) {
|
|
382
|
+
if (!knownActions.has(witness.action)) {
|
|
383
|
+
throw new TypeError(`Observed route witness is absent from the known inventory: ${witness.action}`);
|
|
384
|
+
}
|
|
385
|
+
for (const successorAction of witness.successorActions) {
|
|
386
|
+
if (!knownActions.has(successorAction)) {
|
|
387
|
+
throw new TypeError(`Observed route successor action is unresolved: ${successorAction}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
for (const route of OBSERVED_ROUTE_MATRIX) {
|
|
392
|
+
const classWitness = CLASS_WITNESS_BY_ROUTE.get(route.routeId);
|
|
393
|
+
if (classWitness.kind === 'terminal') {
|
|
394
|
+
if (route.successorWitness.kind !== 'terminal') {
|
|
395
|
+
throw new TypeError(`Terminal class has no terminal observation: ${route.routeId}`);
|
|
396
|
+
}
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (route.successorWitness.kind !== 'reachable' || route.successorWitness.routeIds.length === 0) {
|
|
400
|
+
throw new TypeError(`Transition class has no reachable fresh-next successor: ${route.routeId}`);
|
|
401
|
+
}
|
|
402
|
+
for (const successor of route.successorWitness.routeIds) {
|
|
403
|
+
if (!routeIds.has(successor))
|
|
404
|
+
throw new TypeError(`Observed route successor is unresolved: ${successor}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const unclaimedActions = new Set([
|
|
408
|
+
...OBSERVED_UNWITNESSED_ACTIONS.map((entry) => entry.action),
|
|
409
|
+
...OBSERVED_UNSUPPORTED_ACTIONS.map((entry) => entry.action),
|
|
410
|
+
]);
|
|
411
|
+
const witnessedActions = new Set(OBSERVED_ROUTE_WITNESS_CONTRACTS.map((entry) => entry.action));
|
|
412
|
+
for (const action of knownActions) {
|
|
413
|
+
if (witnessedActions.has(action) === unclaimedActions.has(action)) {
|
|
414
|
+
throw new TypeError(`Known observed action must be exactly one of witnessed or unclaimed: ${action}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
export function observedRouteMetadataForAction(action) {
|
|
419
|
+
validateObservedRouteMatrix();
|
|
420
|
+
const matches = OBSERVED_ROUTE_INVENTORY.flatMap((route) => route.actions
|
|
421
|
+
.filter((binding) => binding.action === action)
|
|
422
|
+
.map((binding) => ({ route, binding })));
|
|
423
|
+
if (matches.length !== 1) {
|
|
424
|
+
throw new WorkflowError('STATE_CORRUPT', 'Workflow next attempted to advertise an unknown or ambiguous action.', {
|
|
425
|
+
action,
|
|
426
|
+
matchCount: matches.length,
|
|
427
|
+
matrixVersion: OBSERVED_ROUTE_MATRIX_VERSION,
|
|
428
|
+
matrixFingerprint: OBSERVED_ROUTE_MATRIX_FINGERPRINT,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
const { route, binding } = matches[0];
|
|
432
|
+
const witness = WITNESS_CONTRACT_BY_ACTION.get(action);
|
|
433
|
+
const classWitness = CLASS_WITNESS_BY_ROUTE.get(route.routeId);
|
|
434
|
+
const witnessedSuccessor = witness ? {
|
|
435
|
+
kind: 'reachable',
|
|
436
|
+
routeIds: [...new Set(witness.successorActions.map((successorAction) => OBSERVED_ROUTE_INVENTORY.find((candidate) => candidate.actions.some((candidateBinding) => candidateBinding.action === successorAction))?.routeId)
|
|
437
|
+
.filter((routeId) => Boolean(routeId)))],
|
|
438
|
+
evidence: `Executable next -> WorkflowService.${witness.executorRef} -> fresh next witness.`,
|
|
439
|
+
} : null;
|
|
440
|
+
return {
|
|
441
|
+
matrixVersion: OBSERVED_ROUTE_MATRIX_VERSION,
|
|
442
|
+
matrixFingerprint: OBSERVED_ROUTE_MATRIX_FINGERPRINT,
|
|
443
|
+
routeId: route.routeId,
|
|
444
|
+
priority: route.priority,
|
|
445
|
+
predicateRef: witness || classWitness?.action === action
|
|
446
|
+
? `WorkflowService.next.action===${JSON.stringify(action)}`
|
|
447
|
+
: 'unclaimed',
|
|
448
|
+
routeFingerprint: hashCanonical({
|
|
449
|
+
version: OBSERVED_ROUTE_MATRIX_VERSION,
|
|
450
|
+
routeId: route.routeId,
|
|
451
|
+
action,
|
|
452
|
+
witness: witness ?? null,
|
|
453
|
+
}),
|
|
454
|
+
actionSupport: witness
|
|
455
|
+
? 'executable-witnessed'
|
|
456
|
+
: binding.support === 'unsupported'
|
|
457
|
+
? 'unsupported'
|
|
458
|
+
: 'unwitnessed',
|
|
459
|
+
cliBinding: witness ? witness.action : null,
|
|
460
|
+
executorRefs: witness ? [witness.executorRef] : [],
|
|
461
|
+
successorWitness: witnessedSuccessor ?? (classWitness?.kind === 'terminal' && classWitness.action === action
|
|
462
|
+
? { kind: 'terminal', evidence: 'Fresh production next returned the exact terminal observation.' }
|
|
463
|
+
: action === 'none'
|
|
464
|
+
? { kind: 'terminal', evidence: 'Terminal marker with no executor.' }
|
|
465
|
+
: {
|
|
466
|
+
kind: 'unwitnessed',
|
|
467
|
+
evidence: binding.support === 'unsupported'
|
|
468
|
+
? 'Manual instruction or blocked boundary with no single executable transition.'
|
|
469
|
+
: 'Known route is not claimed until an executable next/executor/successor E2E exists.',
|
|
470
|
+
}),
|
|
471
|
+
coverageBoundary: 'signal-observed-route-class-witnesses',
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
export function verifyObservedRouteTransitionWitness(input) {
|
|
475
|
+
const action = input.before.action;
|
|
476
|
+
const successorAction = input.after.action;
|
|
477
|
+
if (typeof action !== 'string' || typeof successorAction !== 'string') {
|
|
478
|
+
throw new TypeError('Observed transition witness requires fresh before and after actions.');
|
|
479
|
+
}
|
|
480
|
+
const witness = WITNESS_CONTRACT_BY_ACTION.get(action);
|
|
481
|
+
if (!witness || witness.executorRef !== input.executorRef) {
|
|
482
|
+
throw new TypeError(`Observed transition witness does not match the claimed production executor: ${action}.`);
|
|
483
|
+
}
|
|
484
|
+
if (!witness.successorActions.includes(successorAction)) {
|
|
485
|
+
throw new TypeError(`Observed transition witness reached unsupported successor ${successorAction} after ${action}.`);
|
|
486
|
+
}
|
|
487
|
+
const metadata = observedRouteMetadataForAction(action);
|
|
488
|
+
const beforeRoute = input.before.route;
|
|
489
|
+
if (!beforeRoute || beforeRoute.routeFingerprint !== metadata.routeFingerprint) {
|
|
490
|
+
throw new TypeError(`Observed transition witness does not start from fresh route metadata: ${action}.`);
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
action,
|
|
494
|
+
successorAction,
|
|
495
|
+
witnessFingerprint: hashCanonical({
|
|
496
|
+
matrixFingerprint: OBSERVED_ROUTE_MATRIX_FINGERPRINT,
|
|
497
|
+
routeFingerprint: metadata.routeFingerprint,
|
|
498
|
+
executorRef: input.executorRef,
|
|
499
|
+
successorAction,
|
|
500
|
+
}),
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
export function verifyObservedRouteClassTransitionWitness(input) {
|
|
504
|
+
const witness = verifyObservedRouteTransitionWitness(input);
|
|
505
|
+
const routeId = observedRouteMetadataForAction(witness.action).routeId;
|
|
506
|
+
const classWitness = CLASS_WITNESS_BY_ROUTE.get(routeId);
|
|
507
|
+
if (!classWitness || classWitness.kind !== 'transition' || classWitness.action !== witness.action
|
|
508
|
+
|| classWitness.executorRef !== input.executorRef
|
|
509
|
+
|| !classWitness.successorActions.includes(witness.successorAction)) {
|
|
510
|
+
throw new TypeError(`Transition is not the representative witness for observed route class ${routeId}.`);
|
|
511
|
+
}
|
|
512
|
+
return { routeId, ...witness };
|
|
513
|
+
}
|
|
514
|
+
export function verifyObservedRouteTerminalWitness(next) {
|
|
515
|
+
const action = next.action;
|
|
516
|
+
if (typeof action !== 'string')
|
|
517
|
+
throw new TypeError('Terminal route witness requires a fresh next action.');
|
|
518
|
+
const metadata = observedRouteMetadataForAction(action);
|
|
519
|
+
const route = next.route;
|
|
520
|
+
const classWitness = CLASS_WITNESS_BY_ROUTE.get(metadata.routeId);
|
|
521
|
+
if (!route || route.routeFingerprint !== metadata.routeFingerprint
|
|
522
|
+
|| !classWitness || classWitness.kind !== 'terminal' || classWitness.action !== action) {
|
|
523
|
+
throw new TypeError(`Observation is not the representative terminal witness for ${metadata.routeId}.`);
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
routeId: metadata.routeId,
|
|
527
|
+
action,
|
|
528
|
+
witnessFingerprint: hashCanonical({
|
|
529
|
+
matrixFingerprint: OBSERVED_ROUTE_MATRIX_FINGERPRINT,
|
|
530
|
+
routeFingerprint: metadata.routeFingerprint,
|
|
531
|
+
terminal: true,
|
|
532
|
+
}),
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
export function withObservedRouteMetadata(next) {
|
|
536
|
+
const action = next.action;
|
|
537
|
+
if (typeof action !== 'string' || action.trim().length === 0) {
|
|
538
|
+
throw new WorkflowError('STATE_CORRUPT', 'Workflow next produced no advertised action.', {
|
|
539
|
+
matrixVersion: OBSERVED_ROUTE_MATRIX_VERSION,
|
|
540
|
+
matrixFingerprint: OBSERVED_ROUTE_MATRIX_FINGERPRINT,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
return { ...next, route: observedRouteMetadataForAction(action) };
|
|
544
|
+
}
|
|
545
|
+
//# sourceMappingURL=observed-routes.js.map
|