Repository navigation
fix(run): a hop route to a module that starts after the hop is frozen, not refused - #1178
Merged
Merged
Conversation
…, not refused A fresh run starts authority/hop before every routed module ordered after it, and a module that calls through the hop is (robin's playground with annotations 3fae5dc1). When the hop resolved its generated routes, that module had loaded but not initialized, so freezeHopAddresses found no recorded mapping for it and refused the hop's own start with "published hop routes changed: stop and restart this Codefly run" on every fresh start, although nothing had changed. The address the hop's resolution derived for such a module is the one its own Init will be proposed. The snapshot now freezes it, and the module's Init is the check: RecordNetworkMappings refuses any accepted set that drops or moves an address a hop snapshot holds, on the first record as well as on a reload (before, only a reload was checked). A module that never loaded still has no mapping and is refused by name; a module that has initialized must still be at the address it recorded. The mid-run refusal is unchanged, and every restart refusal now names the route and the reason. Tests: TestHopRoutesFreezeAModuleThatInitializesAfterTheHop — the hop's delivery succeeds with an uninitialized routed module and routes it at its proposed address; that module's Init is refused when it accepts another address or drops the endpoint, and accepted at the frozen one. Sabotage-checked both halves: without the freeze the hop's delivery fails with the reported error; without the first-record check the moved Init is accepted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 26, 2026
Merged
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.
What broke
With CLI 0.1.213 (and main), robin's playground — once it composes the document store's tool runnable-bindings and annotations 3fae5dc1 — died at
authority/hopruntime-init on every fresh start:Root cause
pkg/orchestration/hop_configurations.gofreezeHopAddressesrequired every hop route's producer to already have recorded network mappings (s.networkMappings[unique]), and treated "no recorded mapping" as a changed route. A fresh run starts the hop before every routed module ordered after it, and a module that calls through the hop is (annotations calls robin through the hop, and the hop routes annotations). That module has loaded but not initialized, so the hop's resolution derives its address (producerNetworkMappings→localProducerMappings, the address its own Init will be proposed) — and the freeze refused it. Instrumented on the live run:route annotations/annotations/connect … selected=<derived localhost:52650> recorded=[].It was not the excluded document-store services: the store's API catalog publishes only
documents/documents, so no route names them. A routed service that never loaded still has no mapping and is still refused by name (hop route … has no resolved mapping).Fix (at the owner)
RecordNetworkMappingsnow checks every frozen route owned by the recording service on the first record too (before: only on a reload). So the late module's Init is the check: the same address is accepted; an Init that moves or drops a routed address is refused naming the route, and nothing is recorded. This is strictly tighter than before — the mid-run refusal is not loosened.Fix, not a hack: the freeze lives where the snapshot is taken and the check where addresses are accepted; no flag, no fallback.
Reused: the existing fence (
routingMappings,RecordNetworkMappings,NetworkMappingHash) and the existing derivation of an uninitialized producer's address. Nothing new resolves addresses.Tests
TestHopRoutesFreezeAModuleThatInitializesAfterTheHop: the hop's delivery succeeds with an uninitialized routed module and routes it at its proposed address; that module's Init is refused when it accepts another address or drops the endpoint, and accepted at the frozen one; the next delivery reads the same snapshot. Sabotage-checked both halves: without the freeze, the hop's delivery fails with the reported error; without the first-record check, the moved Init is accepted.go test ./pkg/orchestration/ ./cmd/run/,go vet,golangci-lint run ./pkg/orchestration/are clean.Live evidence
robin
authority/interface7389d1ac + the playground store-tools patch, own naming scope, freshcodefly run:authority/hopruntime-init with the error above.authority/hopinitializes, thenannotations/annotationsinitializes after it at the frozen address; every service comes up andauthority/edgeruns (pre-rebase build of this commit).tracing/evidence, because its ClickHouse data directory (~/Library/Caches/codefly/clickhouse/c043abb67fcd3379) is the one another naming scope's live run already holds ("Another server instance in same directory is already running"). That collision is unrelated to this change and is reported separately.🤖 Generated with Claude Code