Skip to content

WS-27 S5 + the docked-pane overflow fix: the Projects detail panel stops being a plain form - #420

Merged
vjvarada merged 6 commits into
mainfrom
claude/paca-research-task-management-a1f6zd
Aug 10, 2026
Merged

WS-27 S5 + the docked-pane overflow fix: the Projects detail panel stops being a plain form#420
vjvarada merged 6 commits into
mainfrom
claude/paca-research-task-management-a1f6zd

Conversation

@vjvarada

Copy link
Copy Markdown
Contributor

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

ItemDetail was built for the max-w-3xl modal, where its grid-cols-2 details 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' TaskPanel was the weaker one:

before after
Status bare native <select> <Select> in a StatusChip field cell
Files raw <input type="file">"Choose Files / No file chosen" <Button> raising a hidden input, filenames listed by the app
Comment raw <textarea> + raw bg-primary button <Textarea> + <Button>
Layout flat stack of labels header · DETAILS · DESCRIPTION · PROPERTIES · LINKS & SUBTASKS · FILES · ACTIVITY, one scroll region, pinned composer
@/components/ui imports 1 4 + StatusChip

A 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: accentForStatus takes 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.tsx gains Select. 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.
  • Conformance rule 7 — raw <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_DEBT 30 → 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 in ItemDetail an 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.py and test_projects_personal_mirror.py. Neither exists — the second is test_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

tsc clean · 1634 tests / 76 files · theme suite 361 · production next build compiles 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 /projects and /tasks beside 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: SectionLabel is now a deliberate second copy, labelled as such in its doc comment — promoting it to src/components/ touches src/app/tasks/**, which another change holds open.


Generated by Claude Code

claude added 6 commits August 10, 2026 13:57
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.
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
@vjvarada
vjvarada merged commit 0afa05d into main Aug 10, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants