her / tools /phase5_gate.py
geekwrestler's picture
Squash history (purge pre-scrub demo session blobs)
5f43c7d
#!/usr/bin/env python3
"""Phase 5 gate — falsification test for multi-session discovery.
Runs against the REAL ``~/.claude/projects`` (Non-negotiable: read cwd from
INSIDE each file; never decode the lossy encoded folder name). Demonstrates:
1. PREFIX SURFACING — ticking a parent folder surfaces EVERY session whose
real cwd is under it (the parent itself or any descendant).
2. DEEPEST-WINS — ticking that parent AND a nested child together: the
sessions in the nested dir are attributed to the CHILD, not the parent.
3. NO DOUBLE-COUNT — the parent+child tick attributes each session exactly
once; the union count equals the parent-alone count (the child's sessions
just move down a level, none are duplicated or lost).
Roots are chosen data-driven from the real cwds, so the gate stays honest as the
session store changes:
parent = deepest common ancestor of all discovered cwds.
child = a real intermediate directory strictly between parent and some cwd
that actually owns sessions (a genuine nested tick).
Run:
python3 "tools/phase5_gate.py"
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
if str(REPO) not in sys.path:
sys.path.insert(0, str(REPO))
from engine import discovery # noqa: E402
def _line(label: str, got, want, ok: bool) -> bool:
flag = "OK " if ok else "DIFF"
print(f" [{flag}] {label:<34} got={got!r:>10} want={want!r}")
return ok
def _common_ancestor(paths: list[str]) -> str:
"""Deepest directory that is an ancestor of (or equal to) every path.
Computed on PATH SEGMENTS (never raw os.path.commonprefix, which can cut a
name mid-segment)."""
seg_lists = [p.split(os.sep) for p in paths]
common: list[str] = []
for parts in zip(*seg_lists):
first = parts[0]
if all(x == first for x in parts):
common.append(first)
else:
break
anc = os.sep.join(common)
return anc or os.sep
def _pick_nested_child(parent: str, cwds: list[str]) -> str | None:
"""Find a real intermediate dir strictly BETWEEN ``parent`` and some cwd that
owns sessions — a genuine nested tick. Prefer the cwd itself if it sits below
parent by >=1 segment and other cwds remain outside it (so the move is
observable); otherwise use the cwd's parent directory."""
parent_segs = parent.split(os.sep)
best: str | None = None
for c in sorted(set(cwds)):
if not discovery._is_under(c, parent) or c == parent:
continue
c_segs = c.split(os.sep)
depth = len(c_segs) - len(parent_segs)
if depth < 1:
continue
# candidate nested root = the cwd itself (deepest, cleanest demo)
# keep the FIRST deterministic candidate that is a strict descendant.
best = c
break
return best
def main() -> int:
projects_dir = discovery.default_projects_dir()
print("Her · हेर — Phase 5 gate (multi-session discovery)")
print("=" * 70)
print(f"projects : {projects_dir}")
if not os.path.isdir(projects_dir):
print(f"FAIL — projects dir not found: {projects_dir}")
return 1
# --- discover everything from the real store -------------------------- #
sessions = discovery.discover_sessions(projects_dir)
projects = discovery.list_projects(projects_dir)
with_cwd = [s for s in sessions if s.cwd is not None]
cwds = [s.cwd for s in with_cwd]
print(f"sessions : {len(sessions)} total "
f"({len(with_cwd)} with a real cwd, "
f"{len(sessions) - len(with_cwd)} without)")
print(f"projects : {len(projects)} distinct cwd roots")
print("-" * 70)
for p in projects:
print(f" {p.sessions:3d} {p.cwd}")
print("-" * 70)
checks: list[bool] = []
if not cwds:
print("FAIL — no sessions with a readable cwd; cannot run the gate.")
return 1
# --- choose parent + nested child data-driven ------------------------- #
parent = _common_ancestor(cwds)
child = _pick_nested_child(parent, cwds)
# If every cwd shares the parent exactly (no nesting available), fall back
# to the longest cwd as parent so we still have a child below it.
if child is None:
parent = min(cwds, key=lambda c: (c.count(os.sep), c)) # shallowest cwd
child = _pick_nested_child(parent, cwds)
if child is None:
print("FAIL — could not find a nested child root to demonstrate "
"deepest-wins; the store has no nesting to test.")
return 1
print(f"parent root (tick) : {parent}")
print(f"child root (tick) : {child} [nested under parent]")
print(f"child is strictly under parent : "
f"{discovery._is_under(child, parent) and child != parent}")
print("-" * 70)
# === 1) PREFIX SURFACING ============================================== #
under_parent = discovery.sessions_under(parent, sessions=sessions)
# independent recomputation of the expected set (segment-prefix match)
expect_parent = [
s for s in with_cwd if discovery._is_under(s.cwd, parent)
]
surfacing_ok = (
{s.path for s in under_parent} == {s.path for s in expect_parent}
and len(under_parent) > 0
)
checks.append(_line("parent surfaces all under it",
len(under_parent), len(expect_parent), surfacing_ok))
# surfacing must include sessions from MORE THAN ONE distinct cwd (proves it
# is a real prefix sweep, not a single-folder lookup) — unless the store
# genuinely only has one cwd under parent.
distinct_cwds_surfaced = len({s.cwd for s in under_parent})
checks.append(_line("surfaced cwds (prefix sweep)",
distinct_cwds_surfaced, ">=1",
distinct_cwds_surfaced >= 1))
# === 2) DEEPEST-WINS + 3) NO DOUBLE-COUNT ============================= #
attr = discovery.attribute([parent, child], sessions=sessions)
in_parent = attr.by_root.get(parent, [])
in_child = attr.by_root.get(child, [])
# child must own exactly the sessions under child; parent owns the rest under
# parent (i.e. under parent but NOT under child).
expect_child = [s for s in with_cwd if discovery._is_under(s.cwd, child)]
expect_parent_only = [
s for s in expect_parent if not discovery._is_under(s.cwd, child)
]
deepest_ok = (
{s.path for s in in_child} == {s.path for s in expect_child}
and {s.path for s in in_parent} == {s.path for s in expect_parent_only}
and len(in_child) > 0
)
checks.append(_line("deepest-wins: child claims nested",
len(in_child), len(expect_child), deepest_ok))
# NO DOUBLE-COUNT: every attributed session appears under exactly one root,
# and the union equals parent-alone surfacing (nothing duplicated or dropped).
all_attr_paths = [s.path for s in in_parent] + [s.path for s in in_child]
no_dupes = len(all_attr_paths) == len(set(all_attr_paths))
union_matches_parent_alone = (
set(all_attr_paths) == {s.path for s in under_parent}
)
count_conserved = (len(in_parent) + len(in_child)) == len(under_parent)
no_double_ok = no_dupes and union_matches_parent_alone and count_conserved
checks.append(_line("no double-count (parent+child)",
len(in_parent) + len(in_child), len(under_parent),
no_double_ok))
checks.append(_line("each session attributed once", no_dupes, True, no_dupes))
# partition sanity: attributed + unattributed == total sessions (with cwd
# under parent) — Attribution.total covers the WHOLE pool.
partition_ok = attr.total == len(sessions)
checks.append(_line("attribution partitions pool",
attr.total, len(sessions), partition_ok))
print("-" * 70)
print("attribution under parent+child tick (deepest-wins):")
print(f" parent {parent!r}")
print(f" -> {len(in_parent)} sessions "
f"(cwds: {sorted({s.cwd for s in in_parent})})")
print(f" child {child!r}")
print(f" -> {len(in_child)} sessions "
f"(cwds: {sorted({s.cwd for s in in_child})})")
print(f" parent-alone tick surfaced {len(under_parent)} sessions; "
f"parent+child attributes {len(in_parent) + len(in_child)} "
f"(no double-count).")
# === 4) INTERMEDIATE-DIR DEEPEST-WINS (multi-level, if the store has it) === #
# Find a real intermediate directory that is BOTH under parent AND a strict
# ancestor of some cwd (i.e. a dir between parent and a leaf cwd). Ticking
# parent + that intermediate must pull the leaf's sessions DOWN to the
# intermediate, never leaving them on the parent. This exercises the case
# where the deeper tick is an ANCESTOR of a cwd (not a leaf cwd itself).
mid = None
for c in sorted(set(cwds)):
d = os.path.dirname(c)
if d and d != c and discovery._is_under(d, parent) and d != parent:
# d is a real intermediate ancestor of cwd c, strictly under parent
mid = d
break
if mid is not None:
attr2 = discovery.attribute([parent, mid], sessions=sessions)
mid_hits = attr2.by_root.get(mid, [])
expect_mid = [s for s in with_cwd if discovery._is_under(s.cwd, mid)]
mid_paths = [s.path for r in attr2.by_root.values() for s in r]
mid_ok = (
{s.path for s in mid_hits} == {s.path for s in expect_mid}
and len(mid_hits) > 0
and len(mid_paths) == len(set(mid_paths)) # no dupes
and attr2.total == len(sessions) # nothing lost
)
checks.append(_line("intermediate-dir deepest-wins",
len(mid_hits), len(expect_mid), mid_ok))
print(f" intermediate {mid!r}")
print(f" -> {len(mid_hits)} sessions pulled down from parent "
f"(cwds: {sorted({s.cwd for s in mid_hits})})")
else:
print(" (no intermediate-dir nesting in store; multi-level sub-check "
"skipped — leaf-cwd deepest-wins above already proves it.)")
print("-" * 70)
ok = all(checks)
print("GATE:", "PASS" if ok else "FAIL")
# machine-readable summary for the orchestrator (the MULTI schema fields)
print("SUMMARY",
{
"sessions_found": len(sessions),
"projects": len(projects),
"deepest_wins": bool(deepest_ok),
"no_double_count": bool(no_double_ok),
"gate_ok": ok,
})
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())