Co-authored-by: Kandev Agent <agent@kandev.dev>
9.1 KiB
status, system, requirements
| status | system | requirements | |
|---|---|---|---|
| current | ui |
|
Comment Markdown Rendering System Design
Purpose and boundaries
UI owns the reusable interaction contract that distinguishes web links from file references in
rendered task messages and routes authorized file references into the active desktop or phone file
viewer. The task system remains authoritative for task-repository membership, session worktrees,
and the effective workspace_path; the workspace file service remains authoritative for final
filesystem containment and file reads.
An absolute path beneath a registered repository's source checkout is an alias for the matching file in the active task worktree. It is never authority to read or edit the source checkout. This design extends the task-workspace behavior in Attach Workspace Sources without changing its persistence, materialization, or file-service contracts.
Requirement mapping
| Requirement | Design sections |
|---|---|
REQ-UI-COMMENT-MARKDOWN-001 |
Trusted file roots, Link resolution, Responsive behavior, Failure behavior |
Components and responsibilities
TaskChatPanelis the task/session boundary. It supplies the shared Markdown renderer with the active session's effective workspace root, file-open action, and repository-source aliases.resolveSessionWorktreescombines restored session worktrees with live worktree state. Alias construction uses these repository-qualified worktrees rather than directory-name inference.repositories.itemsByWorkspaceIdsupplies each registered repository's canonicallocal_path. The active task's repository links restrict which workspace repositories can become aliases.MarkdownFileLinkContextcarries one stable file-link context through the transcript so message renderers do not independently subscribe to repository and worktree state.MemoizedMarkdownpreserves an inherited task-level file-link context while allowing an explicit message renderer to override the workspace root or open action.- The shared Markdown anchor resolver classifies and normalizes a link, produces a task-workspace-
relative file target when authorized, and calls the existing
onOpenFileaction after preventing browser navigation. - Existing desktop
useFileEditorsand phoneMobileFileViewerPanelflows fetch and display the resolved task-workspace file. They do not receive the registered checkout path.
Trusted file roots
The task chat boundary builds repository-source aliases from authoritative identities already in frontend state. An alias has this logical shape:
repository_id
source_root canonical Repository.local_path
workspace_relative_root matching session worktree path relative to workspace_path
An alias is eligible only when all of these conditions hold:
- The repository is linked to the rendered message's task.
- A worktree in the rendered message's session has the same
repository_id. - The repository has a non-empty canonical
local_path. - The matched worktree path equals the effective workspace root or is contained beneath it on a path-segment boundary.
For a legacy single-worktree session that has no worktrees collection, the session's
repository_id and worktree_path provide the same identity-qualified fallback. An empty,
ambiguous, stale, or cross-task association produces no alias. Nested source roots are evaluated
most-specific-first and must match on a complete path segment.
The effective workspace root remains workspace_path, falling back to worktree_path for legacy
session projections. Repository aliases supplement that runtime root; they never replace it.
Link resolution
The shared resolver performs these steps in order:
- Preserve fragment-only links and links with an explicit web or application URL scheme as normal navigation targets.
- Decode the path once, remove query/fragment data used only by Markdown navigation, normalize supported path separators, strip a supported trailing source-location selector, and reject malformed encoding, home-relative syntax, or parent traversal. Windows drive roots accept an optional leading slash and use case-insensitive containment after normalization.
- Resolve an absolute path already beneath the effective workspace root directly to a workspace-relative target. This remains the preferred path for single- and multi-repository tasks.
- Otherwise, match the absolute path against an eligible registered source root. Replace only
that root with its
workspace_relative_root, append the file suffix, and return the resulting workspace-relative target. - Apply the existing file-shape check and invoke the current file-open action. The source checkout path never crosses the file-open boundary.
For example:
source_root: /home/user/projects/example
active workspace root: /home/user/.kandev/tasks/task-a
active worktree: /home/user/.kandev/tasks/task-a/example
message href: /home/user/projects/example/ui/bundle.js:61
file-open target: example/ui/bundle.js
The mapping is based on repository_id, not a shared basename such as example. This prevents two
same-named repositories or an unrelated sibling directory from becoming accidental authority.
Failure behavior
- An absolute host path that matches neither the active workspace root nor an eligible repository
alias is non-actionable. Its anchor prevents same-origin
/home/...,/Users/..., drive-path, or equivalent filesystem navigation and does not call the workspace file service. - An absolute path contained by a trusted root but not recognized as an openable file is also non-actionable, so a directory or extensionless target cannot fall through to browser navigation.
- A missing repository cache, worktree projection, task link, or identity match fails closed. A later state update can make the link actionable without rewriting stored message content.
- External HTTP, HTTPS, mail, issue, and application links keep their existing navigation behavior.
- If a resolved task-workspace file no longer exists, the existing desktop or phone file-open flow surfaces its localized failure feedback. Resolution does not fall back to the registered source checkout.
- Backend path containment and symlink checks remain the final authorization boundary even after a frontend alias has produced a workspace-relative target.
Responsive behavior
This change does not alter chat composition, link styling, scroll ownership, navigation, or touch geometry. The inline Markdown link remains the entry point at every viewport.
On desktop and compact desktop, selecting a resolved link opens or focuses the existing Dockview
file tab. On phone, the same semantic action switches to the existing Files surface and renders the
file in MobileFileViewerPanel; back/close behavior, the single viewer scroll owner, dynamic
viewport handling, and safe-area behavior remain unchanged. The nearest shipped phone exemplar is
the absolute attached-worktree link flow in mobile-add-workspace-sources.spec.ts, which already
proves the native file viewer rather than a compressed desktop editor.
Because the repair changes target normalization rather than layout or controls, no new mobile surface is introduced. Desktop and phone end-to-end scenarios use the same repository identity mapping and prove the same file content is opened from the active workspace.
Testing
- Pure unit coverage builds eligible aliases for single- and multi-repository sessions and rejects missing task membership, mismatched repository IDs, paths outside the active workspace, ambiguous roots, stale worktrees, traversal, unsupported contained targets, and unmatched Windows paths.
- Shared Markdown component coverage reproduces a registered source checkout link with a trailing line selector, asserts the workspace-relative open target, and asserts an unrelated host path is inert.
- Memoization/context coverage proves a task-level alias context reaches nested agent and user message Markdown without replacing explicit open actions.
- The existing desktop attach-workspace-sources scenario cites the attached repository through its
original
local_pathand asserts the active worktree file content and tab while the task URL remains unchanged. - The existing phone scenario taps the same form of source-path link and asserts the native viewer, expected active-worktree path/content, close control, and absence of document horizontal overflow.
Observability
Resolution is deterministic frontend behavior and adds no log or metric. Existing file-open errors remain observable through the localized failure notification and browser diagnostic history. Tests cover the fail-closed paths directly.