Ports settled-reach's decisions_sync.py + ticket + decision scripts,
Scrum-stripped. Writes to .pql/pql.db (gitignored). Verb shape
mirrors the eventual `pql` subcommands so migration is a call-site
find-replace once pql ships feature parity (D-040, R-011).
Ticket IDs are T-NNN (TEXT PKs) — reshape from settled-reach's
integer auto-increment so the stopgap's writes are compatible with
pql's future reads without a data migration. No `sprints` table; the
pyramid is kanban / waterfall (D-035).
Verified end-to-end: sync parses 71 records (39 confirmed, 23 open
questions, 9 rejected) with 62 cross-refs and zero broken links;
`ticket new`, `ticket status`, `ticket board`, and `decisions show
--with-refs` all round-trip correctly.
Co-Authored-By: Claude <noreply@anthropic.com>
tools/ui/ — scripts + Playwright config that let Claude Code (and
humans) drive the Flutter WASM build in a real browser. The point is
to avoid screenshot round-trips: every interactive widget in clide
ships a Semantics wrapper anyway (a11y requirement), so the automation
layer just queries the `flt-semantics[aria-label]` DOM.
* build.sh — `flutter build web --wasm` from app/
* serve.sh — `python3 -m http.server 4280` in the background.
Before binding, kills any stale listener on the port (orphans
from earlier failed runs no longer accumulate).
* stop.sh — port-based kill; escalates to SIGKILL after 300ms.
The pidfile is now advisory — port ownership is the source of
truth.
* driver.ts — `ClideDriver` class with `byLabel`, `click`, `type`,
`readText`, `screenshot`, `dumpSemanticsTree`, and
`waitUntilReady` that auto-clicks the `flt-semantics-placeholder`
so the semantic tree is populated before queries.
* tests/smoke.spec.ts — first driver test; asserts welcome +
disconnected labels surface in the Semantics DOM.
Co-Authored-By: Claude <noreply@anthropic.com>