feat(route): route dynamic-predicate reads through the warm daemon (NRN-218)#106
Merged
Conversation
…N-218) Add DynamicFieldRefusal + gate_dynamic_refusal to grammar (the daemon variant of gate_dynamic_query, composing the same field_universe + gate_dynamic_fields), and run it in vault.find/vault.count against the warm cache. A refused dynamic key crosses back as a private dynamic_field_error envelope sibling; carrier fields (dynamic_keys input, dynamic_field_error output) are #[schemars(skip)] so they stay off the published schemas and tools/list.
… (NRN-218) Thread the desugared dynamic_keys onto the vault.find/vault.count wire (to_mcp_arguments) and drop the client-side is_empty guard that forced Direct. In route_read, a daemon-computed dynamic_field_error is re-emitted byte-identically (exit 1) after forwarding operator notes, committing to routing instead of falling back to a second direct execution.
E2E: known dynamic field routes byte-identically; unknown field is refused daemon-side with byte-identical stderr + exit 1, served (proven via the served-marker counter, and no 'using direct execution' under --verbose). CHANGELOG [Unreleased] notes the fixed asymmetry + the private wire addition; the v0.45.0 shipped note stays intact.
Negative schema assertions on both vault.find and vault.count: the dynamic_keys input carrier and dynamic_field_error output carrier must be absent from the tools/list inputSchema/outputSchema, with a positive vacuity guard on a real field — removing a #[schemars(skip)] now fails a test instead of leaking the private channel into the public catalog.
One review round + fix (engine=code-review, agents pinned to Opus): 8 candidates, heavy verifier refutation (4). Honest tally: 3 real findings — 2 fixed (private-channel schema pins on BOTH vault.find and vault.count; a verifier wrongly refuted the find-side gap, the implementer's grep proved it real and covered it), 1 dismissed (foreign-client dynamic_keys injection: out-of-contract input, deterministic self-describing refusal, no data risk). Adversarial-Review: run engine=code-review tier=high findings=3 fixed=2 dismissed=1 deferred=0
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.
Summary
Closes the perf cliff where the ergonomic dynamic-predicate spelling (
norn find --type note) was systematically ~3× slower than canonical--eq type:notebecause it forced Direct execution. The field-universe gate now runs daemon-side: dynamic-predicate reads route as-is, the daemon validates against its warm cache with the exact gate code the direct path uses, and a refusal crosses back byte-identically. One round trip, no new public MCP surface.Routed
--type note: 17ms (≈ canonical's 15ms) vs 52ms direct on a real ~1.4k-doc vault.Design
grammar::gate_dynamic_refusalcomposes the samefield_universe+gate_dynamic_fieldsthe direct path uses — one gate, two call sites, cannot drift.dynamic_keys—#[serde(default)] #[schemars(skip)]: a private norn-CLI↔daemon channel, absent from published input/output schemas andtools/list(parity gate proves it; new negative schema-pin tests on both tools make removing askipfail loudly).dynamic_field_errorsibling; the client re-emits it verbatim and exits with direct's exit code, committed to routing — no double execution. NRN-222's request-connection version gate guarantees daemon and client are the same build, so byte-identity is structural.Verification
check --locked/ 2327 tests, 0 failed / clippy-D warnings/fmt;Cargo.lockuntouched.Adversarial review
One round (all agents Opus): 8 candidates, 4 refuted by verifiers. Honest tally 3 findings — 2 fixed, 1 dismissed: the private-channel schema pins were missing on BOTH tools (a verifier wrongly refuted the find-side gap; the implementer's grep proved it real and covered both), and the foreign-client
dynamic_keysinjection surprise is dismissed on the record (out-of-contract input, deterministic self-describing refusal, no data risk).Adversarial-Review: run engine=code-review tier=high findings=3 fixed=2 dismissed=1 deferred=0