critpath
critpath
Section titled “critpath”A dependency-graph + critical-path tool for a package of markdown artifacts.
When a canonical decision changes (e.g. OPEN PANEL’s pivot from a 501(c)(3) Foundation
to a PBC), the change must propagate through a web of docs. critpath models the docs
as a slime-mold network — artifacts are nodes, conforms_to/depends_on lineage
edges are tubes — and treats a change as a nutrient pulse from the source. It finds the
trunk (highest-flux source nodes), the critical path (longest conformance
chain), and for any changed term the sweep set plus the starved/stale nodes the
pulse hasn’t reached.
Stdlib-only Python (no installs). Annotation standard: see LINEAGE.md.
cd /path/to/package # the artifact root (e.g. the openpanel repo)
# trunk + critical path + untracked/danglingpython3 -m critpath --root . graph
# what must change when the meaning of a term changespython3 -m critpath --root . sweep Foundationpython3 -m critpath --root . sweep PBC
# which artifacts still lack a lineage blockpython3 -m critpath --root . lint(From inside tools/critpath, point --root at the repo: --root ../...)
What the verdicts mean (sweep)
Section titled “What the verdicts mean (sweep)”| Verdict | Meaning | Action |
|---|---|---|
SOURCE | owns the term (defines) | change it here first |
REVIEW | on the path, contains the term | confirm it reads in the new sense |
HIDDEN-DEP | contains the term but lineage omits it | add the term to consumes |
UNTRACKED | contains the term, no lineage at all | add a lineage block, then re-sweep |
Design notes
Section titled “Design notes”- The graph is a DAG of
derived → sourceedges;flux(node)= size of its transitive dependent set (downstream blast radius). The trunk isflux-sorted. - The critical path is the longest source→leaf chain (cycle-safe DP).
- Sweeps combine the lineage graph (precise, declared dependencies) with a literal token scan (catches reality the annotations missed) — the gap between them is the actionable finding.
- Extensible: add fields to the lineage spec and read them in
parse.py.