From c44b8c0accad72ecdf0d43396d5b464c8d376a3e Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 26 May 2026 13:40:50 +0100 Subject: [PATCH] feat(cartridges): canonicalise BoJ cartridge format in standards/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the canonical cartridge format specification and JSON schema as a new top-level standards/cartridges/ directory: * CARTRIDGE-FORMAT.adoc — prose spec * cartridge-v1.json — JSON Schema (canonical URL: https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json) * 0.2-AI-MANIFEST.a2ml — directory manifest Plus docs/decisions/ADR-002-cartridge-format-canonical-home.adoc ratifying standards as the canonical home, with boj-server's schema becoming a SHA-pinned mirror. The schema: * Drops boj-server's `-mcp`-only name restriction. Cartridges may now end in any of the canonical role suffixes: mcp, lsp, dap, bsp, debug, format, lint, build, nesy, agentic, fleet — covering the full BoJ server-role taxonomy that panll/src/abi/cartridge-schema.json v0.3.0 already enumerated. * Adds `category` (domain | cross-cutting | template), `states` (state-machine states per panll v0.3.0), and `source` (registry + path + optional sha256 for remote-fetched cartridges). * Preserves all existing required fields. Existing boj-server cartridge manifests remain valid against this schema once they gain the new required `category` field (mechanical sweep, separate PR). This is the foundational PR for extracting cartridges out of boj-server into a new boj-server-cartridges repository and expanding cartridge coverage to LSP/DAP/BSP/Format/Lint/Build/NeSy/Agentic/Fleet beyond the current MCP-only set. The follow-up PRs in this initiative will: 1. boj-server: replace boj-server/schemas/cartridge-v1.json with a SHA-pinned vendor of this canonical, update `$schema` URLs in all 125 cartridges, and add `category` fields. 2. Create hyperpolymath/boj-server-cartridges repository. 3. Refactor boj-server's catalog (BojRest.Catalog) for on-demand fetch from the new registry. 4. Port polystack's LSP halves as cartridges in the new repo (12 LSPs). 5. Redesign poly-orchestrator-lsp as fleet-mcp. No code paths consume this spec yet; CI surface is additive only. Co-Authored-By: Claude Opus 4.7 (1M context) --- cartridges/0.2-AI-MANIFEST.a2ml | 25 +++ cartridges/CARTRIDGE-FORMAT.adoc | 152 +++++++++++++++++ cartridges/cartridge-v1.json | 153 ++++++++++++++++++ ...R-002-cartridge-format-canonical-home.adoc | 91 +++++++++++ 4 files changed, 421 insertions(+) create mode 100644 cartridges/0.2-AI-MANIFEST.a2ml create mode 100644 cartridges/CARTRIDGE-FORMAT.adoc create mode 100644 cartridges/cartridge-v1.json create mode 100644 docs/decisions/ADR-002-cartridge-format-canonical-home.adoc diff --git a/cartridges/0.2-AI-MANIFEST.a2ml b/cartridges/0.2-AI-MANIFEST.a2ml new file mode 100644 index 00000000..867da4f1 --- /dev/null +++ b/cartridges/0.2-AI-MANIFEST.a2ml @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +a2ml_version: "0.2" +manifest_kind: directory +name: standards/cartridges +purpose: | + Canonical specification of the BoJ cartridge format. + Hosts (boj-server, panll, others) consume this spec; cartridge registries + (canonically hyperpolymath/boj-server-cartridges) produce manifests conforming + to cartridge-v1.json. +contents: + - file: CARTRIDGE-FORMAT.adoc + purpose: Prose specification (this document is its companion machine-readable form). + - file: cartridge-v1.json + purpose: JSON Schema for cartridge.json manifests. Canonical URL. + schema_url: https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json + - file: 0.2-AI-MANIFEST.a2ml + purpose: This file. +mirrors: + - location: boj-server/schemas/cartridge-v1.json + relationship: SHA-pinned mirror (vendored copy with explicit pinning in boj-server's manifest). + - location: panll/src/abi/cartridge-schema.json + relationship: Predecessor (v0.3.0 multi-protocol fleet manifest). Absorbed into v1 single-cartridge form. +related_adrs: + - ADR-002-cartridge-format-canonical-home diff --git a/cartridges/CARTRIDGE-FORMAT.adoc b/cartridges/CARTRIDGE-FORMAT.adoc new file mode 100644 index 00000000..c2380464 --- /dev/null +++ b/cartridges/CARTRIDGE-FORMAT.adoc @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) += BoJ Cartridge Format — Canonical Specification +Jonathan D.A. Jewell +:toc: macro +:icons: font + +toc::[] + +== Status + +*Stable v1* — supersedes the locally-held schemas at: + +* `boj-server/schemas/cartridge-v1.json` (MCP-only subset; now a SHA-pinned mirror of this file) +* `panll/src/abi/cartridge-schema.json` v0.3.0 (multi-protocol fleet manifest; absorbed) + +See link:../docs/decisions/ADR-002-cartridge-format-canonical-home.adoc[ADR-002] for the +canonicalisation rationale. + +== What is a cartridge? + +A *cartridge* is a self-contained server unit consumed by a host (typically `boj-server`) +to extend its tool surface, language-server reach, debug-adapter capabilities, build-tool +integration, or other server-role mode. Cartridges are *process-isolated* (each backend +listens on its own loopback port) and *content-addressable* (each manifest may include +an optional `source.sha256` for fetched cartridges). + +A cartridge directory at rest looks like: + +---- +/ +├── cartridge.json ← manifest (this spec) +├── mod.js ← host entry point (calls loopback backend) +├── abi/ ← Idris2 ABI definitions (REQUIRED for Teranga/Shield tier) +├── ffi/ ← Zig FFI implementation +├── adapter/ ← V-lang or Deno adapter (the running server) +├── panels/ ← optional UI panels +├── schemas/ ← cartridge-internal JSON schemas +└── README.adoc +---- + +== Naming and role suffix + +Cartridge names *must* end in one of the canonical role suffixes: + +[cols="1,3", options="header"] +|=== +| Suffix | Role + +| `-mcp` | Model Context Protocol server (default for tool surfaces) +| `-lsp` | Language Server Protocol +| `-dap` | Debug Adapter Protocol +| `-bsp` | Build Server Protocol +| `-debug` | Debugger / debug-only operations (when not strictly DAP) +| `-format` | Code formatter +| `-lint` | Linter / static analyser +| `-build` | Build orchestration +| `-nesy` | Neurosymbolic reasoning surface +| `-agentic` | Agent harness (OODA-style state machine) +| `-fleet` | Fleet orchestrator / multi-cartridge coordinator +|=== + +Regex: `^[a-z0-9]+(-[a-z0-9]+)*-(mcp|lsp|dap|bsp|debug|format|lint|build|nesy|agentic|fleet)$` + +A single domain may have multiple cartridges across roles: e.g. `database-mcp`, +`database-lsp`, `database-format` are three cartridges sharing the `database` domain. + +== Taxonomy (directory layout in registries) + +Cartridge registries (canonically `hyperpolymath/boj-server-cartridges`) lay out +their contents as the *Hybrid taxonomy*: + +---- +cartridges/ +├── domains/ +│ ├── cloud/ +│ │ ├── mcp.cartridge/ ← cloud-mcp +│ │ ├── lsp.cartridge/ ← cloud-lsp +│ │ └── format.cartridge/ ← cloud-format +│ ├── database/ +│ │ ├── mcp.cartridge/ +│ │ ├── lsp.cartridge/ +│ │ ├── postgres-adapter/ ← collection: database/-adapter +│ │ └── mongodb-adapter/ +│ └── orchestration/ +│ └── fleet-mcp/ +├── cross-cutting/ +│ ├── nesy/ +│ ├── agentic/ +│ └── debug-harness/ +└── templates/ + └── gossamer-mcp/ ← canonical scaffold for new cartridges +---- + +The directory name `.cartridge` is preferred for in-domain cartridges; the +cartridge.json `name` field carries the role suffix explicitly +(e.g. `"name": "cloud-mcp"`). + +== Manifest fields + +See link:cartridge-v1.json[`cartridge-v1.json`] for the machine-readable schema. + +Required: + +* `$schema` — must point to this canonical URL +* `spdx`, `copyright` — licensing +* `name` — role-suffix-terminated, kebab-case +* `version` — semver (pre-release suffix permitted) +* `description` — one-line human prose +* `domain` — functional domain (cloud, database, git, ...) +* `category` — `domain` | `cross-cutting` | `template` +* `tier` — `Teranga` (core) | `Shield` (security-critical) | `Ayo` (community) +* `protocols` — at least one of {MCP, LSP, DAP, BSP, REST, GraphQL, gRPC, NeSy, + Agentic, Fleet, Format, Lint, Build, Debug} +* `auth` — `{method, env_var, credential_source}`. `method` ∈ {none, api-key, oauth2, vault} +* `api` — `{base_url, content_type}`. `base_url` form: `local://` (canonical loopback) or `http://127.0.0.1:` (explicit port) +* `tools` — array of `{name, description, inputSchema}` + +Optional: + +* `ports` — `{allowed, denied}` port permission lists +* `states` — state-machine states (per panll v0.3.0 — useful for Agentic/BSP cartridges) +* `source` — `{registry, path, sha256}` for cartridges loaded from a remote registry + +== Trust tiers + +* *Teranga* (core) — always available; ships with the host or fetched at first run. + Requires Idris2 ABI + Zig FFI + formal correctness proof. Cannot be removed by + user without reinstall. +* *Shield* (security-critical) — handles credentials, audit trails, sandboxing + boundaries. Same proof obligations as Teranga; additionally requires CVE-watch + subscription + signed releases. +* *Ayo* (community-contributed) — broader surface; formal proofs are encouraged + but not mandatory for v1 admission. Subject to sandbox quarantine on first run. + +== Registry and on-demand fetch + +The canonical cartridge registry is the GitHub repository +`hyperpolymath/boj-server-cartridges`. Hosts fetch cartridges from this registry +on demand into a host-local cache (e.g. `~/.boj/cartridges/` for boj-server). +The host catalog (e.g. `BojRest.Catalog`) loads cartridge.json from the cache +at boot or on demand. See ADR-002 for the catalog-refresh contract. + +Cartridges may also be fetched from other registries identified by GitHub URL. +The `tray` UI exposes "Add cartridge source" with optional verification against +`source.sha256`. + +== Versioning + +This document is v1. Backwards-incompatible changes will be published as +`cartridge-v2.json` with a migration ADR. v1 manifests remain consumable +indefinitely; hosts implementing v2 must accept both. diff --git a/cartridges/cartridge-v1.json b/cartridges/cartridge-v1.json new file mode 100644 index 00000000..2cc334e4 --- /dev/null +++ b/cartridges/cartridge-v1.json @@ -0,0 +1,153 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json", + "title": "BoJ Cartridge Manifest (canonical)", + "description": "Canonical schema for BoJ cartridge manifests. Mirrors the schema previously held in boj-server/schemas/cartridge-v1.json, expanded to admit the full BoJ server-role taxonomy (MCP, LSP, DAP, BSP, Debug, Format, Lint, Build, NeSy, Agentic, Fleet) per panll/src/abi/cartridge-schema.json v0.3.0. SPDX-License-Identifier: MPL-2.0.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "format": "uri", + "description": "Schema URL. Canonical form: https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json" + }, + "spdx": { + "type": "string", + "description": "SPDX license identifier (e.g. MPL-2.0)" + }, + "copyright": { + "type": "string" + }, + "name": { + "type": "string", + "description": "Lowercase kebab-case cartridge name with role suffix. The suffix indicates the server role this cartridge implements.", + "pattern": "^[a-z0-9]+(-[a-z0-9]+)*-(mcp|lsp|dap|bsp|debug|format|lint|build|nesy|agentic|fleet)$" + }, + "version": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-z0-9.-]+)?$", + "description": "Semantic version. Pre-release suffix permitted." + }, + "description": { + "type": "string" + }, + "domain": { + "type": "string", + "description": "Functional domain (e.g. cloud, database, git, k8s, observability, proof-verification, ssg, orchestration). For cross-cutting cartridges, use cross-cutting." + }, + "category": { + "type": "string", + "enum": ["domain", "cross-cutting", "template"], + "description": "Taxonomy category. Domain cartridges live under cartridges/domains//. Cross-cutting under cartridges/cross-cutting/. Templates under cartridges/templates/." + }, + "tier": { + "type": "string", + "enum": ["Teranga", "Shield", "Ayo"], + "description": "Trust tier. Teranga = core/always-available. Shield = security-critical/elevated-trust. Ayo = community-contributed." + }, + "protocols": { + "type": "array", + "description": "Server protocols this cartridge speaks. A cartridge may expose more than one protocol if its role naturally spans them (e.g. MCP+REST). The role suffix in 'name' indicates the PRIMARY protocol.", + "items": { + "type": "string", + "enum": ["MCP", "LSP", "DAP", "BSP", "REST", "GraphQL", "gRPC", "NeSy", "Agentic", "Fleet", "Format", "Lint", "Build", "Debug"] + }, + "minItems": 1 + }, + "auth": { + "type": "object", + "properties": { + "method": { + "type": "string", + "enum": ["none", "api-key", "oauth2", "vault"] + }, + "env_var": { + "type": ["string", "null"] + }, + "credential_source": { + "type": ["string", "null"] + } + }, + "required": ["method", "env_var", "credential_source"] + }, + "api": { + "type": "object", + "properties": { + "base_url": { + "type": "string", + "description": "Loopback URL the cartridge backend listens on. Form: local:// for the canonical loopback scheme, or http://127.0.0.1: for explicit-port form." + }, + "content_type": { + "type": "string" + } + }, + "required": ["base_url", "content_type"] + }, + "ports": { + "type": "object", + "description": "Port permissions for the cartridge runtime.", + "properties": { + "allowed": { + "type": "array", + "items": { "type": "integer", "minimum": 1, "maximum": 65535 } + }, + "denied": { + "type": "array", + "items": { "type": "integer", "minimum": 1, "maximum": 65535 } + } + }, + "required": ["allowed", "denied"] + }, + "tools": { + "type": "array", + "description": "Tool surface exposed to the host (MCP-style; LSP/DAP/BSP cartridges may declare their protocol-native operations here too, with inputSchema describing the expected payload).", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "inputSchema": { "type": "object" } + }, + "required": ["name", "description", "inputSchema"] + } + }, + "states": { + "type": "array", + "description": "Optional state-machine states a cartridge backend transitions through (per panll v0.3.0). E.g. observe/orient/decide/act/halted for an Agentic cartridge.", + "items": { "type": "string" } + }, + "source": { + "type": "object", + "description": "Where this cartridge is fetched from when not bundled locally.", + "properties": { + "registry": { + "type": "string", + "description": "Registry identifier. Canonical default: hyperpolymath/boj-server-cartridges" + }, + "path": { + "type": "string", + "description": "Path within the registry, e.g. cartridges/domains/cloud/mcp.cartridge" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$", + "description": "Optional content hash for verification of fetched cartridge artifact." + } + } + } + }, + "required": [ + "$schema", + "spdx", + "copyright", + "name", + "version", + "description", + "domain", + "category", + "tier", + "protocols", + "auth", + "api", + "tools" + ] +} diff --git a/docs/decisions/ADR-002-cartridge-format-canonical-home.adoc b/docs/decisions/ADR-002-cartridge-format-canonical-home.adoc new file mode 100644 index 00000000..d514581c --- /dev/null +++ b/docs/decisions/ADR-002-cartridge-format-canonical-home.adoc @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) += ADR-002: Cartridge format — canonical home in standards, mirrored in boj-server +:adr-status: Accepted +:adr-date: 2026-05-26 +:adr-author: Jonathan D.A. Jewell + +== Status + +*Accepted* 2026-05-26. + +== Context + +Two cartridge schemas have lived in parallel in the estate: + +* `boj-server/schemas/cartridge-v1.json` — single-cartridge manifest, MCP-only + (`name` regex requires `-mcp` suffix). Schema URL: `https://boj.dev/schemas/cartridge/v1.json`. +* `panll/src/abi/cartridge-schema.json` v0.3.0 — multi-cartridge "fleet" manifest + enumerating cartridges with their `tier`, `domain`, `protocols`, `states`, + and `tools`. Protocols listed include MCP, LSP, DAP, BSP, NeSy, Agentic, + Fleet, GRPC, REST — the full BoJ server-role taxonomy. + +A separate planned initiative will: + +1. Extract the 125 cartridges currently bundled in `boj-server/cartridges/` to + a new repository `hyperpolymath/boj-server-cartridges`. +2. Wire boj-server's catalog to fetch cartridges on demand rather than ship them + baked-in. +3. Expand cartridge coverage beyond MCP into the LSP/DAP/BSP/Format/Lint/Build/ + NeSy/Agentic/Fleet roles already enumerated in panll's schema. + +This requires a single canonical, role-agnostic cartridge schema accessible to +multiple consumers (boj-server, panll, the new cartridges repo). + +== Decision + +* The canonical cartridge format is owned by *`standards/cartridges/`*: +** `standards/cartridges/cartridge-v1.json` — JSON Schema +** `standards/cartridges/CARTRIDGE-FORMAT.adoc` — prose specification +** `standards/cartridges/0.2-AI-MANIFEST.a2ml` — directory manifest +* Canonical schema URL: `https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json`. +* `boj-server/schemas/cartridge-v1.json` becomes a *SHA-pinned mirror* of the + canonical file, refreshed by a separate PR when standards advances the schema. +* `panll/src/abi/cartridge-schema.json` v0.3.0 is *absorbed* — the v1 schema + here takes panll's broader protocol set into the single-cartridge form. + Panll's fleet manifest semantics (multiple cartridges in one file) is + reconstructible as an array of v1 manifests; we do not preserve the + fleet-manifest container shape. +* The v1 schema *admits* the full role suffix list: + `(mcp|lsp|dap|bsp|debug|format|lint|build|nesy|agentic|fleet)`. boj-server's + prior `-mcp`-only restriction is dropped. +* New fields added on top of boj-server's prior schema: +** `category` — taxonomy bin (`domain` | `cross-cutting` | `template`). +** `states` — state-machine states (per panll's v0.3.0). +** `source` — `{registry, path, sha256}` for remote-fetched cartridges. +* Versioning: the new schema is v1 at this canonical home; v2 will accompany + any backwards-incompatible change with its own ADR. + +== Consequences + +*Positive:* + +* One canonical specification across the estate; mechanical to verify. +* Schema URL becomes stable (`hyperpolymath.dev/standards/...` rather than + `boj.dev/...` — the latter URL is not currently served). +* Future cartridge work (LSP/DAP/BSP/format/lint/build/nesy/agentic ports of + polystack components) becomes legal at the schema level on day one. +* `boj-server-cartridges` consumes the schema directly from standards, removing + the implicit dependency on the boj-server repo. + +*Negative:* + +* All existing 125 cartridges in `boj-server/cartridges/` need a `category` + field added (mechanical sweep; mostly `domain` with a few `cross-cutting`). +* Schema URL has changed; consumers referencing the old URL need an update. + (Currently only `cartridge.json` files inside boj-server's cartridges + reference it — same-PR sweep can update them.) + +*Neutral:* + +* Panll's `cartridge-schema.json` v0.3.0 file is not deleted by this ADR; it + remains until panll's own catalog code is migrated to consume the v1 schema + (separate PR). + +== Implementation plan (referenced from companion PRs) + +1. *This PR* — land `standards/cartridges/` with spec + schema + ADR. +2. *boj-server PR* — replace `schemas/cartridge-v1.json` with SHA-pinned vendor + of the canonical, update `$schema` URLs in all 125 cartridges. +3. *Future:* boj-server-cartridges repo creation + cartridge extraction + + catalog refactor + polystack LSP-half ports.