Skip to content

fix: finish the cartridges/ retirement — stop six more things passing over an empty set - #308

Merged
hyperpolymath merged 7 commits into
mainfrom
fix/retirement-tails
Aug 7, 2026
Merged

fix: finish the cartridges/ retirement — stop six more things passing over an empty set#308
hyperpolymath merged 7 commits into
mainfrom
fix/retirement-tails

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

The audit behind #306 found the cartridges/ retirement was roughly 70% done. This finishes the consumers.

The recurring defect is the same one #306 fixed in two gates, found six more times: a loop over a glob that now matches nothing, reporting success. just test printed "All FFI tests passed!" over zero items. aspect_tests.sh Aspect 4 printed "All cartridges accounted for (0 complete, 0 stub, 0 ffi_only)". Both now fail on an empty set.

Changes

Item What
Justfile 8 sites in 6 recipes now read CARTS="${BOJ_CARTRIDGES_PATH:-tests/fixtures/cartridges}", the convention tests/e2e_full.sh already set. test/test-verbose fail on an empty set; build/heal/tour say so out loud. clean targets the fixture only — deliberately not the operator's fetched cache. 108 recipes still parse.
scripts/refresh-bundled-cartridges.sh Deleted — it existed solely to sync the retired tree, and rm -rf'd inside it. No references anywhere.
scripts/boj-selinux-contexts.sh Was labelling <repo>/cartridges/. Repointed at the cache root, and skips with a message when no cache exists rather than labelling an impossible path.
generate-offline-menu.js Its fallback was the deleted tree, so with no env var it silently regenerated an empty menu. Now falls back to the fixture and exit(1)s on an empty root.
aspect_tests.sh, integration.sh, federation_multinode.sh Repointed. integration.sh step 5's bare cd under set -e was killing the run outright.
SECURITY.md The vulnerability template sent reporters to cartridges/browser-mcp/ and cartridges/cloudflare/ — the latter was never even a real directory name.
Gitlinks Two .claude/worktrees entries were committed as mode-160000 with no .gitmodules, so a fresh clone got two empty dirs and both showed permanently modified. git rm --cached + .gitignore. Directories on disk untouched.

One judgement call worth surfacing: the fixture manifests use catalogued/ready where aspect_tests.sh expects stub/ffi_only, so a naive repoint produced 23 spurious failures. Rather than loosen the check, all 23 manifests were verified to confirm catalogued ≡ manifest-only and ready ≡ ffi-without-abi — the two rules that already existed — and the synonyms documented. Likewise integration.sh skips manifest-only cartridges rather than failing them: reporting "missing adapter" against a catalogue that is manifest-only by design would be a fabricated defect in the other direction.

Verification

check result
just --list 108 recipes parse
bash tests/aspect_tests.sh exit 0 — 43 passed, 0 failed; Aspect 4 names the root and 23 items
same, with an empty BOJ_CARTRIDGES_PATH exit 1 — "completeness verified nothing"
workflows YAML 25 parse
node --check + deno check clean
shellcheck -S error, all touched scripts clean

Build residue — reported, not touched

