Files
settled-reach/.claude/skills/pql-board/SKILL.md
T
jpmschweitzerandClaude Opus 5 338644b409 refactor(tooling): T-1286 — generate, pr and dev become reach domains
Twelve scripts retired, three domains registered. `reach` now covers nine.

generate: `generate-brands` and `generate-corporations` were the second and
third copies of the same 24-line build-if-missing-then-exec bash `tooling/atlas`
carried, so they collapsed into `core.process.cargo_binary` rather than being
ported. `import_economics` shelled out to the first of those, so it now calls
that helper — `generated_brands.toml` comes back byte-identical, and the stamp
registry swaps the retired wrapper for `core/process.py`.

pr: `watchlist-diff` derives its watched set from `generator_sources.py` instead
of restating it, so it cannot drift from the stamp check.

dev: the environment scripts split decision from performing, per D-263's
guarded-exec rule. `godot_plan()` and `worktree_plan()` decide what would
happen; `install_godot()`, `install_rust()` and `setup_worktree()` do it.
`tooling/test_environment.py` pins the version pin, both override precedences,
the already-current skip, the platform refusal and both worktree refusals —
none of them performed. `make setup` now installs reach first, since the
targets that install rust and godot are reach verbs.

Two live bugs found while porting:

- The clerk read its decision index from `decisions/README.md`, a path that
  stopped existing when the DQR tree moved to `governance/`. Every clerk agent
  has been grepping blind; its prompt pointed at the same dead directory.
- The conformance exec-check matched any `x.system()` regardless of receiver,
  so `platform.system()` read as `os.system()`. Narrowed and re-proved against
  a real mutant.

`process.run` gains `input=`, `timeout=` and a `ProcessTimeout` subclass so a
killed run stays distinguishable from a verdict. The pre-push hook no longer
merges the clerk's stderr into its stdout — under streaming the last merged
line is a JSONL event, which would read as an unrecognised verdict and block.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:00:46 +02:00

4.3 KiB

name, description
name description
pql-board Two board surfaces for pql in the Claude desktop app: (1) refresh (or first-create) the ticket-board Artifact — the clide-style two-column snapshot at a stable claude.ai URL; (2) render an interactive in-chat board panel with one-click ticket pick-up (sendPrompt). Use when the user says "refresh the board", "update the pql board", "/pql-board", "board panel", "pick-up panel", or asks to see/act on the ticket board in the desktop app.

pql Board Artifact

The board is a self-contained interactive HTML snapshot of the pql ticket store (status-grouped rail, ticket dossier with dependencies/children, filter, deep-links), deployed as a claude.ai Artifact. Artifacts are network-sandboxed, so the page is a snapshot — this skill regenerates and redeploys it.

Canonical URL (redeploy target — keep stable so the user's open tab survives):

https://claude.ai/code/artifact/0064e59d-84a9-4a31-a830-f7d677df9d92

Workflow

  1. Generate the fragment (from the repo root; pql must resolve to this vault — run from the MAIN checkout, not a worktree):

    reach pr board-html --output <scratchpad>/pql-board.html
    

    The script pulls pql ticket list --full, batch-fetches blocker edges for non-terminal tickets, and pql plan status for the header. Output is an Artifact-ready fragment (no doctype/html/head/body).

  2. Deploy with the Artifact tool, always passing the canonical URL so the existing artifact updates in place (a fresh session without url would mint a new address and orphan the user's tab):

    • file_path: the generated file
    • url: the canonical URL above
    • favicon: 🗂️ (keep stable — the user finds the tab by it)
    • label: short, e.g. board-YYYY-MM-DD
  3. Reply with one line: ticket/edge counts from the generator's stdout + the URL. The user reloads their tab.

Notes

  • If the Artifact call 409s (another session touched it), reconcile then retry with force only if intentionally replacing.
  • If the canonical artifact was deleted and a new URL gets minted, update the URL in this skill file in the same turn and commit it.
  • The generator is reach pr board-html (python, pql-native JSON only). Known scope limits vs clide: no decisions view, no kanban columns, no live editing. Dep edges cover non-terminal tickets only — if pql grows ticket list --with-blockers, simplify the script (candidate upstream FR per the pql-gaps-go-upstream rule).

Interactive panel (in-chat, one-click pick-up)

The artifact is reference-only (sandboxed: clipboard-out is its ceiling). Real one-click interactivity lives in the in-chat widget — render it when the user asks for "the panel", "pick-up panel", or wants to act on tickets rather than read them.

  1. Generate the widget code with the bundled script (template + live pql data — do NOT hand-compose the HTML):

    .claude/skills/pql-board/support/make-panel          # stdout = widget_code
    

    Flags: --phase T-NNN (default T-750, the active phase epic), --cap N (default 10). Contents: actionable WIP + unblocked leaf work from the active phase AND the maintenance initiative (T-1037), sorted status→priority→recency, overflow noted in the panel. Template: support/panel-template.html (edit that file to change the panel's look; the pick-up/detail sendPrompt contract lives there).

  2. Render with mcp__visualize__show_widget: load its read_me first (interactive module) if not yet loaded this conversation, then pass the script's stdout verbatim as widget_code.

  3. On receiving a "pick up the following ticket:" prompt (from the panel or typed): treat it as single-ticket batch activation — the /whats-next Step-3 flow scoped to one ticket: show the ticket with --with-context, set it in_progress, create the topic branch/worktree if it is code work, and start the work (or spawn the implementing agent per standing practice). Ticket mutations must land in a commit per the changelog persistence rule.

Widget read-back does NOT work (verified 2026-07-24: read_widget_context returns empty for these widgets before and after interaction) — the panel talks to Claude via sendPrompt only; never design around querying it.