mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-27 07:55:07 +00:00
Two knowledge-preservation fixes. No behavior change: server.mjs gains comments only, verified by `git diff --stat` (comment lines) and an unchanged suite. #200 — structuredHash and dedupKey compute the same value and must NOT be collapsed. They take identical arguments and read as obvious duplicate work, which is how someone "optimises" them into a bug. Their GUARDS differ, verified line by line rather than asserted: structuredHash: if (CACHE_TTL > 0 && !conversationId && !hasCacheControl(...)) dedupKey: (!conversationId && !hasCacheControl(...)) <- no CACHE_TTL CLAUDE_CACHE_TTL defaults to 0, so in the DEFAULT configuration structuredHash is null while dedupKey must still be computed — it drives #153's single-flight stampede protection, which is deliberately independent of response caching. `dedupKey = structuredHash` would silently disable stampede protection by default, in exactly the concurrent-AI-Task case it exists to bound. The comment records that, and says what a safe deduplication would look like if anyone still wants one (compute under the weaker guard, derive under the stronger, add a stampede test first). This was first raised in the #194 review as a cosmetic nit and then RETRACTED by that reviewer after reading the guards — the retraction is the valuable part, so it goes in the code rather than staying in a PR thread. #197 — new "Testing: reaching faults inside server.mjs" section in AGENTS.md. The premise the issue was originally filed on was wrong twice over, and both errors were mine: first that no live-server harness existed (ltBoot has been there all along, and I had used it myself in #191), then that a synchronous fault deep inside spawnClaudeProcess needed a production test hook (it needs --stack-size, which ltBoot already forwards). The section documents the fixture, the --stack-size fault lever with the concrete numbers (~124k element spread threshold at the default stack vs ~24k at --stack-size=200, which is what fits Linux's 131072-byte MAX_ARG_STRLEN so the test RUNS in CI instead of skipping), and the three rules that made it hold: discover the threshold in a child under the same flags, assert the fault actually fired, and wait for the thing you are about to assert rather than a proxy for it. Each rule cites the issue that taught it (#193/#199/#203). Written to be read BEFORE someone concludes a bug class is untestable, since that conclusion has now been reached wrongly twice in this repo. cli.js citation: NOT APPLICABLE — comments only, no wire behavior, no code path, no argv change. Authorized by ADR 0006 as a behaviour-preserving edit; the field set and every request/response contract are byte-identical. Tests: 457 passed, 0 failed (unchanged, as expected for a comments-only diff). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017gbqUZ8HfBZpjjbzQ85oH8
91 lines
7.0 KiB
Markdown
91 lines
7.0 KiB
Markdown
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`/`https` built-ins for the proxy core (no Express, no Fastify)
|
|
- `models.json` as the single source of truth for model metadata
|
|
- GitHub Actions for CI (`alignment.yml`, `release.yml`)
|
|
- `gh` CLI assumed for PR creation and release automation
|
|
- No TypeScript. No test framework beyond `test-features.mjs` (run via `npm 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 by `ALIGNMENT.md`.
|
|
- `models.json` — single source of truth for model IDs, aliases, and context windows. See ADR 0003.
|
|
- `setup.mjs` — first-time installer; reads `models.json` to derive bootstrap config.
|
|
- `scripts/sync-openclaw.mjs` — idempotent OpenClaw registry sync invoked by `ocp update`. See ADR 0004.
|
|
- `ocp` — user-facing CLI (install, update, start, stop, status, logs, etc.).
|
|
- `ALIGNMENT.md` — the constitution. Binding for any `server.mjs` change. 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. See `docs/adr/README.md` for 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 from `server.mjs`).
|
|
- `memory/constitution.md` — spec-kit's project constitution (its standard `memory/` location). Distinct from `~/.cc-rules/memory/` (cross-machine personal memory) and from this repo's `ALIGNMENT.md` (the OCP code-level constitution).
|
|
|
|
---
|
|
|
|
## Project-specific constraints
|
|
|
|
- **`ALIGNMENT.md` is binding.** Any PR touching `server.mjs` must cite `cli.js:NNNN` (or `cli.js vE4 <functionName>`) in the commit body and PR description. See `CLAUDE.md` § "Hard requirements for `server.mjs` changes" and ADR 0002.
|
|
- **Alignment CI is not suppressible.** The `alignment.yml` workflow greps `server.mjs` for known-hallucinated tokens (currently blocking `api.anthropic.com/api/oauth/usage`). Adding new tokens is done via PR amendment to `alignment.yml`; removing entries requires an `ALIGNMENT.md` amendment PR.
|
|
- **No self-approval.** Implementation author cannot merge their own PR (Iron Rule 10). A fresh-context reviewer must open `cli.js` at the cited lines and confirm in the review comment.
|
|
- **`models.json` is the only place to add/edit models.** Do not touch `MODEL_MAP` or `MODELS` arrays directly in `server.mjs` or `setup.mjs`. See ADR 0003.
|
|
- **OpenClaw boundary.** `scripts/sync-openclaw.mjs` only writes `models.providers["claude-local"].models` and `agents.defaults.models["claude-local/*"]` in `~/.openclaw/openclaw.json`. Do not expand scope. See ADR 0004.
|
|
|
|
---
|
|
|
|
## Testing: reaching faults inside `server.mjs`
|
|
|
|
`test-features.mjs` cannot `import` `server.mjs` (it calls `server.listen()` at top level), and that has twice led to the wrong conclusion that a class of bug is untestable. It isn't. Read this before writing "no regression test is possible here".
|
|
|
|
**There is a real live-server fixture.** `ltBoot(env, dir, nodeArgs)` (around `test-features.mjs:990`) spawns the actual `server.mjs` as a child with a **fake `claude` binary**, so integration tests cost no quota. `ltPost` / `ltWait` / `ltFreePort` / `ltDiag` round it out. It already covers boot gates, cache-epoch invalidation across two boots sharing one SQLite store, and system-prompt capture.
|
|
|
|
**`--stack-size` is a fault lever.** `ltBoot`'s third argument passes V8 flags to the child, which puts recursion- and argument-count-limited failures in reach at a much smaller input. `#193` needed a *synchronous throw* deep inside `spawnClaudeProcess`; `buildCliArgs` does `args.push("--allowedTools", ...ALLOWED_TOOLS)`, and under `--stack-size=200` that spread throws at ~24k elements instead of ~124k — which is what brings the trigger under Linux's `MAX_ARG_STRLEN` (131072 bytes for a single env string) so the test runs in CI rather than skipping. **No production fault hook was needed.**
|
|
|
|
Three rules that made it hold up, all learned the hard way:
|
|
|
|
- **Discover the threshold in a child under the same flags**, never in the test process — the parent's stack is not the one that matters.
|
|
- **Assert that the fault actually fired**, not just that the outcome looks right. `#193` asserts HTTP 500 *and* that the body carries `call stack size exceeded`; a control mutation (trigger neutered, bug still present) proves the test fails rather than passing vacuously.
|
|
- **Wait for the thing you are about to assert**, not a proxy for it. Waiting on `listening on` and then asserting a different line is a race (`#199`); waiting on `exit` and then reading `stderr` is another (`#203` — use `closed`, which guarantees the pipes drained).
|
|
|
|
Allocate ports with `ltFreePort()`. Fixed ports have caused at least one flake here.
|
|
|
|
## 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:
|
|
|
|
1. This file (`AGENTS.md`).
|
|
2. `ALIGNMENT.md` — constitution; non-optional.
|
|
3. `CLAUDE.md` — tool-specific instructions and release_kit overlay.
|
|
4. `docs/adr/` — most recent ADRs first; they explain why the current structure exists.
|
|
5. Any active plan under `docs/superpowers/plans/` (excluding `shipped/` which is the archive).
|
|
6. `~/.cc-rules/memory/auto/MEMORY.md` — cross-machine memory index.
|
|
|
|
Only after these should the session touch code.
|