Skip to content

fix(kg): report a note's tags and give a nameless note a label - #2631

Merged
ohdearquant merged 2 commits into
mainfrom
fix/note-projection-tags-and-label
Sep 12, 2026
Merged

fix(kg): report a note's tags and give a nameless note a label#2631
ohdearquant merged 2 commits into
mainfrom
fix/note-projection-tags-and-label

Conversation

@ohdearquant

Copy link
Copy Markdown
Owner

Two things a note record failed to say about itself, both on the read path, no schema change and no backfill.

Tags. The notes table has no tags column. A caller's tags are stored under properties.tags, and the tag filter matches with a JSON extract on that same path. So a note returned from create, get or list reported no tags at all while being perfectly findable by them. A reader cannot tell that apart from a note that genuinely has none, and the workaround, reading properties.tags yourself, is exactly the detail the projection exists to hide.

The shared note projection now lifts the stored array to the top level. Only an array is lifted, because promoting a string would hand callers a tags field they still have to type-check, and an explicit top-level value wins, so a substrate that does carry its own tags is never overwritten by this.

Name. A note's name is optional and nothing defaults it, so a listing keyed on name renders blank rows for notes carrying full paragraphs. The projection now emits display_name: the name when there is one, the first non-empty line of content otherwise, trimmed and capped at 120 characters with an ellipsis.

The stored name is deliberately untouched. Deriving a value into that column would destroy the difference between a note someone titled and one nobody did, and that difference is real: it is the difference between a title and a first sentence. A label is a view; a name is a record.

Tests. Each arm separates this from a plausible wrong implementation. A non-array properties.tags is not promoted. An explicit top-level tags wins over the stored one. A whitespace-only name still derives, so an implementation testing only for null fails. Whitespace-only content yields no label rather than an empty one. A long line is capped and marked, and the name field is asserted still null in the same test, so an implementation that writes the derived label into name fails.

Two things a note record failed to say about itself.

Tags. The notes table has no tags column, so a caller's tags are stored
under properties.tags, and the tag filter matches with a JSON extract on
that path. A note returned from create, get or list therefore reported no
tags at all while being findable by them, which reads as "this note has
no tags" rather than "this projection does not look there". The shared
note projection now lifts the stored array to the top level. Only an
array is lifted, and an explicit top-level value wins, so a substrate
that does carry its own tags is never overwritten.

Name. A note's name is optional and nothing defaults it, so a listing
keyed on name renders blank rows for notes carrying full paragraphs. The
projection now emits display_name: the name when there is one, the first
non-empty line of content otherwise, trimmed and capped. The stored name
is untouched. Deriving a value into the name column would destroy the
difference between a note someone titled and one nobody did, and that
difference is real.

Tests cover the arms that separate this from a plausible wrong version: a
non-array properties.tags is not promoted, an explicit top-level tags
wins, a whitespace-only name still derives, whitespace-only content
yields no label rather than an empty one, and a long line is capped and
marked.
@ohdearquant
ohdearquant merged commit f41f96c into main Sep 12, 2026
28 checks passed
@ohdearquant
ohdearquant deleted the fix/note-projection-tags-and-label branch September 12, 2026 13: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.

1 participant