refactor(disambiguation): extract F3 rewrite path (#26)

This commit is contained in:
2026-08-24 01:44:40 +02:00
parent eccf6212f1
commit 2ed55ef131
4 changed files with 247 additions and 61 deletions
@@ -1,12 +1,13 @@
"""Integration: every `tht ...` command the gate invokes must exist in the Typer CLI.
Root cause of the Blocco 1 critical bugs: the gate (tht-gate.js) and the Python CLI
Root cause of the Blocco 1 critical bugs: the Pi gate and the Python CLI
were ported separately and never run together, so the gate called commands/flags that
did not exist (`phase advance --if-ready`, `cte plan` without `--name`, `set-question
--session` on a positional arg). This test extracts every `["group","sub",...,"--flag"]`
array literal from tht-gate.js and asserts, via `tht <group> <sub> --help`, that the
subcommand exists (exit 0) and that each long flag used is offered. It makes that whole
class of drift impossible to reintroduce silently.
array literal from tht-gate.js, plus the subcommand arrays passed through domain-specific
CLI capabilities, and asserts via `tht <group> <sub> --help` that the subcommand exists
(exit 0) and that each long flag used is offered. It makes that whole class of drift
impossible to reintroduce silently.
"""
from __future__ import annotations
@@ -22,6 +23,7 @@ from tht.cli import app
_ROOT = Path(__file__).resolve().parent.parent.parent
_GATE = _ROOT / ".pi" / "extensions" / "tht-gate.js"
_DISAMBIGUATION_GATE = _ROOT / ".pi" / "extensions" / "gate" / "disambiguation" / "index.js"
_THT = Path(sys.executable).parent / "tht"
# The command groups the gate drives. Anything else in an array literal is data, not a CLI call.
@@ -75,6 +77,15 @@ def _extract_invocations() -> list[tuple[str, str, list[str], str]]:
continue
flags = [f for f in re.findall(r'"(--[a-z][a-z-]*)"', tail) if f not in _SKIP_FLAGS]
out.append((group, sub, flags, m.group(0)))
# Disambiguation receives a session-scoped capability, so its arrays start at
# the subcommand while tht-gate.js supplies the fixed `session` group prefix.
domain_src = _DISAMBIGUATION_GATE.read_text()
domain_pattern = re.compile(r'\[\s*"([a-z][a-z-]*)"((?:\s*,\s*[^\]\[]+?)?)\]')
for m in domain_pattern.finditer(domain_src):
sub, tail = m.group(1), m.group(2)
flags = [f for f in re.findall(r'"(--[a-z][a-z-]*)"', tail) if f not in _SKIP_FLAGS]
out.append(("session", sub, flags, m.group(0)))
return out