~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

A gallery titled past its lexicon is indexed whole and shown cut

Closed
Closed chadtmiller.com asked to merge into feature/rust from grain.social/grain:gaps/long-titles

What changes

Indexing. A social.grain.gallery whose own strings (title, description) run past their lexicon maxLength is indexed, by Jetstream, backfill and grain-import alike, instead of refused. It goes through one function, Lexicons::validate_indexed in crates/grain/src/lexicon.rs: for a gallery it checks a copy with those top-level strings cut to their bound, so every other rule (formats, required fields, nested strings such as address.country, other collections) still refuses as before. The index keeps the whole record, so record in a view round-trips.

Writes. Index::check, which web and space writes go through before they reach the PDS, stays strict: grain never writes a title past 100.

Views. galleryView.title is the first 100 graphemes of the title and galleryView.description the first 1000 of the description, with nothing appended (lexicon::cut, views::gallery_title). The notification list's gallery title and the OG card's are cut the same way; the card's own ellipsis on width is unchanged.

Why

Six galleries on the network have titles over 100 (e.g. at://did:plc:7dgiqud6mtxy7kat43rh4c2c/social.grain.gallery/3mymoygaeefbg). The import refused them with title: string too long (max 100) where the old server had them. The owner decided to index them and cut the title where it is shown. Description went with it because it is the same check on the same record; nothing else is loosened.

Mirrored from knotless: theme.rs's colours, which take a value one way "as a reader takes them" and another "as a writer must give them", with a strict flag. Here Index::parsed takes strict the same way: check (writers) passes true, put (the network) false. knotless has no lexicon validator with an exception like this, so the exception itself is new.

How it was checked

check, harness, rust, test and lexicons pass at 0362694. Tests added:

  • grain::lexicon: an_index_keeps_a_gallery_that_runs_long (long title and description pass; a bad createdAt, a non-string title, a long nested address.country and a long profile displayName still refuse) and a_view_cuts_by_graphemes (combining marks count as one).
  • grain-serve index: a_gallery_titled_past_its_lexicon_is_indexed_whole_but_not_written (check refuses, put takes it whole); records_that_do_not_fit_are_refused now uses a bad datetime where it used a long title.
  • grain-serve read: a_gallery_titled_past_its_lexicon_is_shown_cut (getGallery cuts by graphemes, record whole).
  • grain-import: a_gallery_titled_past_its_lexicon_is_imported_whole (imported whole; one with a bad createdAt still refused).

Shortcomings

The six galleries come in at the next import or backfill of their repos; nothing re-fetches them by itself. A gallery's search entry holds the whole title, so search matches words past the cut. Space galleries read from member repos (spaces/pool.rs, spaces/gallery.rs) are not cut.

Review

Start at validate_indexed in crates/grain/src/lexicon.rs, then parsed in crates/grain-serve/src/index/mod.rs.

🤖 Generated with Claude Code

Approved an earlier version

This pull lets the index keep galleries whose title or description is past its lexicon bound. Lexicons::validate_indexed checks a copy with those top-level strings cut to maxLength, so every other rule still refuses. Index::parsed takes strict: check (writes) passes true, put passes false. Views, notifications and the OG card cut the title to 100 graphemes, and getGallery cuts the description to 1000.

I found nothing that must change.

  • Edge cases. The byte cut backs up to a char boundary, so it can't split a character. A non-object record falls through to the normal validator, which refuses it. Non-string, bad-format and nested-string failures still refuse, and the tests cover them.
  • Scope. Only social.grain.gallery is loosened.
  • Cut sizes. The lexicon's maxLength counts bytes and the view cuts by graphemes, so a view can show up to 100 graphemes even when that is over 100 bytes. That fits the stated intent.
  • Checks. I didn't read the CI results.
  • Known gaps. The pull body already lists the search and space-gallery gaps.
Approved

The only change since my last review is in a test, an_index_keeps_a_gallery_that_runs_long. The strict-refusal assertion now uses a gallery with a long title and no long description. Before, it used the gallery that also had a 1001-byte description. The test therefore shows that the title alone is refused by the strict validator, and that message is now the title's. This makes the assertion more precise and doesn't change any behaviour. My earlier approval stands, and I found nothing that must change.

Landed on feature/rust in batch/four (…/3mzbjho3qgd2b, 785dbe4), with its head 0362694 unchanged.