Skip to content

m3: depgraph (reverse-dep closure)#7

Merged
oubiwann merged 7 commits into
mainfrom
m3/depgraph-closure
May 7, 2026
Merged

m3: depgraph (reverse-dep closure)#7
oubiwann merged 7 commits into
mainfrom
m3/depgraph-closure

Conversation

@oubiwann

@oubiwann oubiwann commented May 7, 2026

Copy link
Copy Markdown
Contributor

What

M3 — the depgraph package: pure-data import-graph construction and reverse-transitive closure traversal over []golist.Package. Edges merge Imports + TestImports + XTestImports per node; RevDepClosure is BFS over reverse edges with a visited set, output sorted lexicographically. Five exported methods (Has, DirectImports, DirectImporters, RevDepClosure, Stats); uniform *Graph receivers; Stats pre-computed at Build time.

Two carry-forwards land alongside the milestone: a Makefile lint-cache fix (OUT-1, the M1/M2 retro carry-forward) and a CC self-check checklist line in the PR template.

Where

  • depgraph/depgraph.go (Graph, Build, mergeImports, sortValues, computeStats), methods.go (Stats type + the five methods), depgraph_test.go + helpers_test.go (public-API tests in package depgraph_test per TE-43).
  • MakefileLINT_CACHE_DIR := $(shell mktemp -d ...) exported into the lint recipe via GOLANGCI_LINT_CACHE. Fresh cache per invocation; ~5–10s overhead. Earned its keep on first run (caught an unused-parameter issue local would have shipped).
  • .github/PULL_REQUEST_TEMPLATE.md — adds a single ledger-row evidence-vs-criterion checklist line. Promoted from the impl plan's §Risks mitigation to deliverable so the protocol gets installed in the artifact contributors actually see.
  • docs/dev/0006-m3-implementation-retrospective.md — closing report. Walks F-1..F-20 row-by-row with grep-verifiable evidence; substrate loaded + pattern IDs cited per F-19.

How to verify

make check-all                              # build + lint + test + coverage gate
bash scripts/coverage-check.sh              # F-15: per-package gate at 100% on depgraph
go test -count=10 -run TestRevDepClosure ./depgraph    # F-13
go test -timeout=10s -run 'TestRevDepClosure_StandardTopologies/cycle|TestRevDepClosure_StandardTopologies/self-loop' ./depgraph   # F-14
go test -race -run TestGraphConcurrentRead ./depgraph  # F-17
go doc github.com/geomyidia/cascade/depgraph | head -30  # F-18

Abridged ledger (full version in docs/dev/0006-m3-implementation-retrospective.md)

