Skip to content

docs(persistence): teach the segment-wildcard grammar in the README - #241

Merged
lxsaah merged 7 commits into
mainfrom
claude/github-issue-214-review-l11wu5
Sep 1, 2026
Merged

docs(persistence): teach the segment-wildcard grammar in the README#241
lxsaah merged 7 commits into
mainfrom
claude/github-issue-214-review-l11wu5

Conversation

@lxsaah

@lxsaah lxsaah commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

PersistenceBackend::query moved from a prefix glob to the MQTT-style
grammar subscriptions use, but the README still keyed every example with
:: and queried it with ::*. A wildcard is a whole dot-separated
segment, so accuracy::vienna is a single segment no pattern can
partition and accuracy::* is a literal key that matches nothing — a
reader copying the examples got zero rows and no error.

Migrate the example keys to dot-separated form, then fix the patterns over
them, and state the grammar once where the reader has to choose: * for a
known depth, # where it varies. query_raw takes # because it feeds
the AimX record.query handler, whose callers key at their own depth.

Issue §6 — the out-of-scope :: keys stay, deliberately. The keys in
examples/remote-access-demo/src/server.rs and
aimdb-wasm-adapter/tests/transform_join_integration_tests.rs are unchanged.
They are exact-match only — the demo subscribes to one literal key
(client.rs:179) and exact lookup is separator-agnostic — and nothing in the
repo pairs a :: key with a wildcard outside the historical CHANGELOG entries
and the superseded design 022. The :: there qualifies a Rust type name rather
than addressing a topic, which is a coherent separate convention, not the same
mistake. Revisit if the demo ever grows a wildcard subscribe: those four
server::* records are a set under a common prefix, which is the shape that
would want one.

Closes #214

claude added 7 commits August 31, 2026 20:39
`PersistenceBackend::query` moved from a prefix glob to the MQTT-style
grammar subscriptions use, but the README still keyed every example with
`::` and queried it with `::*`. A wildcard is a whole dot-separated
segment, so `accuracy::vienna` is a single segment no pattern can
partition and `accuracy::*` is a literal key that matches nothing — a
reader copying the examples got zero rows and no error.

Migrate the example keys to dot-separated form, then fix the patterns over
them, and state the grammar once where the reader has to choose: `*` for a
known depth, `#` where it varies. `query_raw` takes `#` because it feeds
the AimX `record.query` handler, whose callers key at their own depth.

Refs #214
Same defect as the aimdb-persistence README, smaller surface: the
quickstart persisted `accuracy::vienna` and queried it with
`accuracy::*`, which matches nothing under the segment-wildcard grammar.

CHANGELOG.md's `"temp::*"` stays as written — it is the historical entry
describing this break, not an example to copy.

Refs #214
`query_latest` taught the old grammar directly — `"accuracy::*"` as the
pattern example, `"accuracy::vienna"` as its contrast. The first matches
nothing now and the second is no longer a contrast, just a wildcard-free
pattern. `query_range` and `query_raw` said "pattern" without defining it.

State the grammar once on the trait, referring to
`PersistenceBackend::query` where the crate-level statement lives, and let
each method describe only what it does with the matches. `query_raw` names
`QUERY_ALL_PATTERN` since that is what the AimX handler passes it.

`StoredValue::record_name`'s example key loses its `::` for the same
reason, and now says it is always concrete — it is the type a third-party
backend author reads the contract from.

Refs #214
`MockBackend::query` took `_record_pattern` and returned every row, so no
test in the crate proved a pattern ever reached a backend — let alone that
it was read under the segment-wildcard grammar. The six existing tests pass
today and would have kept passing through a careless key rename, which is
the gap review named.

Filter the mock's rows through the re-exported `topic_matches`, the same
matcher `PersistenceBackend::query` obliges a real backend to decide with,
and dot-separate the fixture keys so a pattern can address them. `QueryParams`
stays ignored: row-level filtering is `AimDbQueryExt`'s job and remains what
the original six tests are about.

Three cases pin the grammar at the backend-contract level, where a
third-party backend author looks (aimdb-persistence-sqlite covers it against
real SQLite, but only there):

- `sensor.*` must not return `sensor.deep.nested` — the regression #201 was
  made for. Fails if the mock goes pattern-blind again.
- `sensor.#` reaches that row, so the exclusion above is the pattern's doing
  and not a missing fixture.
- a key with no dots is one segment no wildcard can address.

Refs #214
The client-facing doc promised the marker always arrives ("the engine
reserves a sink slot for this update, so it arrives even from a burst that
overran"). The reservation is real, but it only defends against overrun.
The marker rides the final snapshot's frame, so a burst with no final
snapshot ends without one:

- **Empty burst.** `snapshots()` filters through `try_latest_as_json`, so a
  pattern matching nothing — or only records with no value yet — leaves
  `total == 0`, the loop body never runs and nothing is sent. Ordinary on a
  fresh database.
- **Tail failed to encode.** The frame is logged and skipped, and `last`
  goes with it.
- **Exact-topic subscribe.** Takes no snapshots at all, by design.

A consumer following the old text reads "no `snapshot_end` yet" as "the
burst is still arriving" and waits forever. Say instead that the marker
appears when the burst produced at least one encodable snapshot, that its
absence carries no information, and that `skipped` is still the loss signal.

Documentation only. Making the marker unconditional needs a
record-independent terminator frame — a new frame kind, `SubUpdate::data`
becoming `Option`, a reservation refactor — and still could not promise
delivery, since an unrecognized frame is skipped by design, which is the
same property that keeps new frame kinds backward-compatible. That was
considered and rejected as disproportionate.

`Delivery::BurstEnd` gets the same correction internally: its "guaranteed
delivery into the slot `BurstBody` kept free" holds only once a `BurstBody`
has run. A single-snapshot burst rests on ordering instead — the sink
predates the subscribe reply and the server sends snapshots before
registering the event pump — which is what a future reader would build on.

Refs #214
The `[Unreleased]` entry carried the same overstatement the rustdoc did —
"a burst closes even when it overran the sink and even when no live event
ever follows" reads as an unconditional guarantee. Unlike the historical
`"temp::*"` note in aimdb-persistence-sqlite, this describes the contract
about to ship, so it is corrected in place rather than left as a record.

The reservation defends against overrun and that half stands; what it never
covered is a burst with no final snapshot to carry the marker.

aimdb-core's entry already says the flag is not an unconditional promise, and
design 047 already tells consumers not to loop until it. Neither needs a
change.

Refs #214
Same content, less of it: the grammar rule and the `snapshot_end`
conditions were each stated once and then restated in different words.
Trims ~30 lines across the READMEs, the query rustdoc, the mock's comment
and the `snapshot_end` sites without dropping a fact — every case that
leaves the marker unset is still named, and `*` vs `#` is still contrasted
where the reader picks one.

Refs #214
@lxsaah
lxsaah merged commit 161e0bc into main Sep 1, 2026
7 checks passed
@lxsaah
lxsaah deleted the claude/github-issue-214-review-l11wu5 branch September 1, 2026 04:25
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.

Pre-release doc pass: persistence pattern grammar, and the snapshot_end contract

2 participants