Skip to content

Commit 871f328

Browse files
authored
Merge pull request #34 from HarperZ9/feat/repo-art-and-diagrams
Draw the claim oracle, and give index its own generated header
2 parents 24999ea + 06fc731 commit 871f328

10 files changed

Lines changed: 777 additions & 4 deletions

File tree

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docs/art/*.svg text eol=lf

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<p align="center"><img src=".github/assets/zentropy-banner.png" alt="index: Maps a multi-repo workspace in seconds on the nine-ecosystem reference tree it ships with (self-measured): nine ecosystems, dependency and symbol graphs, fully offline, zero dependencies." width="100%"></p>
1+
<p align="center"><img src="docs/art/index-header.svg" alt="index: map a workspace offline, and refute a claim the code does not support." width="100%"></p>
22

33
**Maps a multi-repo workspace in seconds on the nine-ecosystem reference tree it ships with (self-measured): nine ecosystems, dependency and symbol graphs, fully offline, zero dependencies.**
44

@@ -43,6 +43,22 @@ New here? [`docs/INTRODUCTION.md`](docs/INTRODUCTION.md) is the ten-minute walkt
4343

4444
Every codebase has a shape. Past a handful of repos, that shape lives only in someone's head, and they are usually busy or already gone. `index` draws it for you: how your repositories depend on each other, what lives inside each one down to the symbol level, and the docs that explain why, as maps you can open, search, and re-derive. It reads nine ecosystems (Python, JavaScript and TypeScript, Rust, Go, Java, C#, Ruby, PHP, C and C++) from their manifests and their real imports, and it records each dependency edge with the file and line that shows it. Pure Python 3.11+ standard library, zero runtime dependencies.
4545

46+
## How a claim is settled
47+
48+
`index verify` answers a structural claim from the graph it extracted, not from
49+
a model's memory. This is the path one claim takes.
50+
51+
<p align="center"><img src="docs/art/claim-oracle.svg" alt="Eight stages from workspace discovery to a rechecked certificate: discover, classify, graph, freshness, claim, ground, certificate, recheck. Fingerprints ask whether ground truth has moved, and a graph built over changed code is rebuilt before any claim is grounded against it. A claim is answered from the graph rather than from memory, ending as match, refuted or unverifiable." width="100%"></p>
52+
53+
The triad here is deliberately not the one `index wiki --verify` reports. A
54+
sealed artifact can DRIFT away from a tree that moved underneath it. A claim
55+
cannot drift: either the graph carries the edge, or the graph is complete on
56+
that point and does not, which is REFUTED. UNVERIFIABLE is reserved for the case
57+
where the question itself falls outside what was scanned, such as an endpoint
58+
that is not a repository in the workspace. A MATCH on a dependency hands back
59+
the file and line of its strongest supporting edge, so the answer arrives with
60+
its witness rather than as an assertion you have to take on faith.
61+
4662
## The surfaces, coolest first
4763

4864
**`index wiki`, the single-repo wiki.** Model-based wiki generators write confident prose about structure that is not there; repo packers dump source without comprehending it. `index wiki` takes the third path: it derives the wiki for one repo from the module and symbol graph it extracts itself, and generates no prose at all. You get an overview page, one page per module with imports, dependents, and cycle membership, one page per Python function, class, and method with its callers and callees, and an architecture diagram rendered from the real dependency graph. Point it at a git URL (`index wiki https://github.com/org/repo`) and it shallow-clones, derives, and cleans up, so you can read a repo you have not checked out. The artifact is commit-pinned and sealed, and `index wiki --verify` re-checks it against the current tree: MATCH, DRIFT, or UNVERIFIABLE, exit codes 0, 1, 2.

docs/art/claim-oracle.svg

Lines changed: 19 additions & 0 deletions
Loading

docs/art/index-header.svg

Lines changed: 1 addition & 0 deletions
Loading

docs/art/index.art.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"$comment": "Source of truth for the artwork on this repository's front page. Edit a sentence here and re-run scripts/render_repo_art.py; tests/test_repo_art.py fails if the committed SVG stops matching.",
3+
"header": {
4+
"name": "index",
5+
"role": "offline workspace graph",
6+
"tagline": "Map a workspace offline. Refute a claim the code does not support.",
7+
"words": ["scan", "graph", "symbols", "verify", "certificate"]
8+
},
9+
"flows": [
10+
{
11+
"file": "claim-oracle.svg",
12+
"kicker": "one claim, end to end",
13+
"title": "How a structural claim is settled against the code itself",
14+
"alt": "Eight stages from workspace discovery to a rechecked certificate. Fingerprints ask whether ground truth has moved, and a graph built over changed code is rebuilt before any claim is grounded against it. A claim is answered from the graph rather than from memory, ending as match, refuted or unverifiable.",
15+
"footnote": "The verdict is computed from the graph, so an agent's confidence has no route into it.",
16+
"stages": [
17+
{"title": "Discover", "note": "The workspace is walked and pruned into a repo list."},
18+
{"title": "Classify", "note": "Each repo takes its ecosystem from its own manifests."},
19+
{"title": "Graph", "note": "Imports and symbols become edges carrying a file and line."},
20+
{"title": "Freshness", "note": "Fingerprints answer one question: has ground truth moved?"},
21+
{"title": "Claim", "note": "An agent asserts a dependency, or that a symbol exists."},
22+
{"title": "Ground", "note": "The claim meets the graph, and never the agent's memory."},
23+
{"title": "Certificate", "note": "The verdict is sealed over a hash of the whole graph."},
24+
{"title": "Recheck", "note": "A consumer recomputes that hash instead of trusting it."}
25+
],
26+
"returns": [
27+
{"from": 3, "to": 2, "label": "GROUND TRUTH MOVED: THE GRAPH IS REBUILT FIRST"}
28+
],
29+
"outcomes": [
30+
{"label": "MATCH", "note": "the graph carries the edge, with file and line", "tone": "verified"},
31+
{"label": "REFUTED", "note": "the graph is complete here, with no such edge", "tone": "drift"},
32+
{"label": "UNVERIFIABLE", "note": "an endpoint sits outside the scanned workspace", "tone": "none"}
33+
]
34+
}
35+
]
36+
}

scripts/render_repo_art.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""Render a repository's front-page artwork from its spec.
2+
3+
python scripts/render_repo_art.py # write the SVGs
4+
python scripts/render_repo_art.py --check # fail if any is stale
5+
6+
The check mode is the point. Committed artwork drifts from the words it
7+
illustrates the moment someone edits one and not the other, and nobody
8+
notices, because a picture in a README is never diffed. Here the picture is
9+
a pure function of a spec that IS diffable, so a test can re-render and
10+
compare bytes.
11+
"""
12+
from __future__ import annotations
13+
14+
import argparse
15+
import json
16+
import sys
17+
from pathlib import Path
18+
19+
sys.path.insert(0, str(Path(__file__).resolve().parent))
20+
21+
from repo_art import header_svg # noqa: E402
22+
from repo_flow import flow_svg # noqa: E402
23+
24+
ART = Path(__file__).resolve().parents[1] / "docs" / "art"
25+
26+
27+
def rendered(spec_path: Path) -> dict[Path, str]:
28+
"""Every file one spec produces, as path to text."""
29+
spec = json.loads(spec_path.read_text(encoding="utf-8"))
30+
stem = spec_path.name.removesuffix(".art.json")
31+
out = {spec_path.parent / f"{stem}-header.svg": header_svg(spec["header"])}
32+
for flow in spec.get("flows", []):
33+
out[spec_path.parent / flow["file"]] = flow_svg(flow)
34+
return out
35+
36+
37+
def specs() -> list[Path]:
38+
return sorted(ART.glob("*.art.json"))
39+
40+
41+
def main(argv: list[str] | None = None) -> int:
42+
parser = argparse.ArgumentParser(description=__doc__)
43+
parser.add_argument("--check", action="store_true",
44+
help="report stale artwork instead of rewriting it")
45+
args = parser.parse_args(argv)
46+
47+
stale: list[str] = []
48+
for spec_path in specs():
49+
for path, text in rendered(spec_path).items():
50+
body = text + "\n"
51+
if args.check:
52+
current = path.read_text(encoding="utf-8") if path.exists() else ""
53+
if current != body:
54+
stale.append(str(path.relative_to(ART.parents[1])))
55+
continue
56+
# newline="" so a Windows run writes the same bytes a Linux
57+
# run does. The whole point of this file is that committed
58+
# artwork and a fresh render are comparable.
59+
path.write_text(body, encoding="utf-8", newline="")
60+
print(f"wrote {path.relative_to(ART.parents[1])} ({len(body)} bytes)")
61+
62+
if stale:
63+
print("stale artwork, re-run scripts/render_repo_art.py:", file=sys.stderr)
64+
for name in stale:
65+
print(f" {name}", file=sys.stderr)
66+
return 1
67+
if args.check:
68+
print(f"artwork matches its spec ({len(specs())} spec files)")
69+
return 0
70+
71+
72+
if __name__ == "__main__":
73+
raise SystemExit(main())

0 commit comments

Comments
 (0)