Repository navigation
Fix stale cached title on tab titles, query views, and collections - #2179
Merged
Merged
Conversation
…2012) Follow-up to #2011: sharedNodeStore's cached `title` only refreshes via a backend round-trip, while optimistic content updates patch `content` and spread `title` forward untouched. Several surfaces preferred a plain `node.title || node.content`, so they kept showing a title computed for an earlier, possibly differently-typed state. Introduces `resolveTitleOrContent` (lib/utils/node-display-title.ts) as the single shared rule: prefer `title` only when the node's schema is title_template-driven, `content` otherwise. `computeHeaderDisplayValue` (node-type-predicates.ts) now delegates to it instead of re-deriving the rule. `PluginRegistry` gains `resolveDisplayTitle(node)`, which applies the rule using `findSlashCommand(nodeType)?.hasTitleTemplate` as the title-template signal (the same flag node-row.svelte already uses for this), and `getNodeTitle` (tab titles) now delegates its non-plugin-override fallback to it too. Call sites fixed to route through `resolveDisplayTitle`: plugin-registry's getNodeTitle (backs tab titles via computeTabTitle), list-view.svelte, kanban-view.svelte, table-row.svelte (both the content column and the row tooltip), and collections.svelte.ts's selectedCollectionMembers. Query views and the collections sidebar deliberately use resolveDisplayTitle rather than getNodeTitle, so they don't pick up type-specific getTitle overrides (e.g. the date plugin's "Today"/"Tomorrow" formatting) that only make sense for a tab's specific contract. Adds regression coverage for the exact stale-title scenario (title captured before a type conversion, content since changed) across all fixed surfaces, plus unit tests for resolveTitleOrContent and resolveDisplayTitle. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…down to content resolveTitleOrContent's "is this title resolved" guard used ASCII-only \w, so a title_template value fully resolved in a non-Latin script (CJK, Arabic, ...) was misclassified as unresolved and rendered empty. This PR had just propagated that guard from one call site (BaseNodeViewer's header) to six (tab titles, list/kanban/table views, collections). Switch to a Unicode-aware "any letter or number" test. collections.svelte.ts wrapped resolveDisplayTitle's return value in stripMarkdown unconditionally, including on the title_template branch — unlike the old node.title || stripMarkdown(node.content), which only ever stripped the content fallback. A computed title containing an underscore, asterisk, or backtick (property values, not markdown) would get silently mangled. resolveDisplayTitle now takes an optional formatContent callback applied only when the resolved value came from content. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Contributor
Author
|
Adversarial review complete. Two real findings, both fixed in 117da3d:
Both fixes have regression tests. Full frontend suite green (226 files / 4767 tests), |
5 tasks
mstomar125
added a commit
that referenced
this pull request
Aug 20, 2026
…yTitle Merging main pulled in #2179 (Fix stale cached title on tab titles, query views, and collections), which added PluginRegistry.resolveDisplayTitle() and made getNodeTitle() delegate to it. Both derive hasTitleTemplate via findSlashCommand(nodeType)?.hasTitleTemplate — the exact field this PR's earlier commit stopped populating when it emptied createPluginFromSchema's slashCommands array. Combined, every title_template-driven user-defined schema type would have shown a blank/stale title instead of its computed name across node-row.svelte, resolveDisplayTitle's consumers (query list/ kanban/table views, the collections sidebar), and getNodeTitle's consumers (tab titles). Moves hasTitleTemplate/titleTemplate off SlashCommandDefinition and onto PluginDefinition as top-level fields (alongside deletableViaBackspace), since the signal is a per-type property, not tied to slash-command availability. Adds PluginRegistry.hasTitleTemplate()/getTitleTemplate() accessors mirroring the acceptsContentMerge()/deletableViaBackspace() pattern, and updates every consumer (resolveDisplayTitle, node-row.svelte, base-node-viewer.svelte's now-dead custom-entity-slash-command branch) to read from them instead of findSlashCommand(...)?.hasTitleTemplate. createPluginFromSchema now sets hasTitleTemplate/titleTemplate at the plugin level. Updates the 5 test files that constructed synthetic plugins with these fields nested in a slash command, adds direct coverage for the new accessors, and restores the two schema-plugin-loader.test.ts titleTemplate assertions that had collapsed into duplicate bodies (both previously only checked slashCommands is empty, losing coverage of hasTitleTemplate/ titleTemplate propagation). Also fixes stale doc comments (core_schemas.rs, capture_service.rs, agent_session_service.rs, agent_session_service.proto) that described ai-chat nodes as created via the now-removed `/ai-chat` slash command, and drops two GitHub issue-number references from comments this PR was already touching. Co-Authored-By: Claude <noreply@anthropic.com>
mstomar125
added a commit
that referenced
this pull request
Aug 20, 2026
…pes) (#2181) * Remove entity-type slash commands (/ai-chat, /person, user-defined types) core#2133 established that slash commands are for content primitives plus task; entity types are not slash-creatable. core#2151 documented three surfaces that predated the rule as known exceptions, explicitly not precedent. This retires them: - aiChatNodePlugin and personNodePlugin now register slashCommands: [] while keeping their viewer/reference/schema-form/backspace-guard registrations intact. - createPluginFromSchema no longer generates a slash command for user-defined schema types; the plugin registration still exists for reference/viewer resolution. - PLUGIN_PRIORITIES is removed from schema-plugin-loader.ts — CUSTOM_ENTITY was its only consumer, and CORE/USER_COMMAND were already unreferenced anywhere in the codebase. - Inverted the five schema-plugin-loader.test.ts assertions that expected a generated slash command; updated core-plugins.test.ts and slash-command-service-registry-integration.test.ts command counts (13 -> 11 core commands). - Deleted the now-resolved "known exceptions" paragraph from core-plugins.ts's slash-command doc comment. Verified via an isolated dev:browser + real daemon stack that person, ai-chat, and a fresh user-defined schema (Venue) all still create instances through the sidenav/sidebar surfaces after their slash commands are gone, and that the slash-command dropdown offers only the 11 primitive+task commands. Co-Authored-By: Claude <noreply@anthropic.com> * Fix title_template regression from merging with #2179's resolveDisplayTitle Merging main pulled in #2179 (Fix stale cached title on tab titles, query views, and collections), which added PluginRegistry.resolveDisplayTitle() and made getNodeTitle() delegate to it. Both derive hasTitleTemplate via findSlashCommand(nodeType)?.hasTitleTemplate — the exact field this PR's earlier commit stopped populating when it emptied createPluginFromSchema's slashCommands array. Combined, every title_template-driven user-defined schema type would have shown a blank/stale title instead of its computed name across node-row.svelte, resolveDisplayTitle's consumers (query list/ kanban/table views, the collections sidebar), and getNodeTitle's consumers (tab titles). Moves hasTitleTemplate/titleTemplate off SlashCommandDefinition and onto PluginDefinition as top-level fields (alongside deletableViaBackspace), since the signal is a per-type property, not tied to slash-command availability. Adds PluginRegistry.hasTitleTemplate()/getTitleTemplate() accessors mirroring the acceptsContentMerge()/deletableViaBackspace() pattern, and updates every consumer (resolveDisplayTitle, node-row.svelte, base-node-viewer.svelte's now-dead custom-entity-slash-command branch) to read from them instead of findSlashCommand(...)?.hasTitleTemplate. createPluginFromSchema now sets hasTitleTemplate/titleTemplate at the plugin level. Updates the 5 test files that constructed synthetic plugins with these fields nested in a slash command, adds direct coverage for the new accessors, and restores the two schema-plugin-loader.test.ts titleTemplate assertions that had collapsed into duplicate bodies (both previously only checked slashCommands is empty, losing coverage of hasTitleTemplate/ titleTemplate propagation). Also fixes stale doc comments (core_schemas.rs, capture_service.rs, agent_session_service.rs, agent_session_service.proto) that described ai-chat nodes as created via the now-removed `/ai-chat` slash command, and drops two GitHub issue-number references from comments this PR was already touching. Co-Authored-By: Claude <noreply@anthropic.com> * Address round-2 review: remove dead entity-slash-command branch, fix Unicode/empty-template edge cases - base-node-viewer.svelte: delete handleCustomEntitySlashCommand and its two rendersAsEntityRow(newNodeType) call sites — unreachable now that no first-party plugin's slash command ever targets an entity-row type (ai-chat, person, and all user-defined schema types have slashCommands: [] after this PR). No production registration path can still reach it, so per this repo's no-dead-code convention it's deleted rather than kept as latent "extension point" infrastructure. - node-row.svelte: use the same Unicode-aware HAS_RESOLVED_CHARACTER_RE (exported from node-display-title.ts) that resolveDisplayTitle's consumers use, instead of the ASCII-only /\w/ — a title_template resolving to a non-Latin-script name (CJK, Arabic, Cyrillic, ...) now renders correctly here too instead of falling back to the raw template placeholder. - plugin-registry.ts: getTitleTemplate() now gates on hasTitleTemplate() instead of titleTemplate's own truthiness, so a plugin with titleTemplate: '' (which hasTitleTemplate correctly treats as "no template") can't return a defined-but-empty string, matching the method's documented "undefined means no template" contract. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
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.
Summary
Follow-up to #2005 / #2011, which fixed the stale cached
titleinBaseNodeViewer's header but deliberately scoped itself there.sharedNodeStore's cachedtitleonly refreshes via a backend round-trip, while optimistic content updates patchcontentand spreadtitleforward untouched — so any surface still preferring a plainnode.title || node.contentkept showing a title computed for an earlier, possibly differently-typed state (most visibly, tab titles).resolveTitleOrContent(lib/utils/node-display-title.ts) as the single shared rule: prefertitleonly when the node's schema is title_template-driven,contentotherwise.computeHeaderDisplayValue(node-type-predicates.ts) now delegates to it instead of re-deriving the rule.PluginRegistry.resolveDisplayTitle(node), which applies that rule usingfindSlashCommand(nodeType)?.hasTitleTemplateas the title-template signal (the same flagnode-row.sveltealready uses for this purpose).getNodeTitle(which backs tab titles viacomputeTabTitle, and additionally applies a plugin'sgetTitleoverride, e.g. date's "Today"/"Tomorrow") now delegates its non-override fallback branch toresolveDisplayTitletoo.resolveDisplayTitle(or, for tab titles,getNodeTitle):plugin-registry.ts'sgetNodeTitle,list-view.svelte,kanban-view.svelte,table-row.svelte(both thecontentcolumn and the row tooltip),collections.svelte.ts'sselectedCollectionMembers.resolveDisplayTitlerather thangetNodeTitle, so they don't inherit a type'sgetTitleoverride — those surfaces show plain node text, not a tab-specific title, and (concretely) a date node'sidis only guaranteed to parse as a real date in production, not in every synthetic id these surfaces might see.Closes #2012
Test plan
list-view.svelte,kanban-view.svelte,table-row.svelte, andcollections.svelte.ts's member list, each also asserting a title_template-driven type still shows its computed title.resolveTitleOrContentandPluginRegistry.resolveDisplayTitle/getNodeTitle.plugin-registry.test.ts's pre-existinggetNodeTitletest that had encoded the old (buggy) title-wins-over-content behavior.bun run quality:fixclean (eslint + svelte-check, 0 errors/warnings).bun run test: 226 files / 4763 tests, 0 failures — two flakes seen under parallel contention,core-plugins.test.ts's viewer-resolution timeout and a perf-benchmark threshold, both confirmed pre-existing and unrelated by re-running in isolation).bun run test:all(frontend + skill + Rust + e2e + Tauri-seam integration) green via the pre-push gate — all passed before this branch was pushed.🤖 Generated with Claude Code