Skip to content

feat(route): route new through the warm daemon, byte-identically (NRN-230 PR C) - #118

Merged
dbtlr merged 1 commit into
mainfrom
nrn-230-pr-c-route-new
Jul 11, 2026
Merged

feat(route): route new through the warm daemon, byte-identically (NRN-230 PR C)#118
dbtlr merged 1 commit into
mainfrom
nrn-230-pr-c-route-new

Conversation

@dbtlr

@dbtlr dbtlr commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

Final PR of NRN-230: norn new's agent-facing paths now route through the warm daemon via route_call, byte-identically — completing the create verb and leaving repair/apply (NRN-231) as the last unrouted mutations.

Routes (proven by a 30-case routed-vs-direct matrix: (stdout, stderr, exit) triples + full-vault byte snapshots + served vault.new markers):

  • All three creation modes — explicit PATH / --as rule / inbox fallback — × dry-run/apply × records/json
  • --field and --field-json (unlike set: vault.new already carries ordered token lists, so nothing gates), --var (parsed CLI-side, malformed --var errors pre-send byte-identically), --title/--force/--parents
  • {{seq}}: routed dry-run predicts non-bindingly, routed apply resolves under the shared flock; a routed/direct/routed interleave allocates unique ids
  • Every PR-A coded refusal rendered format-independently as error: {message} + exit 2 (the new path deliberately has no JSON error envelope), including body-scaffold render failures and the NRN-114 empty-frontmatter quirk

Gates to Direct (served-count 0 asserted): --body-from-stdin, interactive TTY without --yes, and routing_forced_direct (--config/--no-cache-refresh) — with regression shapes for the #113 bypass class.

Honest divergence pinned, not fudged: lock-timeout prose differs routed-vs-direct (same as the set/move precedent); the suite pins safety (non-zero exit, nothing written, contended call genuinely served) rather than byte-identity there.

Review

Adversarial review: APPROVE, no changes — targeted the two blocker classes from the sibling PRs: same-file guarantee probed with path-normalization shapes (a/../b.md, ./dot.md, trailing slash, --parents deep create) and a {{date}}-templated rule (daemon resolves once; no CLI-side second computation to disagree with); consent ladder verified line-for-line against the direct arm (both key TTY off stdout; routed sends confirm:true only for --yes); dead-daemon and adversarial-wire probes confirmed clean pre-send fallback / post-send-uncertain per ADR 0011.

Surfaced pre-existing (filed separately, NRN-s16): norn new ignores --config on both paths — the guard still forces Direct correctly and both paths behave identically.

Verification

cargo check --workspace --locked / cargo test --workspace (2484 passed, 0 failed; serve_new_routing 7/7) / clippy --all-targets -D warnings / fmt --check — clean, Cargo.lock untouched.

Closes NRN-230 (3/3, after #116 and #117).

…-230 PR C)

`norn new`'s agent-facing paths now route through a live `norn serve` daemon
via the NRN-229 routing seam, rendering byte-identically to direct execution —
the fifth routed mutation after set/edit/move/delete/rewrite-wikilink.

- src/new/route.rs: pure `to_mcp_arguments` / `reconstruct` / `emit` (the
  `set::route` template). `to_mcp_arguments` builds the `vault.new` NewParams
  wire object (path/rule/title/vars/field/field_json/parents/force/confirm);
  `reconstruct` serde-round-trips PR B's NewReport out of `{report}`; `emit`
  renders through the SAME `new::report::{render_records,render_json}` the
  direct path uses. Refusals render format-INDEPENDENTLY as `error: {message}`
  + exit 2 (audit F5 — new has no JSON error envelope, unlike set).
- src/lib.rs: `try_route_new` (unix + non-unix stub) + Command::New wiring.
  Routes BEFORE the local mutation lock (the daemon takes the same per-vault
  lock in-process). `--var` is parsed CLI-side with the direct path's
  `parse_var_args` before routing OR the lock, so a malformed `--var` refuses
  pre-send byte-identically. Confirm ladder mirrors new's direct dry-run/apply
  tree (TTY keyed off STDOUT, not stdin like set). Gated to Direct:
  `--body-from-stdin`, interactive TTY, and `routing_forced_direct`
  (`--config` / `--no-cache-refresh`) — the #113 lesson.
- Unlike set, both `--field` AND `--field-json` route: vault.new's params carry
  them as ordered Vec<String> token lists (audit F6), so the wire preserves
  last-wins.
- tests/serve_new_routing.rs: a 30-case byte-identity matrix (three modes ×
  dry-run/apply × records/json + title-ignored warning + valid --field-json +
  NRN-114 empty-frontmatter + every coded refusal), (stdout,stderr,exit)
  triples + full post-state snapshots + `served vault.new` markers; {{seq}}
  dry-run-predicts / interleaved-routed+direct-applies-stay-unique;
  --body-from-stdin and forced-direct-flags served-count guards; malformed
  --var pre-send refusal; and the held-lock contention safety case (safety
  pinned, not byte-identity — the same legitimate divergence set documents).
- CHANGELOG: operator-facing PR C entry in the NRN-229 routing register.
@dbtlr
dbtlr merged commit bf1a636 into main Jul 11, 2026
7 checks passed
@dbtlr
dbtlr deleted the nrn-230-pr-c-route-new branch July 11, 2026 01:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant