feat(pre-commit): outbound-link-hygiene, the offline half dead-cross-links skips #1229

Merged
coilyco-ops merged 1 commit from aos/claude/ve67 into main 2026-08-25 07:25:15 +00:00
Owner

Closes coilyco-flight-deck/agentic-os#1228 (hook 1). Hook 2 stays deferred per the issue; hook 3's scheduled job is tracked separately.

What lands

outbound-link-hygiene, an offline pre-commit hook for the links dead-cross-links deliberately skips. Four checks, all reading agentic_os/outbound_link_rules.json so a rename edits a table rather than a validator:

  • retired names and paths - seeded with the four known renames (ward-mcp, Ward MCP, cli-guard, agent-guard) and the coilysiren.me/orgs/ path that never resolved. Each failure names its replacement.
  • canonical host - repository links resolve to canonical_repo_host when a repo declares one. Unset means no opinion, so nothing is imposed on repos where Forgejo is the right outward ref.
  • text-target mismatch - link text naming one project while the target names another. The slug set is derived from the tree being scanned, so an unknown label like [tap](.../homebrew-tap) is not a false positive.
  • placeholder and local URLs - example.com, localhost, TODO, empty targets, bare #.

Also check-link-liveness: hook 3's checker half, a report-only CLI deliberately absent from .pre-commit-hooks.yaml alongside the other authored-but-not-hooked validators. Reports non-2xx and non-3xx only, tolerates rate limiting and transport failure, never edits.

Use versus mention

Fenced and inline code are stripped before the name scan, so a doc narrating a rename backticks the retired name and a doc still using it does not. I measured this across agentic-os, agentic-os-kai, coilysiren and infrastructure before committing to it: that one rule exempts every legitimate historical mention found, including SSM parameter paths and IAM ARNs that keep a pre-rename spelling on purpose. Zero allowlist entries needed.

Verification

Against coilysiren/coilysiren:

  • at 9c259e5~2 it reports both defect classes - ward-mcp, Ward MCP and cli-guard in the README, and the three coilysiren.me/orgs/ links
  • at 9c259e5^ the README half is already fixed, so it reports the three org links plus cli-guard in the portfolio skill
  • on today's main it still finds two live ones: the portfolio skill names and links cli-guard, which is now umbra

716 tests green, full pre-commit run --all-files green.

Two things worth a reviewer's eye

The issue says both defects sit at 9c259e5^. The org-path half does; the retired-name half was already fixed by 77a54fb and sits one commit earlier at 9c259e5~2. Both classes still reproduce, so the criterion holds, but the revision in the issue is off by one for that half.

