Skip to content

Let notes be about any record, not only an attendee - #1978

Merged
stefan-burke merged 18 commits into
mainfrom
claude/site-tidyup-migrations-status-yl8jsv
Jul 28, 2026
Merged

Let notes be about any record, not only an attendee#1978
stefan-burke merged 18 commits into
mainfrom
claude/site-tidyup-migrations-status-yl8jsv

Conversation

@stefan-burke

@stefan-burke stefan-burke commented Jul 28, 2026

Copy link
Copy Markdown
Member

What changed

Notes used to belong to an attendee and nothing else. Each note now says what
kind of record it is about, and which one. Nothing has changed for the people
using the site: every note that exists today is a note about an attendee, and
it still shows up in the same places. What changed is that the store is now
ready for notes on other kinds of record — a listing, a booking — without
another change to the table.

Why

The importer and the entity pages both want notes on records that are not
attendees. Doing it one record type at a time would mean a new column each
time. Naming the record instead means the store is written once.

How

  • The notes table swaps its attendee_id column for a pair: the kind of record
    and its id.
  • A small new folder, src/shared/db/notes/, holds the notes store: the kinds
    of record a note can be about, how a note's text is sealed and opened, and the
    reads and writes. The part that decides which rows to touch is plain
    data-in, data-out code with no database in it, so it is easy to test on its
    own.
  • Every read and write names the record it is about, so asking for one record's
    notes and asking for many take the same path.
  • A migration adds the new columns, fills them in from attendee_id, and
    rebuilds the table without it. It can be run again safely: each step takes the
    database's own word for whether it is already done, so a run that stopped
    halfway is picked up by the next one.

Testing

  • deno task precommit passes.
  • The new migration and the notes store are both mutation tested at a 100% kill
    rate.
  • The migration has tests for a run that stopped after each step, for the
    rebuilt index, and for a real failure at each statement being raised rather
    than swallowed.

Summary by CodeRabbit

  • New Features

    • Notes can now be associated with different record types, while preserving existing attendee notes.
    • Added secure handling for system and owner notes, including encrypted storage and controlled access.
    • Admin note creation, viewing, grouping, and deletion now use the generalized notes experience.
  • Documentation

    • Updated importer guidance and project tasks to reflect generalized notes and testing requirements.
  • Refactor

    • Improved internal mutation-test file discovery and directory traversal.

claude added 6 commits July 28, 2026 18:46
…ull suite not yet run

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC
The single read had a fallback that no input could reach, only there to
satisfy the compiler. Opening one note is now the shared step a batch is
built from, so there is no unreachable branch to test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC
…moved test not stop a mutation run

The mutation gate demands a test at the mirrored path; the note routes'
suite was under test/integration, so the gate had nothing direct to
mutate against. It drives real pages through the session helpers, which
is what test/features holds.

The walk that finds test files also died on a path that had moved: the
guard for a missing directory was around creating the reader, but the
disk is only reached when the entries are read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 41 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1fcf0fe7-ee57-4002-ba74-847868cb88f1

📥 Commits

Reviewing files that changed from the base of the PR and between 2c5ffa7 and 21e7fd2.

📒 Files selected for processing (1)
  • test/shared/db/notes/queries.test.ts
📝 Walkthrough

Walkthrough

The PR generalizes encrypted system notes from attendee-only storage to typed entity targets, adds the schema migration and deletion path, updates application consumers and tests, and extracts mutation glob expansion into reusable tooling.

Changes

Entity-based notes migration

