Skip to content

A notification is written once per occurrence: a unique index behind the dedupe reads, both writers write around a refusal, and the medication key names its day - #956

Closed
NewWorldVenture wants to merge 3 commits into
claude/a-renewed-renewal-is-reminded-again-20261003from
claude/a-notification-is-written-once-per-occurrence-20261004
Closed

NewWorldVenture wants to merge 3 commits into
claude/a-renewed-renewal-is-reminded-again-20261003from
claude/a-notification-is-written-once-per-occurrence-20261004

Conversation

@NewWorldVenture

@NewWorldVenture NewWorldVenture commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #936 (claude/a-renewed-renewal-is-reminded-again-20261003); the diff against that branch is this unit alone. Slot 0489 reserved on #699 (5979948710), filename announced on #771 (5980002347). Repository candidate only — the migration is not applied anywhere by this PR.

What was wrong

Both writers of notifications decide what to write with a read and then insert. The engine (generateFamilyNotifications) reads the keys it is about to announce and skips the ones already there; notify() reads the unread copies of its key. Nothing in the database stood behind either read, and two of the engine's three callers run for one family at the same minute — the daily notifications cron and the two-hourly push-scan are each mirrored by Vercel and the GitHub dispatcher. Two runs could both read "not yet announced" and both write: one duplicate per occurrence per concurrent pair, for every candidate type the engine writes. (Audit note of 2026-10-04 07:34 UTC on #936; #936 scoped it out as its own unit — this is that unit.)

What this does

0489 — one unread row per occurrence. A partial unique index on public.notifications (family_id, type, related_id, user_id) nulls not distinct where related_id is not null and is_read = false.

  • related_id is the dedupe key (text since 0293); user_id is NULL for the family-wide row, and two family-wide copies of one occurrence are exactly the duplicate this is about, so NULLs are equal (NULLS NOT DISTINCT, Postgres 15; CI replays on 16).
  • Unread rows only, because notify() dedupes against unread copies by default and a later notice under a read key is legitimate (once: true widens that to ever). A concurrent duplicate is always unread at the instant it is written, so the predicate still refuses exactly the race. Keyless rows dedupe by title and are outside it.
  • The repair deletes nothing. The index cannot be built over duplicates, so of each group of unread rows sharing a key — same family, type, key and recipient, whatever their title or body — the oldest (created_at, then id) stays unread and the others are marked read. Every row is kept with its text; the unread badge counts one notice per occurrence; the row a member keeps unread is the first one written, and it is that row's text they see. The file header carries the read-only pre-flight query an operator records before applying, and the statement is in its own section for separate review.
  • The one writer inside the database steps aside, and it is replaced first. 0191's price-drop trigger writes a family-wide notice keyed by the listing to every family that saved it; a second drop while that notice is unread would now be refused inside a trigger, failing the seller's price change. The function is re-created with on conflict do nothing on that insert, otherwise 0191's statement for statement, and it comes before the index in the file so no state the file can leave has the index without the clause. (grep -il 'into public.notifications' supabase/migrations names 0191 and this file; 0301 only quotes a statement.)
  • What changes for a watcher. 0191 wrote a notice for every drop. Under the index a watcher who has not read the first drop's notice is not written a second: they keep one unread notice, and it carries the first drop's prices until they read it; the next drop after that is announced with its own prices. The price history is unchanged (every change logged, drop, rise or on a completed listing); a rise and a drop on a listing that is no longer available notify nobody, as in 0191. The header documents this as the cadence and payload 0489 trades for the index.
  • Not atomic, and ordered so it need not be. The replay (docs/audit/pg-bootstrap.sh) and the repair rehearsal run each file with psql -v ON_ERROR_STOP=1 -f, without --single-transaction, and the file opens no transaction of its own, so its statements commit one at a time. The header says so and says what each prefix leaves: after the function alone the clause is inert; after function and repair the duplicates are read and the race remains until the file is run again; the verification block fails the file if the index is missing or an unread duplicate remains; a re-run completes it and then changes nothing (LB-016 §4). Nothing about how any environment executes migrations is changed.

