Skip to content

fix(admin): keep the lookup's prefill through a repeat lookup and lay out the NSFW row by its column (SONA-220) - #440

Merged
sparkyfen merged 18 commits into
mainfrom
sparky/sona-220-lookup-prefill-tests
Sep 16, 2026
Merged

sparkyfen merged 18 commits into
mainfrom
sparky/sona-220-lookup-prefill-tests

Conversation

@sparkyfen

@sparkyfen sparkyfen commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #435 and #436. When SONA-156 and SONA-220 met on the upload and edit forms, a repeat artist lookup blanked the source post URL the moment it started, and the tag suggestion control answered that change by dropping its chips. The two rating pills also shared one row in the edit form's 600px column and pushed the "Mark it NSFW" button onto a second line.

Overview: the upload and edit forms, the lookup panel, and the one status-to-sentence mapping both forms and the announcer read

Repeat lookup flow: a second lookup keeps the filled source and date until its own result lands, then keeps, replaces, or clears them

What changes

On both forms the source post URL and commissioned date that a lookup filled now stay in place until the next result lands. A result that names the same post leaves them alone. A different post replaces them. A result with nothing to put there clears them. Whatever changes, the panel's status line says so, and the upload form's live region says it only when no panel is on screen to carry it. The panel and the announcer read one mapping from status to sentence, so they cannot disagree about why a field changed.

On the upload form, a parent move onto a tile whose lookup is still out keeps a record of what the move emptied until that result arrives, and drops the record when the request settles in any mode or is cancelled. A field the operator types into is never described as cleared, and the panel never attributes their own deletion to Sona.

The NSFW row on both forms is laid out by the width of its own column rather than the window. Below 560px of column it stacks with a 24px indent. Above that, the two rating pills shrink and wrap so the button stays beside the checkbox. The rating pill has a fixed 12px radius, so a wrapped pill keeps its end caps clear of the text.

Copy

Five new status sentences report that a field the last lookup filled was cleared, and a clash that clears a filled URL gets its own sentence. The three parent-move announcements say "the last lookup filled", the phrasing the status lines already use. English and Japanese carry every key.

Verified

  • A 45-row truth table pins every field state per field for both forms' wordings. Each row also checks that the sentence claims nothing the screen contradicts.
  • Playwright covers same-post, different-post, no-post, no-match and clash repeat lookups on both forms; typing during an in-flight search; the four-lookup sequences; parent moves onto idle, searching and failed tiles; the group-mode round trip; and the NSFW row at 320, 900, 1024 and 1280.
  • Fourteen ship-loop rounds. The last two found no defect.

After merge

There is nothing to run: no migration and no setting.

…, and pin the merged SONA-156 and SONA-220 rows (SONA-220)

On the upload form a second artist lookup blanked the source post URL the
first one had written before the new result was in, and the tag suggestion
control answered that change by dropping its chips and rating. The fields now
wait for the result: a lookup that lands on the same post leaves the URL
untouched, a different post replaces it, and no post clears it. The panel's
own state (applied artist, extra parents) still resets when the search
starts. A parent change keeps the full reset.

The lookup's rating pill wraps at every width on both forms, and shares the
wrapped indent with the suggestion note in the NSFW row.

Tests pin the joined aria-describedby on the source field, the three re-seed
lines in the edit page's resetForImage, the parent-move reset, the clash
sentence after a lookup-written URL, and both directions of the "set that
lookup aside" line. The e2e watch-ignores test now knows the two SONA-220
persist roots.
…d fields, and key the NSFW row to its column (SONA-220)

The edit page now keeps the lookup-written source post URL and commissioned
date until the new result lands, the same way the upload form does, so a
repeat lookup no longer drops the tag-suggestion chips. When a result empties
a field the previous lookup filled, the panel's status line says so instead of
claiming the field was left alone.

The NSFW row on both forms is laid out by the width of the form's column, not
the window: both forms are a container, the row stacks with the 24px indent
below 560px of column, and above it the two rating pills take a zero flex
basis with a 14ch floor so the button stays beside the checkbox while the
pills wrap their text. The pill radius is a fixed 12px so a wrapped pill keeps
its end caps clear of the text.

Tests cover the edit page's same-post, different-post and no-post repeat
lookups, the emptied-field status kinds, the date-clearing branch on the
upload form, and the row at 320, 900 and 1280.
…he NSFW row's rules in one place (SONA-220)

A second lookup that finds nothing now empties the URL and date the previous
lookup filled, drops their tags, and says so in the panel's no-match state.
The status sentences for an emptied field say "cleared", the same verb as
the panel's announcement, and no longer claim the result had no post when the
post exists but belongs to another piece; a clash that empties the URL gets
its own sentence. The Japanese strings use 検索 like the rest of the file.
The searching announcement names the new artist details it clears, and a
parent change on the upload form announces which shared fields it emptied.

