fix(desktop): draw an issue in the plan's own document column
Summary
Follow-up on the plan Improve Issues editor and Plan editor (https://tryluke.dev/stage/plan/675f8e6b-8ab5-4c81-bbcf-ed20dd63ceb7). The developer saw that the issue page's editor was only as wide as the title, and asked that it use the plan's width and reuse the plan's pieces so the two feel the same.
The bug. #3642 moved the issue page onto @luke/ui's DetailPage and put its body in .desktop-document > .plan-body. .desktop-document is a flex column and .plan-body centres itself with auto margins, so the body shrank to its widest line. An issue with no description stood only as wide as its title (244px in the harness). One with text stopped at 710px instead of the plan's 720px, under a 16px top gap instead of the plan's 40px, and with no faded edges.
The fix: one document frame, shared (tasks/document-frame.tsx):
DocumentScrollis the plan's own scrolling column (.plan-document-scroll.scroll-fade). It sets the page inset, the room under the last line, the faded edges, selectable words, and Luke's accent. The plan, a version, the version skeleton, and now the issue and its reading skeleton all use it, andeditor-picturesfinds its scroller by the same class constant.DocumentBylineis the line under the title: "Created by [face] name · 3d ago", then the page's own phrase for the last edit. The plan's byline and the issue's byline both use it, and the duplicate.issue-bylineCSS is gone.- The issue page (
IssueDocumentPageinissue-detail.tsx) is now the toolbar over.desktop-document > DocumentScroll > .plan-body, the same structure a plan uses, so the title, byline, and text stand at the plan's 720px measure and 40px inset. The reading skeleton stands in the same frame, so the issue lands where its blocks stood. The "This issue is gone" / "could not be read" state keepsDetailPage's centredErrorState. - The description at rest is a plan block (
plan-document-block, the plan's cursor and focus tint). It is drawn a section at a time, as plan sections are, so its sections stand the plan's 1.5em gap apart (.document-sections). This is also the gap the editor stands them at, so opening the editor no longer moves the text. The issue-only focus-tint CSS is gone. - Under the editor, a refused save, an over-long text, or an editor that failed to load now uses the plan's own note (
.plan-editor-note). A refused save gets the plan's Retry button, which sends the words again. The red.issue-description-refusedstyle is gone.

Evidence
- Layout: the real pages were rendered by a throwaway jsdom test, painted by Chromium at 1100px with the desktop stylesheet built from this branch, and then measured. Plan: title and body at 720px wide, top at 91px. Issue before: 710px, top 67px, and 244px when the description was empty. Issue after: 720px and top 91px with or without a description. The gap between sections is the same as the plan's (Goal → Risks 110px in the issue vs 109px in the plan).
pnpm exec vitest run --project desktop: 218 files, 2115 tests passed.- New tests in
issues-page.test.ts:- The issue's document stands in a plan's own column and body.
- The reading blocks stand in that column.
- The description is drawn a section at a time.
- Retry under a refused save sends the words again.
tscfor the desktop app,pnpm lint,pnpm knip, andpnpm discarded-effectall exit 0.discarded-effectwas run alone because of the sandbox's memory.
Invariants
-
./scripts/check.shgreen. (CI) - JSON Schema goldens unchanged.
- Every behavior change has, in this PR, the test that fails without it; no change-detector test added. The width itself is CSS that jsdom can't lay out, so the measurements above are its evidence.
- Gateway envelope goldens unchanged.
- No new
astype assertion outside the allowlisted files. - No
Effect.runPromise/runSync/runForkoutside the runtime edges. - No new
setTimeout/setInterval/new Promise/AbortController/fs.watch. - No Effect, Stream, or Layer written as a statement and dropped.
- Test count: issues-page.test.ts 69 → 73.
- Each hand-rolled copy this replaces is deleted:
.issue-document,.issue-byline,.issue-descriptionfocus tint,.issue-description-refused. - Every
CLAUDE.md/AGENTS.mdsentence naming a changed part is edited (none name it). -
PRIVACY.mdunchanged. - Package deps unchanged.
- Barrel/door rule unaffected.
Notes
- This takes the issue page back off
DetailPage, which #3642 adopted, except for its error state. A document page now uses the plan's document frame instead, which is what the plan's own page, its versions, and the plan template already do.
Made with Luke