Conversation
rmcdaniel
pushed a commit
that referenced
this pull request
Apr 24, 2026
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
rmcdaniel
pushed a commit
that referenced
this pull request
Apr 24, 2026
The seven `docs/architecture/` contract docs (worker-compatibility, task-matching, control-plane-split, scheduler-correctness, rollout-safety, operational-liveness, routing-precedence) carried bare `#NNN` tokens that GitHub auto-resolves against the public workflow repo, producing broken links and exposing private-tracker numbering. Replace each `(#NNN)` parenthetical with the existing Phase name that already identifies the contract, drop the bare `#72` / `#78` / `#83` / `#578` references in routing-precedence narrative, and update the seven matching `*DocumentationTest` pinning tests to assert on Phase names and contract paths instead of bare issue numbers. Extend `scripts/check-public-boundary.sh` so the regression cannot land again: any `#NNN` in tracked content under `docs/` or in `tests/Unit/V2/*DocumentationTest.php` now fails the public-boundary scan.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #82.