The NSFW row's container queries and the rating pill's rule live once in
app.css, the two ranges abut, the wide row uses a 4px gap so all four items
fit the 600px column, and the narrow row stacks so the 24px indent only
lands on an item on its own line. The page-scoped copies are gone.
…he emptied copy (SONA-220)

When the parent tile changes and the new parent's result refills only one of
the two shared fields, the panel now says the other was cleared and the live
region announces it, instead of claiming it was left as it was. The Japanese
clash sentence brackets the title like its siblings, the parent-change
announcements say "parent image", and the date sentence on the no-match state
blames the lookup rather than a result that does not exist.

The panel derives its emptied-only state from the sentence it renders, the
tile pill keeps overflow-wrap: anywhere inside its narrow tile, the button in
the wide NSFW row sits 4px off the rating items, and the cleared sentence
uses the foreground colour so it reads as a report rather than a hint.
…anel cannot report it (SONA-220)

On the upload form a parent change used to speak its clearing per field and
then have the refill announcement overwrite it in the same tick, so the
operator never heard which field went blank. The move now returns what it
wrote and what it cleared, applyShared is the single writer of the cleared
record, and each caller says exactly one sentence: the return to a new set
borrows the panel's own filled-and-cleared line for the mixed case, and the
plain parent change stays silent unless the new parent has no lookup to
report through. The panel's failed state shows the cleared sentence above
the retry advice, and every sentence that reports a field going blank uses
the foreground colour.

The date sentence gains its next step in both languages, and the markup
tests pin the single writer and the single announcement rather than the
literal shape of the code.
… panel and the announcer (SONA-220)

The panel and the upload form's live region now read the same function to
turn a status kind into a sentence, so a parent move can never announce a
reason the panel contradicts. A dated clash over a URL the last lookup
filled gets its own sentence that names both halves, a move onto a tile whose
lookup is still running keeps what it emptied until that result lands, and
a thrown result resets the record.

Three status sentences gain their next step, the cleared sentence on the
failed state sits clear of the failure lead, and the announcement chain is
one helper used by both callers. Tests cover the spoken sentence for a move
onto a tile with no lookup, one mid-search, one that then fails, removing
the parent tile itself, and the emptied styling on both clash sentences.
…n in-flight lookup (SONA-220)

When a parent move lands on a tile whose lookup is still running, the record
of what the move emptied now drops any field the operator types into before
the result arrives, and cancelling that lookup drops the record entirely. The
new clash sentence names the post Sona just found rather than a pronoun that
pointed at the wrong noun, the Japanese for a link-less match says 検索結果,
the set of status kinds that name no site is one predicate shared by the
mapping and the panel, and the tests pin the appended next-step clause and
the adjacency the 18px rule depends on.
…d for (SONA-220)

When a parent move lands on a tile whose lookup is still out, the record of
what the move emptied now lives exactly as long as that request: it is
consumed when the result lands on the parent, and discarded when the result
lands in any other mode, so a later unrelated lookup can never re-report a
clearing that was already spoken. The thrown-result path builds its failed
state and hands it to the same apply function as every other result, so the
cleared record has one writer and one normalisation.

The status mapping is a flat switch again, the untagged-and-non-empty test
exists once, and the announcements say "the result Sona found" and "the
parent image's last lookup" in line with their neighbours.
…e announcements name the last lookup (SONA-220)

The panel no longer keeps saying Sona cleared a field once the operator has
typed into it: the edited record now covers a field the operator filled that
no lookup had touched, and the status kind drops a cleared flag for any field
the edited record names. The three parent-move announcements say "the last
lookup filled" in both languages, the phrasing the status lines already use
and the one that is true on every path. Each settle arm takes the pending
record once and hands it to the apply function, the held-URL test is one
named value, and the comments on the record's lifetime say exactly which
exits drop it.
… screen for what is empty (SONA-220)

statusLineKind keeps the raw record of what a result emptied beside the
record the operator's typing has neutralised, and routes each question to the
right one: whether to claim a field was kept reads history, so a field this
result emptied is never called "left as it was"; whether to invite the
operator to fill a field reads the screen, so a field they have typed into is
never called cleared. A clash over a URL the operator has since typed says
the URL was left as it was rather than empty. Once the operator has typed
into an emptied field the cleared claim stays down even if they delete the
text, so the panel never attributes their own deletion to Sona.

A 45-row truth table pins every field state per field for both pages'
wordings, each row also checking the sentence claims nothing the screen
contradicts.
…he upload form (SONA-220)

