5 Commits

Author SHA1 Message Date
Patrick Buckley b991dc2e83 fix: CI lint pin + copilot-thread hardening
The wiring-lint test used percent-formatted regex patterns — UP031 under
the ruff 0.15.6 the CI pre-commit pins (the older venv binary let it
through; checked repo-wide against the exact pin now). f-strings with
doubled quantifier braces, plus one over-long fixture line in the
livepass generator split.

Copilot threads, both validated rather than blindly applied:
- closeShelf's scrim-ownership scan now skips detached entries. The
  thread's throw scenario doesn't occur on the real removal path (a pane
  close detaches an ANCESTOR, so _hostOf still resolves inside the
  detached subtree) — but a detached shelf is genuinely not a scrim
  owner, so the guard is correct beyond being defensive.
- toast.js drops the popover attribute via removeAttribute instead of
  the null assignment. The claim that null leaves popover="null" is
  refuted — the IDL is nullable and null removes the attribute (verified
  empirically in headless Chrome) — but removeAttribute reads correct
  without requiring that spec knowledge.
2026-06-10 13:31:53 -07:00
Patrick Buckley 8b5af71603 fix(console): wire the model shelf's Save button — and lint the whole class
The models conversion dropped the legacy onclick= from the submit button
and wired detect/recalibrate/capgrid/thinking in the boot IIFE — but never
the submit itself. submitCreateModel existed with nothing calling it: Save
clicked dead with no error, exactly what a live test surfaced. None of the
gates could see it — the markup lint checks anatomy not wiring, the review
finders verified the submit function's internals, and the livepass renders
never clicked Save.

Audited every id-bearing button inside every dialog.hatch across both apps
for click wiring: model-create-submit was the only true positive (the
batch confirm buttons wire through cards.js's $() prefix helper and
new-ws-submit wires 106 lines from its getElementById — audit false
alarms). The new test_every_hatch_button_is_wired pins the class: direct
getElementById wiring or wiring through the assigned variable, with the
two prefix-built cards.js ids allowlisted; verified it fails against the
pre-fix tree. Livepass now drives the actual click: Save → busy → one PUT
→ shelf closes + toast.
2026-06-10 13:31:53 -07:00
Patrick Buckley c592486046 feat(ui): standalone app onto the hatch dialog tier — new-ws, rename, confirms
The ui app has no admin pane host and new-ws is a launcher invokable from
anywhere, so every surface lands on the document-modal DIALOG tier (no
shelves).  hatch.css gains the one md dialog width (560) the v1 design
specified; the ui index links hatch.css/hatch.js alongside the other
shared assets.

- New workstream: md create dialog, WS-NEW plate; the fork path keeps its
  title/semantics (WS-FORK plate, skill + attach rows hidden via the
  hidden attribute).  Submit brackets with setBusy; errors land in the
  sh-alert with the scroll-into-view rule.
- Rename: a styled prompt() — single field, Enter submits, no plate.
- Delete-workstream + revoke-MCP mirror the console confirm exactly:
  danger chrome, prose body, Cancel autofocus, red filled action.

The four hand-rolled focus traps, the Escape/overlay-click dispatch and
the body-overflow lock die (native dialog + hatch.js own all of it); the
global-shortcut handler defers on dialog:modal instead of the overlay-ID
list.  The forked legacy modal CSS leaves style.css (~270 lines).  The
markup-shape lint now scans both index files, the asset assertion and the
parse-time-bridge guard extend to the ui app.
2026-06-10 13:31:53 -07:00
Patrick Buckley 360ed48f62 fix(ui): design-review round — busy lock holes, broken auth cards, contrast
Dual design review of the shelf stack (one primed on intent, one cold)
adjudicated; accepted findings:

- The busy lock held the door for the mouse only: Enter on the focused
  primary re-fired submits, scrim clicks closed a shelf mid-flight, and
  the dialog tier's native Escape (cancel event) dismissed a busy confirm.
  One capture-phase guard + a scrim busy check + cancel interception close
  all three for every surface; setBusy now announces aria-busy. Contract
  assertions added to the busy test (it previously claimed scrim coverage
  it didn't assert).
- The MCP auth radio cards were destroyed by the .sh-body label cadence —
  the exact specificity war the toggle-switch/cap exceptions guard
  against, missed for .segmented-option. Restated at 0,2,1.
- Light-theme off-state toggle tracks measured ~1.2:1 (WCAG 1.4.11 wants
  3:1): real ink fill on light, recessed look kept on dark.
- Failed submits on tall shelves rendered out of view — _showModalError
  scrolls the alert into view. user-roles gains the missing busy bracket
  and in-shelf errors instead of toast-only.
- Toasts render under the dialog tier's top layer: promote the toast to a
  manual popover only while a modal dialog is open (popovers stack above
  later dialogs); attribute dropped after so the everyday fade survives.
- Ambient glow now follows the kind accent (cyan/red surfaces no longer
  bloom amber); skill editor's content column pins sticky so the SKILL.md
  pane stays visible while the meta column scrolls; MCP-detail stacking
  re-keyed from viewport to @container pane; dark-body micro-text and the
  origin badge brought up to the contrast floor; quiet foot actions
  (Validate regex, Detect) lifted from body-copy gray.
2026-06-10 13:31:53 -07:00
Patrick Buckley 5b0255f468 feat(ui): service-hatch container system — pane-scoped shelf + modal dialog tier
One chrome vocabulary (machined head/foot strips on --code-bg, kind LED,
designation plate, center-fading hairline), two mounting points:

- .hatch--shelf: pane-scoped, NON-modal (dialog.show()). Mounts inside the
  pane's .hatch-host, docks right, dims only that pane via a lazy sibling
  .pane-scrim, and contains focus with inert on the pane's other children —
  rail/tabs/other panes stay live. Split panes work by construction; the
  bottom-sheet degradation is an @container query on the pane, not a
  viewport media query, so a narrow split degrades too. Controller-owned
  Escape defers to any document-modal dialog stacked above.
- .hatch--dialog: document-modal (showModal()) confirm/show-once tier;
  top layer stacks it above any shelf with no z-index ladder.

Smart-input primitives ship alongside (seg, chips, readout, capgrid,
match-strip, rawhatch, autofill) for the Phase-1 surfaces. data-busy locks
the container while a submit is in flight (LED pulses, dismissal refused).
Light-theme micro-text gets the .tab-menu-key one-step-up contrast pass.
Classic scripts reach the ESM controller via the window.TurnstoneHatch
bridge (toast.js pattern, handler-time only); invariants pinned in
tests/test_hatch_js.py incl. a markup-shape lint over dialog.hatch.
2026-06-10 13:31:53 -07:00