Repository navigation
Mixed external+note entity tab hides note Markdown behind URL-only card #1623
Description
Summary
Opening a single entity tab for an entity typed both external and note shows the external URL links but hides the note's actual Markdown body. The tab should present both: clickable external URLs and the note's rendered content, without falling back to a dump of internal metadata. This is distinct from the completed external-only URL card (#250) and multi-type note composition (#1164/#1171).
Root Cause
features/Phantom.Workspaces/ViewModels/EntityWorkspaceTabViewModel.cs:37-48 creates the single-tab card with cardViewName: this.entityCardViewResolver.ResolveViewName(this.Entity). That resolver gives any external-typed entity precedence, regardless of its other types (features/Phantom.Workspaces/ViewModels/EntityCardViewResolver.cs:7-16):
if (!string.Equals(requestedViewName, RawViewName, System.StringComparison.Ordinal)
&& entity.IsEntityType("external"))
{
return "external";
}
return RawViewName;features/Phantom.Workspaces/ViewModels/EntityCardViewModel.cs:63-94 then creates ExternalCard when cardViewName == "external". The normal field editors can still be built from all non-abstract types (EntityCardViewModel.cs:581-591); the note type view declares an inline content field (features/Phantom.Workspaces.Data.Core/JsonEntities/entity-type-views/note-entity-type-view.json:7-12). But features/Phantom.Workspaces/Controls/EntityCardControl.axaml:126-130 makes these presentations mutually exclusive:
<ItemsControl ItemsSource="{Binding FieldEditors}"
IsVisible="{Binding ExternalCard, Converter={x:Static ObjectConverters.IsNull}}" />
<ContentControl Content="{Binding ExternalCard}"
IsVisible="{Binding ExternalCard, Converter={x:Static ObjectConverters.IsNotNull}}" />Thus a populated ExternalCard hides the note editor entirely; the external template at features/Phantom.Workspaces/Templates/WorkspaceDataTemplates.axaml:492-506 iterates Urls only. The existing note editor is not a plain text field: tests show content.default contains a text/markdown MIME attachment with content.text, and MarkdownMimeAttachmentFieldEditorViewModel renders that text via WorkspaceMarkdownView (WorkspaceDataTemplates.axaml:349-396).
Affected Files
| File | Role |
|---|---|
features/Phantom.Workspaces/ViewModels/EntityCardViewResolver.cs |
Selects the URL-only external presentation for mixed types. |
features/Phantom.Workspaces/ViewModels/EntityCardViewModel.cs |
Owns URL card, lazy field-editor construction, and snapshot refresh; needs a composite presentation state. |
features/Phantom.Workspaces/Controls/EntityCardControl.axaml |
Currently hides every field editor whenever URL card exists. |
features/Phantom.Workspaces/Templates/WorkspaceDataTemplates.axaml |
Existing URL link and Markdown editor templates should remain reusable; single-entity tab hosts the common card at lines 35-57. |
features/Phantom.Workspaces/ViewModels/EntityWorkspaceTabViewModel.cs |
Single-entity tab resolves the card presentation; cover this route on open/reopen. |
features/Phantom.Workspaces.Tests/ExternalEntityCardViewModelTests.cs, EntityCardControlTests.cs, EntityCardViewModelTests.cs, EntityWorkspaceTabViewModelTests.cs |
Existing resolver, visual card, editor, and tab test homes. |
Design / Fix
Keep the explicit raw request authoritative. Otherwise identify external and note before the existing external-only branch (e.g. return a dedicated external-note view name). Make the card produce its existing ExternalEntityCardViewModel URL links and the curated note content editor, and allow both controls to be visible in that composite mode. Reuse FieldEditorFactory and the note type view's inline content path / existing Markdown MIME editor; do not read an invented top-level body field, show raw metadata, duplicate the Markdown, or convert URL buttons into plain text.
if (requestedViewName == RawViewName) return RawViewName;
if (entity.IsEntityType("external") && entity.IsEntityType("note")) return "external-note";
if (entity.IsEntityType("external")) return "external";
return RawViewName;
// In the common card: construct ExternalCard for external AND external-note;
// show FieldEditors for raw AND external-note, not for external-only.Leave the pure external URL-only behavior (#250), pure note Markdown behavior (#1171), and explicit raw view unchanged. Because the entity tab and other card hosts reuse EntityCardControl, make the composite behavior consistent wherever a non-raw card is resolved; note the entity browser and ViewEntityViewModel explicitly request raw (EntityBrowserWorkspaceTabViewModel.cs:203,365, ViewEntityViewModel.cs:39) and should not be inadvertently converted. On live snapshot update rebuild both links and realized field editors, and on reopening/reloading the tab resolve the mixed type afresh. If the type set changes while the same card is open, update presentation or recreate the card instead of retaining a stale constructor-time view name. Preserve lazy editor construction on first realization.
Expected Tests
| Test Name | Class | What It Verifies |
|---|---|---|
EntityCardViewResolver_ExternalAndNote_ReturnsCompositeViewName |
ExternalEntityCardViewModelTests |
Mixed types resolve composite while an explicit raw request still returns raw. |
EntityCardControl_ExternalAndNote_RendersLinksAndNoteMarkdown |
EntityCardControlTests |
Realized common card visibly shows a clickable URL and the inline WorkspaceMarkdownView with synthetic note content, once, without generic metadata fields. |
EntityCardViewModel_ExternalAndNote_SnapshotUpdateRefreshesLinksAndContent |
EntityCardViewModelTests |
Updated snapshot refreshes URL model and realized note editor without hiding either or duplicating the body. |
EntityWorkspaceTabViewModel_ExternalAndNote_OpenAndReopenShowsBoth |
EntityWorkspaceTabViewModelTests |
Single-entity tab selects the mixed card initially and after reopening/reloading; pure external and pure note remain unchanged. |
EntityCardViewResolver_ExternalEntity_WithRawRequested_ReturnsRaw |
ExternalEntityCardViewModelTests |
Keep existing explicit-raw regression guard. |
Activity
JoshuaRowePhantom commentedon Oct 8, 2026
Verification result
Status: ❌ 1 issue(s) found
Commits checked: b8b1adc Fix mixed external and note card presentation (#1623)
Outstanding gaps
EntityWorkspaceTabViewModel_ExternalAndNote_OpenAndReopenShowsBothconstructs tabs without anEntityBrokerorEntityTypeViewCatalog, so its cards have noFieldEditorFactory; it asserts only view name, URL model andShowFieldEditors, leavingFieldEditorsempty. Cover the actual tab path with a factory and assert the rendered Markdown alongside the URL on initial open and after reopening/reloading. The separate visual test constructs a card directly, not through the entity tab. This is a missing described test case, not a failure of the 59 tests executed.Tests
worktrees\19\scripts\run-tests.ps1 -Mode fast -TestNames EntityCardControlTests,EntityCardViewModelTests,EntityWorkspaceTabViewModelTests,ExternalEntityCardViewModelTestsscripts\test-results.log: Executed: 59; Passed: 59; Failed: 0; Skipped: 0.No filesystem/Git repository layer files changed; slow suite not required.
JoshuaRowePhantom commentedon Oct 8, 2026
Verification result
Status: ✅ Verified
Commits checked: 48020b5 Test mixed external-note tab rendering across reopen (#1623); b8b1adc Fix mixed external and note card presentation (#1623).
Outstanding gaps
None — all criteria satisfied. The prior missing tab-path coverage is addressed: the new test constructs the tab with an initialized broker/catalog, realizes its actual card template in a window, and checks a commanded URL button and exactly one visible Markdown view with the expected content on initial open, snapshot refresh, and reopen. It also excludes internal metadata and stale/duplicated URL or body content. Resolver, raw override, pure-type handling, lazy editor construction, and type-change behavior have targeted coverage. No prohibited test synchronization, disabled tests, unresolved TODOs, or synchronous task blocking introduced. No filesystem/Git repository layer changes; slow suite not required.
Tests
worktrees\19\scripts\run-tests.ps1 -Mode fast -TestNames EntityCardControlTests,EntityCardViewModelTests,EntityWorkspaceTabViewModelTests,ExternalEntityCardViewModelTestsscripts\test-results.log: Executed: 60; Passed: 60; Failed: 0; Skipped: 0.JoshuaRowePhantom commentedon Oct 9, 2026
Checked into features via #1626 (merge 1b86a98). Hosted fast CI 37966216282 passed 8,019/8,019; full CI 37966209695 passed on Windows and Linux. #1617 and #1618 retain their separate acceptance gates.