Skip to content

fix: stop a film's save from erasing its custom field values - #761

Merged
helmerzNL merged 1 commit into
release/v26-betafrom
claude/issue-719-review-features-u9cbuh
Sep 3, 2026
Merged

fix: stop a film's save from erasing its custom field values#761
helmerzNL merged 1 commit into
release/v26-betafrom
claude/issue-719-review-features-u9cbuh

Conversation

@helmerzNL

Copy link
Copy Markdown
Owner

What

A one-line accessor fix, and the tests that should have caught it. Saving any film currently deletes every custom field value that film had.

Why

renderMovieEditCustomFields reads its stored values from detail.customValues. movie_detail_entity attaches them as detail.movie.custom_values; the key customValues exists only on sync-mutation results (next_app.py:24749, :24794) and on the PUT /custom-values response (:38604), and nothing anywhere sets it on a detail payload.

So every input renders empty on every load. On its own that is a display fault. It is not on its own:

  1. collectMovieEditCustomValues turns an empty input into null.
  2. replace_movie_custom_values deletes the row for a null — deliberately, so clearing a field works.
  3. saveMovieCustomValues sends the whole collected map on every save.

Opening a film, correcting its title and pressing save therefore erased every custom value on that film. Nothing errored and nothing logged; the loss was visible only to whoever later went looking for a value they had entered.

The two commits that introduced it are a pair — 8b91677 attached the values to the detail entity, fb19a1e read them back under a different name — and no test touched the read path, so both halves looked finished. Found while investigating #719, where @jeff363b mentions going into Edit to see what a custom field holds.

How

detail?.customValuesdetail?.movie?.custom_values.

Tests

Both sides, because a rename on either one alone restores exactly the same silence:

  • tests/test_next_custom_fields_ui.py — the accessor reads the detail payload's own key, and does not read the key no detail payload carries.
  • tests/test_next_custom_fields_routes.py — the round trip: write a value, GET /api/next/movies/<id>, and find it under detail.movie.custom_values with no customValues on the detail object.

Both fail on the previous code. Full backend suite green against a local PostgreSQL 16 with all migrations applied (3,458 tests, 2 skipped), as is scripts/check_undefined_names.py.

Impact on existing data

This stops the loss; it cannot undo it. Values erased by a save before this fix are deleted rows and are not recoverable except from a backup. Instances that have defined custom fields since 26.9.98 and edited films afterwards should expect gaps.

Deployment-file changes

No deployment-file changes in this PR.

Documentation

To be recorded in App-Guidance (projects/discvault/specs/custom-fields.md) alongside the detail-page work that follows: values travel on the detail payload as movie.custom_values and nowhere as detail.customValues. Not documentation-neutral — the whole defect is a contract read under the wrong name.

Translations

No new UI strings.


📦 Change Spec — release notes (fill at merge)

version: ""
name: ""
headline: "Custom field values survive editing a film"
highlights:
  - ""
fixes:
  - "A film's custom field values no longer disappear when you save the film"
  - "The edit form fills in the custom field values a film already has"
breaking: []
cta: ""
internal_only:
  - ""
  • Change Spec filled in, or this PR is internal-only (no user-facing release notes).

🤖 Generated with Claude Code

https://claude.ai/code/session_01PUwMZZ585LnTxaWMUuKVgT


Generated by Claude Code

The edit form read its stored custom values from `detail.customValues`.
`movie_detail_entity` attaches them as `detail.movie.custom_values`;
`customValues` exists only on sync-mutation results and on the PUT response,
and nothing ever sets it on a detail payload. So every input rendered empty on
every load.

That is not a display fault on its own. `collectMovieEditCustomValues` turns an
empty input into a null, `replace_movie_custom_values` deletes the row for a
null, and `saveMovieCustomValues` sends the whole collected map with every
save. Opening a film, correcting its title and pressing save therefore erased
every custom value on that film. Nothing errored, nothing logged, and the loss
was only visible to whoever went looking for a value they had entered earlier.

The two commits that introduced it are a pair: 8b91677 attached the values to
the detail entity, fb19a1e read them back under a different name. No test
touched the read path, so both halves looked finished.

Both sides are now asserted, because a rename on either one alone restores the
same silence: `test_next_custom_fields_ui.py` pins the accessor, and
`test_next_custom_fields_routes.py` pins the payload key by writing a value and
reading it back off `GET /api/next/movies/<id>`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PUwMZZ585LnTxaWMUuKVgT
@helmerzNL
helmerzNL merged commit 18514f8 into release/v26-beta Sep 3, 2026
2 checks passed
@helmerzNL helmerzNL mentioned this pull request Sep 3, 2026
2 tasks
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