No cap was raised. docs/pre-commit-hygiene.md was exactly at its 120-line cap and docs/*.md is exactly at its 40-file cap, so a new page and a bigger page were both closed. The new section is paid for by compressing the leak-guard half in place. No rule or fact was removed - five H2s became two and restated sentences went. Worth checking that I did not cut something you wanted kept.

Rollout stays out per the authoring-vs-rollout split: no DEFAULT_HOOK_IDS entry. The fleet fan-out is ansible's.

Closes coilyco-flight-deck/agentic-os#1228 (hook 1). Hook 2 stays deferred per the issue; hook 3's scheduled job is tracked separately. ## What lands `outbound-link-hygiene`, an offline pre-commit hook for the links `dead-cross-links` deliberately skips. Four checks, all reading `agentic_os/outbound_link_rules.json` so a rename edits a table rather than a validator: - **retired names and paths** - seeded with the four known renames (`ward-mcp`, `Ward MCP`, `cli-guard`, `agent-guard`) and the `coilysiren.me/orgs/` path that never resolved. Each failure names its replacement. - **canonical host** - repository links resolve to `canonical_repo_host` when a repo declares one. Unset means no opinion, so nothing is imposed on repos where Forgejo is the right outward ref. - **text-target mismatch** - link text naming one project while the target names another. The slug set is derived from the tree being scanned, so an unknown label like `[tap](.../homebrew-tap)` is not a false positive. - **placeholder and local URLs** - `example.com`, `localhost`, `TODO`, empty targets, bare `#`. Also `check-link-liveness`: hook 3's checker half, a report-only CLI deliberately absent from `.pre-commit-hooks.yaml` alongside the other authored-but-not-hooked validators. Reports non-2xx and non-3xx only, tolerates rate limiting and transport failure, never edits. ## Use versus mention Fenced and inline code are stripped before the name scan, so a doc narrating a rename backticks the retired name and a doc still *using* it does not. I measured this across agentic-os, agentic-os-kai, coilysiren and infrastructure before committing to it: that one rule exempts every legitimate historical mention found, including SSM parameter paths and IAM ARNs that keep a pre-rename spelling on purpose. Zero allowlist entries needed. ## Verification Against `coilysiren/coilysiren`: - at `9c259e5~2` it reports both defect classes - `ward-mcp`, `Ward MCP` and `cli-guard` in the README, and the three `coilysiren.me/orgs/` links - at `9c259e5^` the README half is already fixed, so it reports the three org links plus `cli-guard` in the portfolio skill - on today's `main` it still finds two live ones: the portfolio skill names and links `cli-guard`, which is now `umbra` 716 tests green, full `pre-commit run --all-files` green. ## Two things worth a reviewer's eye **The issue says both defects sit at `9c259e5^`.** The org-path half does; the retired-name half was already fixed by `77a54fb` and sits one commit earlier at `9c259e5~2`. Both classes still reproduce, so the criterion holds, but the revision in the issue is off by one for that half. **No cap was raised.** `docs/pre-commit-hygiene.md` was exactly at its 120-line cap and `docs/*.md` is exactly at its 40-file cap, so a new page and a bigger page were both closed. The new section is paid for by compressing the leak-guard half in place. No rule or fact was removed - five H2s became two and restated sentences went. Worth checking that I did not cut something you wanted kept. Rollout stays out per the authoring-vs-rollout split: no `DEFAULT_HOOK_IDS` entry. The fleet fan-out is ansible's.
feat(pre-commit): add outbound-link-hygiene, the offline half dead-cross-links skips
All checks were successful
ci / aos-eval-tests (pull_request) Successful in 6s
ci / ward-doctor (pull_request) Successful in 5s
ci / aos-cli-tests (pull_request) Successful in 18s
ci / gate (pull_request) Successful in 46s
4314289a68
dead-cross-links returns early on anything carrying a scheme, so every link
leaving the estate was unchecked by anything. Two renames proved that costs
real links: ward-mcp became mcp-beaver and cli-guard became umbra while the
profile README kept naming and linking both, and three coilysiren.me/orgs/<org>/
links in the same file had never resolved, found only by hand-checking 25 links.

The new hook is static analysis only, because pre-commit must not depend on the
network. Four checks read agentic_os/outbound_link_rules.json, so a rename edits
a table rather than a validator: retired names and paths with their
replacements, canonical host for repository links when a repo declares one, link
text naming one project while the target names another, and placeholder or local
URLs. The table is seeded with the four known renames.

Use versus mention is the whole exemption mechanism. Fenced and inline code are
stripped before the name scan, so a doc narrating a rename backticks the retired
name while a doc still using it does not. Measured across the estate first: that
one rule exempts every legitimate historical mention found, including SSM
parameter paths and IAM ARNs that keep a pre-rename spelling on purpose, without
a single allowlist entry.

Liveness ships as check-link-liveness, a report-only CLI deliberately absent
from .pre-commit-hooks.yaml alongside the other authored-but-not-hooked
validators. It reports non-2xx and non-3xx only, tolerates rate limiting and
transport failure, and never edits.

Verified against coilysiren/coilysiren: at 9c259e5~2 it reproduces both defects
found by hand, and on today's main it still finds cli-guard named and linked in
the portfolio skill.

docs/pre-commit-hygiene.md was exactly at its 120-line cap and docs/ is exactly
at its 40-file cap, so the new section is paid for by compressing the leak-guard
half in place rather than by raising a fleet-wide cap. No rule or fact removed.

Rollout stays out: no entry in DEFAULT_HOOK_IDS, per the authoring-vs-rollout
split.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: platform
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agentic-os!1229
No description provided.