Skip to content

Derive a Subject's name from facts instead of carrying it - #1362

Closed
JeroenDeDauw wants to merge 1 commit into
masterfrom
subject-name-from-facts
Closed

Derive a Subject's name from facts instead of carrying it#1362
JeroenDeDauw wants to merge 1 commit into
masterfrom
subject-name-from-facts

Conversation

@JeroenDeDauw

@JeroenDeDauw JeroenDeDauw commented Sep 6, 2026

Copy link
Copy Markdown
Member

I got triggered by the new displayName and displayNameIsGenerated fields in TS Subject. Display concerns leaking into the domain model. This PR fixes that.


Follows-up to #1309 and #1359.

displayName and displayNameIsGenerated on the frontend Subject were one fact in three fields, with an invariant only withLabel maintained. Clearing a label kept the removed label as the name, so the save toast announced a name the Subject had just lost.

The entity now holds stored facts only: id, nullable label, Schema, statements. SubjectWithContext gains isMainSubject and derives the chosen name from label, page name and that flag; the presentation layer adds the Schema fallback and the (unnamed …) marker. The rule exists once in PHP (SubjectDisplayName) and once in TypeScript (chosenSubjectName), cross-referenced and each unit-tested on the same four cases; no shared fixture ties them.

Deriving in the browser is also what multilingual labels will need: which label to show depends on the reader's language, which the browser knows and the server does not. A server-computed name would have needed a language parameter on every read.

REST returns isMainSubject for every Subject and drops displayNameIsGenerated; a GitHub code search finds no consumer outside this repository. displayName stays for clients that would rather not derive it. Lua, {{#neowiki_value}}, RDF and the graph are unchanged.

Cost: every Subject in a response now says whether it is its Page's Main Subject. Only the Page knows, so the two read queries (a Subject with its relation targets; a Page's Subjects with their referenced Subjects) read each Page that has a Subject in the response, once per Page, where a label used to skip that read. The Main Subject flag on the subject-page index (#1335) would make it an index read.

Not this PR's question: whether the Page-name fallback should exist at all. That is ADR 31's. Dropping it later is a one-line change in the PHP rule and its TypeScript mirror, and would remove the read above.

Visible change: after clearing a label, the toast and the tree name the Subject (unnamed <Schema>).

#1356 needs a rebase: eight shared files and three getDisplayName() calls, which become subjectDisplayName( subject ).

Considered, omitted:

  • Renaming PHP SubjectDisplayName, which decides what the graph and RDF materialize, not what is displayed.
  • Dropping the Schema tier from rdfs:label, which puts into the data the ambiguity PR 1359 removed from the UI.
  • A name-source enum on the wire instead of isMainSubject.
  • Memoizing page content reads in the repository to absorb the extra read: the save path mutates the loaded SubjectContent in place, so a memo needs invalidation; a change of its own if the cost shows in measurements.

AI-authored — Claude Code, Fable 5.1 (max); design discussion with @JeroenDeDauw, redirected twice, then a one-line ask for the PR, description revised once on his feedback; diff not yet human-reviewed; PHPUnit, phpcs, phpstan, vitest, build and lint green locally, label clear and restore verified live on a worktree wiki, CI pending.

Production notes

Design and this description by Fable 5.1 (max); implementation by an Opus 5 subagent from a written spec. The subagent removed the label short-circuit in the two read queries on its own, after finding it made isMainSubject wrong for labelled Subjects; accepted on review, and it is the source of the read cost above.

Follows-up to #1309 and #1359.

`displayName` and `displayNameIsGenerated` on the frontend `Subject` were one fact in three fields, with an invariant only `withLabel` maintained. Clearing a label kept the removed label as the name, so the save toast announced a name the Subject had just lost.

The entity now holds stored facts only: id, nullable label, Schema, statements. `SubjectWithContext` gains `isMainSubject` and derives the chosen name from label, page name and that flag; the presentation layer adds the Schema fallback and the `(unnamed …)` marker. The rule exists once in PHP (`SubjectDisplayName`) and once in TypeScript (`chosenSubjectName`), cross-referenced and each unit-tested on the same four cases; no shared fixture ties them.

Deriving in the browser is also what multilingual labels will need: which label to show depends on the reader's language, which the browser knows and the server does not. A server-computed name would have needed a language parameter on every read.

REST returns `isMainSubject` for every Subject and drops `displayNameIsGenerated`; a GitHub code search finds no consumer outside this repository. `displayName` stays for clients that would rather not derive it. Lua, `{{#neowiki_value}}`, RDF and the graph are unchanged.

Cost: every Subject in a response now says whether it is its Page's Main Subject. Only the Page knows, so the two read queries (a Subject with its relation targets; a Page's Subjects with their referenced Subjects) read each Page that has a Subject in the response, once per Page, where a label used to skip that read. The Main Subject flag on the subject-page index (#1335) would make it an index read.

Not this PR's question: whether the Page-name fallback should exist at all. That is ADR 31's. Dropping it later is a one-line change in the PHP rule and its TypeScript mirror, and would remove the read above.

Visible change: after clearing a label, the toast and the tree name the Subject `(unnamed <Schema>)`.

#1356 needs a rebase: eight shared files and three `getDisplayName()` calls, which become `subjectDisplayName( subject )`.

Considered, omitted:
- Renaming PHP `SubjectDisplayName`, which decides what the graph and RDF materialize, not what is displayed.
- Dropping the Schema tier from `rdfs:label`, which puts into the data the ambiguity PR 1359 removed from the UI.
- A name-source enum on the wire instead of `isMainSubject`.
- Memoizing page content reads in the repository to absorb the extra read: the save path mutates the loaded `SubjectContent` in place, so a memo needs invalidation; a change of its own if the cost shows in measurements.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@JeroenDeDauw
JeroenDeDauw force-pushed the subject-name-from-facts branch from cc2132d to 3de1526 Compare September 7, 2026 17:36
@JeroenDeDauw

Copy link
Copy Markdown
Member Author

Started a deeper investigation into the sensibility of having "domain/" in the FE code. Some things seem off. The goal I started pursuing with this PR might not be the right one.

@JeroenDeDauw
JeroenDeDauw deleted the subject-name-from-facts branch September 7, 2026 18:25
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.

1 participant