Ticket graphs

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.

← all ticket-graphs

DONE#455 congruency

VM registry -> DIRECTORY SERVICES (v2, supersedes #454): register every VM image as kind='forge-vm' via artifact.record so the universal resolver (#193, DONE) can FIND it. Gate: resolver.py <sha256> must resolve noble-arm64-base.img, where today it returns 0.

goal

Maintain a REGISTRY of virtual machines, tagged. Use the registry that ALREADY EXISTS — do not invent one. This is a small, honest tool: it records what is actually on disk so the next person (or agent) does not have to guess. USE WHAT IS BUILT. `ticket_artifacts` already has exactly the right shape and has ZERO rows: ticket_artifacts (id, ts, ticket_id, name, version, forge_sha, kind) routes: artifact.record (POST) artifact.list (GET) * kind -> THE TAG. VMs register as kind='forge-vm'. * name -> the domain / image name (e.g. 'workstation', 'noble-arm64-base'). * forge_sha -> the sha256 content address (this is ALREADY the filename in config/forge/large-blobs/<sha256> and the shard key in config/forge/artifacts/blobs/<xx>/...). * version -> the image's identifying version where one exists, else the short sha. Record THROUGH the governed route (artifact.record). NEVER hand-write the db. WHY THIS EXISTS (measured today, all four are the reason): 1. All 6 libvirt domains are 'shut off' and EVERY disk path is DEAD: the xmls point at /Users/stevenpeterson/bin/config/vm/<disk>, but the lab actually lives at <project>/config/vm/. One directory move darkened the whole lab and nothing recorded it. appliance -> appliance-run.qcow2 DISK-EXISTS: NO fabric-root -> fabric-root.qcow2 DISK-EXISTS: NO pi-imaging -> overlay.qcow2 DISK-EXISTS: NO workstation -> workstation.qcow2 DISK-EXISTS: NO 2. QCOW2 backing chains are broken by the same move: large-blobs/ec281818... is a 6.4G delta whose base IS present (config/vm/noble-arm64-base.img, 586M) but is referenced at the pre-move path. 3. 3.46G of large-blobs are BYTE-COPIES of the live lab (verified by sha256: noble-arm64-base.img, raspios-trixie-arm64-lite.img, appliance-run.qcow2 each exist twice). 4. 7.24G are deltas whose bases reference /Users/steve/... — a pre-crash machine/username. Dead. DELIVERABLE: checkouts/current/congruency/tools/vm_registry.py — standalone, stdlib + virsh + qemu-img. 1. SCAN (read-only) and register one artifact per VM image found, kind='forge-vm': * libvirt domains: `virsh -c qemu:///session list --all`, then dumpxml for each disk path. * the image lab: config/vm/*.{qcow2,img} * the blob stores: config/forge/large-blobs/<sha256>, config/forge/artifacts/blobs/<xx>/... Dedup by forge_sha — the SAME bytes in two places is ONE artifact with two locations, not two artifacts. That is the whole point; record every location it was seen at. 2. RECORD, per artifact: name, forge_sha, size, format (qcow2/raw/iso), and — this is the part whose absence caused every problem above — the RESOLVED STATE: * backing_file (from `qemu-img info`), and whether that base EXISTS (by path, and by sha if the base is itself registered) * which libvirt domain(s) reference it, and whether the xml's path resolves * status: LIVE (domain + disk resolve) | ORPHANED-DELTA (base missing) | STALE-PATH (file exists but the referrer points elsewhere) | COPY (byte-identical to another registered artifact) | UNREFERENCED (no domain, no chain) 3. REPORT the lab honestly: a table of every VM, its status, and for STALE-PATH the path it WANTS vs the path where the bytes ACTUALLY ARE. That report alone answers "which VM is which". 4. --json for machines; a readable table by default. 5. READ-ONLY. Do NOT rebase, do NOT edit any libvirt xml, do NOT move or delete a byte, do NOT write to the db except via artifact.record. This tool DESCRIBES the lab; it does not repair it. Repair is a separate, operator-authorized ticket. 6. --selftest against a THROWAWAY temp dir: synthesize a qcow2 with a present base (-> LIVE), one with a missing base (-> ORPHANED-DELTA), two byte-identical files (-> ONE artifact, two locations, COPY), and a file whose referrer points at a moved path (-> STALE-PATH). Skip cleanly with a NAMED reason if qemu-img is absent. No network, no live db, no writes outside the temp dir. 7. DIRECTORY SERVICES — THIS IS THE POINT, NOT A SIDE EFFECT. The universal resolver (#193, DONE, checkouts/current/congruency/tools/resolver.py) ALREADY probes 'forge-artifacts' when handed a sha256. Verified today: resolver.py cafa1a965b591b7c4184b484ffd8e625981a79d48f9b4ae8a4adf7b4c5ade927 -> kind: sha256 | probes: content-sha256, forge-artifacts, git-object-db -> resolved: 0 blob(s) (no match — the collection is empty) That sha IS config/vm/noble-arm64-base.img, 586M, sitting in config/forge/large-blobs under that exact filename. The lookup path exists; the registry is empty, so a live 586M VM is INVISIBLE to the directory service. Registering it fixes that with no new query surface. NOTE the resolver is READ-ONLY and rebuilds nothing; do not modify it. Just make the artifacts exist so its existing probe finds them. (source_dedup.py, #194 DONE, then clusters those matches.) ALSO: forge_refactor_ticketer.py's docstring claims "#193 (the universal resolver) and #194 (the dedup engine) are both still REQUESTED — no resolver module exists to import today." That is STALE — both are DONE and on disk. Do not repeat that claim anywhere. ACCEPTANCE: --selftest green; a live read-only run registers every image in config/vm and both blob stores as kind='forge-vm' artifacts via artifact.record; `artifact.list` returns them (the table goes from 0 rows to populated); the report correctly marks all 4 domains STALE-PATH naming ~/bin/config/vm vs config/vm, marks ec281818 ORPHANED-DELTA-with-recoverable-base, marks the 3 verified duplicates COPY, and marks the /Users/steve/ deltas ORPHANED-DELTA (unrecoverable); nothing on disk is modified. AND — THE DIRECTORY-SERVICE GATE — after the run, `resolver.py cafa1a965b591b7c4184b484ffd8e625981a79d48f9b4ae8a4adf7b4c5ade927` MUST resolve to a match naming config/vm/noble-arm64-base.img (and its large-blobs copy) via the forge-artifacts probe, where today it returns 0. A populated table that the resolver still cannot see is a FAILED ticket. RUN ON CLAUDE.

patch

none

approval

unapproved

runs

no runs recorded

mandate (clauses)

not yet recorded — lands with the ticket-class build

manifestations

not yet recorded — lands with the ticket-class build

guarantees

not yet recorded — lands with the ticket-class build

journey (blunders & successes)

not yet recorded — lands with the ticket-class build

artifacts (forge)

f979ebdec229:f979ebdec229 forge-vm · forge f979ebdec229
overlay:351e1ddf7d22 forge-vm · forge 351e1ddf7d22
ec281818347d:ec281818347d forge-vm · forge ec281818347d
workstation:0e8b1bafb242 forge-vm · forge 0e8b1bafb242
672fc92b9896:672fc92b9896 forge-vm · forge 672fc92b9896
71d17c1ac42b:71d17c1ac42b forge-vm · forge 71d17c1ac42b
raspios-trixie-arm64-lite:beb8f3f599ea forge-vm · forge beb8f3f599ea
noble-arm64-base:cafa1a965b59 forge-vm · forge cafa1a965b59
a8ff4a327ebc:a8ff4a327ebc forge-vm · forge a8ff4a327ebc
ee03afe07920:ee03afe07920 forge-vm · forge ee03afe07920
d1fb068e8080:d1fb068e8080 forge-vm · forge d1fb068e8080
fabric-root:7b7750b0e6db forge-vm · forge 7b7750b0e6db
fabric-root-base:53598a689858 forge-vm · forge 53598a689858
appliance-run:311e2f6accdc forge-vm · forge 311e2f6accdc
cidata:1c265598105c forge-vm · forge 1c265598105c
cidata:6a6dbd2bef09 forge-vm · forge 6a6dbd2bef09
workstation-seed:84b04d26f4be forge-vm · forge 84b04d26f4be
seed:9c4c2c9aff70 forge-vm · forge 9c4c2c9aff70
fabric-cidata:f1bf3523334a forge-vm · forge f1bf3523334a
workstation-base:04082b5e0bf8 forge-vm · forge 04082b5e0bf8

source (forge tree)

browse congruency source (forge-parked tree)

children

#461 resolver.py: an artifact hit must BE a match — it prints 'forge artifacts: config/vm/noble-arm64-base.img' and 'no match — the collection is empty' in the same output. The match shape is source-only; VMs can be reached but not counted. (spawned · DONE)
#458 <<<ArtifactRegistry>>> on ?page=artifacts — make the registry VIEWABLE and INTERACTIVE in congruency: kind is the tag, its items drill in, sha detail shows every location. Reads ticket_artifacts (0 rows today; #455/#457 fill it). (spawned · DONE)