* feat(models): commit models.schema.json and validate the SPOT against it in CI (#196) models.json has declared `"$schema": "./models.schema.json"` since v3.11.0, but that file was never committed — `git log --all -- models.schema.json` is empty. So the SPOT that ADR 0003 makes canonical had NO structural validation: a missing contextWindow, a typo'd openclawName, or a boolean written as a string would pass every existing test and only surface downstream — in OpenClaw's registry, or silently inside a truncation budget. Validated with the repo's OWN validator (lib/structured-output.mjs, shipped for #153) rather than adding ajv. AGENTS.md requires minimal dependencies and the repo currently has ZERO; this keeps it that way and exercises that validator on a second real input. Capability-probed first rather than assumed: it enforces type / required / enum / const / additionalProperties (both the boolean and the schema form) / items / minItems, which covers everything the SPOT needs. Three tests: - the $schema reference resolves to a committed file (the #196 bug itself) - models.json validates against models.schema.json, strict - the schema actually REJECTS 8 corruption shapes — missing required field, wrong scalar type, non-integer window, unknown extra field, alias mapped to a non-string, empty models array, wrong document version, unknown top-level key That third test is the guard-on-the-guard: without it a vacuous schema (`{}` or a typo'd `properties`) would make the second test pass on anything. Mutation-proven in three directions: schema replaced with {} -> "schema failed to reject: missing required field" additionalProperties:false removed -> guard fails (loosened constraint caught) models.json corrupted (drop a field) -> strict validation fails What the schema deliberately does NOT encode: referential integrity — that every aliases/legacyAliases target exists as a models[].id. That is not a JSON Schema concept; it is already covered by two dedicated tests, and the schema says so in its description so the next reader doesn't assume it is covered. The field descriptions carry the non-obvious consequences that have bitten this repo before: contextWindow is a GLOBAL lever (max across all entries x 3 sets MAX_PROMPT_CHARS for every model, ADR 0009), maxTokens is advertising only and is enforced by OpenClaw rather than OCP, and models[] order is load-bearing because ocp-connect uses model_ids[0] as a fallback. Documented per release_kit (new file/SPOT/schema -> contributor section): AGENTS.md "Key files to know" and README's Available Models section. Tests: 460 passed, 0 failed (457 + 3). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017gbqUZ8HfBZpjjbzQ85oH8 * fix(schema): warn that only a subset of draft 2020-12 is enforced; cover the gaps it cannot Both review findings taken. MED — the schema declared `$schema: draft/2020-12`, which promises full draft semantics, while the validator that actually runs it enforces only a subset. Someone adding `"minimum": 1` to contextWindow would get SILENT no-op with a green suite — a trap made worse by the file looking authoritative. The description now enumerates exactly what is enforced (type, required, const, enum, additionalProperties in both forms, items, minItems/maxItems, anyOf/allOf/oneOf, $ref, nullable) and names what is ignored (minimum, maxLength, pattern, uniqueItems, propertyNames, not), with the instruction to add such a constraint as a test instead. LOW — three corruptions neither the schema nor any sibling test caught: duplicate models[].id, empty-string id/displayName/openclawName, and non-positive contextWindow/maxTokens. They are structurally NOT expressible with the supported keyword set (no uniqueItems, no minLength, no minimum), so they are asserted directly rather than by pretending the schema covers them. Mutation-proven, all three previously slipped through: duplicate id -> CAUGHT empty displayName -> CAUGHT zero contextWindow -> CAUGHT Tests: 461 passed, 0 failed (460 + 1). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017gbqUZ8HfBZpjjbzQ85oH8 * test(schema): close the fourth gap — uniqueness on all three name fields, not just id Review approved the delta and named this exact follow-up as non-blocking: "extend the same test to assert uniqueness of displayName/openclawName and use m[f] === m[f].trim()". Implemented as specified. Why it matters rather than being tidiness: scripts/sync-openclaw.mjs maps `claude-local/<id>` -> { alias: displayName } and writes openclawName as the registry label, so a duplicate in EITHER field collapses two models onto one OpenClaw entry — the same defect class as a duplicate id, which the previous version covered while leaving its two siblings open. The trim check also changed shape. It was `m[f].trim().length > 0`, which a PADDED id passes by construction — and that id is handed VERBATIM to `claude --model`, so " claude-opus-5" would have failed upstream instead of here. Now asserts `m[f] === m[f].trim()`. Mutation-proven, all three previously slipped through: duplicate displayName -> CAUGHT duplicate openclawName -> CAUGHT whitespace-padded id -> CAUGHT Tests: 461 passed, 0 failed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017gbqUZ8HfBZpjjbzQ85oH8 --------- Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.2 KiB
Inherits: @~/.cc-rules/AGENTS.md
OCP — Open Claude Proxy — Agent Guidelines
Scope: the dtzp555-max/ocp repository.
Audience: any AI coding agent (Claude Code / Cursor / OpenCode / Copilot / Codex / Gemini) touching OCP source.
What this project is
OCP (Open Claude Proxy) is an open-source HTTP gateway that sits between the Claude Code CLI (cli.js) and Anthropic's public API. It forwards, observes, and multiplexes traffic that cli.js already emits — it is explicitly not an extension layer. A secondary role: registering OCP as a local provider inside OpenClaw (a sibling IDE-agnostic tool), so that users running OpenClaw against OCP see the same model list as native Claude Code.
Runtime: Node.js (ESM, .mjs throughout). No build step. No bundler. server.mjs is the single executable entrypoint; ocp and ocp-connect are CLI wrappers.
Stack
- Node.js >=18, native ESM modules
http/httpsbuilt-ins for the proxy core (no Express, no Fastify)models.jsonas the single source of truth for model metadata- GitHub Actions for CI (
alignment.yml,release.yml) ghCLI assumed for PR creation and release automation- No TypeScript. No test framework beyond
test-features.mjs(run vianpm test; CI workflow.github/workflows/test.yml). Keep dependencies minimal.
Key files to know
server.mjs— the proxy itself; every request path lives here. Governed byALIGNMENT.md.models.json— single source of truth for model IDs, aliases, and context windows. See ADR 0003.models.schema.json— the schemamodels.jsondeclares in its$schema. CI validates the SPOT against it (test-features.mjs) using the repo's ownvalidateJsonSchema, so a malformed entry fails the build instead of surfacing downstream in OpenClaw.setup.mjs— first-time installer; readsmodels.jsonto derive bootstrap config.scripts/sync-openclaw.mjs— idempotent OpenClaw registry sync invoked byocp update. See ADR 0004.ocp— user-facing CLI (install, update, start, stop, status, logs, etc.).ALIGNMENT.md— the constitution. Binding for anyserver.mjschange. See ADR 0002..github/workflows/alignment.yml— CI blacklist grep; fails the build on known-hallucinated tokens.CLAUDE.md— Claude-Code-specific session instructions + release_kit overlay (Iron Rule 5.5).docs/adr/— Architecture Decision Records. Read these before proposing governance or SPOT changes. Seedocs/adr/README.mdfor the index.docs/superpowers/plans/— active spec-kit plans.docs/superpowers/plans/shipped/archives plans that have been delivered (don't propose changes against shipped plans — they're history).docs/superpowers/specs/holds long-lived design documents that other code references (e.g., the SSE heartbeat design referenced fromserver.mjs).memory/constitution.md— spec-kit's project constitution (its standardmemory/location). Distinct from~/.cc-rules/memory/(cross-machine personal memory) and from this repo'sALIGNMENT.md(the OCP code-level constitution).
Project-specific constraints
ALIGNMENT.mdis binding. Any PR touchingserver.mjsmust citecli.js:NNNN(orcli.js vE4 <functionName>) in the commit body and PR description. SeeCLAUDE.md§ "Hard requirements forserver.mjschanges" and ADR 0002.- Alignment CI is not suppressible. The
alignment.ymlworkflow grepsserver.mjsfor known-hallucinated tokens (currently blockingapi.anthropic.com/api/oauth/usage). Adding new tokens is done via PR amendment toalignment.yml; removing entries requires anALIGNMENT.mdamendment PR. - No self-approval. Implementation author cannot merge their own PR (Iron Rule 10). A fresh-context reviewer must open
cli.jsat the cited lines and confirm in the review comment. models.jsonis the only place to add/edit models. Do not touchMODEL_MAPorMODELSarrays directly inserver.mjsorsetup.mjs. See ADR 0003.- OpenClaw boundary.
scripts/sync-openclaw.mjsonly writesmodels.providers["claude-local"].modelsandagents.defaults.models["claude-local/*"]in~/.openclaw/openclaw.json. Do not expand scope. See ADR 0004.
Release protocol
OCP follows the machine-readable release_kit: overlay in CLAUDE.md (Iron Rule 5.5). Before any version bump or tag push, re-read that YAML block and walk every item in new_feature_doc_expectations and bootstrap_quirk_policy. Tag push triggers .github/workflows/release.yml, which creates the GitHub Release automatically — do not create the release manually.
Version is sourced from package.json; changelog from CHANGELOG.md; user-facing docs from README.md.
Handoff expectations
A fresh session picking up OCP work should read, in order:
- This file (
AGENTS.md). ALIGNMENT.md— constitution; non-optional.CLAUDE.md— tool-specific instructions and release_kit overlay.docs/adr/— most recent ADRs first; they explain why the current structure exists.- Any active plan under
docs/superpowers/plans/(excludingshipped/which is the archive). ~/.cc-rules/memory/auto/MEMORY.md— cross-machine memory index.
Only after these should the session touch code.