On the upload form the latch that records the operator typing into an emptied
field was lowered only on a parent move, so once raised it survived every
later lookup on the same tile and could silence a genuine clearing by a
later result. applyShared now recomputes both latches against the result it
is applying, so a refill lowers them and the operator's own text keeps them.
The seed guard that erased the emptied fact for a refilled field is gone;
the latch and the raw flags answer instead, so a field a parent move emptied
and the operator retyped is described by the kept sentence rather than as
left as it was.

The truth table gains a typed-over state and folds its duplicate rows, the
markup test pins the recompute site and counts the latch reset at every
reset site, and the four-lookup sequences run on both pages.
… the comments say what the code does (SONA-220)

While the moved-onto tile's lookup is still out, the panel now shows the same
causal-free sentence the live region spoke for the move, chosen by one
shared clearedLine mapping so the two channels cannot drift; the emptied
sentences with a reason wait for the result that settles it. The comments on
the upload form's latch and on sourceHeld name their real readers and the
accepted trade for text typed and deleted during a round trip, the seed
alias is gone, the four-lookup story is told once, and the two four-lookup
end-to-end sequences share one helper.
…220)

Now that the panel's searching state carries the move sentence inside its
status region, the upload form's announcer speaks only when the new parent
has no lookup at all, so the clearing is heard once rather than from two
regions in the same tick. clearedLine takes the edited record as a required
argument, the markup tests slice the function they inspect instead of
counting characters, and the comments quote the shipped sentence.
… a search in flight (SONA-220)

Starting a lookup on a parent tile that had none drops the cleared record the
move left behind, so the panel's searching state does not repeat a sentence
the live region already spoke. The announcer's guard reads the same value
the panel renders, so whether a panel exists decides who speaks.

The markup tests slice function bodies with one brace-aware helper that
survives a multi-line signature, which is what let the onParentChanged
assertion pass without reaching the body; each slice now proves it found the
function before asserting what it lacks.
…ult's cleared record (SONA-220)

The reset was gated on the tile having been idle, so a third lookup on a
tile whose no-match had already reported a clearing carried that record into
the searching state and spoke it again. A tile that is still searching never
reaches this code, so the reset is unconditional on the parent branch; the
record for a move that landed on a search in flight lives in the pending
record and is unaffected.
@coldtea-pr-lens

coldtea-pr-lens Bot commented Sep 16, 2026

Copy link
Copy Markdown

◈ PR Lens

🟢 +0 new · 🟠 ~5 changed · 🔴 -0 removed · 1 flow · 9 files · commit 1f418ea


Architecture

Architecture diagram for sona-fast/sona at 1f418ea

5 components touched across 5 lanes.

Open the interactive canvas


Inside the changed components — 2 views

Component view — Artist lookup prefill and clearance

Internal components coordinating reverse artist lookups, prefill lifecycles, and cleared field status reporting.

Architecture view of Component view — Artist lookup prefill and clearance in sona-fast/sona

Component view — Admin form container styling and rating badges

CSS container query row styling and rating pill wrapping beside the NSFW checkbox.

Architecture view of Component view — Admin form container styling and rating badges in sona-fast/sona

Data flow

Data flow diagram for sona-fast/sona at 1f418ea

Artist lookup prefill and clearance

Open the interactive canvas


View

  • Architecture lens
  • Data flow lens
  • Expand every detail

Tip

Push a commit and the comment redraws for the new head. A slow older run never overwrites a newer one.

🪧 More tips
  • Run npx skills add coldteadotai/pr-lens, then tell your coding agent: "Diagram the change you just made with PR Lens and attach it to the pull request."
  • Run npx @coldtea/pr-lens-cli analyze --base origin/main on a branch, then npx @coldtea/pr-lens-cli render .pr-lens/graph.json. Same lenses, your own model key, before the pull request exists.
  • Untick Architecture lens or Data flow lens under View to hide a diagram, or tick Expand every detail to open every section. The comment redraws in a few seconds.
  • Click the link under each diagram to open it on a canvas you can zoom, pan and step through.
  • The diagrams are links. Click one to open it on the canvas, then press W or click play to walk through the change.
  • Open a diagram on the canvas, then press W or click play to walk through the change one step at a time.
  • The CLI's render reads .github/pr-lens.yml and applies your renames, exclusions and lane pins at draw time.
  • Set github.comment.collapsed: true in .github/pr-lens.yml to fold the comment behind one View architecture and data flow row. Drawing still runs on every push.
  • Add .github/workflows/pr-lens.yml with coldteadotai/pr-lens/packages/action@v0 and your model provider's key as its api-key to run PR Lens from your own CI. Any /chat/completions endpoint works.
  • Switch GitHub to dark mode and the diagrams follow. The moving dots are this pull request's data in motion.

