Skip to content

Commit dc60117

Browse files
Freeze v2 routing precedence and inheritance contract
Freeze v2 routing precedence and inheritance contract Issue #78 freezes how every workflow-task and activity-task routing target is resolved, snapped into the durable row it belongs to, and inherited across retries, continue-as-new, and parent-to-child transitions. Today the concrete logic lives in RoutingResolver, WorkflowOptions, ActivityOptions, ChildWorkflowOptions, WorkflowStub::start, WorkflowExecutor's scheduleChildWorkflow / scheduleActivity / continueAsNew / retryChildWorkflow paths, ScheduleManager, PhpClassScheduleStarter, and TaskRepair, but "which input wins at which level", "when a retry reuses the snapshot", "where snapped routing is exposed in projections", and "what is intentionally deferred to matching, sharding, and priority roadmap work" were not frozen as one product contract. A subsystem that recomputes (connection, queue) from class defaults after a snapshot is written, that widens WorkflowOptions to carry additional fields, or that silently rewrites snapped routing during retry/repair/continue-as-new is therefore silently out of contract. This lands the contract doc and a pinning test. docs/architecture/routing-precedence.md: - scopes the contract to resolution precedence, snapshot columns, inheritance, retries, continue-as-new, schedule-triggered runs, effective routing in projections, and the interaction with compatibility - freezes RoutingResolver as the sole authority on turning per-call options and class defaults into a (connection, queue) pair, and names the four resolver methods workflowConnection, workflowQueue, activityConnection, activityQueue as the only surface callers may use - freezes the WorkflowOptions wire shape at exactly ?string $connection and ?string $queue; additional fields are a protocol-level change - freezes the resolution precedence at workflow start: per-call WorkflowOptions > workflow class $connection/$queue defaults > null with config('queue.connections.<connection>.queue', 'default') tail - freezes that workflow start options override workflow defaults only for workflow tasks; WorkflowOptions does not propagate into activity scheduling - freezes the resolution precedence at child-workflow scheduling: per-call ChildWorkflowOptions/WorkflowOptions > child workflow class defaults > null with the same tail, and makes explicit that child-call options override the child's own task routing, not every activity inside that child - freezes the resolution precedence at activity scheduling: ActivityOptions > activity class defaults > parent run's snapped routing > config('queue.default') tail; the resolver never walks past the owning run row - freezes the schedule-triggered-run precedence: schedule.connection and schedule.queue flow into a WorkflowOptions value appended to the start arguments, after which workflow start resolution applies as above - freezes the durable snapshot columns on workflow_runs, workflow_tasks, activity_executions, and workflow_schedules, and states that workflow-task rows and timer-task rows inherit from $run->connection/$run->queue and activity-task rows inherit from $execution->connection/$execution->queue without re-reading class defaults - freezes retry inheritance: activity retries reuse the execution's snapped routing, child-workflow retries copy the failed child run's snapped routing, and TaskRepair::repairRun / recoverExistingTask paths reuse the run's snapshot; heartbeats never change routing - freezes continue-as-new inheritance: the continued run inherits both routing and compatibility from the previous run by default, with WorkflowOptions as the only override surface, and snapshots inherited values on creation so later mutations to the original run do not retroactively move the continued run - freezes the dedicated-queue and same-server affinity patterns as operator-visible contract behaviour preserved by the snapshot and inheritance rules, including across the repair path - freezes which projections expose effective routing for operator diagnostics: RunDetailView, RunListItemView, and OperatorQueueVisibility consume the snapshot and must not recompute from class defaults - freezes the independence of routing and compatibility as two separate claim-time axes, so a routing override cannot silently weaken the compatibility guarantee frozen in docs/architecture/worker-compatibility.md - freezes the config surface at queue.default, queue.connections.<name>.queue, and workflows.v2.namespace, and explicitly states the contract introduces no new environment variables - defers queue priority semantics, task-id sharding, retry-time rerouting, and cross-namespace routing to follow-on roadmap issues (#72, #83) and to future contract extensions so a future phase extends this contract rather than redefining it tests/Unit/V2/RoutingPrecedenceDocumentationTest.php: - pins the frozen headings, terminology, authority class names, durable column names, resolver method names, config keys, cross-contract citations, and roadmap references - pins the wire shape of WorkflowOptions at exactly (connection, queue) via reflection, and pins the routing fields on ActivityOptions and ChildWorkflowOptions including their hasRoutingOverrides() surface - pins the rule statements that workflow start options affect workflow tasks only, child-call options affect the child's own task routing only, activity-level defaults win for activity executions, retries reuse the snapshot, continue-as-new inherits both routing and compatibility, and snapshots are written at creation - pins the dedicated-queue / same-server affinity preservation rule, the repair-path snapshot reuse, the projection contract that Waterline-facing surfaces consume snapshots and never recompute, the resolver's role as sole authority, the "compatibility and routing are independent" rule, and the explicit deferrals for priority and sharding The routing precedence shift is a contract change, not a code change: RoutingResolver, WorkflowOptions, ActivityOptions, ChildWorkflowOptions, WorkflowStub, WorkflowExecutor, ScheduleManager, PhpClassScheduleStarter, TaskRepair, RunDetailView, RunListItemView, OperatorQueueVisibility, WorkerCompatibilityFleet, and the workflow_runs, workflow_tasks, activity_executions, and workflow_schedules tables are preserved verbatim, and this document adds the rules that govern which of their inputs wins at which level and when the resulting snapshot is reused rather than recomputed. Verified: - bash scripts/check-public-boundary.sh (exit 0) - vendor/bin/phpunit tests/Unit/V2/RoutingPrecedenceDocumentationTest.php (26 tests, 129 assertions, OK) against PHP 8.5 - vendor/bin/phpunit across all eight V2 DocumentationTest suites (165 tests, 1015 assertions, OK) - vendor/bin/ecs check docs/architecture/routing-precedence.md tests/Unit/V2/RoutingPrecedenceDocumentationTest.php (no errors) Refs: #78
1 parent 8342eb8 commit dc60117

2 files changed

Lines changed: 1077 additions & 0 deletions

File tree

0 commit comments

Comments
 (0)