Every ticket as the root of its provenance graph — goal, patch, approval, runs and (as the ticket-class build lands) mandate, manifestations, guarantees, journey, artifacts and children. Rendered through the <<<TicketGraphList>>> tag.
goal
PART 1 OF 2 (supersedes #443, which timed out at 2400s having written NOTHING — its spec was too
large for one worker pass: 8.3KB, 7 requirements, plus reasoning over 264 repos. Its two siblings
with identical engine/timeout succeeded — #440 wrote 1723 lines, #438 completed two rounds — so the
ticket size was the variable, not the worker. THIS TICKET IS DELIBERATELY HALF THE SCOPE.)
SCOPE: DISCOVER + CLASSIFY ONLY. Emit the family tree. The containment retirement gate and the
merge-home planning are PART 2 and are explicitly OUT OF SCOPE here — do not build them.
DELIVERABLE: checkouts/current/congruency/tools/forge_family_tree.py — standalone, stdlib + git only.
1. CLUSTER. Walk config/forge/remotes/*.git (264 bare repos). Group by root-commit SET
(`git rev-list --max-parents=0 --all`). Repos sharing a root set are ONE LINEAGE.
2. RESOLVE TIPS — THE GOTCHA THAT ALREADY PRODUCED A WRONG ANSWER ONCE. Most of these bare repos
have an UNRESOLVABLE HEAD: `git rev-parse HEAD` returns the literal string "HEAD", and
`rev-parse --verify HEAD` FAILS. A first prototype used --verify, silently dropped 42 of 109
families, and confidently reported "0 diverged" — which was false. Resolve tips via
`git for-each-ref refs/heads`, preferring main/master, else the ref with the most commits.
3 repos have NO refs at all: REPORT them explicitly, never skip silently.
3. CLASSIFY each lineage:
IDENTICAL — every member shares one tip (pure ingestion duplicates).
LINEAR — the max-commit tip is an ancestor of every other tip (superset); the rest
are stale. Report the superset tip + the stale aliases.
FORK-CAME-HOME — *** THE SAME GIT SHAPE AS LINEAR. *** A fork whose additions belong merged
BACK into its parent/home, after which the fork retires. Topology CANNOT
distinguish this from LINEAR — they differ only in INTENT, which is not in
the DAG. So NEVER auto-decide direction: REPORT the topology, PROPOSE a
direction, and accept an operator-supplied override. Mark the jail.git /
agent_tools_suite.git family as FORK-CAME-HOME (see below).
DIVERGED — a tip is NOT reachable from the candidate: report `merge-base`, each side's
unique commits, and the file-level diff. Distinguish ADDITIVE divergence
(no conflicting hunks — mechanical union) from CONFLICTING (both sides
changed the same file) — that distinction IS the reconciliation cost.
SPLIT-STORE — a member's tip object is ABSENT from the candidate's store. Do not guess:
fetch into a scratch object store (or use --alternates), then re-classify.
4. THE JAIL CASE (operator-defined, do not re-derive): jail.git (47 commits) is topologically a
linear superset of agent_tools_suite.git (46) — shared root 379ac05319, ats tip 888d3447c6 is an
ancestor of jail tip 38e95ad16d, jail adds 6 files. BUT JAIL IS **NOT** CANONICAL AND NOT A
DESTINATION. jail was an expedient: a name wrapped around some packages in a folder, with a mess
of tests cleaned up in a hurry. It is a GRAB-BAG whose packages belong to SEVERAL DIFFERENT home
repos. Direction is REVERSED from what topology suggests: jail merges HOME and is then omitted.
agent_tools_suite IS a home repo. Do NOT name jail canonical.
5. OUTPUT: (a) machine-readable — sqlite or JSON: lineages, members, tips, classification, proposed
direction, fork edges; (b) a human-readable ASCII family tree.
6. --selftest on THROWAWAY git repos in a temp dir synthesizing each class (identical / linear /
additive-diverged / conflicting-diverged / split-store / no-refs / unresolvable-HEAD), proving
each is classified correctly. No network, no live db, no writes outside the temp dir.
DO NOT USE omni.git AS THE SOURCE OF TRUTH — it is INCOMPLETE (measured: it is MISSING
agent_tools_suite's tip 888d3447c6). config/forge/remotes/*.git is authoritative.
ACCEPTANCE: --selftest green; a real run reproduces the measured headline — 264 repos -> 109
LINEAGES, of which 68 IDENTICAL / 36 LINEAR / 3 DIVERGED (jazz.git, notes_db.git, z3_mcp.git) /
2 SPLIT-STORE (big-jazz.git, code__code.git), with the split-store pair RESOLVED after fetch rather
than left unknown; the jail/agent_tools_suite family classified FORK-CAME-HOME and jail NOT named
canonical; prints a readable tree. RUN ON CLAUDE.