Thanks for using PR Lens! It's built by Coldtea, free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

@linear-code

linear-code Bot commented Sep 16, 2026

Copy link
Copy Markdown

SONA-220

@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: 63ce8369-a9b8-48dd-85a1-6e07b06077a4

📥 Commits

Reviewing files that changed from the base of the PR and between 0c88d74 and 1f418ea.

📒 Files selected for processing (12)
  • messages/en.json
  • messages/ja.json
  • src/app.css
  • src/lib/artist-lookup-markup.test.ts
  • src/lib/artist-lookup.test.ts
  • src/lib/artist-lookup.ts
  • src/lib/components/ArtistLookupPanel.svelte
  • src/lib/e2e-watch-ignores.test.ts
  • src/routes/admin/images/[id]/edit/+page.svelte
  • src/routes/admin/upload/+page.svelte
  • tests/e2e/artist-lookup.spec.ts
  • tests/e2e/suggest-tags.spec.ts

Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour.


📝 Walkthrough

Walkthrough

Lookup state now records fields cleared by settled artist searches and preserves later operator edits. Upload and edit pages pass this state to localized status rendering. Shared rating rows use container-query layouts. Tests cover lookup transitions, announcements, navigation, and responsive behavior.

Changes

Artist lookup state and presentation

Layer / File(s) Summary
Lookup status contract and localization
src/lib/artist-lookup.ts, messages/en.json, messages/ja.json, src/lib/artist-lookup.test.ts
Lookup status handling identifies cleared, retained, refilled, edited, and clash fields with localized messages.
Panel and edit-page integration
src/lib/components/ArtistLookupPanel.svelte, src/routes/admin/images/[id]/edit/+page.svelte
The panel renders cleared-field status separately from failure guidance. The edit page tracks lookup-cleared fields and operator edits.
Upload lookup and parent transitions
src/routes/admin/upload/+page.svelte
Upload lookups preserve operator values and track clearance across results, failures, cancellations, parent changes, and mode changes.
Responsive rating layout
src/app.css, src/routes/admin/upload/+page.svelte, src/routes/admin/images/[id]/edit/+page.svelte
Rating rows use container queries and shared wrapping and stacking styles.
Static and browser validation
src/lib/artist-lookup-markup.test.ts, src/lib/artist-lookup.test.ts, tests/e2e/artist-lookup.spec.ts, tests/e2e/suggest-tags.spec.ts, src/lib/e2e-watch-ignores.test.ts
Tests cover lookup state, announcements, focus behavior, parent transitions, responsive layouts, tile removal, navigation, and persisted image-specific state.

Priority: ➖ Normal

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

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 1f418

This change preserves lookup-prefilled source URLs and commissioned dates across repeat lookups, adds matching English/Japanese status text, and makes the NSFW/rating row responsive to its container width. Prior review passes across all five stack layers found no unresolved correctness or safety issues, and this final pass did not surface a new confirmed defect, so the change appears ready to merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed SONA-156: Upload and edit lookup state now preserves lookup-filled source URLs and commissioned dates during repeat searches, replaces values for different posts, clears missing values, and preserves …
Out of Scope Changes check ✅ Passed The changed lookup helpers, panel announcements, locale strings, parent and group state, suggestion tests, and responsive NSFW layout support SONA-156 or SONA-220. The upload validation and retry chan…
Docstring Coverage ✅ Passed Docstring coverage is 90.48% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 6 files. (6 skipped: 6 …
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses Conventional Commits format with the scoped type fix(admin):, describes the repeat-lookup prefill and NSFW layout changes, and includes the Linear issue key SONA-220.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sparky/sona-220-lookup-prefill-tests
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch sparky/sona-220-lookup-prefill-tests

Warning

Git: CodeRabbit could not clone the repository, so clone-backed analysis was skipped and this review may be incomplete. Verify repository clone access, such as SSH credentials, before requesting another full review. If clone access is intentionally unavailable, use path_filters to narrow the review scope.


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

Comment thread src/lib/artist-lookup-markup.test.ts Fixed
Comment thread src/lib/artist-lookup-markup.test.ts Fixed
Comment thread src/lib/artist-lookup-markup.test.ts Fixed
@sparkyfen

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@sparkyfen

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@sparkyfen
sparkyfen merged commit e1d2a9e into main Sep 16, 2026
8 checks passed
@sparkyfen
sparkyfen deleted the sparky/sona-220-lookup-prefill-tests branch September 16, 2026 18:35
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