Skip to content

ci: add the blocking dep-sync gate for the three-grouping workspace#480

Open
mfw78 wants to merge 1 commit into
feat/m5-three-grouping-workspacefrom
feat/m5-dep-sync-ci
Open

ci: add the blocking dep-sync gate for the three-grouping workspace#480
mfw78 wants to merge 1 commit into
feat/m5-three-grouping-workspacefrom
feat/m5-dep-sync-ci

Conversation

@mfw78

@mfw78 mfw78 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

What

Adds scripts/check-dep-sync.sh, a blocking dep-sync CI job, a just check-dep-sync recipe, and docs/design/cross-repo-deps.md documenting the convergence path (git-tag pins now, crates.io/wkg later).

Why

The three-grouping workspace needs an enforcing check that the crate DAG stays acyclic (nexum <- videre <- shepherd) and that the carve dependency artefacts (umbrella [patch], git-tag pins, per-group Cargo.repo.toml, wit-deps manifests/locks) stay in agreement as later waves land them, per issue scope.

Testing

  • nix develop -c ./scripts/check-dep-sync.sh: DAG and wit checks pass across 37 members; pin/Cargo.repo.toml/wit-deps checks skip visibly (not yet written by later waves).
  • nix develop -c cargo fmt --all --check: clean.

AI Assistance

Drafted with Claude Code assistance; reviewed and tested by the author.

Closes #403

scripts/check-dep-sync.sh asserts the crate DAG points strictly up
across nexum, videre and shepherd for every workspace member (path
deps and wit/ symlinks included), and that the carve dependency
artefacts agree wherever they exist: umbrella [patch] entries target
the owning grouping's in-tree crates and neutralise every written
git-tag pin with no orphans, pins agree on one tag per upstream repo,
each Cargo.repo.toml member list mirrors the umbrella, and wit-deps
manifests and locks stay key-synced with tags matching the Rust pins.
Artefacts a train wave has not written yet skip visibly and enforce
the moment they land. Wired as the blocking dep-sync CI job and a
just check-dep-sync recipe; the git-tag to crates.io and wkg/OCI
convergence path is documented in docs/design/cross-repo-deps.md.

@lgahdl lgahdl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core acyclicity direction logic is actually correct this time — verified gr > r (line 86) and the WIT/tag-sourcing rank comparisons all correctly flag only upstream-depends-on-downstream edges, with no inversion, unlike the polarity bugs I've found in several of the earlier boundary-check scripts this session. Skip-vs-pass is genuinely distinguishable and the existence checks use the exact filenames the design doc promises, so enforcement will correctly switch on once later-wave artifacts land rather than silently skipping forever. Two real latent bugs worth fixing before those artifacts actually show up (both are currently dormant since no git-pinned deps exist yet, per the PR's own "skip visibly" testing note — but they'll misfire exactly when the carve's git-tag pins get written, which is the highest-stakes moment for this gate):

Comment thread scripts/check-dep-sync.sh
for slug in nexum-runtime videre-nexum-module; do
url="https://github.com/nullislabs/$slug"
owner="$(slug_owner "$slug")"
pins="$(rg -Hn --no-heading "git *= *\"${url}(\\.git)?\"" "${manifest_files[@]}" || true)"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This greps for the literal git = "..." line, then checks tag = "..." against that SAME matched line (line ~163 in the loop below). Cargo's expanded dotted-table style writes them as separate lines under [dependencies.<crate>] (git = "..." then tag = "..." on the next line) — under that style, the git= line never contains tag=, so this reports a false "git pin without an inline tag" failure on a perfectly valid pin, permanently blocking the gate for any pin written that way. Search the whole [dependencies.<crate>]/inline-table block for the tag, not just the single matched line.

Relatedly: the pin regex tolerates an optional .git suffix ("${url}(\.git)?"), but the corresponding [patch] header lookup built later (hdr="[patch.\"$url\"]") doesn't — if a pin is written .git-suffixed, the umbrella's matching [patch."...​.git"] header will never be found, reporting "umbrella [patch] section missing" on a working patch.

Comment thread scripts/check-dep-sync.sh
rest = substr($0, i + 1 + length(root))
sub(/\).*/, "", rest)
split(rest, a, "/")
gr = a[1] == "nexum" ? 0 : a[1] == "videre" ? 1 : a[1] == "shepherd" ? 2 : -1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This AWK ternary hardcodes a second, independent group→rank mapping, duplicating whatever rank()/layers source of truth defines nexum/videre/shepherd's ranks elsewhere in the script (understandable since AWK can't call the bash function directly, but it's still a second copy of the same taxonomy). If a group is ever renamed and only the bash source of truth gets updated — the natural place to look — this literal falls out of sync, gr resolves to -1 for that group's crates, and gr > r never fires for it: the flagship acyclicity check silently stops catching real down-layer dependencies from that renamed group. Worth generating this ternary from the same source (e.g. passing the rank table in via -v from the bash function's output) instead of a second hardcoded literal.

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.

2 participants