#!/usr/bin/env python3 """Phase 0 gate — deterministic pre-flight, NO model, NO parser logic. Reproduces the Build-Plan Phase 0 gate (docs/BUILD-PLAN.md): - the fixture's line count must be 1316 - print the distinct top-level `type` counts And confirms the Phase-0 capability check: we can enumerate ~/.claude/projects and read a .jsonl (TRACE-CONTRACT §1). This is a pre-flight only. Turn/token/provenance numbers belong to Phase 1+. Run: python3 tools/phase0_check.py """ from __future__ import annotations import json import sys from collections import Counter from pathlib import Path REPO = Path(__file__).resolve().parent.parent FIXTURE = REPO / "fixtures" / "sample-session.jsonl" EXPECTED_LINES = 1316 # TRACE-CONTRACT §6 — ground truth def type_counts(path: Path) -> tuple[int, int, Counter]: """Return (parsed_lines, parse_failures, Counter of top-level `type`).""" counts: Counter = Counter() parsed = 0 failures = 0 with path.open(encoding="utf-8") as fh: for line in fh: line = line.strip() if not line: continue parsed += 1 try: obj = json.loads(line) except json.JSONDecodeError: failures += 1 continue counts[obj.get("type", "")] += 1 return parsed, failures, counts def enumerate_projects() -> tuple[int, int]: """Phase-0 capability check: can we walk ~/.claude/projects and see sessions?""" root = Path.home() / ".claude" / "projects" if not root.is_dir(): return 0, 0 dirs = [p for p in root.iterdir() if p.is_dir()] sessions = list(root.glob("**/*.jsonl")) return len(dirs), len(sessions) def main() -> int: if not FIXTURE.exists(): print(f"FAIL — fixture missing: {FIXTURE}") return 1 parsed, failures, counts = type_counts(FIXTURE) print("Her · हेर — Phase 0 pre-flight") print("=" * 52) print(f"fixture : {FIXTURE}") print(f"line count : {parsed} (expected {EXPECTED_LINES})") print(f"json parse errors: {failures}") print("-" * 52) print("top-level `type` counts:") width = max(len(k) for k in counts) if counts else 0 for name, n in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])): print(f" {name:<{width}} {n}") print(f" {'TOTAL':<{width}} {sum(counts.values())}") print("-" * 52) n_dirs, n_sessions = enumerate_projects() print(f"~/.claude/projects: {n_dirs} project dirs, {n_sessions} sessions readable") print("=" * 52) ok = (parsed == EXPECTED_LINES) and (failures == 0) print("GATE:", "PASS ✓" if ok else "FAIL ✗") return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())