mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-27 07:55:07 +00:00
7f15921f69a3bc531e9b74b1dbee8f90c3e56650
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c180987376 |
docs: guard the asymmetric cache keys (#200); document the ltBoot fault lever (#197) (#207)
* docs: guard the asymmetric cache keys (#200); document the ltBoot fault lever (#197) 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 * docs: reference only helpers that exist on main (AGENTS.md) Review caught a real forward-reference: the new AGENTS.md section named `ltDiag` and told readers to wait on `closed`, and NEITHER exists on main — both are introduced by the still-unmerged #204. `git grep ltDiag` matched exactly one line: the documentation itself. That is worse than a broken link. AGENTS.md is, by its own "Handoff expectations", the FIRST file a fresh session reads; it would have sent that session looking for two identifiers it cannot find, in the file whose whole job is to orient them. It also meant this PR had an undeclared merge-order dependency on #204, which I had not stated anywhere. Fixed so each PR is self-consistent regardless of merge order: - names `ltPostStatus` (which does exist) instead of `ltDiag` - states the #203 rule as a PRINCIPLE — a terminated child's pipes may still hold unread data, so wait for stdio to close rather than for the exit — instead of naming the `closed` flag that #204 adds Verified: every helper the section now names resolves to a `function <name>` in test-features.mjs on this branch, and neither `ltDiag` nor `closed` is referenced any more. Tests: 457 passed, 0 failed (comments/docs only, unchanged). 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> |
||
|
|
f2f9058db4 |
feat(models): commit models.schema.json and validate the SPOT against it in CI (#196) (#205)
* 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> |
||
|
|
313cb13a78 |
docs: align README + governance docs with current state (uninstall, files, ADR index, ship-archive) (#59)
Multiple documentation polish items rolled into one PR (one layer: "docs alignment with current state"). ### README.md - **Uninstall section** added between Server Setup and Client Setup (was missing — `node uninstall.mjs` exists but went undocumented). - **OpenClaw definition** added as a footnote on first README mention (the architecture diagram and Supported Tools table both reference OpenClaw without ever defining it). - **Repository Layout section** added before Security — table of top-level files (server.mjs, setup.mjs, uninstall.mjs, keys.mjs, models.json, ocp/ocp-connect, dashboard.html, scripts/, .claude/skills/, ocp-plugin/, docs/adr/, ALIGNMENT.md, AGENTS.md, CLAUDE.md) so a new contributor knows what each file does. - **LICENSE link** added to the License section footer. ### docs/adr/README.md (new) - Index of the three published ADRs (0002, 0003, 0004) with a one-line description each. - Explains the `0001` placeholder (early internal proposal that was superseded; numbering deliberately starts at `0002`). - Guidance on when to write a new ADR vs. when a commit message suffices. ### Spec/plan housekeeping - `specs/.gitkeep` removed (the empty `specs/` placeholder confused the picture; canonical paths are `docs/superpowers/plans/` for active plans and `docs/superpowers/specs/` for long-lived design docs that other code references). - Shipped plans moved to `docs/superpowers/plans/shipped/`: - `2026-04-10-lan-mode.md` (shipped: README LAN mode section) - `2026-04-25-47-sse-heartbeat-plan.md` (shipped: v3.12.0 per CHANGELOG) - `2026-04-25-47-sse-heartbeat-design.md` left in `docs/superpowers/specs/` unchanged because both `server.mjs:565` and `CHANGELOG.md:7` link to that exact path; moving it would require a `server.mjs` edit, which needs `cli.js` citation per ALIGNMENT.md Rule 1. ### AGENTS.md - Updated "Key files to know" to add `docs/adr/README.md`, `docs/superpowers/plans/`, and `memory/constitution.md`. - Note explaining `memory/constitution.md` is spec-kit's standard location, distinct from `~/.cc-rules/memory/` and `ALIGNMENT.md`. - Updated "Handoff expectations" item 5 from `docs/superpowers/specs/*/tasks.md` (which never matched anything — there were no `tasks.md` files there) to `docs/superpowers/plans/` (excluding `shipped/`). ### Coordination with PR #53 PR #53 is open and adds "Why OCP?", "Comparison", and "Governance" sections to README. This PR deliberately avoids those areas — only edits the Supported Tools table footnote, inserts Uninstall before Client Setup, inserts Repository Layout before Security, and updates the License footer. No expected merge conflict. Refs: audit findings M6, M8, M9, M11, L2, L3, L6. Co-authored-by: dtzp555 <dtzp555@gmail.com> |
||
|
|
0752f666fb |
test: wire test-features.mjs to npm test + add minimal CI smoke workflow (#60)
`test-features.mjs` shipped at v3.8.0 (per CHANGELOG of the keys.mjs quota+cache work) and is referenced from AGENTS.md as the project's only test artifact, but until now nothing actually ran it — no `npm test` script, no CI step. Wiring it up so it runs on every push and PR. ### Changes - `package.json`: add `"test": "node test-features.mjs"` to scripts. - `.github/workflows/test.yml` (new): single-job workflow that runs `npm test` on push to main and on every PR. Uses Node 24 because `keys.mjs` imports `node:sqlite`, which is stable in Node 23+ (Node 24 is the current LTS; Node 22 would need `--experimental-sqlite`). No `npm install` step — OCP has zero external runtime dependencies per `package-lock.json`. - `AGENTS.md`: note that `test-features.mjs` runs via `npm test` and is enforced by `.github/workflows/test.yml`. ### Why this is a hard check, not a soft check `test-features.mjs` is self-contained — it imports `keys.mjs` and exercises the SQLite-backed key/quota/cache code paths against a throwaway test DB at `~/.ocp/ocp-test.db`. It does NOT require: - a live claude CLI binary - a running OCP server - any network access So CI can run it as a real check; no `continue-on-error` needed. ### Local verification ``` $ npm test [...] === Results: 24 passed, 0 failed === ``` 24 assertions cover createKey / listKeys / quota math / cache hash determinism / cache TTL / clearCache. Exit code is 1 on any failure (`process.exit(failed > 0 ? 1 : 0)` at the bottom of test-features.mjs). ### Future expansion If the suite later grows to include tests that DO require a live claude CLI or a running OCP, mark those steps `continue-on-error: true` (or split them into a separate job). The comment in `test.yml` documents this contract. Refs: audit (test-features.mjs orphan / unrunnable in CI). Co-authored-by: dtzp555 <dtzp555@gmail.com> |
||
|
|
39ca20536e |
docs(governance): correct CLAUDE.md + AGENTS.md to reflect actual alignment.yml blacklist (#56)
Both `CLAUDE.md` (line 25) and `AGENTS.md` (line 46) previously claimed the `alignment.yml` blacklist included two tokens — `api/oauth/usage` and `api/usage`. The actual workflow has a single token in `BLACKLIST`: `api.anthropic.com/api/oauth/usage`. Doc-vs-CI drift. Fix is doc-side only — `alignment.yml` itself is unchanged because adding a second blacklist token (`api/usage`) is a constitutional change that requires an `ALIGNMENT.md` amendment PR per ALIGNMENT.md Amendment Procedure. If `api/usage` should be added to the blacklist, that's a separate PR with ALIGNMENT.md amendment + reviewer sign-off. Refs: audit findings (governance doc accuracy). Co-authored-by: dtzp555 <dtzp555@gmail.com> |
||
|
|
2ecc4945ad |
docs(governance): add AGENTS.md bridge + 3 historical ADRs (#36)
Integrates OCP with Tao's cross-device development system (Phase 1 L1). - AGENTS.md: project-level tool-agnostic instructions (read by Cursor, OpenCode, Copilot, etc. in addition to Claude Code). Inherits from ~/.cc-rules/AGENTS.md. - CLAUDE.md: prepends @AGENTS.md + @~/.cc-rules/AGENTS.md imports. - docs/adr/0002-alignment-constitution.md: captures why ALIGNMENT.md exists (2026-04-11 drift response). - docs/adr/0003-models-json-spot.md: captures v3.11.0 SPOT refactor. - docs/adr/0004-openclaw-auto-sync.md: captures v3.11.0 auto-sync design. ADRs numbered 0002-0004 because an untracked 0001-cross-device-system.md already exists locally and was out of scope for this PR. Governance/docs only. No code change. No version bump (not a release). Co-authored-by: Tao Deng <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |