Skip to content

feat(fetcher+catalog): consume boj-server-cartridges schema + validator (closes consumer-side of cartridges#19) #183

@hyperpolymath

Description

@hyperpolymath

Context

hyperpolymath/boj-server-cartridges (the canonical cartridge registry) just landed schema-foundation infrastructure via hyperpolymath/boj-server-cartridges#21:

  • Pinned schema mirror — commit 7c2b8155 of hyperpolymath/standards@cartridges/cartridge-v1.json, content SHA-256 033e2fa8..., with a schemas/PINNED-SHA pin file.
  • Zero-dep Deno validator at tools/validate-cartridges/ exposing audit, audit-verbose, strict, and test tasks.
  • Audit-mode CI gate running the validator against the registry on every push.
  • Captured drift inventory at audits/cartridge-schema-2026-06-01.md documenting current registry deviations from the v1 schema.

Boj-server is the runtime consumer of that registry. Right now it has two known data-loss / validation gaps relative to the new infrastructure:

  1. scripts/fetch-cartridges.sh (introduced by On-demand cartridge fetching of boj-server-cartridges. #169) silently strips the category field when copying cartridges from the canonical registry into the runtime cache, and performs no schema validation on what it copies. This is the silent-data-loss problem tracked as hyperpolymath/boj-server-cartridges#18 and rooted in hyperpolymath/boj-server-cartridges#19.
  2. elixir/lib/boj_rest/catalog.ex accepts any JSON object with a name field at load time — no schema validation. That's the consumer-side mirror of hyperpolymath/boj-server-cartridges#19.

With a pinned schema + validator now living in the canonical registry, boj-server can finally close both gaps without inventing its own source of truth.

Proposed work

These are proposed shapes, not a final design — happy to iterate before implementation.

1. Fetcher schema-preservation

scripts/fetch-cartridges.sh should copy cartridge.json byte-for-byte (no field stripping). If the existing category-strip is intentional (e.g., for runtime compactness or backwards-compat with an older catalog.ex), promote it from silent transformation to a logged warning so the drift is visible.

2. Fetcher schema-validation gate

After fetch, run the boj-server-cartridges Deno validator (either invoke it via deno task against a pinned commit, or vendor tools/validate-cartridges/ into this repo) against every copied manifest:

  • Fail the fetch on a strict-mode violation.
  • Warn (non-fatal) on audit-mode-only violations, mirroring the current registry CI gate's posture.

3. catalog.ex schema-validation

At load time, validate each manifest against the bundled schema. Reject malformed manifests with a clear error rather than silently registering broken cartridges. (A small Elixir JSON-schema dep, or shelling out to the Deno validator at boot, are both viable.)

4. PINNED-SHA mirror

Boj-server should pin the schema itself — probably as a fetch + SHA-check step in the same fetcher script. Cross-link to hyperpolymath/boj-server-cartridges's schemas/PINNED-SHA so the two pins move together (when the registry bumps its pin, this repo's pin-bump PR is mechanical).

5. Optional, lower priority — staleness warning

Surface a canonical-only warning when boj-server's bundled cache lags the registry. Today there's a 14-cartridge deficit per hyperpolymath/boj-server-cartridges#20 (LSP/MCP cartridges that exist canonically but aren't in the runtime cache). A boot-time log line listing those would make the gap visible without forcing immediate parity.

Refs

  • hyperpolymath/boj-server-cartridges#18 — silent category strip discovered
  • hyperpolymath/boj-server-cartridges#19 — root-cause: no schema validation either side
  • hyperpolymath/boj-server-cartridges#20 — 14-cartridge canonical/runtime deficit
  • hyperpolymath/boj-server-cartridges#21 — schema-foundation PR (pinned schema + Deno validator + CI gate + audit inventory)
  • hyperpolymath/boj-server-cartridges PR adding scripts/fetch-cartridges.sh upstream context: this repo's On-demand cartridge fetching of boj-server-cartridges. #169 (original fetcher PR)

Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions