CR-DD-017: Blocked local-only route evidence parity - #158
Merged
coreytshaffer merged 5 commits intoAug 11, 2026
Merged
Conversation
Requirements-contract proposal only. Grants no implementation authority. When choose_resilience_route selects a non-local-safe route for a local_only packet, client.py writes a RouteDecisionAudit (reason_code=ambiguous_or_remote_route) and raises before ever calling build_route_decision_payload() or appending a route_decision event -- both of which already exist and are used two branches later on the allowed path. The router already computes the distinguishing cause (ResilienceRouteDecision.reason: sensitivity_requires_human_ review vs. no_reliable_automated_route_available); it's discarded, not missing. This is what produced the Aug-8 misdiagnosis corrected in CR-DD-016's #156. Proposes persisting the existing route_decision payload on that one branch via the existing _append_route_decision_event helper -- preserving its optional route-decision signing switch, never a direct ledger.append_event bypass -- before raising LocalRouteUnavailableError. reason_code stays exactly ambiguous_or_remote_route; no new vocabulary for Unknown vs ObservedUnavailable; no routing, capability-resolution, or privacy behavior changes. Acceptance requires two end-to-end tests against the real choose_resilience_route (the existing tests mock it with reason="", which is the same blind spot this CR closes) plus an exact evidence-order/integrity criterion: one route_audit, one route_decision, then raise, no synthesized worker_result. Status: Proposed. Implementation authority: Not authorized.
✅ Deploy Preview for poetic-quokka-0fd859 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
…sistence-failure invariant
…nce-failure invariant
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.
Authority boundary
Proposed evidence/observability CR only. No runtime, routing, capability-resolution,
or privacy-enforcement changes are authorized by this PR.
This PR establishes the governed change request. It does not itself edit
client.pyorany test file — that implementation remains separately gated behind its own approval,
per
Status: Proposed/Implementation Authority: Not authorizedin the documentitself.
Scope
Exactly one new documentation file,
246insertions, zero other changes.docs/change/requests/CR-DD-017-blocked-route-evidence-parity.mdtriage_core/, any test file, any schema or configdefault. Downstream of CR-DD-016 (CR-DD-016: Document capability-probe operator workflow discoverability #155/Correct CR-DD-016 capability-observation semantics #156/Implement CR-DD-016: capability-probe operator workflow discoverability #157, all merged); does not reopen or
amend it.
What this proposes
When a
local_onlypacket's resilience-routed selection is not in the explicitlylocal-safe set (
local_heavy,local_fast,deterministic),client.pywrites aRouteDecisionAudit(reason_code="ambiguous_or_remote_route") and raisesLocalRouteUnavailableError— without ever callingbuild_route_decision_payload()orappending a
route_decisionevent, both of which already exist and are used twobranches later on the allowed path for exactly this purpose.
choose_resilience_routealready computes the distinguishing cause internally(
ResilienceRouteDecision.reason:sensitivity_requires_human_reviewvs.no_reliable_automated_route_available) — it's discarded at the point of failure, notmissing. This is precisely the gap that produced the Aug-8 trial misdiagnosis corrected
in CR-DD-016's #156: a sensitivity-driven handoff and a capability-exhaustion handoff
are indistinguishable in the durable ledger today.
Proposed fix: on that one branch, persist the existing
route_decisionpayload viathe existing
_append_route_decision_eventhelper — never a directledger.append_event(...)call, which would silently bypass the helper's optionalroute-decision signing switch to
ledger.append_signed_route_decision_event(...)—before raising. Exact evidence order: one
route_audit, then oneroute_decision,then the raise. No
worker_resultis synthesized, because no worker was attempted.route_audit.reason_codestays exactlyambiguous_or_remote_route— this CR does notrename, split, or parameterize it, and does not introduce a new reason-code vocabulary
for
UnknownvsObservedUnavailablecapability sub-states (that distinction alreadyexists on the
route_decisioncapability fields).Deliberately out of scope
offload_recommended_for_local_onlybranch, immediately below inclient.py, has the identical structural gap. Named here, not touched — bundling itwould mix two separate acceptance-evidence surfaces into one CR.
choose_resilience_route,resolve_capability, or any routing/capability-resolutionlogic. This CR persists a decision already made; it does not change how any decision
is made.
Acceptance criteria (full list in the CR doc)
route_audit.reason_codeunchanged.route_decisionevent persisted via the existing helper, in exact order (audit →decision → raise), no synthesized
worker_result.choose_resilience_route— the existing tests(
test_local_only_remote_route_blocked_audit,test_ambiguous_route_blocked_audit)mock it with
reason="", which is the same blind spot this CR closes: a sensitivitycase and a capability-exhaustion case, each asserting both the
route_auditcode andthe
route_decision.reason.six stated invariants hold.
Numbering
CR-DD-015remains reserved for the separate comparative-lane track. Verified viagit ls-treeacrossorigin/mainand every local/remote branch before drafting — noCR-DD-015orCR-DD-017+ exists anywhere. This CR takesCR-DD-017.🤖 Generated with Claude Code