Walkthrough: a code merge → knowledge graph → pan-graph¶
A merge to adepthood's main traced through the graph-build workflow,
the rolling knowledge-graph release, the nightly federation of six
repos into one pan-graph, and the way an agent session actually consumes
the result with graphify query. Paths are repo-relative to
Geoffe-Ga/adepthood unless a repo is named.
The distribution design in one sentence: three workflows — graph-build
(code-only), graph-semantic, graph-federate — publish disjoint asset
sets onto one rolling GitHub release tagged knowledge-graph, so
"three writers, one release, three disjoint asset sets" never clobber
each other (.github/workflows/graph-federate.yml:9-13).
From merge to published graph¶
-
The trigger. A push to
maintouchingbackend/**,frontend/**,scripts/**, or the workflow itself starts graph-build (.github/workflows/graph-build.yml:20-27); a nightly cron at 04:40 UTC and manual dispatch run the full-rebuild path (.github/workflows/graph-build.yml:27-30). Runs serialize under agraph-buildconcurrency group and are never cancelled mid-publish (.github/workflows/graph-build.yml:40-42). -
Restore the prior graph. On the push path, the job downloads the previously published
graph.jsonfrom the rolling release so it can refresh incrementally (.github/workflows/graph-build.yml:87-99). -
Incremental refresh.
scripts/graph/update.shrunsgraphify update— an AST-only re-extract of changed files, "no LLM calls, no API keys" (scripts/graph/update.sh:1-10and29). If the incremental update fails — "most often because deleted files would shrink the graph past graphify's shrink guard" — the step forces a full rebuild withGRAPHIFY_FORCE=1(.github/workflows/graph-build.yml:101-113). -
Nightly full rebuild. Scheduled and manual runs always build from scratch via
scripts/graph/build.sh(graphify extract --code-only,scripts/graph/build.sh:46-47) withGRAPHIFY_FORCE=1, because only a forced full build "can shrink the graph when files were deleted" (.github/workflows/graph-build.yml:115-124). -
Provenance sidecar. The job writes
graph-meta.json— built-at timestamp, short commit SHA, node/edge counts, the pinned graphifyy version, and"kind": "code-only"(.github/workflows/graph-build.yml:126-158). The version pin is read fromscripts/graph/requirements.txtand fails the run loudly if unreadable (.github/workflows/graph-build.yml:65-75). -
Publish to the rolling release.
gh release upload ... --clobberupsertsgraph.json+graph-meta.json(and, after full builds,GRAPH_REPORT.md) onto theknowledge-graphrelease, retrying once after 10 s and then failing loudly — "publishing is never silently skipped" (.github/workflows/graph-build.yml:159-199). The release is explicitly "rolling": assets are re-uploaded in place and the tag is not a version (.github/workflows/graph-build.yml:178-183). -
Nightly observability tail. Schedule/dispatch runs also append a benchmark line to
graph/metrics/benchmark-trend.jsonl(committed back tomain), probe the semantic layer's staleness, and — past a 14-day threshold — file a deduplicatedgraph-stalenessissue (.github/workflows/graph-build.yml:201-332); all of it best-effort so "a clustering/benchmark/parse failure ... must warn and move on" (.github/workflows/graph-build.yml:203-210).
From six graphs to one pan-graph¶
-
The federate trigger. Graph-federate runs nightly at 06:10 UTC — deliberately "after graph-build's 04:40 nightly rebuild has published a fresh own graph" — plus on manual dispatch and on a
repository_dispatchof typegraph-updated, which "lets a satellite repo poke a re-federation when its own graph updates" (.github/workflows/graph-federate.yml:31-38). -
Own graph, fatal fetch. The job downloads adepthood's
graph.jsonfrom the rolling release intofed/adepthood/graphify-out/graph.json; a miss is the only fatal fetch — "without adepthood's own graph there is nothing to federate" (.github/workflows/graph-federate.yml:94-112). The odd directory layout is load-bearing: merge-graphs derives each input's repo tag from the graph file's grandparent directory name, sofed/<repo>/graphify-out/graph.jsonmakes the tag resolve to<repo>(.github/workflows/graph-federate.yml:95-103). -
Satellite fetches, degradable. Five satellites — Creek-Vault, aptitude-course, wavelength-demo, WavelengthWatch, and this docs repo — are fetched over plain public HTTPS in a fixed order that pins the deterministic per-repo node-id prefixes (
.github/workflows/graph-federate.yml:113-133). Each download is JSON-validated; a satellite that fails "degrades to a::warningand an excluded repo; it never fails the run" (.github/workflows/graph-federate.yml:144-153). Afed/repos.jsonmanifest records exactly which repos made it (.github/workflows/graph-federate.yml:155-180). -
The merge.
graphify merge-graphscombines the own graph with every present satellite intopan-graph.json; because the tool requires at least two inputs, a zero-satellite night falls back to copying adepthood's graph verbatim (.github/workflows/graph-federate.yml:182-213). -
Pan-meta bookkeeping.
pan-meta.jsonrecords built-at, SHA,"kind": "pan-graph", merged node/edge totals, and a per-repo table withpresentflags, per-repo node/edge counts, and the source URL, plus flatrepos_present/repos_missinglists (.github/workflows/graph-federate.yml:230-301). -
Publish, together.
pan-graph.jsonandpan-meta.jsonare uploaded in a single--clobbercall "so a pan-graph is never published without its manifest", with the same retry-once-then-fail-loud policy (.github/workflows/graph-federate.yml:303-332). This workflow never writesgraph.json/graph-meta.json— the asset sets stay disjoint (.github/workflows/graph-federate.yml:9-13).
How a session consumes it¶
-
Restore on session start. The Claude Code
SessionStarthook refreshesgraphify-out/from the rolling release unless a local copy is under 48 h old (GRAPH_FRESH_MAX_AGE_SECONDS=172800), fail-soft: a failed or malformed download "must never clobber an existing graphify-out/graph.json", so it validates JSON in a temp dir first;pan-graph.jsonandgraph-meta.jsonare best-effort extras (.claude/hooks/session-start.sh:35-39and113-137). -
Prefer the pan-graph. The graph skill points read subcommands at
graphify-out/pan-graph.jsonwhenever it exists — "it also carries the four satellite repos" — falling back to the repo's owngraph.json(.claude/skills/graph/SKILL.md:30-40). -
Query instead of grep. Agents then answer codebase questions with
graphify query "<question>"(pluspath,explain,affected) before any file sweep, per the repo instructions (CLAUDE.md, "Knowledge Graph (graphify)" section), and refresh after edits with./scripts/graph/update.sh.
Failure modes¶
- Missing own graph at federate time — hard error, run fails:
"adepthood own graph.json missing from release — cannot federate"(.github/workflows/graph-federate.yml:108-112). - Unreachable satellite — warning + exclusion; the pan-graph
ships without that repo, and
pan-meta.jsonlists it underrepos_missing(.github/workflows/graph-federate.yml:148-153). - A satellite goes private — documented as the dangerous case: the
fetch 404s (silent drop), and the workflow header forbids "fixing"
it with a token because that "would leak private structure onto a
public asset" (
.github/workflows/graph-federate.yml:20-29). - Shrink guard trips on incremental update — automatic
GRAPHIFY_FORCE=1full rebuild keepsmaingreen (.github/workflows/graph-build.yml:106-113). - Release upload fails — one retry after 10 s, then a loud
::errorand job failure in both workflows (.github/workflows/graph-build.yml:184-196,.github/workflows/graph-federate.yml:320-330). - Semantic layer goes stale — nightly probe files one deduplicated
graph-stalenessissue pointing at the graph-semantic dispatch (.github/workflows/graph-build.yml:303-332). - Session restore fails — every error path in the hook warns and
returns 0; a session never aborts because the graph could not be
fetched (
.claude/hooks/session-start.sh:107-140).
Grounded in Geoffe-Ga/adepthood@55eef11, 2026-07-31.