Deep analysis of migrating decisions + ticketing from the SQLite CLI to pql: - pql-migration.md: phased branch-only plan (decisions→DQR tree, ticket changelog seed with T-N≡#N, #N→T-N find-replace, big-bang consumer cutover, docs/workshops/ wiki fold-in, SQLite retirement) + verification gate + benefits. - pql-requirements.md: 9 surfaced gaps for the pql team (critical: seeded ticket ids + counter-advance; high: dup-id detection, core.hooksPath awareness, changelog seed-format docs). Key finding: milestones are vestigial (milestone_deps empty, 42/1013 tickets linked) → mapped to a label, dropped as an entity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.4 KiB
title, description, type, status, created, updated, tags
| title | description | type | status | created | updated | tags | |||
|---|---|---|---|---|---|---|---|---|---|
| pql Requirements — gaps surfaced by the Settled Reach migration | Feature gaps and friction points found while migrating a 1013-ticket + 358-decision project onto pql, for the pql team | requirements | draft | 2026-06-06 | 2026-06-06 |
|
pql Requirements — gaps for the pql team
Compiled while planning a migration of The Settled Reach (a Godot+Rust game) off a SQLite-backed ticket CLI + markdown-decisions-sync onto pql. Scale: 1013 tickets, 481 dependency edges, 48 labels, 16 history rows; 358 decision records (237 D / 109 Q / 12 R) with 1040 cross-refs; a ~4385-file markdown vault (decisions, docs, 22 workshops, ~3500 wiki pages).
pql is a strong fit and most of our system maps cleanly. The items below are the gaps we hit. Each lists why we need it, a suggested shape, and a priority. Where we have a viable workaround, it's noted — but the workaround is friction we'd rather not ship.
1. Seeded / explicit ticket IDs + id-counter advance — CRITICAL
Why. Our 1013 tickets have integer ids referenced as #440 in thousands of immutable git
commits, in decision "Ticket: #N" lines, and by our PR automation. We need the pql id to preserve
the number — T-440 ≡ old #440 — so the mapping stays a trivial bijection. pql's id is a TEXT
column, so we can seed 'T-440' directly into the changelog. But pql ticket new auto-assigns
T-N sequentially starting at T-1 — after seeding T-1…T-1013, the next native create would
mint T-1 and collide.
Suggested shape.
pql ticket new --id T-1014(accept an explicit id), AND/OR- on
pql plan rebuild/import, advance the internal id counter past the max existing id so native creates never recycle a seeded id.
Workaround. None safe — we'd have to hold all native ticket creation until this lands, or hand-manage a sentinel. This is the one true blocker for a clean numbered migration.
2. Duplicate-ID detection in pql decisions sync / validate — HIGH
Why. Our markdown had a genuine integrity bug: D-035 defined in two domain files, R-011
defined three times. pql decisions sync silently last-writer-wins-collapsed them (reported
broken: 0), so the duplication was invisible. Our legacy Python check-dupes errors on it. A
duplicate canonical-ID is almost always a data bug, not an intended merge.
Suggested shape. pql decisions validate (and sync --strict) should warn or error on
duplicate D/Q/R ids across the vault, reporting file:line for each occurrence.
Workaround. Keep our Python check-dupes wired into pre-commit alongside pql — extra tooling
we'd like to retire.
3. core.hooksPath awareness in pql init — HIGH
Why. Our repo sets core.hooksPath = .config/hooks (so hooks are version-controlled). pql init
installs its pre-commit/post-merge/post-checkout hooks into .git/hooks/, which git ignores when
core.hooksPath is set. pql's changelog-staging + replay hooks would be silently dead — the whole
durable-versioning model depends on them. This is a quiet, dangerous failure (data appears to work, but
.pql/changelog/ is never staged).
Suggested shape. pql init/pql doctor should detect core.hooksPath and either install into the
configured dir or emit a loud warning with the manual fold-in snippet.
Workaround. Manually fold pql's hook logic into .config/hooks/* (we will), but pql doctor
should flag the mismatch so others don't get bitten.
4. Document the changelog seed format + hash/canonical_version algorithm — HIGH
Why. pql plan import --legacy is documented as retired; the real durable artifact is
.pql/changelog/<table>/<YYYY-MM>.sql. To migrate 1013 tickets we must emit those SQL rows directly.
We reverse-engineered the UPSERT shape, but the per-row hash and canonical_version columns +
the LWW guard (WHERE excluded.updated_at > … OR (… AND excluded.hash > …)) are opaque. If our seed
hashes don't match pql's scheme, later native edits still win by updated_at (so correctness holds),
but the LWW tie-break becomes unpredictable.
Suggested shape. Document (a) the canonical changelog row format per table, (b) the hash
input + algorithm, (c) how canonical_version increments — OR provide a supported one-shot
pql plan seed <json|csv> import that computes them.
Workaround. Emit our own stable per-row hash; rely on updated_at for correctness. Works, but
under-specified.
5. Bare-text reference detection / auto-link for D-NNN / T-NNN — MEDIUM
Why. Across ~4385 vault files (esp. ~3500 generated wiki pages), decision/ticket references are
mostly bare text (D-117, #440) rather than markdown/wikilinks. pql search finds them, but
pql backlinks/related only follow real links — so "what references D-117?" is incomplete. Rewriting
3500+ generated files to add links is impractical (they're regenerated from a pipeline).
Suggested shape. Optional reference recognizers (configurable regex → entity, e.g.
D-\d+→decision, T-\d+→ticket) so backlinks/related treat bare-text mentions as edges without
requiring link syntax in source.
Workaround. Adopt a link convention in authored files only; accept that generated files are search-only.
6. WIP-limit enforcement (advisory) on ticket status … in_progress — MEDIUM
Why. Our kanban flow enforces a WIP limit (max 5 in_progress); our activation step relied on the CLI warning when exceeded. pql has no forced state machine / WIP concept.
Suggested shape. An optional config (wip_limit: 5) that makes pql ticket status <id> in_progress
emit an advisory warning (not a hard block) when the in_progress count would exceed it.
Workaround. Compute pql ticket list --status in_progress | count in our skill and warn there.
7. Fan-out / downstream-unblock count on ticket show — MEDIUM
Why. Our batch-selection ranks ready tickets partly by fan-out — how many downstream tickets a
ticket unblocks. We currently compute it from the dependency graph. pql has --unblocked (great — it
replaced our manual blocker walk) but no "how many does this unblock" signal.
Suggested shape. A blocks_count / unblocks_count field on pql ticket show/list, or a
pql ticket list --orders-by fanout.
Workaround. Compute client-side from ticket show --with-context dep data.
8. Multi-team / array team field — LOW
Why. One ticket carries a comma-separated team (client,server). pql treats team as a single
string. (1 of 1013 — genuinely low.)
Suggested shape. Accept an array/comma team, or document the single-team constraint.
Workaround. Pick the primary team for the one affected ticket.
9. Bless a "labels-as-milestones" pattern (or an optional milestone entity) — LOW / nice-to-have
Why. Our schema has a milestones subsystem (milestones, ticket_milestones, milestone_deps,
cascade_phase) — but in practice it's vestigial: 2 milestones (1 active, 1 completed-with-0-tickets),
milestone_deps empty, 42/1013 tickets linked, cascade ordering never used. We're mapping the one
active milestone to a label (phase:4) and dropping the rest. This works today, so it is not
blocking.
Suggested shape. Either bless labels-as-milestones as the documented pattern (with helper queries / rollups), or offer an optional lightweight milestone entity (M:N ticket links, status, ordering, done/total rollups) for projects that actually use phase gating. We'd only need the latter if we later adopt real cross-phase dependency gating.
Summary
| # | Requirement | Priority | Have workaround? |
|---|---|---|---|
| 1 | Seeded/explicit ticket id + counter-advance | Critical | No (real blocker) |
| 2 | Duplicate-ID detection in sync/validate | High | Yes (keep our checker) |
| 3 | core.hooksPath awareness |
High | Yes (manual fold-in) |
| 4 | Documented changelog seed format + hash algo | High | Partial |
| 5 | Bare-text ref detection for backlinks | Medium | Partial (authored files only) |
| 6 | WIP-limit advisory | Medium | Yes (skill-side) |
| 7 | Fan-out / unblocks count | Medium | Yes (client-side) |
| 8 | Multi-team field | Low | Yes |
| 9 | Labels-as-milestones blessing / optional entity | Low | Yes (labels) |