diff --git a/.pql/changelog/ticket_history/2026-08.sql b/.pql/changelog/ticket_history/2026-08.sql index 924d723e4..37cf76f60 100644 --- a/.pql/changelog/ticket_history/2026-08.sql +++ b/.pql/changelog/ticket_history/2026-08.sql @@ -1218,3 +1218,28 @@ the same root cause — measuring VALUES without reading the CODE that produced them. First correction: tectonics is derived. Second: the whole continuous/ categorical split is an artefact of the resolution tiers. Both were available in a docstring the original pass grepped but never read.', NULL, '2026-08-19 22:52:46', '2026-08-19 22:52:46.636', '2026-08-19 22:52:46.636', NULL, '7fcf06ee54c5fbc71568e6b5e30760ff', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06G1RWR7M1H1PC3TY2RT7AC174', 'description', 'Answer Q-124. Investigation and costing FIRST — do not open with a rewrite. + +Price the cheap alternative before recommending Rust: a single Python dispatcher entrypoint (one command, many subcommands) would resolve the permission-prompt friction and most of the startup cost without touching the numerics. If that is enough, say so and close Q-124 with it. + +Scope order if Rust is chosen: port the check/gate family first (check-client-version, check-canvas-version, check-systems-db-stamp, check-dataflow-graph) — small, pure, no numerics, highest prompt-friction, runs on every push. Leave planet-gen numerics (numpy/scipy/PIL) in Python behind a stable interface. + +Hard constraint: import_economics is stamped by source SHA via tooling/generator_sources.py. Any port must keep the stamp/pre-push contract intact through the transition, not disabled during it. + +Outcome: a recommendation with costs, then a D-record and Jeroen''s ruling. Epics only after that.', 'Answer Q-124. Investigation and costing FIRST — do not open with a rewrite. + +Price the cheap alternative before recommending Rust: a single Python dispatcher entrypoint (one command, many subcommands) would resolve the permission-prompt friction and most of the startup cost without touching the numerics. If that is enough, say so and close Q-124 with it. + +Scope order if Rust is chosen: port the check/gate family first (check-client-version, check-canvas-version, check-systems-db-stamp, check-dataflow-graph) — small, pure, no numerics, highest prompt-friction, runs on every push. Leave planet-gen numerics (numpy/scipy/PIL) in Python behind a stable interface. + +Hard constraint: import_economics is stamped by source SHA via tooling/generator_sources.py. Any port must keep the stamp/pre-push contract intact through the transition, not disabled during it. + +Outcome: a recommendation with costs, then a D-record and Jeroen''s ruling. Epics only after that. + +2026-08-20 — Jeroen raised Typer (typer.tiangolo.com) as the Python-CLI option. Q-124 amended with the costing. + +Start here, not with Rust. Measured state: pyproject.toml already exists and make setup-venv already does pip install -e ''.[dev]'', but there is NO [project.scripts] entry — no console entrypoint exists at all. 22 tooling files already use argparse. So this is consolidation, not authorship. + +The real deliverable is ''one bare command reliably on PATH'', not ''a CLI framework''. A [project.scripts] entrypoint lands in .venv/bin/, which is on PATH only when activated — and agents and git hooks never activate it. That is the same split VENV_PY papers over in the Makefile, and the same failure recorded for tea (absolute path breaks the Bash(tea *) rule and prompts every time). Install via uv tool/pipx into ~/.local/bin, or symlink. A Typer app behind an absolute venv path solves nothing. + +Watch startup cost: a single entrypoint importing every subcommand eagerly pays all 123 modules on every invocation, four times per push. Lazy subcommand registration is mandatory, not an optimisation — measure before and after.', NULL, '2026-08-20 00:00:11', '2026-08-20 00:00:11.592', '2026-08-20 00:00:11.592', NULL, '45f6dd41a5c643a730209c654bd57c76', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-08.sql b/.pql/changelog/tickets/2026-08.sql index 9ea63295c..0e1a994bd 100644 --- a/.pql/changelog/tickets/2026-08.sql +++ b/.pql/changelog/tickets/2026-08.sql @@ -1185,3 +1185,20 @@ Scope order if Rust is chosen: port the check/gate family first (check-client-ve Hard constraint: import_economics is stamped by source SHA via tooling/generator_sources.py. Any port must keep the stamp/pre-push contract intact through the transition, not disabled during it. Outcome: a recommendation with costs, then a D-record and Jeroen''s ruling. Epics only after that.', 'backlog', 'low', NULL, 'tooling', 'Q-124', '2026-08-19 23:54:55.776', '2026-08-19 23:54:55.776', NULL, 'cfb08832c72e06f85b075a46cd3223f2', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= tickets.updated_at; +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06G1RWR7M1H1PC3TY2RT7AC174', 'story', '06FBPPMZNNEV052DBYYY3A897C', 'Investigate retooling Python tooling into a Rust CLI (Q-124)', 'Answer Q-124. Investigation and costing FIRST — do not open with a rewrite. + +Price the cheap alternative before recommending Rust: a single Python dispatcher entrypoint (one command, many subcommands) would resolve the permission-prompt friction and most of the startup cost without touching the numerics. If that is enough, say so and close Q-124 with it. + +Scope order if Rust is chosen: port the check/gate family first (check-client-version, check-canvas-version, check-systems-db-stamp, check-dataflow-graph) — small, pure, no numerics, highest prompt-friction, runs on every push. Leave planet-gen numerics (numpy/scipy/PIL) in Python behind a stable interface. + +Hard constraint: import_economics is stamped by source SHA via tooling/generator_sources.py. Any port must keep the stamp/pre-push contract intact through the transition, not disabled during it. + +Outcome: a recommendation with costs, then a D-record and Jeroen''s ruling. Epics only after that. + +2026-08-20 — Jeroen raised Typer (typer.tiangolo.com) as the Python-CLI option. Q-124 amended with the costing. + +Start here, not with Rust. Measured state: pyproject.toml already exists and make setup-venv already does pip install -e ''.[dev]'', but there is NO [project.scripts] entry — no console entrypoint exists at all. 22 tooling files already use argparse. So this is consolidation, not authorship. + +The real deliverable is ''one bare command reliably on PATH'', not ''a CLI framework''. A [project.scripts] entrypoint lands in .venv/bin/, which is on PATH only when activated — and agents and git hooks never activate it. That is the same split VENV_PY papers over in the Makefile, and the same failure recorded for tea (absolute path breaks the Bash(tea *) rule and prompts every time). Install via uv tool/pipx into ~/.local/bin, or symlink. A Typer app behind an absolute venv path solves nothing. + +Watch startup cost: a single entrypoint importing every subcommand eagerly pays all 123 modules on every invocation, four times per push. Lazy subcommand registration is mandatory, not an optimisation — measure before and after.', 'backlog', 'low', NULL, 'tooling', 'Q-124', '2026-08-19 23:54:55.776', '2026-08-20 00:00:11.592', NULL, 'b48a1523bdfbddb038c083b365414e9f', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at >= tickets.updated_at; diff --git a/governance/questions/architecture.md b/governance/questions/architecture.md index eebe2fe5c..97cb6b378 100644 --- a/governance/questions/architecture.md +++ b/governance/questions/architecture.md @@ -506,6 +506,14 @@ Technical foundation questions: engine, protocols, data structures, performance, - Python is where one-off analysis gets written. A Rust CLI must not make ad-hoc investigation expensive, or it trades a small recurring cost for a large occasional one. - **Therefore the likely shape, if pursued:** not a big-bang rewrite. Port the **check/gate family first** (small, pure, no numerics, highest prompt-friction, most push-frequent), leave the numerics in Python behind a stable interface, and judge from there. A partial answer is a legitimate outcome of this question. - **Also worth asking:** whether the fix is a CLI at all, or a shared `tooling/` entrypoint dispatcher in Python — one command, many subcommands — which would resolve friction (1) and most of (3) at a fraction of the cost, without touching the numerics. **This is the cheap alternative the investigation must price before recommending Rust**, or the record will have argued for the expensive option by never costing the other one. + +- **Amendment 2026-08-20 — the cheap alternative has a name, and the repo is already most of the way to it.** Jeroen raised [Typer](https://typer.tiangolo.com/) (Click-based, type-hint-driven subcommands, by the FastAPI author — an idiom already in the house via tatlock). Measured against the current state: + - `pyproject.toml` **already exists** at the repo root, and `make setup-venv` already does `pip install -e ".[dev]"`. It has **no `[project.scripts]`** — no console entrypoint is defined at all. Adding one plus a Typer app is the whole delta. + - **22 tooling files already use `argparse`.** The CLI structure exists; it is fragmented across files rather than absent. This is consolidation, not authorship. + - **This resolves friction (1) completely and cheaply.** `Bash(sr *)` as one allowlist entry replaces per-script prompting, which is the single largest recurring cost. + - **But the PATH problem is the actual requirement, and it is separate from the framework.** A `[project.scripts]` entrypoint lands in `.venv/bin/`, which is on PATH only when the venv is activated — and non-interactive shells (agents, git hooks) never activate it. That is the *same* split `VENV_PY` already papers over in the Makefile (`test -x .venv/bin/python || python3`). It is also exactly the failure recorded for `tea`: an absolute path breaks the `Bash(tea *)` rule and prompts every time, and the fix was a bare name on PATH. **So the deliverable is "one bare command reliably on PATH", not "a CLI framework"** — via `uv tool install` / `pipx` into `~/.local/bin`, or a symlink. Choose the framework second; a Typer app behind an absolute venv path solves nothing. + - **It does not help friction (2), and may worsen (3).** Typer + Click are two more venv dependencies. And a single entrypoint that imports every subcommand eagerly pays all 123 modules' import cost on every invocation — including four times per push. Lazy subcommand registration is then mandatory, not an optimisation; measure it before and after. + - **Net:** this looks like the answer for the check/gate family and the day-to-day scripts. It leaves the numpy/scipy/PIL planet-gen path untouched, which is the part a Rust port would have had to prove numerical equivalence for. Price this first. - **Raised by:** Jeroen, 2026-08-20: *"those python files are a pain. they throw permission prompts all over the place and the venv dependencies are annoying... We should ticket a Q record to retool that to rust probably. maybe make it into an actual cli of the quality level of pql. one can dream"* - **Cross-reference:** [D-223](../decisions/architecture.md#d-223) and `.claude/rules/asset-pipeline.md` (the stamp contract a rewrite must preserve), [D-262](../decisions/architecture.md#d-262) (`check-dataflow-graph`, the most recent addition to the per-push Python set), `.claude/rules/ticket-cli.md` (`pql` as the quality bar being referenced).