Skip to content

ci: check transitive crate edges in check-crate-edges.sh (#407) - #408

Merged
entelecheia merged 3 commits into
mainfrom
ci/407-transitive-crate-edges
Oct 5, 2026
Merged

entelecheia merged 3 commits into
mainfrom
ci/407-transitive-crate-edges

Conversation

@entelecheia

@entelecheia entelecheia commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Summary

scripts/check-crate-edges.sh now walks the full normal-dependency tree instead of direct edges only, so a forbidden crate reached through an intermediate crate fails the gate (for example hwp-render -> hwpx -> hwp-convert). --self-test gains a transitive case, and AGENTS.md invariant 1 and REVIEW.md "Crate direction" now say the edges are direct or transitive.

Closes #407

Changes

  • deps(): drop --depth 1, keep -e normal, dedupe names with sort -u; matching stays anchored on the dependency name. A manifest variable (default Cargo.toml) lets the self-test point the same code at a fixture.
  • A cargo tree failure now fails the gate. Before, deps() discarded cargo's stderr and exit status, so a broken cargo tree (for example a missing manifest) gave an empty list and every edge read as "ok". deps() now returns cargo's status with the error left on stderr, and has_dep() reports match / no match / cargo failure separately; forbid() prints FAIL: cargo tree failed for <crate> and fails the gate on the third.
  • Fixed the self-test's real-workspace sanity check, which could never fire: under pipefail, deps hwp-cli | grep -Ecx hwp-render | grep -qx 0 is false both when the count is 0 (grep -c exits 1) and when it is not (grep -qx 0 exits 1). Both self-test cases now go through has_dep(), which matches on a here-string so pipefail cannot flip the result.
  • The self-test also runs forbid() against the fixture and requires fail=1 both for the forbidden edge (forbid edge-a edge-c) and for a cargo failure (forbid no-such-crate edge-c), so neither verdict arm can silently stop failing the gate. The edge case runs only after has_dep has shown cargo works on the fixture, so a cargo failure cannot satisfy it vacuously.
  • --self-test transitive case: builds a throwaway path-only workspace in mktemp -d (edge-a -> edge-b -> edge-c, no direct edge-a -> edge-c) and requires has_dep edge-a edge-c to hit. No internal crate reaches another only transitively today, so a synthetic fixture is the only way to cover the case.
  • FAIL message for an edge now says "directly or transitively", since a transitive hit has no matching line in the crate's own Cargo.toml.
  • AGENTS.md invariant 1 and REVIEW.md: "normal-dependency edges, direct or transitive; dev-dependencies are allowed".

Verification

bash scripts/check-crate-edges.sh on this branch (main's crate graph):

ok: hwp-render does not depend on hwp-convert
ok: hwp-convert does not depend on hwp-render
ok: hwp5 does not depend on hwpx
ok: hwpx does not depend on hwp5
ok: hwp-model does not depend on hwp5|hwpx|hwp-convert|hwp-render|hwp-cli
== crate-edges: OK ==

bash scripts/check-crate-edges.sh --self-test:

ok: self-test caught the transitive edge edge-a -> edge-b -> edge-c
ok: self-test saw forbid() fail on a forbidden edge and on a cargo tree failure
== crate-edges self-test: OK ==

shellcheck scripts/check-crate-edges.sh: clean.

Mutants (each run on a copy of the script or a temporary edit, then reverted); every one must fail, and does:

Mutant Run Result
sanity check expects no-such-crate instead of hwp-render --self-test FAIL: self-test could not see hwp-cli -> hwp-render, so the matcher is broken, exit 1
manifest=Missing.toml gate error: manifest path ... does not exist then FAIL: cargo tree failed for <crate>, so its edges were not checked for all 5 crates, == crate-edges: FAILED ==, exit 1
manifest=Missing.toml --self-test cargo error, then FAIL: self-test could not see hwp-cli -> hwp-render ..., exit 1
fixture manifest points at a missing file --self-test cargo error, then FAIL: self-test missed the transitive edge edge-a -> edge-b -> edge-c, exit 1
forbid() forbidden-edge arm sets fail=0 --self-test FAIL: self-test: forbid() did not fail on a forbidden edge, exit 1
forbid() cargo-failure arm sets fail=0 --self-test FAIL: self-test: forbid() did not fail when cargo tree failed, exit 1
--depth 1 put back into deps() --self-test FAIL: self-test missed the transitive edge edge-a -> edge-b -> edge-c, exit 1
hwpx = { path = "../hwpx" } added to crates/hwp-render/Cargo.toml (the #407 example) gate FAIL: hwp-render depends on hwp-convert, directly or transitively (AGENTS.md invariant 1), == crate-edges: FAILED ==, exit 1

Against main's script, the same two breakages (sanity check pointed at no-such-crate; --manifest-path Missing.toml injected into its cargo tree call) both exit 0 with == crate-edges self-test: OK == and == crate-edges: OK == respectively.

bash scripts/check.sh (tail), run on the first commit; the follow-up commits change only scripts/check-crate-edges.sh, verified with the gate, --self-test, shellcheck and the mutants above:

== bash scripts/check-crate-edges.sh
== crate-edges: OK ==
== bash scripts/check-crate-edges.sh --self-test
== crate-edges self-test: OK ==
...
== pdf-parity: SKIP (requires pdfinfo version 24.02.0; set HWP_PDF_PARITY=1 to require)
== fixture skips: 47 (4 optional), one line each in target/fixture-skips.log (HWP_REQUIRE_FIXTURES=1 fails the required ones)
== check: OK (fmt/clippy/test/crate-edges/pdf-runner/structured-corpus/claims/doc-surface/release-block/readiness-selfcheck/skip-accounting/public-parity=skipped) skipped-for-missing-fixtures=47 (optional=4) ==

The 47 skips are local-only fixtures absent from a fresh worktree; this change touches no Rust code or fixture-backed test.

Checklist

  • scripts/check.sh passes (fmt -> clippy --all-targets -D warnings -> test; the same gates as CI)
  • Does this change need verification in Hancom Office? (writer or compatibility-rule impact)
    • If it does not: [x] CI script and docs only; no reader or writer code changes
  • Data policy respected (no fixtures committed; the self-test fixture is generated in a temp directory and removed on exit)
  • Design documents updated where applicable: AGENTS.md invariant 1 (and REVIEW.md)
  • New features come with tests (the --self-test transitive case)
  • User-facing documentation updated on both sides (NAME.md and NAME.ko.md) in the same commit: not applicable, no user-facing docs change

Notes

  • Out of scope, unchanged: cargo tree still resolves the host target and default features, so an internal dependency behind a cfg(target_os = ...) or a non-default feature would not be seen. Raise a follow-up if that should be covered (--target all, --all-features).
  • Branch and PR policy: AGENTS.md "Branch · PR policy". No direct pushes to main; squash merge once CI is green.

🤖 Generated with Claude Code

The gate inspected only direct normal dependencies (`cargo tree --depth 1`),
so a forbidden crate reached through an intermediate one passed: hwp-render
depending on hwpx would pull in hwp-convert via hwpx -> hwp-convert.

Walk the full normal-dependency tree (`-e normal`, no depth limit) and
dedupe the names before matching. The self-test now builds a throwaway
three-crate workspace (edge-a -> edge-b -> edge-c) and requires the gate to
fail `edge-a` against `edge-c`, since no internal crate reaches another only
transitively today. AGENTS.md invariant 1 and REVIEW.md now say the edges
are direct or transitive.
Copilot AI balanced review requested due to automatic review settings October 5, 2026 20:24

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…407)

The self-test's real-workspace check could never fire: under pipefail,
`deps hwp-cli | grep -Ecx hwp-render | grep -qx 0` is false both when the
count is 0 (grep -c exits 1) and when it is not (grep -qx 0 exits 1).

deps() also discarded cargo's stderr and exit status, so a broken
`cargo tree` (for example a missing manifest) produced an empty list and
every edge read as "ok".

deps() now returns cargo's failure with the error left on stderr, and a
new has_dep() reports match, no match, or cargo failure separately.
forbid() fails the gate on a cargo failure, and both self-test cases go
through has_dep(), matching on a here-string so pipefail cannot flip the
result.
The self-test checked has_dep() only, so forbid() setting fail=0 on a
forbidden edge or on a cargo tree failure still passed. Run forbid()
against the fixture for both cases and require fail=1. The hit case runs
after has_dep() has shown cargo works on the fixture, so a cargo failure
cannot satisfy it vacuously.
@entelecheia
entelecheia merged commit 6a10ccc into main Oct 5, 2026
9 of 10 checks passed
@entelecheia
entelecheia deleted the ci/407-transitive-crate-edges branch October 5, 2026 22:11
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.

ci: check transitive crate edges in check-crate-edges.sh

2 participants