1,346 files / 226 .so / 4.3 GB survive under cartridges/ on disk, invisible to git status (gitignored). Investigated rather than assumed:

  • Nothing has touched it in three weeks — newest mtime 2026-07-17, retirement commit 2026-08-04. Zero cartridge.json, zero .zig sources: it is purely build output.
  • The local-coord-mcp.service unit does not exist on this machine, user or system.
  • No live process maps any .so under cartridges/ (/proc/*/maps is clean).

Deletion looks safe, but it is disk state outside git — your call, so nothing was removed.

Found en route, deliberately out of scope

container/Containerfile.fly:80 does COPY cartridges/ /tmp/carts-meta/ from the host build context and never runs fetch-cartridges.shCOPY on a missing source is a hard failure, so that image cannot build. Note the main container/Containerfile is fine: it fetches into the builder stage first, so its COPY --from=zig-builder /build/cartridges/ is populated. One file, not both.

Also stale and untouched: stapeln.toml:44 (same empty-loop class, plus || true so it can never fail), coord-tui/install.sh:28, guix.scm:34, elixir/test/js_worker_pool_test.exs:6 (two tests currently pass by short-circuiting when Deno is absent), and dead entries in .hypatia-ignore / .gitleaksignore / .dockerignore. These are logged rather than swept so each gets a reviewable change.

🤖 Generated with Claude Code

hyperpolymath and others added 6 commits August 7, 2026 12:40
…eted tree

The bundled cartridges/ tree was retired in #300, but eight recipes still
globbed it. Every one of them matched nothing:

  * `build`, `test`, `test-verbose`, `heal` looped `cartridges/*/ffi` —
    zero iterations, and `test` then printed "All FFI tests passed!" over
    an empty set. A green `just test` proving nothing is exactly the
    failure mode the retirement cleanup is meant to remove.
  * `clean` rm -rf'd `cartridges/*/ffi/{.zig-cache,zig-out}` and
    `cartridges/*/abi/build` — no such paths in tree.
  * `tour` computed `CART_COUNT=$(ls -d cartridges/*-mcp | wc -l)`, so it
    reported "Current cartridge count: 0" beside a line saying cartridges
    now live in the registry.

All loops now read a catalog root, defaulting to the tracked fixture
catalogue exactly as tests/e2e_full.sh does, and overridable with
BOJ_CARTRIDGES_PATH for a cache populated by scripts/fetch-cartridges.sh.
Each loop counts what it visited: `test`/`test-verbose` FAIL on an empty
set rather than pass, `build`/`heal`/`tour` say so out loud. `clean`
targets the in-tree fixture artefacts only — it must not delete an
operator's fetched cache.

Also drops the stale "17 cartridges" count from `help-me`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ule at the cache

refresh-bundled-cartridges.sh existed solely to overwrite the in-tree
cartridges/ bundle from the canonical registry — it bailed with
"run from repo root (cartridges/ not found here)" and, had that check
passed, would have `rm -rf`'d directories that no longer exist. Its
subject was deleted in #300; the script is deleted with it. Nothing in
the tree referenced it (grepped: only its own header).

boj-selinux-contexts.sh wrote an fcontext rule for
  <repo>/cartridges/.*/ffi/zig-out/lib/.*\.so
and ran `restorecon -Rv <repo>/cartridges/`, neither of which resolves
any more. Cartridge .so files now live in the host-local cache that
scripts/fetch-cartridges.sh populates, so the rule is written against
that root (BOJ_CARTRIDGES_PATH, default $HOME/.boj/cartridges) and is
skipped with an explicit message when no cache is present — rather than
labelling a path that cannot exist. The core ffi/zig rule is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…leted tree

generate-offline-menu.js fell back to join(__dirname, "../../cartridges")
when BOJ_CARTRIDGES_PATH was unset. That path was removed in #300, so on a
fresh clone the generator scanned nothing; where stale build residue still
sits under cartridges/ on a developer's disk it scanned *that* instead, and
either way the operator would be told to hand-edit offline-menu.js from a
result that describes no real catalogue.

The fallback is now the tracked fixture catalogue, matching
tests/e2e_full.sh, and an empty or unreadable catalog root is a hard error
with a pointer to scripts/fetch-cartridges.sh — regenerating a menu from
zero cartridges must not look like success.

offline-menu.js: corrected the header (it is generated from a catalog root,
not from cartridges/; the regenerate command is a Deno invocation) and the
`summary.total` note, which claimed 127 cartridges were "on disk" under a
directory that no longer exists. The number is left as the snapshot it is,
now labelled as such.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… passing over empty sets

All three scripts still walked the cartridges/ tree deleted in #300.

tests/aspect_tests.sh
  Aspect 1's cartridge-FFI loop globbed $PROJECT_DIR/cartridges/*/ffi,
  matched nothing, and said nothing. Aspect 4 was worse: `shopt -s nullglob`
  plus an empty glob gave zero iterations, so incomplete stayed 0 and it
  printed "PASS: All cartridges accounted for (0 complete, 0 stub, 0
  ffi_only)" — a green check over an audit of nothing.
  Both loops now read a catalog root (BOJ_CARTRIDGES_PATH, default the
  tracked fixture catalogue). Aspect 1 reports how many FFI dirs it
  audited and WARNs when there are none; Aspect 4 FAILs outright on an
  empty root — "completeness verified nothing" — and otherwise names the
  root and the count in its pass line.
  Aspect 4 also learns the catalogue's status vocabulary: `catalogued` is
  the fixture/registry spelling of `stub` (manifest-only) and `ready` of
  `ffi_only`. Verified against all 23 fixture manifests: the 22
  `catalogued` entries carry neither abi/ nor ffi/, and `ready`
  feedback-mcp carries ffi/ without abi/ — exactly the two existing rules.
  Without this they would all fall through to the strict `complete` branch
  and fail for having a shape they are supposed to have.

tests/integration.sh
  Steps 4, 5 and 7 hard-coded cartridges/<name>/{adapter,ffi,abi}. Step 5's
  bare `cd "cartridges/$cart/ffi"` under `set -e` killed the run outright.
  All three now read the catalog root and distinguish three cases per
  cartridge: absent (SKIP, named), manifest-only catalogue entry (SKIP,
  named — nothing to audit, and reporting a missing adapter there would be
  a phantom defect), or implemented-but-missing-a-layer (FAIL, as before).
  Each step also says when it checked nothing at all.
  Fixes SC2144 while there: `[ -f dir/*_ffi.zig ]` is not a glob test — it
  errors on two matches and tests a literal pattern on none.

tests/federation_multinode.sh
  LD_LIBRARY_PATH named cartridges/container-mcp/ffi/zig-out/lib, a path
  that cannot exist, so the entry was inert. Replaced with the
  catalog-root sweep tests/e2e_full.sh already uses, which adds only lib
  dirs that are really there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The "Affected Component" example in the report template named
cartridges/browser-mcp/ and cartridges/cloudflare/ — neither of which is
in this repo since #300, and cartridges/cloudflare/ was never even a
cartridge directory name. A reporter following the template would file
against a path the maintainer cannot open. Replaced with real in-tree
components, plus an explicit pointer to hyperpolymath/boj-server-cartridges
for cartridge-side findings.

The rest of SECURITY.md's cartridge references are about the cartridge
system as a concept (isolation, loading, sandbox escape) and remain
accurate; left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
.claude/worktrees/gov-red and .claude/worktrees/zig-mutex were tracked
with mode 160000 (gitlink) while the repo has no .gitmodules. Git had no
URL to clone them from, so every fresh clone produced two empty
directories that `git status` reported as permanently modified, and no
tooling could ever resolve them. They are per-developer Claude Code
scratch checkouts and were never meant to be shared.

`git rm --cached` removes them from the index only — the directories on
disk are untouched — and .gitignore now covers .claude/worktrees/ so the
next scratch worktree cannot be committed by accident.

Verified: `git ls-files -s` now reports no mode-160000 entries anywhere,
and `git check-ignore -v` confirms both paths are covered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Aug 7, 2026
…et loops

Found while finishing the retirement tails (#308):

- D-8 (HIGH): container/Containerfile.fly cannot build. It COPYs
  cartridges/ from the host build context and never runs
  fetch-cartridges.sh, and COPY on a missing source is a hard failure.
  The main Containerfile is NOT affected — it fetches into the builder
  stage first — so this is one file, not both.
- D-9 (MEDIUM): stapeln.toml, coord-tui/install.sh, guix.scm and an
  Elixir test still traverse the deleted tree. stapeln.toml is the same
  empty-loop class plus `|| true`, so it can never fail.
- D-7 (LOW): dead entries left in .hypatia-ignore and .gitleaksignore
  make the allowlists look larger than the real exposure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your trial's included automatic processing has been used for this period. Upgrade now, or comment "Gitar review" to run a review anytime.
Learn more

Code Review ✅ Approved 1 resolved / 1 findings

Finishes the cartridges directory retirement by repointing recipes, tests, and scripts to the active catalog root and preventing empty-set silent successes. Consider harmonizing the global SKIP counter increments between Step 4 and Step 7 in the test scripts.

Auto-approved and auto-merge armed: No blocking issues found.
Please see Auto-approve Docs for details on setting custom approval criteria. — merges when pipeline and required approvals pass.

✅ 1 resolved
Quality: SKIP tally inconsistent between Step 4 and Step 7

📄 tests/integration.sh:104-118 📄 tests/integration.sh:173-187
In Step 4 the per-cartridge skips only bump the local adapter_skipped, and the global SKIP counter is incremented just once at the end regardless of how many subjects were skipped, whereas Step 7's matrix loop increments the global SKIP once per skipped cartridge. The summary's skipped figure therefore under-counts Step 4 skips. It does not affect the exit code (driven by FAIL), so this is cosmetic; align Step 4 to increment SKIP per skipped cartridge for an accurate tally.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Important

Your trial ends in 3 days — upgrade now to keep code review, CI analysis, auto-apply, custom automations, and more.

Was this helpful? React with 👍 / 👎 | Gitar

Comment thread tests/integration.sh
gitar-bot[bot]

This comment was marked as resolved.

@gitar-bot
gitar-bot Bot enabled auto-merge (squash) August 7, 2026 12:00

@gitar-bot gitar-bot 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.

Gitar has auto-approved this PR and enabled auto-merge (configure)

@gitar-bot gitar-bot Bot added the gitar-approved Added by Gitar label Aug 7, 2026
@gitar-bot
gitar-bot Bot dismissed their stale review August 7, 2026 14:41

✅ All code review findings resolved.

Configure merge blocking

@hyperpolymath
hyperpolymath disabled auto-merge August 7, 2026 14:47
@hyperpolymath
hyperpolymath merged commit 516e13e into main Aug 7, 2026
@hyperpolymath
hyperpolymath deleted the fix/retirement-tails branch August 7, 2026 14:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gitar-approved Added by Gitar

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant