Skip to content

fix(deonboard): compare owner/repo case-insensitively when pruning - #52

Merged
hyperpolymath merged 1 commit into
mainfrom
claude/deonboard-case-insensitive
Oct 14, 2026
Merged

hyperpolymath merged 1 commit into
mainfrom
claude/deonboard-case-insensitive

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Why

scripts/deonboard-estate-audit.sh (from #50) prunes a dependency that is still in use.

gh actions-lock writes a dependency's nested uses: with owner/repo in lower case, but keeps the record key's original case. hyperpolymath/januskey's lock (2026-10-14) shows this:

  • its hyperpolymath/standards@7d2041a record lists swatinem/rust-cache@6323deb…;
  • the record itself is keyed Swatinem/rust-cache@6323deb….

The script's byte-for-byte grep -vxF judges that record orphaned and deletes it. That leaves a dangling edge, which standards scripts/close-lock.sh measured killing runs at startup with zero jobs. Dry-running the script on januskey would have broken its rust-ci and governance runs to de-onboard the audit.

Fix

Lower-case owner/repo on both sides before comparing, the way GitHub resolves names. Refs keep their case, because git refs are case-sensitive.

When in doubt the script now keeps a record, which is the safe direction: an extra record is harmless, a dangling edge is fatal.

On januskey's lock it now prunes exactly cicd-suite@main, deed-ecosystem@f9d999b and k9-ecosystem@2155aa2, and keeps Swatinem/rust-cache.

Test

tests/deonboard-estate-audit.sh is new. It runs in the "Composite shell contract" job, and the script is added to both path filters. It builds a fixture lock in gh-actions-lock's layout that contains the mixed-case edge, and asserts:

  • the audit is de-onboarded;
  • the true orphans are pruned;
  • the still-used record is kept;
  • no edge dangles;
  • a second run changes nothing;
  • a mutant with the byte-for-byte compare restored is killed.
Script Result
this branch PASS=9 FAIL=0
main FAIL=3 (prunes the record, leaves the dangling edge, mutant identical)

tests/lock-transitive-closure.sh PASS=8 FAIL=0 and tests/composite-shell-contract.sh PASS=28 FAIL=0. This PR does not touch actions/**, so no repin is needed. shellcheck is clean. actionlint reports only the five $/actions/… findings already on main.

Independent of #51; either can merge first.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Kjdf4Mf4Z41bNQh2TrsEp9


Generated by Claude Code

scripts/deonboard-estate-audit.sh pruned a dependency that was still in use.
gh actions-lock writes the nested `uses:` of a dependency with owner/repo in
lower case and keeps the record key's original case. hyperpolymath/januskey's
lock (2026-10-14) holds the edge 'swatinem/rust-cache@6323deb…' under its
hyperpolymath/standards record, and the record 'Swatinem/rust-cache@6323deb…'.
The byte-for-byte `grep -vxF` judged that record orphaned and deleted it,
leaving a dangling edge. Standards scripts/close-lock.sh measured that shape
killing runs at startup with zero jobs. On januskey the script would have
broken rust-ci and governance to de-onboard the audit.

The script now lower-cases owner/repo on both sides before comparing, the way
GitHub resolves them. Refs keep their case, because git refs are
case-sensitive. When unsure it keeps a record, which is the safe direction:
an orphan record is harmless, a dangling edge is fatal. On januskey's lock it
now prunes exactly cicd-suite@main, deed-ecosystem@f9d999b and
k9-ecosystem@2155aa2, and keeps Swatinem/rust-cache.

tests/deonboard-estate-audit.sh (new, run by the "Composite shell contract"
job; the script joins both path filters) uses a fixture lock in gh-actions-lock
layout with the mixed-case edge. It checks:
- the audit is de-onboarded;
- the true orphans are pruned;
- the still-reached record is kept;
- no edge dangles;
- a second run changes nothing;
- a mutant with the byte-for-byte compare restored is killed.

Against main's script the same suite reports FAIL=3, against this one FAIL=0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kjdf4Mf4Z41bNQh2TrsEp9
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 53 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: aa11759e-b27e-4521-9328-2cafc885d0cd
📥 Commits

Reviewing files that changed from the base of the PR and between 376898b and fd39985.

📒 Files selected for processing: 3
  • .github/workflows/code-hygiene-self-test.yml
  • scripts/deonboard-estate-audit.sh
  • tests/deonboard-estate-audit.sh
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

@hyperpolymath
hyperpolymath marked this pull request as ready for review October 14, 2026 13:42
@hyperpolymath
hyperpolymath merged commit af69a9c into main Oct 14, 2026
17 of 18 checks passed
@hyperpolymath
hyperpolymath deleted the claude/deonboard-case-insensitive branch October 14, 2026 13:43
hyperpolymath added a commit that referenced this pull request Oct 19, 2026
… the schema guard and the case fix (#56)

## What was wrong

`scripts/deonboard-workflow.sh` (#54) is a copy of the estate-audit
de-onboard script, taken before #52 and #53 landed. It has neither fix:

1. **No schema guard.** It accepts any `actions.lock` schema. On a
`v9.9.9` lock it exits 0 and edits the lock. #53's fail-closed guard
(exit 3) lives only in `deonboard-estate-audit.sh`.
2. **Case-sensitive prune.** It compares dependency keys byte for byte.
Take a lock where the `hyperpolymath/standards` record reaches
`swatinem/rust-cache@6323deb…` but the record key is
`Swatinem/rust-cache@6323deb…`. The script prunes that record and leaves
a dangling edge, which startup-fails the run. This is the januskey shape
#52 fixed, but only in the estate-audit copy.

**Who was exposed:** `rollout_language_gates.sh` de-onboards every Rust
CI, Creusot, SPARK and miniKanren caller through
`deonboard-workflow.sh`. Rust CI callers are exactly the repositories
whose locks carry the `standards` → `rust-cache` edge.

As far as this session knows, no rollout has run since #54 merged, so
this is a latent defect rather than a broken estate. That is ASSUMED: I
have no rollout logs.

The `deonboard-workflow.sh` header and README line 273 on main already
said the estate-audit script "now delegates" to it. It did not; it was a
second copy.

## The change

- **`scripts/deonboard-workflow.sh`** is now the one implementation. It
carries #53's `known_versions` guard and #52's `lower_repo` prune, with
their comments carried over verbatim.
- **`scripts/deonboard-estate-audit.sh`** is now just a usage check and
one `exec …/deonboard-workflow.sh "$repo"
.github/workflows/main-estate-audit.yml`. `rollout_estate.sh`, the
templates and the README keep working through it unchanged.
- **`tests/deonboard-lock-schemas.sh`**:
  - It reads `known_versions` from the generic script.
  - Every fixture and the v9.9.9 control run through both entry points.
- The prune-skipping mutant is applied to the generic script and must be
killed through the wrapper as well, which fails if the wrapper ever
stops delegating.
  - The optional argument is now a scripts directory.
- **`tests/deonboard-estate-audit.sh`** (#52):
  - Both entry points run the same checks.
  - The two entry points must produce byte-identical locks.
- The `| lower_repo` mutant is applied to the generic script and must be
killed through both.
- **CI** (`code-hygiene-self-test.yml`):
- `tests/deonboard-estate-audit.sh` was never run in CI. It now runs in
the Composite shell contract job.
  - The workflow also triggers on `scripts/deonboard-workflow.sh`.
  - No `uses:` added.
- **Docs:** README and ARCHITECTURE now describe the delegation as it
is.
- **Ported from #55:** `3325927` is #55's repin, cherry-picked with
`-x`; the diff is byte-identical.
- **Why:** main's `lock-transitive-closure` failure would otherwise stop
the Composite shell contract job before the de-onboard steps run.
  - **If #55 merges first:** this commit becomes a no-op.

## Evidence (TESTED)

I copied the new suites into a tree of `origin/main` (`9a947fa`) and ran
them against main's scripts.

`tests/deonboard-estate-audit.sh`: PASS=14 FAIL=4
- `FAIL pruned Swatinem/rust-cache, which the standards record still
reaches`
- `FAIL dangling edge(s):
Swatinem/rust-cache@6323deb`
- `FAIL the entry points disagree: …`
- `FAIL mutant is identical to the script: the case-folding the test
exists for is gone`

`tests/deonboard-lock-schemas.sh`: PASS=57 FAIL=4
- `FAIL script declares no known_versions`
- `FAIL exit 0 on schema v9.9.9 (want 3)`
- `FAIL lock modified despite refusal`
- `FAIL orphan check passed a mutant that does not prune` (main's
wrapper does not delegate)

On this branch:

| Check | Result |
| --- | --- |
| `tests/deonboard-estate-audit.sh` | PASS=19 FAIL=0 |
| `tests/deonboard-lock-schemas.sh` | PASS=63 FAIL=0 |
| `tests/lock-transitive-closure.sh` | PASS=31 FAIL=0 |
| `tests/composite-shell-contract.sh` | PASS=28 FAIL=0 |
| `bash -n` | clean |
| `shellcheck -S warning` | clean (default severity: same SC2015/SC2016
counts as main) |
| `actionlint` | no new findings (the 8 `"$/actions/…"` findings in the
hygiene self-test are also on main) |

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01VTeVWDEJgdYKsUNr4uhnXX

---------

Co-authored-by: Claude <noreply@anthropic.com>
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