ADR 0005 Amendment 6 (D34) deferred streaming-path D4 singleflight to v1.x with the note "the design alone warrants a dedicated ADR." Round-6 cold-audit F13 (issue #16) raised the sibling TOCTOU window between server.mjs's preCheckHit peek and the streaming-branch spawn. D42 fulfils Amendment 6's deferral note by ratifying the v1.x design as ADR 0005 Amendment 8 — design only, no implementation. **Design ratified (ADR 0005 Amendment 8, 14 sections):** 1. New `cacheStore.getOrComputeStreaming(keyId, cacheKey, sourceFactory)` API with `{ stream, isFirst }` return shape. Mirrors the buffered path's `getOrCompute` to keep the cache API surface coherent. 2. `StreamingInflightEntry` shape — source iterator + AbortController + accumulated-chunks replay buffer + attached-clients Set + state flags + D38 spawn-slot tracker. 3. `AttachedClient` shape — per-client tee buffer + byte-size meter + late-joiner replay flag + done/resolveNext/rejectNext for the single-reader-multi-writer tee. 4. Tee fan-out loop (one reader drains source, fans chunks to all attached clients). 5. Late-joiner replay policy — accumulated chunks burst-drained on attach. 6. Cache TTL race during inflight — late joiners see the inflight entry and join; expired cache slot is overwritten by inflight completion. 7. D38 maxConcurrent coordination — only first caller acquires; release fires once on source-complete/error/abort. 8. New `STREAM_BACKPRESSURE` error code. NOT a hard trigger. Affected client gets synthetic `{ type: 'stop', finish_reason: 'length' }` + `[DONE]` (matches D35 #10 truncation marker). 9. Mid-stream disconnect — remove from attached set; if 0 remaining, abort source via AbortController. 10. Replay buffer cap (10 MB, matches D23 cache-entry cap) — over cap marks entry not cacheable, late joiners get backpressure error. 11. Observability — 4 new log events (streaming_inflight_join / source_done / abort, stream_backpressure_disconnect) + new `X-OLP-Streaming-Inflight: source | attached | solo` header. 12. Server.mjs wiring — replaces the current peek+spawn pattern; TOCTOU window closes because Map check+insert is synchronous. 13. Test surface (when implementation lands) — 10 scenarios covering single-client / 2-concurrent / 3-concurrent / mid-stream join / first-disconnect / all-disconnect / source-error / backpressure / D38 coordination / TTL race / replay cap / X-OLP-* header values. 14. Defaults — PER_CLIENT_QUEUE_CAP=1MB, ACCUMULATED_REPLAY_CAP=10MB, STREAM_BACKPRESSURE not in HARD_TRIGGER_CODES. **Multi-layer safeguards (the maintainer asked: "保证后面这一块会被处理而不会被忽略"):** 1. **`docs/v1x-roadmap.md` (NEW)** — single living landing page for every Phase-1 deferral. 7 items at D42: - #1 Streaming SF (this amendment) - #2 Multi-key auth (lib/keys.mjs) - #3 Soft trigger reactivation (ADR 0004 Amendment 2) - #4 /health activeSpawns integration (D38) - #5 Provider-level cacheKeyFields mask (ADR 0005 Amendment 7) - #6 Streaming-path SPAWN_FAILED salvage - #7 AUTH_MISSING tuple test coverage (D40 follow-up) Each entry names the ratifying ADR, load-bearing code anchor (file:line), GitHub issue (if any), concrete start trigger, and estimated effort. Maintainer's session-startup discipline grep this file at sprint kickoff. 2. **Issue #16 STAYS OPEN** — not closed in D42. Body updated post- commit to reference Amendment 8 with status "design ratified; implementation pending." Do not close until §13 test surface is green on actual implementation. 3. **`lib/cache/store.mjs#getOrCompute` JSDoc** — TODO comment for the sibling streaming API pointing at Amendment 8 + v1x-roadmap.md #1. 4. **`server.mjs` streaming-branch entry (~line 810)** — TODO comment block citing Amendment 8 + issue #16 + roadmap.md #1, naming the exact code lines the v1.x impl will replace. 5. **`README.md § Known limitations` section** — new subsection surfaces 4 limitations to users (streaming SF / soft triggers / multi-key auth / cacheKeyFields mask), each linking to the v1x-roadmap.md entry. 6. **Amendment 8 § "Cross-references and safeguards"** — explicit cross-link block enumerating the above 4 anchors so a future ADR-only reader knows every breadcrumb. **Maintainer decision recorded:** Option 1 (design ADR only) chosen over Option 2 (design + implementation now). Rationale: 200-400 lines of concurrency primitives + 15-20 tests is not "pre-Phase-2 cleanup" in shape — it is real v1.x feature work. Shipping streaming SF in a v0.1.1 patch release would muddy the Phase 1 / Phase 2 contract that v0.1.0 ratified. Personal/family-scale load makes the deferral safe at v0.1. Changes (6 files, +165 / -0): - `docs/adr/0005-cache-cross-provider.md` — Amendment 8 prepended (133 lines). - `docs/v1x-roadmap.md` — NEW file (148 lines). - `lib/cache/store.mjs` — getOrCompute JSDoc gains TODO block (8 lines). - `server.mjs` — streaming-branch entry gains TODO block (6 lines). - `README.md` — Known limitations section (9 lines). - `CHANGELOG.md` — D42 sub-entry under Unreleased (9 lines). No code-behavior change. No new tests. No package.json bump (phase_rolling_mode). Authority: - ADR 0005 Amendment 8 (this commit) — design ratification - ADR 0005 Amendment 6 (D34) — original deferral with "design ADR needed" note that this commit fulfils - GitHub issue #16 (round-6 F13) — sibling TOCTOU; STAYS OPEN - ADR 0002 Amendment 6 (D38) — tryAcquireSpawn semantics - ADR 0004 Amendment 5 (D40) — observability pattern extension - CC 开发铁律 v1.6 § 10.x — design-only amendment; fresh-context reviewer not required per Iron Rule 10 implementation-phase scope - CLAUDE.md release_kit_overlay phase_rolling_mode — under Unreleased Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
26 KiB
Changelog
All notable changes to OLP land here. Per CLAUDE.md release_kit overlay, this file is the source of truth for GitHub release notes.
Unreleased
D38 — maxConcurrent runtime enforcement (issue #1)
- Spawn lifecycle gate —
hints.maxConcurrentis now enforced at runtime per ADR 0002 Amendment 6:lib/providers/index.mjsexports a per-providertryAcquireSpawn/releaseSpawn/getActiveSpawnCountsemaphore;server.mjsgates both the buffered and streaming spawn call sites inhandleChatCompletionswith a try/finally release. Saturation surfaces asProviderError(CONCURRENCY_LIMIT)which the fallback engine treats as a hard trigger (ADR 0004 Amendment 4) — the chain advances to the next hop instead of queueing. If the entire chain is saturated, the user receives a chain-exhausted error via the existing exhaustion path. Closes #1. Queue+timeout deferred (see ADR 0002 Amendment 6 § Design choice). Test count 431 → 447.
D39 — D16 follow-ups (issue #3): explicit cache delete + eviction log + SPAWN_TIMEOUT asymmetry doc
- Part 1 —
CacheStore.delete(keyId, cacheKey)— adds an explicit eviction primitive tolib/cache/store.mjs. Returnsboolean(true if entry present and removed; false otherwise) and removes empty per-keyId namespaceMapentries from the outer store for memory hygiene (matches the D38_activeSpawnspattern).server.mjsD16 salvage path replacescacheStore.set(..., ttlMs=0)(lazy tombstone that lived in the namespaceMapuntil the nextget/peekpurged it) withcacheStore.delete(...)(immediate removal). Cache semantics unchanged — truncated responses still don't persist. ADR 0005 § "Cache write conditions" item 1 authority. - Part 2 —
cache_evicted_truncatedobservability log — adds aninfo-level structured log event fired immediately after the D16 eviction inexecuteHopFn. Carries{ provider, model }so dashboards can surface salvage frequency per (provider, model) pair. P3 polish; no semantic change. - Part 3 — sticky-cache regression test — defense-in-depth test asserting two consecutive identical buffered requests that both trigger SPAWN_FAILED-with-chunks salvage each invoke a fresh spawn (spawnCount=2 across the two requests; second request reports
X-OLP-Cache: miss). Catches any future regression where the eviction is dropped or the gate condition flips. - Part 4 — SPAWN_TIMEOUT salvage asymmetry documented (no code change) — ADR 0004 Amendment 1 gains a new sub-section "Why SPAWN_TIMEOUT is excluded from salvage" with a 4-point rationale: (1) SPAWN_FAILED is a terminal signal, SPAWN_TIMEOUT is a deadline signal; (2) the next hop is a different provider with different speed characteristics, plausibly full-response-soon-after-T; (3) the "user paid for partial" framing applies to SPAWN_FAILED only — for SPAWN_TIMEOUT the user paid for "result within T"; (4) code inspection confirms the catch block matches only
code === 'SPAWN_FAILED'. Includes hard-trigger-taxonomy completeness note and v1.x re-evaluation trigger (opt-in salvage-on-timeout for long deadlines). - Authority: ADR 0005 § Cache layer / CacheStore API extension (Part 1); ADR 0004 Amendment 1 (Part 4); GitHub issue #3 — closed by this commit; D16 commit
bafa6d1non-blocking suggestions — batched here. - Test count: 447 → 452 (3 unit tests for
CacheStore.delete+ 1 log-event integration test + 1 sticky-cache regression test).
D40 — X-OLP-Fallback-Detail header (issue #7)
- New debug header on responses with a non-empty failure trail —
lib/fallback/engine.mjs#executeWithFallbacknow returns afallbackDetailarray of per-hop tuples on every code path.server.mjsemitsX-OLP-Fallback-Detail: <JSON-stringified array>on any response where at least one hop failed before the chain resolved or exhausted (chain-exhausted, non-trigger-error, client-error, AUTH_MISSING, and success-with-prior-failure paths). Header is absent on clean primary success (no failure trail to report). - Tuple schema —
{ hop, provider, model, code, error_message, trigger_type }per failed hop.codeis theProviderErrorcode or'UNKNOWN'for non-ProviderErrorexceptions;error_messageis truncated to 200 chars with a U+2026 ellipsis on truncation;trigger_typematches D28'sclassifyTriggeroutput ('hard'/'soft'/'auth_missing'/'client_error'/'non_trigger'). Field shapes reuse D28's per-hop structured log event keys so logs and the header pivot on the same surface. - 4KB UTF-8 byte cap — if the JSON-stringified array exceeds 4096 bytes, tail tuples are dropped and a
{ truncated: true, omitted_hops: N }sentinel is appended such that the total fits under the cap. Cap calculation usesBuffer.byteLength('utf8'), not string length. - RFC 7230 hygiene — non-ASCII code points (e.g. the em dash in the D38
CONCURRENCY_LIMITsynthesised error message) are escaped as\uXXXXso the header value is pure ASCII. Node's HTTP header validator rejects multi-byte UTF-8 in field values; without this step, em-dash-bearing error messages would crashres.writeHead.JSON.parseround-trips the escaped form correctly. - Gating posture — ungated at v0.1 — the original ADR 0004 § Chain advancement step 4 specified owner-only gating. Per the maintainer decision in issue #7, v0.1 ships the header ungated (single-tenant family-scale per ALIGNMENT.md; no PII risk in error details). Phase 2 will re-introduce owner-vs-non-owner gating when
lib/keys.mjslands — explicit follow-up tracked in AGENTS.md § Key files to know and ADR 0004 Amendment 5. - Authority: ADR 0004 § Decision § Chain advancement step 4 (original promise — D40 fulfils it); ADR 0004 Amendment 5 (D40 ratification); D18 (5 standard X-OLP-* headers; D40 builds on the convention); D28 (per-hop structured log fields; D40 reuses the field shapes); GitHub issue #7 — closed by this commit.
- Test count: 452 → 468 (7 engine-level tuple-shape tests + 6 serialiser unit tests including the 4KB cap + non-ASCII regression + 3 HTTP integration tests).
D41 — X-OLP-Provider-Used semantics documented (issue #8)
- Doc-only clarification. On a chain-exhausted response,
X-OLP-Provider-Usedidentifies the chain's configured primary entry (chain[0].provider), not necessarily the first hop wherespawn()was actually invoked. At v0.1 this is unobservable because soft triggers are deferred (ADR 0004 Amendment 2) — every hop is attempted in order, so chain-origin and first-attempted are equivalent. When soft triggers reactivate in v1.x, a soft-skipped hop 0 followed by hard-failed hops 1+N would still reportproviderUsed=chain[0]despite chain[0] never being spawned. - Option B (document chain-origin) chosen over Option A (track
firstAttemptedProvider). Rationale: Option A would add state toexecuteWithFallbackfor an unreachable v0.1 code path (ALIGNMENT.md Rule 2 — No Invention). The D40X-OLP-Fallback-Detailheader already carries precise per-hop spawn history (including soft-skip records withtrigger_type: 'soft'), so the disambiguation channel exists on the wire without needingproviderUsedto handle it. - Updates: ADR 0004 Amendment 6 documents the semantics;
README.md§ Observability headers replaces "which provider's plugin served the request" with the chain-origin wording;lib/fallback/engine.mjschain-exhausted return site gains an inline comment citing the amendment and the v1.x re-evaluation note. - No code-behavior change. No new tests — the relevant scenario is dead-by-config at v0.1; the v1.x soft-trigger reactivation work should add a test that exercises the soft-skip + chain-exhausted edge case and pins whichever option the v1.x maintainer chooses (the amendment names Option A as the likely v1.x preference).
- Authority: ADR 0004 Amendment 6 (this commit); ADR 0004 § Decision § Chain advancement step 4; ADR 0004 Amendment 2 (soft triggers deferred — precondition); ADR 0004 Amendment 5 (per-hop attribution channel via
X-OLP-Fallback-Detail); ALIGNMENT.md Rule 2 (No Invention rationale); GitHub issue #8 — closed by this commit. - Test count: 468 → 468 (no test change).
D42 — Streaming singleflight design ADR + v1.x roadmap (issue #16)
- Design-only ratification of the v1.x streaming singleflight implementation. ADR 0005 Amendment 6 (D34) had deferred this work with a "design alone warrants a dedicated ADR" note. D42 fulfils the note as ADR 0005 Amendment 8, ratifying the
cacheStore.getOrComputeStreaming(...)API shape, per-(keyId, cacheKey) inflight Map, tee fan-out with bounded per-client backpressure queues, late-joiner replay buffer, AbortController propagation on all-disconnect, D38tryAcquireSpawncoordination (only the first caller's spawn counts against the semaphore), cache TTL race handling, the newSTREAM_BACKPRESSUREerror code (NOT a hard trigger), and the newX-OLP-Streaming-Inflight: source | attached | soloheader. Implementation acceptance criteria are enumerated in Amendment 8 §13. - Multi-layer safeguards to ensure the v1.x work is not forgotten. New file
docs/v1x-roadmap.mdis a single living landing page for every Phase-1 deferral (streaming SF, multi-key auth, soft-trigger reactivation,/healthactiveSpawns, provider-levelcacheKeyFields, streaming-path SPAWN_FAILED salvage, D40 AUTH_MISSING tuple test). Each entry names the ratifying ADR, the load-bearing code anchor, and a concrete trigger to start. Cross-references added at:lib/cache/store.mjs#getOrComputeJSDoc (sibling API TODO),server.mjsstreaming-branch entry (~line 810, the peek+spawn pattern Amendment 8 replaces),README.md § Known limitations(user-facing surface), anddocs/adr/0005-cache-cross-provider.mdAmendment 8 § "Cross-references and safeguards". - Issue #16 status. STAYS OPEN as the v1.x implementation tracker. The body of the issue is updated post-D42 to reference Amendment 8 and clarify scope ("design ratified; implementation pending"). DO NOT close the issue until Amendment 8 §13's test surface is green against an actual implementation.
- No code-behavior change. No new tests. Amendment 8 is design-only. The implementation will go through full Iron Rule 10 (fresh-context opus reviewer + acceptance-criteria-gated test pass) when the v1.x sprint kicks off.
- Authority: ADR 0005 Amendment 8 (this commit); ADR 0005 Amendment 6 (D34 — original deferral note); GitHub issue #16 (round-6 F13 — sibling TOCTOU); ADR 0002 Amendment 6 (D38 —
tryAcquireSpawnsemantics that §7 coordination builds on); ADR 0004 Amendment 5 (D40 — observability pattern §11 extends);CLAUDE.mdrelease_kit_overlay phase_rolling_mode — under Unreleased; CC 开发铁律 v1.6 § 10.x (design-only amendment; fresh-context reviewer not required per the Iron Rule 10 implementation-phase scope, documented in the amendment's procedural mechanism). - Test count: 468 → 468 (no test change — design-only).
v0.1.0 — 2026-05-24
Phase 1 Close — Multi-provider proxy core
Overview. Phase 1 delivers the OLP minimum-viable multi-provider proxy: OpenAI-compatible HTTP entry surface, plugin architecture for 3 Tier-D providers (Anthropic Claude / OpenAI Codex / Mistral Vibe), cache layer (D1 per-key isolation + D4 buffered-path singleflight + size cap + cacheable opt-out), fallback engine with first-chunk safety + spawn-timeout hard trigger + structured per-hop log observability, IR↔OpenAI translation honoring the Rule 2(b) no-invention constraint, and a 416-test suite covering all of it.
Released under phase_rolling_mode (CLAUDE.md release_kit overlay): 25 D-day commits accumulated on main between 2026-05-23 and 2026-05-24 before this version bump + tag.
Provider posture. Three Tier D plugins ship as Candidate (per ALIGNMENT.md § Provider Inventory) — runnable via providers.enabled config but not Enabled by default. Five additional Tier B/C plugin slots exist in models-registry.json as Speculative-Candidate / candidate stubs awaiting CLI authority pins. Zero Enabled providers at v0.1; transition to Enabled requires Phase audit + primary-source pin per ADR 0002.
What landed (D10–D34 commit index)
The per-commit detail is in the git log; this index summarizes the deliverables.
Phase 1 core hardening:
- D10 (
2cfd0b1) — P1 round-3: providers.enabled config wiring + real SSE streaming on single-hop cache-miss + spawn-timeout hard trigger across all 3 plugins.
Round-1 fold-in batch (cold audit caught 17 findings):
- D11 (
f659e29) — ADR 0002 Amendment 1:maxSpawnTimeMsratified into Provider contract hints. - D12 (
4b1a9c8) — IR translator Rule 2(b) compliance: removed invented top-levelerrorfield onchat.completionshapes. - D13 (
f34b690) — Per-hopcache_controlbypass evaluation (was request-global). - D14 (
a7085d9) — Deferres.writeHead(200)until first chunk; early-error returns 502 JSON instead of 200 empty SSE. - D15 (
8ae77c3) — ADR 0005 Amendment 2: cache key includesmax_tokens/top_p/stop/tool_choice. - D16 (
bafa6d1) — ADR 0004 Amendment 1: SPAWN_FAILED-with-chunks salvage (don't discard partial responses). - D17 (
cb86807) — Alias routing SPOT viamodels-registry.json;getProviderForModelcanonicalizes. - D18 (
82ff007) —/v1/modelspopulated from registry; 5 standard X-OLP-* headers on error responses. - D19 (
ed82e65) — Cleanup batch: finish_reason validator, dead alignment.yml KNOWN_PROVIDERS removal, unused imports. - D20 (
d85a2dc) — Docs drift: README/AGENTS/ADR forward-references annotated📋 Planned.
Round-2 fold-in batch (13 findings):
- D21 (
1466d3a) —validateProviderenforcesmaxSpawnTimeMscontract field. - D22 (
e10b7d7) — ADR 0004 Amendment 2: soft triggers deferred to v1.x. - D23 (
7ef5510) —hints.cacheableopt-out + 10MB cache entry size cap (ADR 0002 Amendment 3, ADR 0005 Amendment 3). - D24 (
f8348ad) — Spawn-timeout race fix: post-loopif (spawnTimedOut) throw SPAWN_TIMEOUTcloses the rejectNext-null window across all 3 plugins. - D25 (
cd391b1) — Round-2 P3 docs batch.
Round-3 fold-in batch (13 findings):
- D26 (
a281d3e) — Soft-trigger startup warning, stderr propagation on error-chunk SPAWN_FAILED (codex+mistral), anthropic D4-observation header, streaming truncation marker. - D27 (
c3ba751) — IR validator response_format + tool_choice checks, ADR 0005 Amendment 4 (cache_control IR vs body),/v1/modelsalias surfacing. - D28 (
4a238c9) — Per-hop log observability:chain_id,trigger_type,ir_request_hash,next_provideron all 8 fallback log events. - D29 (
de9f3ca) — Suite 17 port-collision flake fix: 16 test sites switched to OS-assignedlisten(0). - D30 (
5119b42) — README env vars correctness,docs/openai-spec-pin.mdv0.1 baseline. - D31 (
d6347e3) — ADR amendment trio: F5 (ADR 0003 Amendment 1 substitute test strategy), F11 (ADR 0005 Amendment 5 Anthropic wire limitation), F13+F14 (ALIGNMENT.md Speculative-Candidate exception class).
Round-4 fold-in batch (10 findings):
- D32 (
30de965) — Provider auth env vars in README, X-OLP-* on early-return paths, ADR 0002 Amendment 4 ratifyingcontractVersion, dead OUTPUT_PARSE_ERROR removal, codex parser inline assumption labels.
Round-5 fold-in batch (12 findings):
- D33 (
f784fdb) — ALIGNMENT mistral--output streamingpin correction, deterministicfunction_callID (cache key stability),/healthper-provider snapshot, fallback-hop cache-hit X-OLP-Cache correctness,CLAUDE.mdphase_rolling_modepolicy formalization,/v1/modelsstablecreatedtimestamps.
Round-6 final batch (14 findings; 4 closed, 9 filed as issues):
- D34 (
60570ef) — ADR 0005 Amendment 6 (streaming singleflight v1.x deferral), array-field cache key normalization (tools:[]/stop:[]now collide with omitted), QUOTA_EXHAUSTED + RATE_LIMITED dead code removal (ADR 0004 Amendment 3), ADR 0005 Amendment 7 (conservative cache-key v0.1 trade-off).
ADRs in scope
- ADR 0001 — Project founding (Phase 1 founding doc; no amendments)
- ADR 0002 — Plugin architecture (4 amendments —
maxSpawnTimeMs,cacheable,contractVersionratifications) - ADR 0003 — IR design (1 amendment —
__irRoundTripTestremoval + substitute test strategy) - ADR 0004 — Fallback engine (3 amendments — SPAWN_FAILED salvage, soft trigger deferral, hard-trigger taxonomy narrowing)
- ADR 0005 — Cache layer (7 amendments — cache key expansions, cache_control IR-vs-body, cacheable + size cap, Anthropic wire limitation, streaming singleflight deferral, conservative cache-key v0.1 trade-off)
- ADR 0006 — Provider inclusion (Tier framework; no amendments)
- ALIGNMENT.md — Speculative-Candidate plugin Rule 4 exception class added (D31)
- CLAUDE.md —
phase_rolling_modeoverlay added (D33) docs/openai-spec-pin.md— v0.1 baseline pinned (D30)
Test growth
277 (pre-D10) → 416 (post-D34). 6 cold audit rounds reviewed code against ADR claims. Iron Rule v1.6 § 10.x dual-mode review discipline (Diff Review + Cold Audit) caught 78+ findings of which ~50 closed via implementation and ~28 deferred to GitHub issues.
Known limitations carried to v1.x
17 GitHub issues filed for follow-up. Notably:
- Streaming singleflight (#16) — multi-concurrent identical streaming requests each spawn fresh CLI; buffered path participates in D4, streaming path doesn't (deferred via ADR 0005 Amendment 6).
- maxConcurrent runtime enforcement (#1) — declarative-only at v0.1.
- X-OLP-Fallback-Detail debug header (#7) — documented in ADR 0004, never emitted.
- Soft triggers (per ADR 0004 Amendment 2) — evaluation code exists but
quotaStatus()polling not wired; configured thresholds inert at v0.1.
Migration from OCP
OLP supersedes OCP per ADR 0001. The scripts/migrate-from-ocp.mjs migration tool is 📋 Planned (Phase 7).
v0.1.0-bootstrap — 2026-05-23
Phase 0 — Repo bootstrap (founding + post-codex-review hardening)
This is the founding commit set of OLP (Open LLM Proxy), a personal- and family-scale multi-provider LLM proxy that supersedes OCP. The trigger was Anthropic's 2026-05-14 announcement (effective 2026-06-15) splitting claude -p / Agent SDK / third-party agent traffic out of the Pro/Max subscription pool into a separate fixed monthly Agent SDK Credit pool.
What lands at v0.1.0-bootstrap (final state on main as of 2026-05-23):
ALIGNMENT.md— OLP constitution. Three concurrent authorities (per-provider CLI / OpenAI spec / IR contract), 5 Rules, 4-tier Risk Tier Framework, Candidate-vs-Enabled provider inventory, one-shot triggered audits (2026-06-16 Anthropic post-split; 90-day Antigravity primary-source pin).AGENTS.md— multi-tool agent guidelines (inherits~/.cc-rules/AGENTS.md).CLAUDE.md— Claude-Code-specific session instructions + machine-readablerelease_kitoverlay (Iron Rule 5.5).README.md— phase-aware skeleton with Candidate-vs-Enabled provider tables, API endpoint table, environment-variables table, response-headers spec, architecture overview, phase plan, migration-from-OCP outline. Placeholder content marked as such per phase.docs/adr/— 6 founding ADRs:0001-project-founding.md— Mission, non-mission, narrow-scope supersession of OCP ADR 0005 (single-provider-sufficiency premise only; BYOK / no-spawn parts of ADR 0005 not inherited).0002-plugin-architecture.md—lib/providers/<name>.mjsplug-in model with the Provider contract (name / models / auth / spawn / estimateCost / quotaStatus / healthCheck / hints). 8 candidate providers declared, 0 Enabled at v0.1.0003-intermediate-representation.md— OLP-internal canonical IR between OpenAI-compat entry and provider plugins.0004-fallback-engine.md— Trigger taxonomy (Hard / Soft / Deterministic-deferred / Cost-aware-deferred), idempotent-failure safety (first-chunk rule), chain advancement one-at-a-time, observability headers.0005-cache-cross-provider.md— Cache key composition over(provider, model, messages, ...), D1+D2+D3+D4 port from OCP v3.13.0.0006-provider-inclusion.md— 4-tier Risk Framework, Candidate-vs-Enabled distinction, 8-provider candidate classification, Antigravity Tier A (evidence-backed, pending primary-source pin) — exclusion rests on (named prohibition + no cost advantage + reinstatement friction) combination; primary-source URL not yet pinned, follow-up tracked.
.github/PULL_REQUEST_TEMPLATE.md— 8-radio Change Type taxonomy + per-type Authority Evidence sections + Iron Rule 10 reviewer checklist..github/workflows/alignment.yml— CI blacklist (transitiveapi.anthropic.com/api/oauth/usagefrom OCP 2026-04-11 drift; Antigravity provider exclusion enforcement) +models-registry.jsonvalidator + commit-citation soft check (process-substitution form, no Bash subshell trap)..github/workflows/release.yml— Auto-release on tag push withpackage.json-vs-tag version match check (Iron Rule 5)..github/workflows/test.yml— Node 20/24 matrix; tolerates bootstrap-phase absence oftest-features.mjsANDscripts.test.models-registry.json— minimal v0.1 stub with emptyproviders: {}, matching the 0-Enabled posture; populated by Phase audits as providers transition Candidate → Enabled.package.json— minimal: nomain, noscripts.test, noscripts.start(those entries land alongside the real files in Phase 1)..gitignore,LICENSE(MIT),CHANGELOG.md— standard project boilerplate.
Provider posture at v0.1.0-bootstrap (per ALIGNMENT.md § Provider Inventory):
| Tier | Anticipated providers | v0.1 default state |
|---|---|---|
| D (eligible-for-default-enabled) | Anthropic, OpenAI Codex, Mistral Vibe | Candidate (transition gate: authority pin + plugin + Phase audit) |
| C (opt-in) | xAI Grok, Moonshot Kimi | Candidate |
| B (opt-in + consent) | MiniMax, Zhipu GLM, Alibaba Qwen | Candidate |
| A (excluded by default; constitutional-amendment-only re-inclusion) | Google Antigravity | Excluded; pending primary-source pin |
Total Enabled at v0.1.0-bootstrap: 0. Enablement is a Phase audit deliverable, not a bootstrap claim. This explicit zero is intentional and codified — a constitution that names providers as "default-enabled" while their CLI versions, output shapes, auth artifacts, and exit-code semantics are still TBD would violate Rules 1 (Cite First) and 3 (Match the Implementation).
Review history for this version:
-
Initial internal review (Claude Opus, fresh-context, Iron Rule 10). Verdict: APPROVE_WITH_MINOR — 2 minor items (alignment.yml heredoc indent breaking bash parse on failure path; AGENTS.md cross-reference to ADR 0003 imprecise). Both folded in before the founding commit.
-
External review #1 (OpenAI Codex CLI, no spec framing). Verdict: 6 substantive findings beyond internal review.
- Provider Inventory split into Candidate vs Enabled (the v0.1 constitution had declared
anthropic/openai/mistralas Tier D default-enabled while their Authority pins were stillTBD at Phase N spawn— direct violation of Rule 1 / Rule 3 against the constitution's own text). - Antigravity Tier A downgraded to "evidence-backed, pending primary-source pin" (secondary reports disagree on blast radius; Google FAQ URL not yet primary-source-pinned).
- ADR 0001 supersession scope narrowed (OLP rejects ADR 0005's "BYOK + no spawn" qualifiers, which originally applied to a commercial pivot; OLP is non-commercial and spawn-binary by design).
- Anthropic post-2026-06-15 one-shot audit scheduled (annual May 14 audit would leave Anthropic re-eval ~year late after the split takes effect).
- Tier A "permanent" language unified across docs (constitution and ADR 0006 had disagreed).
- OpenAI Tier D wording softened ("maintainer signal indicates low risk; formal ToS pin pending" — Discussion #8338 is a posture statement, not a formal ToS blessing).
- Provider Inventory split into Candidate vs Enabled (the v0.1 constitution had declared
-
External review #2 (OpenAI Codex CLI, second pass after review #1 fold-in). Verdict: 6 additional substantive findings — the self-consistency trap recurred when fold-in of review #1 was scoped only to files codex explicitly named. Round #2 caught:
- ADR 0002 still claimed "three default-enabled" while ALIGNMENT.md said zero Enabled — accepted ADR contradicting constitution.
release.ymlwould publish stale## v0.1.0-bootstrapnotes that ignored the "Unreleased" amendments — fixed by consolidating amendments into the v0.1.0-bootstrap section (this entry).package.jsonadvertisedmain/scripts.test/scripts.startfor files that don't exist —npm test/npm startfailed locally. Removed all three; will return in Phase 1 alongside the real files.models-registry.jsondocumented as SPOT but missing — minimal stub added.alignment.ymlcommit-citation soft check had a Bash subshell trap (whilein pipe losesWARN=1mutation) — fixed via process substitution< <(...).- Tier A "permanent" wording still inconsistent across
alignment.ymlworkflow text, ADR 0006 Consequences section, and the rest of the docs — unified throughout.
All 6 round-#2 findings folded in this consolidated v0.1.0-bootstrap state.
Reviewer framing learning (recorded permanently in ~/.cc-rules/memory/learnings/ai_reviewer_self_consistency_trap.md): Internal AI reviewers framed on a shared source-of-truth miss bugs in the source-of-truth itself. The self-consistency trap recurred during the fold-in of round #1 — when an external reviewer surfaces findings, the fold-in must grep the entire repo for the same concept, not only edit the files the reviewer named. Round #2 caught what round #1's fold-in missed for exactly this reason. Both lessons updated in the cross-machine memory.
Iron Rule 10 status: Satisfied. Initial reviewer = internal opus (independent from drafters). Round #1 reviewer = external codex (independent from drafters and from internal opus). Round #2 reviewer = external codex (independent from the round #1 fold-in implementer). The maintainer's role across all three reviews was approver, not author. The drafting agents and fold-in agents were never the same as the reviewers for any of the three passes.
Next: Phase 1 lands server.mjs skeleton + IR + Anthropic provider plugin + cache D1+D4 port from OCP. At that point, package.json regains main + scripts.test + scripts.start, test-features.mjs lands, models-registry.json populates its first providers.anthropic entry, and Anthropic transitions Candidate → Enabled. Per spec §6 phase plan.