Skip to content

Review mitigation for the q29p/n698/x0wf batch (nxf 6j6v.q29p, n698, x0wf) - #49

Merged
cabcookie merged 4 commits into
mainfrom
fix/q29p-n698-x0wf-review
Oct 11, 2026
Merged

cabcookie merged 4 commits into
mainfrom
fix/q29p-n698-x0wf-review

Conversation

@cabcookie

Copy link
Copy Markdown
Member

Stacked on #48 (→ #47 → #46); this PR is the one to merge — it contains all four.

Mitigates the independent review of PR #48 (.nxf/review/2026-10-11T05-26-00Z.md) for the q29p / n698 / x0wf batch. The eight decisions of docs/specs/derived-ranking-keys.md §12 are untouched: §12 gains one bullet clarification (the untyped rank key is refused in the [ranking.list] default order too), and the now bullet now says the filters narrow the rows before they are ranked.

Mitigation Summary

Finding (source · severity) Disposition Notes
Derived keys read raw parent edges, not the projected single parent (Integrity #1 · Medium) Fixed Store::present_edges_of_kinds reads parent from the present_parent LWW view (one query, UNION ALL); core test + facade test with two concurrent re-parents (count and related … pick = "max")
list_query ranks before it filters (Code Quality #1 · Medium) Fixed read::list_selected applies status + type/container/label filters to the candidates before derived::compute; the rank scope stays every live item (§12 decision 2). Seam test: a goal/project type filter needs no now, person still does
Stale NextRank / n698 / "x0wf decides" lines (Code Quality #2 · Medium) Fixed docs/specs/next-finish-first-tiers.md rewritten; NextRank added to xtask/sweep-retired-vocabulary.sh; sweep run against origin/main: no file names NextRank any more
"Every paged read" prose leaves out list (Code Quality #3 · Low) Fixed next_cache.rs module doc, NextPage, Paging, page_through
Engine::list_query rustdoc silent on now (Code Quality #4 · Low) Fixed list_query / list_query_value document when now is needed and the malformed case
Guide says derived keys only in per-type orders (Code Quality #5 · Low) Fixed en + de plugins.md: the [ranking.list] default order takes them; the untyped rank-key refusal there; only list --sort rank reads the time
nxf list resolves now even when sorting by id (Code Quality #6 · Low) Fixed CLI and MCP resolve now only for --sort rank under a plugin with derived list keys
Ratio guard blind to uncounted reads (Test Quality #1 · Medium) Fixed EdgeReadStats gains per_item and bulk; every edge/label/custom-field read is counted (core test enumerates them); the guard asserts per_item == 0 and no growth across all four buckets
List rank scope ignoring --status not pinned (Test Quality #2 · Medium) Fixed a_list_rank_scope_ignores_the_status_filter (closed box still ranks); mutation list: Some(&items) goes red
Cross-section part of the paging fingerprint untested (Test Quality #3 · Medium) Fixed a_derived_list_token_restarts_when_only_the_next_order_changes; dropping +other goes red
No stale-token test for an outside op that reorders (Test Quality #4 · Medium) Fixed a_page_restarts_when_an_op_outside_the_snapshot_reorders_it (a goal's order moves the actions)
Derived key in [ranking.list] default order only tested at load (Test Quality #5 · Medium) Fixed a_derived_key_ranks_in_the_list_default_order_without_per_type_tables
Determinism test cannot fail / has no full tie (Test Quality #6 · Low) Fixed (in part) Added a real full tie, checked from both id orders. The insertion-order permutations stay as a guard against a future unordered read; the comment says so
count = "parent" has no positive case (Test Quality #7 · Low) Fixed count_reads_every_direction_and_kind
CLI/MCP never fill now in a test; malformed now untested (Test Quality #8 · Low) Fixed / filed a_malformed_now_is_refused at the seam. The CLI/MCP branch can't be reached by a black-box test: the binaries link only the bundled plugins, and neither declares a derived key. Filed as 6j6v.4qga (P3)
Smaller gaps (Test Quality #9 · Low) Fixed of = 3 arm, OfField unknown part, "one read" asserted for mentioners_of, CLI paginate refusals assert their message, archived mentioner at Engine::mentioned_by
Ratio guard via read::* not Engine::* (Test Quality #10 · Info) No action Engine::next_query / list_query are thin try_with_state wrappers over read::next_page / list_page, and the §9 chain tests run at the Engine seam
RankingToml lacks deny_unknown_fields (Integrity #2 · Low) Fixed [ranking.lsit] is refused at load; test
Untyped related … of = "rank" in [ranking.list] default order (Integrity #3 · Low) Fixed Refused at load with and without per-type tables; the typed form loads; test
mentioned_by_bulk duplicates across chunks (Integrity #4 · Info) Fixed targets are deduplicated before chunking; 602-target test
Integrity #5–#7 (Info) No action Confirmations (bounded growth, determinism, clean error paths)

Quality gates

  • cargo fmt --all
  • cargo test -p nexus-flow-facade --test derived_ranking --test list_query --test next_query --test mentioned_by --test next_rank_per_type
  • cargo test -p nexus-flow-facade --lib plugin:: and --lib read
  • cargo test -p nexus-flow-core --lib store::
  • cargo test -p nexus-flow-cli --test list --test mention
  • cargo xtask docs check, cargo xtask changelog check origin/main
  • A mutation check reverted the three fixes the new tests guard (present_parent, rank scope, cross-section fingerprint). Each reversion turned its test red.

Clippy and the full suite are left to CI, by instruction.

🤖 Generated with Claude Code

https://claude.ai/code/session_016q5uMUkZJQW4fckJoA3FME

cabcookie and others added 4 commits October 11, 2026 01:47
…v.q29p)

- Engine::mentions(id): the outgoing targets, the existing read::mentions put on the seam.
- Engine::mentioned_by(id, &MentionedByQuery) / mentioned_by_value: the live items that mention
  a target, in id order, filtered by NextFilter (type, container, label) and cut/paged exactly
  like next_query.
- read::mentioners_of(store, targets): THE bulk reverse lookup (over the new
  Store::mentioned_by_bulk) that the derived ranking key count = "mentioned_by" (x0wf) is to
  share, per docs/specs/derived-ranking-keys.md section 11.
- The paging half of next_page is factored into page_through/Paging/Watermark, so next and
  mentioned_by share one snapshot table, expiry, caps and invalidation; the snapshot key names
  the read, so tokens do not cross between them. next's keys are unchanged.
- CLI: nxf mention by <id> [--type]... [--in] [--label] [--limit [--paginate [--token]]], with
  list records and next's --json envelope; goldens under both plugins, guide EN/DE, generated
  docs, verb-seam alias.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016q5uMUkZJQW4fckJoA3FME
…e (nxf 6j6v.n698)

- Plugin: [ranking.list] takes the same keys as [ranking.next]: per-type tables
  [ranking.list.<type>], the cross-type order types = [...], an optional default order.
  Parsed and validated by the same code (errors name their section). Ranking::list is
  always resolved at load: absent, or without its own order, it inherits the
  [ranking.next] default order, so list --sort rank is unchanged for every plugin that
  declares nothing (both bundled plugins).
- Ranking: NextRank becomes TypeRank over a &RankSpec; next ranks with
  TypeRank::next, list --sort rank with TypeRank::list - one comparator for both, the
  place derived keys (x0wf) plug in once.
- Paging: read::ListQuery + read::list_page reuse NextFilter (type alternatives,
  container = belongs_to OR contributes_to, label) and q29p's Paging / Watermark /
  page_through; the snapshot key names the read ("list"), status, sort, filter and a
  fingerprint of [ranking.list], so tokens never cross reads or plugin orders.
- Engine::list_query(&ListQuery) -> NextPage and Engine::list_query_value (the
  {"items","total"[,"next_token","restarted"]} envelope of list_value records).
- CLI: nxf list --type (repeatable) --in <id> --limit --paginate --token; plain array
  without --limit, next's envelope with it; human view discloses the cut and prints
  the token. Default order stays id.
- Tests at the seam (facade/tests/list_query.rs, plugin unit tests), CLI black-box
  (cli/tests/list.rs), goldens under both plugins, guide EN/DE, generated docs.

Stacked on #46 (q29p).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016q5uMUkZJQW4fckJoA3FME
…idation and the type chain (nxf 6j6v.x0wf)

Builds docs/specs/derived-ranking-keys.md on the local SQL path. A per-type order
([ranking.next.<type>], [ranking.list.<type>], and the [ranking.list] default order)
takes three derived keys besides field keys: `related` (a value on the neighbours over
an outbound edge: `of = "rank"` or `of = { field }`, custom fields included, picked by
min/max), `count` (neighbours over an edge kind and direction, filtered by type/status/
label) and `sum` (counts with equal weight).

- Load-time rules (spec §7): kind exclusivity, edge kinds, parts per kind, declared
  types/statuses/fields, `of = "rank"` needs a declared order, and the acyclic type chain
  across both sections, the cycle named. Derived keys are refused in the [ranking.next]
  default order (it also ranks blocked and the lanes).
- Bottom-up once per query (spec §4/§5): one edge pass (Store::present_edges_of_kinds),
  the reverse mentions through q29p's read::mentioners_of (spec §11), one bulk read of
  the neighbours, one sort and a dense rank per type in the chain, then the comparator
  reads precomputed values. A plugin without derived keys pays nothing.
- `of = "rank"` reads the asking section's order, else the other's (spec §8: persons by
  the projects next ranks); a list query then needs `now` (ListQuery::now, read::list_at;
  the CLI and MCP pass the clock only for such a plugin).
- The store-free read::next_active refuses a plugin with derived next keys until 1c5s.
- Tests: plugin load validation (each refusal, cycles), the spec's orders at the Engine
  seam (next_query, list_query), each kind, determinism, and a query-shape ratio guard
  (EdgeReadStats) that the edge pass runs once per query.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016q5uMUkZJQW4fckJoA3FME
…x0wf)

Mitigates the independent review of PR #48:

- derived keys read `parent` from the `present_parent` projection, the
  single parent belongs_to/--in/show read (Integrity #1);
- `list` narrows by status/type/container/label before ranking, so the
  type chain covers only the orders the kept rows need; the rank scope
  stays every live item (Code Quality #1, spec §12 decision 2);
- `EdgeReadStats` counts every edge, label and custom-field read, and the
  ratio guard asserts none runs per item (Test Quality #1);
- `[ranking]` refuses unknown tables; the `[ranking.list]` default order
  refuses an untyped `of = "rank"` key (Integrity #2, #3);
- `nxf list` and the MCP list resolve `now` only for a ranked list;
- `mentioned_by_bulk` deduplicates targets across chunks;
- tests for the rank scope under --status, the cross-section paging
  fingerprint, a stale token after an outside reorder, a derived key in
  the list default order, a full tie, count = "parent", and the smaller
  gaps; stale spec/guide/rustdoc prose fixed; `NextRank` added to the
  retired-vocabulary sweep.

The CLI/MCP `now` test gap is filed as 6j6v.4qga.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016q5uMUkZJQW4fckJoA3FME
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