WS-27 S5 + the docked-pane overflow fix: the Projects detail panel stops being a plain form - #420
Merged
Merged
Conversation
Owner-reported from the deployed app, with a screenshot: text overflowing in the /tasks detail. My regression from S2. ItemDetail was built for the max-w-3xl modal, where `grid-cols-2` is comfortable. Docking it into DESIGN_SYSTEM 6's 380px pane left each cell about 170px, and labels collided with their values. A media query cannot fix this -- the pane is 380px on a 4K monitor too, so viewport tells you nothing about the space the component actually has. The switch is `focused`, the prop that already distinguishes the modal from the pane and was threaded for exactly this two-mode life. Docked: one column. Maximised/phone modal: two, unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmFScimSbeyHcLdut7RT4W
Owner-reported from screenshots of the deployed app, comparing the two
detail panels side by side ("Task cards seem to be very different").
The standing ruling this wave is "Projects is canonical, Tasks conforms";
this surface is the exception and the direction is reversed, the same way
S4 reversed it for the empty states and the active token.
- `app/projects/components/TaskPanel.tsx`: header (ref · title · actions ·
StatusChip row) → grouped DETAILS cells → discrete sections (Description,
Properties, custom Fields, Links & subtasks, Files, Activity) → pinned
comment composer, on ItemDetail's `SectionLabel`/`MetaEdit` grammar. One
scroll region instead of two. Status is a themed `<Select>` and the file
input is hidden behind a `<Button>` that lists the filenames going up.
Everything Projects has and Tasks does not — ref, tags, relations,
watchers, recurrence, custom fields, timeline, comments — stays; nothing
Tasks-only (context, energy, priority matrix) is imported.
- `components/ui/Input.tsx`: adds `Select`, the themed single-choice field
the tree never had (38 files hand-roll one), plus a `ref` on `Textarea`.
- `lib/theme/conformance.test.ts`: rule 7 — raw `<select>` (per-file
ratchet, 37 files baselined) and a file input that is not hidden behind
a real control (absolute, zero offenders). SOLID_BUTTON_DEBT 30 → 29.
Frontend only: no migration, no API change, no new dependency.
No browser was run — Playwright cannot install here — so the phone
viewport and the Fluent → Material → Graphite sweep are owed at review.
…(R4) The first draft of conformance rule 7's file-picker half tested `/\bhidden\b/` over the whole tag, which `aria-hidden` satisfies — so deleting `className="hidden"` from the converted input left the gate green. Measured by mutation, not reasoned. It now matches the class attribute (`hidden` or `sr-only`), and that mutation is red. R4: project_management_app.md's status header carries S5, and §11.22 is the as-built record (control inventory before/after, why `Select` is a seam and not a one-off, the section structure, the docked-width rule, the two scanner traps, and what is owed — no browser runs here). It also records that §10's verification block names two pytest files that no longer exist. control_plane/AGENTS.md: rule 3 names `Select`/`Textarea` and the hidden file-input rule; the conformance suite is seven rules, not six.
37 files remain baselined (nine in app/projects/, five in app/tasks/), 38 before this change. The first draft said 'four in app/projects/', which was wrong — the code is the fact.
…sk-management-a1f6zd
Owner-reported from screenshots of the deployed app, comparing the two panels side by side. Direction REVERSED from this wave's standing ruling: /projects was the weaker surface here, so Projects conforms to Tasks -- the same way S4 already made Projects conform on empty states and the active token. What it was: a plain vertical form. A bare native <select> for status, a raw <input type="file"> rendering as the browser's own "Choose Files / No file chosen", flat labels stacked with no grouping, one import from ui/. A native control expresses neither Material's pill buttons nor Graphite's uppercase, so this was a straight breach of rule 3, not a style preference. Now: header (ref, copy-link, watch, close) over one scroll region grouped as DETAILS / DESCRIPTION / PROPERTIES / LINKS & SUBTASKS / FILES / ACTIVITY, with a pinned composer. Status is a StatusChip carrying Projects' richer data -- stored colour outranks category -- so it reads better than /tasks', not worse. Controls: Select, Input, Textarea, Button, Badge. The file picker is a Button raising a hidden input, with filenames listed by the app. Two seams the product needed anyway: - ui/Input.tsx gains Select. There was no single-choice field at all, so 38 files had each copied their own class string; 37 remain, baselined. - Conformance gains rule 7: raw <select> (ratcheted) and an absolute rule that a file input must be hidden behind a control. Both mutation-measured red. SOLID_BUTTON_DEBT 30 -> 29, since a raw bg-primary button went with it. Single column with NO responsive variant, deliberately: the page lifts the width cap on a phone, so a sm: breakpoint keys off the viewport and would split the column on a large monitor. That is the mistake I made in ItemDetail an hour earlier and it is fixed here by construction. Also fixed, found by S5 while running it: spec 10's verification block named test_projects_sync.py and test_projects_personal_mirror.py, neither of which exists. pytest exits non-zero on a missing path before running anything, so the block produced a red run unrelated to the caller's change. Now runs: 384 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmFScimSbeyHcLdut7RT4W
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.
Both changes come from the owner testing the deployed app and sending screenshots of the two detail panels side by side. Frontend only — no migrations, no backend.
1. The overflow was my regression
ItemDetailwas built for themax-w-3xlmodal, where itsgrid-cols-2details block is comfortable. #419 docked it into DESIGN_SYSTEM §6's 380px pane, which left each cell ~170px and collided labels with values.A media query cannot fix this — the pane is 380px on a 4K monitor too, so the viewport tells you nothing about the space the component actually has. The switch is
focused, the prop that already distinguishes the modal from the pane. One column docked, two when maximised.2. Direction reversed: Projects conforms to Tasks here
The standing ruling this wave has been "Projects is canonical, Tasks conforms." Not on this surface. Measured on
54e4b880,/projects'TaskPanelwas the weaker one:<select><Select>in aStatusChipfield cell<input type="file">→ "Choose Files / No file chosen"<Button>raising a hidden input, filenames listed by the app<textarea>+ rawbg-primarybutton<Textarea>+<Button>@/components/uiimportsStatusChipA native control expresses neither Material's pill buttons nor Graphite's uppercase labels, and "Choose Files / No file chosen" is the browser's string in the browser's font — no theme can reach it. This was a breach of rule 3, not a style preference.
Projects' richer status data is used rather than flattened:
accentForStatustakes the owner's stored colour first, then the category, so the chip reads better than Tasks', not merely equal.Two seams the product needed anyway
ui/Input.tsxgainsSelect. There was no single-choice field at all, so 38 files had each copied their own class string. 37 remain, baselined and ratcheted down-only. Honest limit recorded in the doc comment: the popup option list belongs to the browser and does not take our tokens.<select>(ratcheted per file) plus an absolute rule that a file input must be hidden behind a control. Both mutation-measured red, file restored byte-identically.SOLID_BUTTON_DEBT30 → 29.The mistake that did not repeat
The Projects panel is single-column with no responsive variant, deliberately. The page lifts its width cap on a phone, so a
sm:breakpoint would key off the viewport and split the column on a large monitor — which is exactly the error I made inItemDetailan hour earlier. Fixed there by prop, avoided here by construction.Also fixed: the spec's own verification command was broken
§10 named
test_projects_sync.pyandtest_projects_personal_mirror.py. Neither exists — the second istest_projects_personal.py, the first never landed under that name. pytest exits non-zero on a missing path before running anything, so anyone pasting that block got a red run unrelated to their change, and anyone "fixing" it by deleting the path silently dropped coverage. Corrected and run: 384 passed.Verification
tscclean · 1634 tests / 76 files · theme suite 361 · productionnext buildcompiles and prerenders all 39 pages · the corrected spec block 384 passed.Owed, and not claimed
No browser ran — Playwright cannot install here — so the phone viewport and the Fluent → Material → Graphite sweep on
/projectsand/tasksbeside it are still owed. Every icon name introduced was checked against the theme registry for all packs; two pre-existing names (Repeat,BellOff) are not in it and fall back to Lucide regardless of theme, which is a finding, not something this PR caused.Also recorded:
SectionLabelis now a deliberate second copy, labelled as such in its doc comment — promoting it tosrc/components/touchessrc/app/tasks/**, which another change holds open.Generated by Claude Code