Both writers write around a refusal. Postgres refuses the whole statement for one duplicate row, and a batch of a hundred candidates is one occurrence another run wrote a moment ago, not a hundred duplicates. lib/notifications/insert-once.ts inserts the batch; on 23505 it writes the rows one at a time and leaves out only the rows the index refuses. The engine logs the refused count and returns what landed; notify() counts them as duplicates. The engine keeps its one batch insert on the ordinary path.

The medication key names its day. medication_due was the one key that could not live under the index: the bare medication id, recurring daily, with a separate same-day dedupe read of its own. It is now <medication>:<dose day> (the day dosesForDay evaluates, in the family's zone) and dedupes like every other candidate, first in the list so a household past the cap still hears about its doses. The bare id is carried as the legacy key from the start of that day — #936's bridge — so the first run with the dated key does not remind a family that was reminded this morning under the old one, and yesterday's bare-key row does not count.

The direct writers were each read. Guardian SMS (stable id, exact read-back), contact-center urgent (upsert by a stable id), briefing and approvals (dedupe read then notify()) are unaffected or now covered. The two Guardian route inserts (screen, escalate: family-wide system rows keyed by the communication) would be refused only for a second unread copy of the same communication; both already log a failed insert without failing the call.

Proof

  • tests/a-notification-is-written-once-per-occurrence.test.ts (13 cases): the race made deterministic — a proxy lets the other run's row land the instant this run's dedupe read resolves. The loser writes nothing and says so; the rest of its batch lands around the taken occurrence; the control without the index writes the duplicate; notify() counts the landed copy as a duplicate and, once that copy is read, a later notice lands; a medication is reminded today once and tomorrow again, honouring this morning's bare-key row and not yesterday's; the helper's statement/row-by-row behaviour, nulls-not-distinct, read rows and keyless rows; and pins on the migration (index, predicate, non-destructive repair, no delete, the trigger's on conflict do nothing), the probe and both writers.
  • docs/audit/a-notification-is-written-once-per-occurrence-check.sql runs in CI's Database job on the replayed schema, one rolled-back transaction, negative control first: the index by definition; the second unread copy refused with 23505 naming the index (GET STACKED DIAGNOSTICS); the family-wide pair likewise; a read first copy lets a second land; other member, other day, other type each land; keyless rows twice. Then the trigger change by change, with the notice bodies read back: a listing saved by another family drops $120→$100→$90 while the first notice is unread — both updates go through, one notice stands and it still reads $120.00 → $100.00; after the watcher reads it, $90→$80 is announced as $90.00 → $80.00; with every notice read, so the index cannot mask the rule, a rise and a drop on a completed listing notify nobody; five changes, five history rows. A mutant control then replaces the trigger function inside the transaction with 0191's body minus the "price went down" condition: a rise now writes a notice, so the rise assertion is live. Finally the repair is exercised on seeded duplicates by re-applying 0489 itself (\ir, same transaction): the index is dropped, groups with distinct titles and bodies are seeded for a member, for the family-wide row and with a created_at tie, plus a row already read and a row under another key; after the file runs every row is still there with its text, the oldest by (created_at, id) of each group is unread and the rest are read, the two untouched rows are untouched, the index is back and the function is 0489's; applied once more, nothing changes.
  • Four mutants of the migration were run here against that probe and each fails at the line that names it: the rise condition removed (a price rise notified the watcher (found 3 rows)), the survivor order reversed (wrong survivors after the repair), the repair removed (could not create unique index at the file's own index statement), the repair as a delete (the repair should keep all 9 rows as written; 5 match). The unmodified file passes and leaves no row behind.
  • The in-memory fake learned partial and nulls-not-distinct unique indexes (UniqueSpec) and now inserts a batch atomically, as a Postgres statement does; tests/medication-reminders.test.ts pins the dated key and its legacy bridge; tests/migration-version-safety.test.ts moves my own entry to 0490.

Composition

Not in this PR

The direct Guardian route writers are not routed through notify(); nothing is applied to any environment; the execution hold on the repair stands.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KP9rt5rVQ9jDMpZp2xBy3K


Generated by Claude Code

Both writers of notifications — the engine and notify() — decide what to
write with a read and then insert, and two of the engine's callers run for
one family at the same minute (the daily cron and the two-hourly push scan
are each mirrored by Vercel and the GitHub dispatcher). Two runs could both
read "not yet announced" and both write: one duplicate per occurrence per
concurrent pair, for every candidate type (audit note of 2026-10-04 07:34 UTC
on #936, scoped out there as its own unit).

0489 puts a partial unique index behind the read: (family_id, type,
related_id, user_id) nulls not distinct, over unread rows with a key. Unread
only, because notify() dedupes against unread copies and a later notice
under a read key is legitimate; nulls not distinct, because the family-wide
row is one occurrence too. Existing unread duplicates are repaired by
marking the later copies read — nothing is deleted. 0191's price-drop
trigger, the one writer inside the database, is re-created with
`on conflict do nothing` so a second drop while the first notice is unread
does not fail the seller's price change.

Both writers insert through lib/notifications/insert-once.ts: one batch, and
on 23505 one row at a time, leaving out only the rows the index refuses.
The engine logs the refused count; notify() counts them as duplicates.

The medication reminder key names its dose day (`<medication>:<day>`) and
dedupes like every other candidate — it was the bare id with a separate
same-day read, the one shape the index could not hold — with the bare id
carried as its legacy key from the start of the day, so the first run with
the dated key does not remind a family twice.

Tests: the race made deterministic through a proxy that lands the other
run's row as the dedupe read resolves; the control without the index writes
the duplicate; notify() under the same index; the medication key and its
legacy bridge; the helper; migration, probe and writer pins. The in-memory
fake learned partial and nulls-not-distinct unique indexes and inserts a
batch atomically. A Database-job probe asserts the index by name on the
replayed schema, including the price-drop trigger stepping aside. Verified
on a replayed database: 0489 applies and re-applies; the probes pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KP9rt5rVQ9jDMpZp2xBy3K
@vercel

vercel Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
bubaly Ignored Ignored Oct 4, 2026 2:20pm UTC

Request Review

Copy link
Copy Markdown
Owner Author

Scope reconciliation for receipt #6995980364390: the new marketplace_log_price_change() replacement was outside the previously announced notification-writer file claim. Before further expansion, identify that function's current owner/dependent PRs and explain the minimal compatibility change and regression proving existing marketplace behavior is preserved. Keep it separately reviewable; do not assume the 0489 number reservation accepts this added behavior.

Changing the proposed cleanup from deleting records to marking them read preserves rows, but still changes users' unread state. It remains a source-only proposal: do not execute it on existing datasets or production. Validate only on disposable synthetic fixtures, document which notices change state and why, and retain the separate execution hold.

The #896 shared notifications.ts composition dependency is still unresolved; using #936 as a baseline does not establish compatibility with #896. Confirm one composition owner and exact dependent heads before resolving shared-file changes. No merge/deploy or production application requested.

…the header says what changes for a member

The price-drop trigger is re-created in 0489 with one clause added; the
probe now asserts what it still does — every change logged, a watcher who
read the notice told of the next drop, a rise telling nobody — beside the
case the clause exists for. The header states exactly which rows the
repair moves from unread to read and why the trigger change cannot be a
file of its own (scope reconciliation 5980402705 on #956).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KP9rt5rVQ9jDMpZp2xBy3K

Copy link
Copy Markdown
Owner Author

Answering the scope reconciliation of 2026-10-04 13:21 UTC (5980402705), at a61d4c5.

Owner and dependents of marketplace_log_price_change(). The function is defined once, in supabase/migrations/0191_marketplace_price_history.sql, and nowhere else in the repository (grep -rn marketplace_log_price_change outside 0191 is empty). A scan of all 93 open PRs' complete file lists at 13:30 UTC finds no PR touching 0191 or patching that function; the only file that names it is 0489 on this PR. No lane owns it besides main.

The minimal change, exactly. The function body in 0489 is 0191's statement for statement; diff -w against 0191 shows one added clause — on conflict do nothing; on the notification insert — and a three-line comment. Nothing else in the trigger changes: the auction guard, the price-history insert, the drop/available/pending condition, the title, the body, the watcher query.

Why it is in 0489 and not a file of its own. The two cannot be applied apart: with the index and without the clause, a second price drop on a saved listing while the first notice is unread raises 23505 inside the trigger and fails the seller's UPDATE; with the clause and without the index the clause is inert. They are one change to one invariant and must land in one transaction, so the same file carries both. I have asked on #699 (below) for the 0489 reservation to be amended to say so rather than assume it.

Regression proving 0191's behaviour is preserved (docs/audit/a-notification-is-written-once-per-occurrence-check.sql, case 7, run on the replayed schema in CI's Database job and here): a saved listing's first drop notifies the watcher (control); a second drop while that notice is unread goes through and the watcher keeps one notice (the case the clause exists for); both changes are logged in marketplace_price_history as before; a watcher who has READ the notice is told of the next drop (the index covers unread rows only, so the trigger's insert lands again); a rise notifies nobody (0191's own rule). Each is a raise exception on failure.

The repair and a member's unread state. Documented in the file header: only rows that are exact unread copies of an older unread row — same family, type, key, recipient — move from unread to read; the member's unread count drops by the number of such copies and by nothing else; the copy kept unread is the first one written; no title, body, time or recipient changes; nothing is removed. The pre-flight query lists every such group before anything runs. The execution hold stands: the file is a repository candidate, validated only on a disposable replayed database with synthetic fixtures, and is not to be run against any existing dataset until that listing has been reviewed and the hold on #699 is lifted.

#896. It is a separate Codex branch (meal-planner request ids, 100 files) that touches lib/server/notifications.ts incidentally; this PR's change to that file is confined to the dedupe-and-insert region (the medication candidates join the generic read; one batch insert goes through insertNotificationRows). Whichever lands second composes at the merge; if the owner sequences #896 first I will rebase this unit onto it and report the head.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Bounded review at a61d4c5 permits the narrow source/test amendment: the marketplace trigger replacement preserves0191 logic except ON CONFLICT DO NOTHING and its comment. Execution and #896 composition holds remain. This is not blanket acceptance of marketplace behavior or the cleanup.

Before acceptance, correct these concrete test/claim gaps:

  1. The rise-guard probe is masked by an existing unread drop notice; the unique index could suppress an erroneous rise notification. Mark that notice read in the disposable fixture before the rise, assert no new row, and prove a removed-rise-guard mutant fails. Add the claimed sold-listing case and check price history after all tested changes.
  2. Same-file placement does not prove atomic application:0489 has no top-level transaction and the cited runner uses psql -f without --single-transaction. Either validate an appropriate all-or-nothing source/test path or remove the atomicity claim; do not alter production execution settings.
  3. Repeated genuine price drops sharing the key now retain the first unread notice/old price text. Document this material cadence/payload difference; do not call all same-key rows exact copies or full behavior unchanged. Add distinct-payload same-key coverage and a synthetic preexisting-row cleanup case demonstrating row retention/survivor selection.
  4. Name one Keep meal planner request IDs stable across retries #896 composition owner and exact baseline before shared-file integration; 'whoever lands second' is not an agreed handoff.

All validation remains disposable synthetic/local/CI only. No existing notification state changes, live backfill, production application, merge or deploy.

…n on seeded rows, and the header says how the file applies

Answers the bounded review of 2026-10-04 13:36 UTC on #956.

The probe's rise assertion was masked: an unread notice stood, so the index
would have refused a rise notice whether or not 0191's "price went down" rule
was there. Case 7 now reads every notice before the rise and before a drop on a
completed listing, pins which body survives a second drop (the first drop's
prices, the payload difference 0489 introduces) and which body the next drop
after a read carries, and counts the price history after all five changes.
Case 8 replaces the trigger, inside the rolled-back transaction, with 0191's
body minus the rise rule and shows a rise then does notify: the assertion in 7
is the rule's doing, not the index's. Case 9 drops the index, seeds duplicate
groups with distinct titles and bodies (a member's, the family-wide row's, and
a created_at tie) plus a row already read and a row under another key, then
re-applies 0489 itself with \ir, twice: every row survives, the oldest by
(created_at, id) stays unread, the rest are read, the index is back, the
function is 0489's, and the second application changes nothing. Four mutants
of the file fail at the line that names them (rise rule removed, survivor
order reversed, repair removed, repair as a delete).

The header no longer implies the file is atomic. The replay and the repair
rehearsal run psql -f per file without --single-transaction and the file
opens no transaction, so it says that, and says why every prefix is a safe
state and why the function is replaced before the index is created. The
member paragraph says "rows that share a key", not "exact copies", and a new
watcher paragraph documents the notice cadence and payload that change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KP9rt5rVQ9jDMpZp2xBy3K

Copy link
Copy Markdown
Owner Author

Answering the bounded review of 2026-10-04 13:36 UTC (5980558502), at 876c044. Everything below ran on a disposable replayed database here and runs in CI's Database job; nothing was applied to any existing dataset.

1. The rise rule was masked; it is now asserted with nothing in the way, and proven live. Case 7 of docs/audit/a-notification-is-written-once-per-occurrence-check.sql reads every notice of the watcher before the rise (7d) and before the drop on a completed listing (7e), and fails itself if any notice is unread at that point, so a row written there could not have been refused by the index. The sold-listing case is 7e: status = 'completed' (0120's terminal state), a drop, no notice. The price history is counted after all five changes (7f: five rows). The mutant you asked for is case 8, inside the probe's rolled-back transaction: 0191's body with the "price went down" condition removed is installed, a rise under it writes a notice (three rows where 7d found two), so 7d's assertion is the rule's doing. The same mutant applied as a change to the migration file itself fails the probe at 7d: 0489 FAIL: a price rise notified the watcher (found 3 rows).

2. The atomicity claim is withdrawn and replaced with what is true. Confirmed: docs/audit/pg-bootstrap.sh and rehearse-ledger-repair.sh run each file with psql -v ON_ERROR_STOP=1 -q -f, no --single-transaction, and 0489 opens no transaction, so its statements commit one at a time. The header now says so under "HOW THIS FILE IS APPLIED, AND WHAT A PARTIAL APPLICATION LEAVES", and says why each prefix is a safe state: the function is replaced before the index is created, so no state the file can leave has the index without the clause; after function and repair without the index the duplicates are read and the race remains until the file is run again; the verification block fails the file (and stops the replay) if the index is missing or an unread duplicate remains; a re-run completes it. The re-run is no longer asserted from the LB-016 §4 rehearsal alone: the probe re-applies the file itself with \ir, twice, inside its transaction (case 9), and asserts the second application changes nothing. No execution setting of any environment is touched. I did not wrap the file in begin/commit as six older migrations do: the probe's in-transaction re-application would then commit the probe's own fixtures, and it would be a change to how the file executes rather than to what it does.

3. The payload difference is documented and pinned, and the repair is proven on seeded rows. The header's member paragraph now says "rows that share their key", not "exact copies", and a new paragraph, "WHAT CHANGES FOR A WATCHER", states the cadence and payload 0489 trades for the index: a watcher who has not read the first drop's notice is not written a second; their one unread notice keeps the first drop's prices until read; the next drop after that is announced with its own prices; the history is complete either way. The probe pins it: after $120→$100→$90 with the first notice unread, one notice stands and its body is $120.00 → $100.00 … (7b); after it is read, $90→$80 lands as $90.00 → $80.00 … (7c). The pre-existing-row case is case 9: with the index dropped, nine rows are seeded — one member's key with three unread rows of different titles and bodies plus a row already read under that key plus an unread row under another key; the family-wide (NULL recipient) key with two unread rows of different bodies; another member's key with two unread rows at the same created_at. 0489 is then applied over them. All nine rows survive with their text; the survivors left unread are the oldest by (created_at, id) of each group and the two untouched rows; the rest are read; the index is back and the function is 0489's. Mutants of the file fail at that case: survivor order reversed → wrong survivors after the repair (id=is_read): a1=true,a3=false,…; repair removed → the file's own create unique index fails on the duplicates; repair as a delete → the repair should keep all 9 rows as written; 5 match.

4. Composition: one owner, exact heads, measured not assumed. #896 (codex/meal-stable-retry-ui-20261003, 5de30a0) does not change lib/server/notifications.ts: its diff against its own base (codex/meal-atomic-writes-20261003) and against its merge-base with main (db97e57) both leave the file untouched; it appears in #896's file list only because that base branch is stale. git merge-tree --write-tree of this head (876c044) with #896's head is clean, with main (bdaec65) is clean, and with the meals composition head #897 (b1b5072) conflicts on one file, tests/migration-version-safety.test.ts (0478 vs 0490 pins). My proposal, for you to accept or redirect: this PR is the composition owner for notifications.ts (it is the only open PR that changes the dedupe-and-insert region), its exact dependents are #936 at ffc6386 (base) → this PR at 876c044, and #897 composes with it by a one-line pin resolution. I will not integrate into any shared file until you confirm the owner and the heads.

The PR body carries the same corrections. The execution hold, the #896 composition hold and the 0489 reservation question on #699 all stand as you stated them.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Audit coordination: confirm #956 as the single writer for this bounded notification composition at 876c044, based on #936 ffc6386. Independent source review confirms the requested probe/comment corrections. Test execution and mutant-run outcomes remain separate author/CI evidence.

Two remaining composition requirements:

  1. Correct the Keep meal planner request IDs stable across retries #896 wording: its notifications.ts equals the common ancestor with A notification is written once per occurrence: a unique index behind the dedupe reads, both writers write around a refusal, and the medication key names its day #956/main and adds no competing post-ancestor change. It does have a published diff against its stale base; those inherited changes are already shared through ancestry.
  2. Preserve both branches' edits in tests/helpers/in-memory-supabase.ts as well as every required migration-version pin when composing with Compose meal atomic writes with audit integration #897 b1b5072. The helper changes occupy different regions. Provide the exact composed head, focused test results and fresh CI receipt; whole-repository conflict count has not been independently reproduced.

This ownership confirmation is limited to the verified heads and notification unit, not every unpublished branch or other owners' files. Keep the existing-dataset cleanup, production application, merge and deployment holds. The transactional synthetic probe does not establish interrupted autocommit-prefix recovery. No second writer or finalaudit.md edit is assigned.

Copy link
Copy Markdown
Owner Author

Answering 5981037540 (14:26 UTC).

1. #896 wording, corrected. lib/server/notifications.ts on #896 (5de30a0) equals the common ancestor it shares with this PR and with main, and #896 adds no competing post-ancestor change to it. Its published diff against its stale base does list the file; those are changes inherited through ancestry, already shared. The PR body now says exactly that in place of my earlier phrasing.

2. The composed head, with both branches' edits kept. #959 (claude/notifications-on-meals-composition-20261004, head c74f552, base #897's codex/final-meal-composition-20261003 at b1b5072) is this PR's head 876c044 (on #936 ffc6386) merged with #897's head b1b5072. It is an evidence draft, not for merge, and will be deleted once #897 and this PR have merged in the order you choose.

  • One conflict, tests/migration-version-safety.test.ts: the composed tree carries 0478 and 0489, so nextVersion is 0490 and the comment names both; every other pin in that file is unchanged from the two parents.
  • tests/helpers/in-memory-supabase.ts merged without a conflict marker: this unit's UniqueSpec partial / nulls-not-distinct indexes and atomic batch insert, and Compose meal atomic writes with audit integration #897's edits, sit in different regions and both are present in c74f552 (git show c74f552f3:tests/helpers/in-memory-supabase.ts against each parent shows only the other parent's additions).
  • Focused results on c74f552: the 18 test files either parent changed against main plus the three probe-rule tests — 21 files, 352 tests, all passing; tsc --noEmit clean. On a disposable database that already carried 0489, 0478 applied cleanly and the 0489 probe and the two meal probes (meal-ballot-is-per-option, meal-plan-grocery-boundary) pass.
  • Fresh CI receipt: the checks on c74f552 run on Composition evidence: #956 (once-per-occurrence notifications) onto #897 (meal atomic writes) #959 (Typecheck · Lint · Test · Build, Database replay + probes, E2E, Mobile). I will quote the conclusions here when they land rather than assert them now.

The whole-repository conflict count you could not reproduce was git merge-tree --write-tree 876c044f4 b1b5072b8: one conflicting path, the pin file above; the actual merge confirms it.

Holds unchanged: existing-dataset cleanup, production application, merge and deployment. Noted that the transactional in-probe re-application does not establish interrupted autocommit-prefix recovery; the header claims only that each prefix is a safe state and that a re-run completes the file, not that an interruption was rehearsed.


Generated by Claude Code

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.

2 participants