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#461 congruency

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.

goal

The universal resolver FINDS an artifact and then reports "no match" in the same breath. It can REACH a VM but cannot COUNT one. Fix the contradiction; do not rebuild the resolver. MEASURED, re-runnable today (the registry is populated — 21 rows, 20 forge-vm + 1 forge-source): python3 tools/resolver.py cafa1a965b591b7c4184b484ffd8e625981a79d48f9b4ae8a4adf7b4c5ade927 identifier: 'cafa1a96...' kind: sha256 probes: content-sha256, forge-artifacts, git-object-db forge artifacts (content-addressed by sha256): config/vm/noble-arm64-base.img <-- FOUND IT resolved: 0 blob(s) -> 0 de-overlapped match(es) <-- DENIES IT (no match — the collection is empty) <-- LIES That sha is config/vm/noble-arm64-base.img, 586M, registered by #455 as kind='forge-vm', and ALSO present in config/forge/large-blobs under that exact filename. The forge-artifacts probe prints the path. The match collection stays empty. Both statements are in the same output. WHY (diagnosis — verify it before acting): #192 specifies every match normalizes to {repo, path, line-range, identifier-kind, associated tickets, score} — a SOURCE-LOCATION shape. A VM image has no repo and no line-range, so an artifact hit apparently cannot become a match and is demoted to a side-note. That is a reasonable original design (the resolver was built to find SOURCE) that became wrong the moment ticket_artifacts started holding non-source artifacts. DELIVERABLE: checkouts/current/congruency/tools/resolver.py 1. AN ARTIFACT HIT IS A MATCH. If the forge-artifacts probe resolves a sha256 to a registered artifact, it MUST appear in the normalized match collection and be counted — not printed as a footnote while the summary says "no match". A resolver that prints the answer and then denies finding it is worse than one that finds nothing: it teaches the reader to distrust the summary. 2. NORMALIZE HONESTLY, DO NOT FAKE A SOURCE SHAPE. Do not invent a repo or a line-range for a VM. Extend the match shape so a non-source artifact is representable: identifier-kind='artifact' (or the registered `kind`, e.g. forge-vm / forge-source), path = the registered location(s), repo = NULL/absent where there is none, line-range absent, tickets = the minting ticket_id from ticket_artifacts, score as the other probes score an exact content hit. State the shape in the docstring. Absent fields must be ABSENT, never zeros or empty strings pretending to be data. 3. ONE SHA, MANY LOCATIONS, ONE ARTIFACT. The same forge_sha registered at several locations is ONE artifact with several locations — that is the point of content addressing, and #455 already registers it that way. Do not emit N matches for N locations; emit one match carrying its locations. Keep de-overlapping consistent with how the source probes already de-overlap. 4. DO NOT BREAK THE SOURCE PATH. Every existing identifier mode (name / file-name / uuid / git-hash / similarity / ticket-ref) must behave EXACTLY as before. This is additive: artifacts become representable; source matches are untouched. Prove it — the same query for a source identifier must return the same collection as before the change. 5. READ-ONLY, rebuild nothing. resolver.py's own docstring: "Read-only: it reuses omni-git and the #189 vector index and rebuilds nothing." Preserve that exactly. No writes to any db. 6. OMNI IS BLACK-BOXED. omni-git is a deduplication DEVICE and was a TEST, not an archive and not a git — every ordinary git command against it fails (rev-parse HEAD prints the literal "HEAD"; ls-tree/log are fatal). Do NOT start walking it. If the content-sha256 probe already consults omni's blob index, LEAVE THAT AS IT IS — this ticket touches the forge-artifacts probe and the match normalization only. ACCEPTANCE (all live, all re-runnable): * `resolver.py cafa1a965b591b7c4184b484ffd8e625981a79d48f9b4ae8a4adf7b4c5ade927` returns >= 1 match naming config/vm/noble-arm64-base.img, with identifier-kind reflecting the artifact and the minting ticket present. It MUST NOT print "no match" — today it prints both. * `--json` includes that match in the collection (not only in prose). * A sha registered at MULTIPLE locations returns ONE match carrying all its locations. * A sha256 that is registered NOWHERE still returns 0 matches and says so — no false positives. * REGRESSION: pick any source identifier that resolves today, run it before and after, and show the match collection is IDENTICAL. Source behaviour is untouched. 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)

checkouts/current/congruency/tools/resolver.py:bc646fd683 forge-source · forge de426704f241

source (forge tree)

browse congruency source (forge-parked tree)

children

#463 pair_loop selftest 179/180: the '#457 GATE (a)' check pinned the resolver bug as PASSING ('resolver holds NO ref to ticket_artifacts'); #461 FIXED it, flipping the check to FAIL. Invert the assertion — same tripwire, correct polarity — back to 180/180. (spawned · DONE)