feat(ux): human-readable titles — UX-1 of the work-topology plan - #157
Open
kevintseng wants to merge 2 commits into
Open
feat(ux): human-readable titles — UX-1 of the work-topology plan#157kevintseng wants to merge 2 commits into
kevintseng wants to merge 2 commits into
Conversation
Adds a nullable `title` column to entities: the display string a human reads, distinct from `name` (which stays the stable machine dedup key). Write side: - ALTER-only migration in BOTH schema owners (src/db.ts and the hook mirror in scripts/hooks/_shared.js — check-schema-drift cannot see ALTER blocks, so the two are hand-locked with comments on each side) - captureEntity + all three write hooks generate heuristic titles (commit subject verbatim; date+project+verb for session/compaction saves), marked metadata.title_source='heuristic' so a later LLM titling pass knows which titles it may replace — an unmarked title is treated as human-provided and permanent - remember() / MCP / HTTP / CLI accept an optional title (shared Zod schema; blank collapses to undefined = leave-untouched, the namespace rule); CLI quick capture uses its positional text as the title - weekly-summary compression (the fourth entity-creation path) titles its digest rows too Search side: - title is folded into the contentless-FTS feed by ONE function used by both insert and delete, so the delete can never build its match text differently from the insert (the stale-row-forever class) - the archived LIKE supplement matches e.title as well — a memory findable by its title while active stays findable once archived - one-time, fill-only, marker-keyed backfill titles pre-existing rows and reindexes what it titles (active rows only; archived rows have no FTS entry and a mismatched contentless delete corrupts the index) Display side: - MemoryRow headline prefers title (fallback: best observation, then type+date — never the machine name); the raw-name meta span is gone, the name survives as the headline's tooltip - LessonsTab cards and ProjectRoadmap rails/mindmap/phase anchors use the same displayTitle helper; Browse/Lessons filter haystacks include the title so filtering by the visible headline matches Tests: 2024 passed / 129 files (was 1997/128), typecheck clean. Three mutation break-tests all KILLED — two of them only after the first probe proved vacuous (kg.search()'s status filter hid stale FTS tokens the raw index still held; the observation arm masked the title arm for backfilled rows), so the tests now probe the raw index and use a manual-title fixture respectively.
…C1 hits UX-1's insertions shifted 18 line-keyed C5 baseline entries; each is re-keyed with its statement verified at the new line, classifications unchanged. cli.ts needed collision-safe ordering: the old :171 entry (relationsCreated) moves onto :177, whose previous occupant (relationErrors) moves to :183 — the exact wrong-sibling hazard the 2026-08-13 correction note in these entries describes. C1: mirror-parity's new emptiness assertion gains a genuine count pin (the token was demonstrably indexed before the re-title deletes it); db.test.ts's hit is PRAGMA notnull flag equality, not set-emptiness — baselined SAFE with that reason.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
UX-1: human-readable titles (work-topology plan, PR 1 of 6)
Entities gain a nullable
titlecolumn — the string a human (or agent) reads first.namekeeps its identity as the stable machine dedup/append key and is no longer used as display anywhere.Write side
src/db.ts+ the hook mirror inscripts/hooks/_shared.js).check-schema-drift.mjscannot see ALTER blocks, so each side carries a comment locking it to the other.truncateTitle(120 chars): commit subject verbatim (post-commit), date+project+verb (session-summary,pre-compact). The weekly-summary compressor — the fourth entity-creation path — titles its digest rows too.metadata.title_source='heuristic'. An unmarked title is treated as human-provided and permanent; the mark is what lets a future LLM titling pass (dreamer backfill) know what it may replace.remember()/ MCP / HTTP / CLI accept an optionaltitlethrough the one shared Zod schema. Blank-after-sanitize collapses toundefined(= leave an existing title untouched — the namespace rule). CLI quick capture titles the memory with its own positional text.Search side
entities_ftsstays two-column: the title is folded into the observations feed by one private helper that bothinsertFtsRowandremoveFromFtscall, so a contentless delete can never build its match text differently from the insert that created the row.e.titletoo — a memory findable by its title while active stays findable once archived.title_backfill_v1, modeled on the signal-score pass) titles pre-existing rows and reindexes what it titles — active rows only: archived rows have no FTS entry, and a contentless delete for text never indexed is itself the corruption.Display side
displayTitle()inentity-display.tsis the single fallback chain:title → pickBestObservation → typeLabel+date— nevername.Verification (all run at HEAD, this branch)
Mutation break-tests (all KILLED — two only after strengthening a vacuous probe)
kg.search()'sstatus='active'JOIN hid the stale FTS tokens the raw index still held (delete failed with "database disk image is malformed" on stderr while the test stayed green). Probe rewritten against rawentities_fts MATCH→ KILLED.o.contentarm masks the title arm). New isolating test with a manual title whose token appears nowhere else → KILLED.Audit baseline
UX-1's insertions shifted 18 line-keyed C5 entries — re-keyed with each statement verified at its new line, classifications unchanged.
cli.tsneeded collision-safe ordering (:171→:177lands on the key:177→:183vacates — the wrong-sibling hazard the 2026-08-13 note in those entries warns about). Two new C1 hits triaged: mirror-parity got a genuine count pin; db.test.ts's.toBe(0)is PRAGMAnotnullflag equality, baselined with that reason.Out of scope (per the reviewed plan)
LLM-generated titles (dreamer backfill, next), status/enum work (display-derived only), work-layer type whitelist (UX-4), M0 measurements.