Layer / File(s) Summary
Generalized note contracts and query APIs
src/shared/db/notes/*, test/shared/db/notes/*
Adds typed note entities and targets, sealing/opening utilities, entity-aware query and deletion helpers, and coverage for targeting, encryption, ordering, grouping, and isolation.
Entity-based schema and deletion migration
src/shared/db/migrations/*, src/shared/db/attendees/delete.ts, test/shared/db/migrations/*
Migrates system_notes from attendee_id to entity_type/entity_id, updates constraints and indexes, registers the migration, and validates reruns, partial recovery, and invalid entity types.
Application and test migration
src/features/*, src/ui/templates/*, test/features/*, test/integration/*, test/ui/*
Updates note creation, retrieval, deletion, grouping, rendering, payment, listing, webhook, servicing, and related tests to use entity-targeted APIs and fields.
Mutation glob tooling and maintenance notes
scripts/mutation/*, scripts/walk-files.ts, TODO.md, docs/importer-plan.md
Extracts glob expansion and directory enumeration into reusable helpers with tests, and documents importer transaction handling and mutation-gate follow-up work.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant AdminPage
  participant NotesQueries
  participant SystemNotes
  participant NoteSealing
  AdminPage->>NotesQueries: request notes for attendee target
  NotesQueries->>SystemNotes: select entity_type and entity_id rows
  NotesQueries->>NoteSealing: open sealed note rows
  NoteSealing-->>AdminPage: return decrypted notes
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: notes now target any record instead of only attendees.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/site-tidyup-migrations-status-yl8jsv
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/site-tidyup-migrations-status-yl8jsv

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc912b906b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/shared/db/migrations/2026-07-28_note_entities.ts Outdated
Comment thread src/shared/db/notes/target.ts Outdated
Comment thread src/shared/db/notes/target.ts
Comment thread src/shared/db/migrations/2026-07-28_note_entities.ts Outdated
The probe that decides which steps are still to do now reads the primary:
a replica can lag behind columns an earlier attempt added, and adding one
twice is an error.

Asking for the notes of no records threw away its own SQL and was reached
only by its test. It now says so loudly, and the one live empty path stays
where it saves a round trip.

The importer plan told a future reader to repoint notes by a column this
change removes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8063d91be7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/mutation.ts Outdated
Comment thread src/shared/db/notes/queries.ts Outdated
Comment thread src/shared/db/notes/target.ts Outdated
claude added 3 commits July 28, 2026 20:26
…s many

The glob expansion moved into its own module so the missing-directory fix
has a direct test; it fails against the old lazy-readDir shape with the
same NotFound the gate hit.

One record's notes are now asked for as a list of one, so filtering and
ordering cannot drift from the list reads. Comments describe the design
as it is rather than what it replaced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC
The catch that classified the error had arms no test could take. Asking
statOrNull first says the same thing in one step, and the base a glob
walks from is always absolute, so its empty-path arm went too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC
The copy that makes a mutation run's snapshot collected directory entries
the same way the glob walk did. Both now ask the shared walk helper, which
also gained the missing-folder answer, and the glob walk stops carrying
its own copy of a recursive walk that already existed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb7beb2b1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/walk-files.ts Outdated
Comment thread src/shared/db/migrations/2026-07-28_note_entities.ts
Comment thread test/scripts/mutation/expand.test.ts Outdated
Comment thread src/shared/db/migrations/registry.ts
Comment thread src/shared/db/notes/target.ts
Comment thread src/shared/db/notes/sealing.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/mutation/isolation-state.ts`:
- Line 143: Update copyDirectory at the directoryEntries call to use a strict
directory-reading helper that throws when fromDir is missing or is not a
directory, rather than returning an empty list. Preserve normal recursive
copying and add a regression test covering a missing source child that verifies
the mutation snapshot operation fails.

In `@scripts/walk-files.ts`:
- Around line 10-18: Update directoryEntries to wrap the statOrNull check and
Deno.readDir iteration in error handling, returning an empty array for expected
missing-path or ENOTDIR failures, including directories removed between calls,
while rethrowing unexpected errors. Add a regression test covering a file path
treated as a directory.

In `@src/features/admin/attendees-edit.ts`:
- Around line 171-174: Update the stale-note cleanup around getNotesFor to
collect IDs for matching system refund-failure notes, then delete them through
the available batch operation or concurrently as independent deletions instead
of awaiting each deletion sequentially. Keep the existing note filtering and
attendeeNotes target, and ensure the refresh path stays within the 40-call limit
when provider calls are included.

In `@src/shared/db/migrations/2026-07-28_note_entities.ts`:
- Line 27: Update src/shared/db/migrations/2026-07-28_note_entities.ts at lines
27-27 so the attendee_id-absent branch calls syncIndexes() before returning. Add
a regression test in test/shared/db/migrations/2026-07-28_note_entities.test.ts
at lines 99-120 that simulates a rebuilt table with unsynchronized indexes,
reruns the migration, and asserts idx_system_notes_entity is restored.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 303d24e1-35bc-404a-bd15-cc16ce4850ce

📥 Commits

Reviewing files that changed from the base of the PR and between a344249 and bb7beb2.

📒 Files selected for processing (54)
  • TODO.md
  • docs/importer-plan.md
  • scripts/mutation.ts
  • scripts/mutation/expand.ts
  • scripts/mutation/isolation-state.ts
  • scripts/walk-files.ts
  • src/features/admin/attendee-notes.ts
  • src/features/admin/attendee-page.ts
  • src/features/admin/attendees-edit.ts
  • src/features/admin/attendees-list.ts
  • src/features/admin/listing-page-data.ts
  • src/features/api/payment-processing/store-refund.ts
  • src/shared/db/attendees/delete.ts
  • src/shared/db/migrations/2026-06-23_system_notes.ts
  • src/shared/db/migrations/2026-07-28_note_entities.ts
  • src/shared/db/migrations/registry.ts
  • src/shared/db/migrations/schema/tables-questions.ts
  • src/shared/db/notes/queries.ts
  • src/shared/db/notes/sealing.ts
  • src/shared/db/notes/target.ts
  • src/shared/db/notes/types.ts
  • src/shared/db/system-notes.ts
  • src/ui/templates/admin/attendee-notes.tsx
  • src/ui/templates/admin/attendees-list.tsx
  • src/ui/templates/admin/listings/types.ts
  • test/features/admin/attendee-notes.test.ts
  • test/integration/migration-restore-verify.test.ts
  • test/integration/migration-round-trip-budget.test.ts
  • test/integration/server/attendee-form-status.test.ts
  • test/integration/server/attendees-list.test.ts
  • test/integration/server/attendees/placeholder-refresh.test.ts
  • test/integration/server/listings/show-actions-and-activity.test.ts
  • test/integration/server/payments-success-refunds.test.ts
  • test/integration/server/payments/replay.test.ts
  • test/integration/server/webhooks/multi-ticket-refunds.test.ts
  • test/integration/server/webhooks/price-signature-stored-refund-and-ignore.test.ts
  • test/integration/servicing/purge-edge-cases.test.ts
  • test/integration/webhook-price-signature/helpers.ts
  • test/scripts/mutation/expand.test.ts
  • test/shared/db/attendees/delete.test.ts
  • test/shared/db/migrations/2026-07-28_note_entities.test.ts
  • test/shared/db/migrations/registry.test.ts
  • test/shared/db/migrations/schema/tables-questions.test.ts
  • test/shared/db/migrations/schema/version/guard.test.ts
  • test/shared/db/notes/queries.test.ts
  • test/shared/db/notes/sealing.test.ts
  • test/shared/db/notes/target.test.ts
  • test/shared/db/orphan-attendees.test.ts
  • test/specs/steps/payment-capacity.ts
  • test/test-utils/reservation/helpers.ts
  • test/test-utils/webhooks.ts
  • test/ui/templates/admin/attendee-notes.test.tsx
  • test/ui/templates/admin/attendee-page.test.ts
  • test/ui/templates/admin/attendees-list.test.ts
💤 Files with no reviewable changes (1)
  • src/shared/db/system-notes.ts

Comment thread scripts/mutation/isolation-state.ts
Comment thread scripts/walk-files.ts
Comment thread src/features/admin/attendees-edit.ts Outdated
Comment thread src/shared/db/migrations/2026-07-28_note_entities.ts Outdated
… deletes

The shared walk is strict again — a caller naming a root that is not
there is asking about something it believes in, and a snapshot copy that
answered nothing would report success on an empty copy. Only the glob
expansion tolerates a path that has moved, which is the case it is for.

A run that died right after the table rebuild left no index behind, and
the next run returned early without making one. It now asks for the
indexes on that path too.

Adding a column is taken as done when the column is already there: a
primary read can still briefly answer with the schema as it was.

Stale notes go in one batch, and the version label names this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC
@stefan-burke
stefan-burke enabled auto-merge July 28, 2026 21:14
@stefan-burke
stefan-burke disabled auto-merge July 28, 2026 21:14
A column already there is taken as done; anything else means the
database could not do what was asked. Only the tolerated case had a
test, so the arm that lets a real failure through was never run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f459d62b9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/shared/db/migrations/2026-07-28_note_entities.ts Outdated
claude added 4 commits July 28, 2026 21:22
I argued on review that a stale read could only send the migration
backwards into work every step tolerated. That was wrong about one step:
if the rebuild had finished, moving the notes across failed on an
attendee_id that had already gone.

Each step now asks the database and believes its answer — a column
already there, an attendee_id already gone — through one shared helper.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC
… nothing

Mutation testing showed both: blanking either "already so" message still
passed, because one test failed every statement and the other message
threw regardless; and the index sync after the rebuild could be removed
with nothing noticing, because the rebuild takes the table's indexes from
SCHEMA as it goes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdDZCWRXwjqsidfRJFarXC
Lines moved in test-browser.ts and attendee-page.ts, so the recorded
positions no longer named the same mutants.
@stefan-burke stefan-burke changed the title Let a note be about any record, not only an attendee Let notes be about any record, not only an attendee Jul 28, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 25e7844584

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/shared/db/notes/queries.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/shared/db/migrations/2026-07-28_note_entities.test.ts`:
- Around line 180-203: Update the finishedDatabase mock used by
noteEntities().up() so its execute method returns a stale schema-read result for
the initial pragma_table_info query instead of rejecting it. Keep the table
initialized in the migrated state, and restrict the simulated “already done”
errors to the ALTER and UPDATE statements so the migration reaches and tolerates
those mutation errors.

In `@test/shared/db/notes/queries.test.ts`:
- Around line 227-235: Update the empty-list test around deleteNotes to
instrument the database executor before calling
deleteNotes(attendeeNotes(owner), []), then assert the executor call count is
unchanged afterward. Keep the existing row-preservation assertion, and verify
the zero-query contract directly rather than inferring it from note survival.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 024c186a-64e6-4500-8fbe-4f0f2a7172c8

📥 Commits

Reviewing files that changed from the base of the PR and between bb7beb2 and 2c5ffa7.

📒 Files selected for processing (15)
  • TODO.md
  • docs/importer-plan.md
  • scripts/mutation/equivalent-mutants.txt
  • scripts/mutation/expand.ts
  • scripts/walk-files.ts
  • src/features/admin/attendee-notes.ts
  • src/features/admin/attendees-edit.ts
  • src/shared/db/migrations/2026-07-28_note_entities.ts
  • src/shared/db/migrations/schema/version.ts
  • src/shared/db/notes/queries.ts
  • src/shared/db/notes/sealing.ts
  • test/scripts/mutation/expand.test.ts
  • test/shared/db/migrations/2026-07-28_note_entities.test.ts
  • test/shared/db/migrations/schema/version/guard.test.ts
  • test/shared/db/notes/queries.test.ts

Comment thread test/shared/db/migrations/2026-07-28_note_entities.test.ts
Comment thread test/shared/db/notes/queries.test.ts
The test's name promised a zero round-trip contract but only checked the
note survived, so an empty batch would have passed it.
@stefan-burke
stefan-burke added this pull request to the merge queue Jul 28, 2026
Merged via the queue into main with commit 3850a54 Jul 28, 2026
3 checks passed
@stefan-burke
stefan-burke deleted the claude/site-tidyup-migrations-status-yl8jsv branch July 28, 2026 22:56
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