Skip to content

test(assistant): cover the compaction provider view - #205

Open
juacker wants to merge 1 commit into
mainfrom
clai/test/compaction-provider-view
Open

juacker wants to merge 1 commit into
mainfrom
clai/test/compaction-provider-view

Conversation

@juacker

@juacker juacker commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Implements roadmap item R6.3. Sibling of #204 (which covered the compaction repository functions); this covers the function that turns one of those rows into an actual truncation of what the provider sees.

provider_history_messages_with_compaction had zero tests, while everything around it in compaction.rs is well covered — the size gates, the window selection, the summary clamping. It is the function that decides what actually reaches the model.

Eight tests, one per property: the summary leads the view; the boundary is inclusive; the summary is emitted once from the front rather than left in the tail it always falls inside; the no-compaction path still strips summaries; an orphan summary in the retained tail is dropped; all four unusable rows fall back to the untruncated history; the view carries the stored summary text and marker; the cut happens exactly where the row says, even mid tool group.

Two findings worth the reviewer's attention, both recorded in the test docs rather than acted on:

  • The four "unusable row" guards are unreachable today. Both callers get their row from latest_completed_compaction, whose WHERE adds IS NOT NULL on both ids; and ON DELETE SET NULL plus the pool's foreign_keys means a deleted message nulls the column rather than leaving it dangling, which that same filter then excludes. The guards are one half of an invariant whose other half is a WHERE clause in another file, so the test pins the half that lives here.
  • compact_session_history writes the summary message before complete_compaction, non-atomically. If the second call fails, the summary is durable while its row stays running forever — nothing writes CompactionStatus::Failed and nothing reaps a stale running row. That is what makes the orphan-summary case reachable, and it is the metadata filter in the tail that saves it. Flagged for a follow-up; no production change here.

Verification. Nine mutations of the function, eight killed, every test killing at least one. The survivor is the message.id != summary_message_id half of the tail filter, unreachable because the one site that writes a summary always sets the marker the other half keys off — documented as redundant defence at the test that would otherwise appear to cover it. An earlier draft of the orphan test was killed by the skip rather than by the filter it named; the sweep caught that and the fixture was re-derived.

A source-only adversarial review found five false claims in the first draft's doc comments (including the reachability story above, which was wrong in three separate ways) and one redundant test, since removed. All applied.

1060 → 1068 tests. cargo fmt clean, cargo clippy --all-targets clean, cargo test --lib 1068/0, bindings.ts untouched. No production code changes.

`provider_history_messages_with_compaction` is the point where a
compaction row stops being bookkeeping and becomes a real truncation of
what the provider sees. It had no tests. Its neighbours in the module are
well covered -- the size gates, the window selection, the summary
clamping -- so the untested function was the one that decides what
actually reaches the model.

Eight tests, one per property of the translation:

- the summary leads the view and the history up to the boundary is gone;
- the boundary is inclusive, so the boundary message goes too;
- the summary, which is always appended *after* its own boundary, is
  emitted once from the front rather than left in the tail;
- with no compaction the history passes through, still minus any summary.
  A summary message can outlive every completed row: it is written before
  `complete_compaction` runs, and a `running` row is never returned by
  `latest_completed_compaction` and is never reaped;
- an orphan summary in the retained tail is dropped. Same non-atomic pair,
  one compaction later: the orphan is durable, the previous compaction is
  still the latest completed one, and its older boundary leaves the orphan
  inside the retained tail where only the metadata filter catches it.
  Replaying it would hand the provider two abridgements of the same
  history, the stale one looking current;
- all four unusable rows (either id NULL, either id dangling) fall back to
  the untruncated history rather than half-applying. None of the four is
  reachable today, and that is the point: both callers get their row from
  `latest_completed_compaction`, whose `WHERE` adds `IS NOT NULL` on both
  ids, and `ON DELETE SET NULL` plus the pool's `foreign_keys` means a
  deleted message nulls the column rather than leaving it dangling -- which
  that same filter then excludes. The guards here are one half of an
  invariant whose other half is a `WHERE` clause in another file, so this
  pins the half that lives here;
- the view carries the stored summary text and keeps its metadata marker,
  which the next compaction keys off;
- the cut happens exactly where the row says, even mid tool group: not
  re-checking the group boundary is `select_compaction_window`'s job, and
  this pins that division.

Every test was mutation-checked: nine mutations of the function, eight
killed, each surviving test killing at least one. The survivor is the
`message.id != summary_message_id` half of the tail filter, which no
fixture can reach because the one site that writes a summary message
always sets the metadata marker the other half keys off. That is recorded
as redundant defence at the test that would otherwise appear to cover it,
rather than papered over -- an earlier draft of the orphan test was killed
by the skip instead of by the filter it named, which the sweep caught.

Also folds the module's two hand-rolled summary-message builders onto one
`summary_msg` fixture.

1060 -> 1068 tests. No production code changes.
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