ID Criterion Status Evidence (verify command output)
F-1 depgraph/doc.go exists with package comment done M1 stub preserved verbatim; head -3 grep matches ^// Package depgraph.
F-2 Graph type opaque (no exported fields) done go doc outputs type Graph struct { followed by // Has unexported fields.; no exported field lines.
F-3 Build signature matches spec done func Build(pkgs []golist.Package) *Graph — exact match.
F-4 RevDepClosure method signature done func (g *Graph) RevDepClosure(seeds []string) []string — exact match.
F-5 DirectImports method signature done func (g *Graph) DirectImports(path string) []string — exact match.
F-6 DirectImporters method signature done func (g *Graph) DirectImporters(path string) []string — exact match.
F-7 Has method signature done func (g *Graph) Has(path string) bool — exact match.
F-8 All 16 TestRevDepClosure_StandardTopologies cases pass done 16 named subtests pass; each also asserts sort.StringsAreSorted(got) so non-determinism would fail in the subtest, not just under -count.
F-9 Hand-traceable 5-package case passes done ok github.com/geomyidia/cascade/depgraph 0.150s. Six sub-cases cover the spec's exact expected closures.
F-10 Test/XTest imports become edges done Verifies Imports=[i,shared] + TestImports=[t,shared] + XTestImports=[xt] produces deduplicated DirectImports(p) = [i, shared, t, xt].
F-11 DirectImports / DirectImporters / Has correct done All three pass; Direct* tests also assert returned slices are copies (TD-07 boundary copy preserved).
F-12 Duplicate-package handling idempotent done Last-entry-wins verified; empty-ImportPath input silently skipped.
F-13 Sorted output, deterministic across runs done -count=10 clean; sort assertion inline in every subtest.
F-14 Cycle / self-loop terminate done Both terminate well under 10s timeout.
F-15 Per-package coverage gate at 100% done ok: github.com/geomyidia/cascade/depgraph coverage 100% >= 100%. Dead-branch dedup loop in sortAndDedup removed (reverse map is dedup-by-construction); gate honest, not masked.
F-16 No non-stdlib imports beyond own module done `go list -m all
F-17 Concurrent read safe under -race done 8 goroutines × five methods on the same *Graph; race detector clean; identical output across all goroutines.
F-18 go doc ./depgraph renders cleanly done Package overview, Graph, Stats, Build, plus five methods — every exported name carries a DC-02-compliant doc comment.
F-19 Closing report names guides + pattern IDs done Retrospective §"Substrate loaded at session start" enumerates: TD-01/05/09/17, IM-01/04 (with cite-vs-content note), IM-17, API-42, TE-01..08/15/43, DC-01/02.
F-20 Stats() + TestStats (M3-1) done Stats struct exported with documented fields; method returns by value (cached at Build). TestStats covers four cases including edges-to-absent-nodes.

Total rows: 20. Done: 20. Deferred: 0. No-op: 0. Iteration count: 1.

Notable findings

  • OUT-1 paid for itself on first invocation. The fresh-cache make lint immediately surfaced a revive unused-parameter issue in the goroutine fan-out — exactly the M1/M2 carry-forward failure mode it exists to prevent. Without OUT-1, this would have been the third consecutive milestone where local lint passed and CI failed for the same class of issue.
  • Dead defensive code surfaced via the 100% coverage gate, not a lint warning. The existing in-progress sortAndDedup had an unreachable dedup loop; the reverse map is dedup-by-construction (each (src, dst) pair appends once). Removed rather than worked around. Pattern: pure-data packages with strict coverage gates expose dead defensive code at the right time.
  • IM-04 cite/content discrepancy in the impl plan. The plan cites IM-04 for receiver consistency; the actual IM-04 in the guide is single-method-interface naming. Receiver-consistency lives at TD-09. The principle (uniform *Graph receivers) is honored regardless. Documented in the retrospective's substrate section.

Checklist

  • make check passes locally
  • New/changed exported symbols have godoc comments
  • Tests added/updated for behavior changes (table-driven where applicable)
  • No public-API breakage (or if there is, it's flagged below)
  • If this PR closes a milestone ledger, each row's planned evidence text matches the criterion text (per M2 retro carry-forward)

Breaking change?

No. M3 introduces a new package; no existing surface changes.

🤖 Generated with Claude Code

oubiwann and others added 7 commits May 6, 2026 16:14
The algorithmic heart of cascade: pure-data graph construction over
[]golist.Package values, with reverse-transitive closure traversal
under the imports relation. Build merges Imports + TestImports +
XTestImports into each node's outgoing edges so changes to a tested
dep correctly invalidate the consuming package. RevDepClosure is a
BFS over reverse edges with a visited set; output is sorted
lexicographically. Seeds not present in the graph are silently
skipped (per resolved Q3). Methods take uniform *Graph receivers
(per TD-09 / IM-04 receiver consistency).

Surface: Build, Graph, Stats, plus Has / DirectImports /
DirectImporters / RevDepClosure / Stats methods. Stats is computed
once at Build time and cached on the Graph (per resolved M3-1, the
user override of the spec's deferral lean).

Coverage at 100% statement coverage on depgraph; the dead dedup
branch in the existing in-progress sortAndDedup helper is removed
(reverse map is dedup-by-construction, forward map is pre-dedup'd
by mergeImports), keeping the gate honest rather than masking it
with test contortions.

Closing report walks F-1..F-20 row-by-row with grep-verifiable
evidence; substrate (guides loaded, pattern IDs cited) recorded per
F-19. Adds OUT-1 fix to Makefile (fresh lint cache per invocation)
which surfaced an unused-parameter issue on its first run, exactly
the M1/M2 carry-forward failure mode it exists to prevent. Adds CC
self-check checklist line to PR template per the M2 retro carry-
forward — the protocol gets installed in the artifact contributors
actually see.

Spec: docs/design/05-active/0004-cascade-m3-depgraph-reverse-dep-closure.md
Plan: docs/dev/0005-m3-implementation-plan-depgraph-reverse-dep-closure.md
Closing report: docs/dev/0006-m3-implementation-retrospective.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Records 835b4b0 as the closing commit for M3 work. Convention
matches M2's retrospective (0004-m2-implementation-retrospective.md)
where the closing-commit SHA is concrete and the merge OID is
filled in post-merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@oubiwann oubiwann merged commit 0048991 into main May 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant