Round-4 cold-audit Finding 10 (filed as issue #8): on a chain-exhausted response, X-OLP-Provider-Used returns chain[0].provider — but if hop 0 were soft-skipped (quota threshold exceeded), the header would attribute a provider whose plugin's spawn() was never called. README's "which provider's plugin served the request" is technically false in that edge case. At v0.1 the scenario is unreachable because soft triggers are deferred (ADR 0004 Amendment 2) — evaluateSoftTriggers always returns false. The ambiguity is latent and only activates when soft triggers reactivate in v1.x. **Option B chosen — document chain-origin semantics, no code change.** Option A would track firstAttemptedProvider separately in executeWithFallback and return that on chain exhaustion. Adding state for an unreachable v0.1 code path would violate ALIGNMENT.md Rule 2 (No Invention). The D40 X-OLP-Fallback-Detail header (Amendment 5) already carries per-hop spawn history including soft-skip records (trigger_type: 'soft'), providing the disambiguation channel on the wire without needing providerUsed to handle it. Changes (4 files, +35 / -1): 1. **docs/adr/0004-fallback-engine.md** — Amendment 6 added above Amendment 5 in the amendments stack. Documents the chain-origin contract, names Option A as the likely v1.x preference, cites the Rule 2 rationale + the X-OLP-Fallback-Detail disambiguation channel. 2. **README.md** — Observability header description updated: "which provider's plugin served the request" gains a clarifying sentence about chain-origin semantics on exhausted responses, with a pointer to ADR 0004 Amendment 6. 3. **lib/fallback/engine.mjs** — Inline comment block at the chain-exhausted return site explicitly cites the amendment and captures the v0.1-vs-v1.x semantic. No behavior change. 4. **CHANGELOG.md** — D41 sub-entry under existing D38/D39/D40 entries in Unreleased section. No code-behavior change. No new tests — the relevant scenario is dead-by-config at v0.1. v1.x soft-trigger reactivation work should add a test exercising soft-skip + chain-exhausted that pins whichever option (A or B) the v1.x maintainer chooses, and coordinate the README + ADR Amendment 6 update if Option A is adopted. Authority: - ADR 0004 Amendment 6 (this commit) — chain-origin semantics - ADR 0004 § Decision § Chain advancement step 4 — original promise - ADR 0004 Amendment 2 — soft triggers deferred (precondition) - ADR 0004 Amendment 5 (D40) — per-hop attribution channel - ALIGNMENT.md Rule 2 — No Invention rationale - GitHub issue #8 — closed by this commit - D32 round-4 cold-audit F10 — original filing - CC 开发铁律 v1.6 § 10.x — Iron Rule 10's implementation-phase scope is unmet here (doc-only amendment); no fresh-context reviewer dispatched per the documented exception in this amendment's procedural mechanism Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Architecture Decision Records — OLP
This directory holds the OLP Architecture Decision Records (ADRs) — short documents that capture the why behind structural choices.
Read these before proposing governance, plug-in contract, IR, fallback, cache, or provider-inclusion changes.
Numbering
ADRs in OLP start at 0001 (the project's founding decision). OLP did not inherit the 0001 placeholder convention OCP used; the project starts on a fresh numbering line.
New ADRs increment from the highest existing number. Filenames are NNNN-<short-slug>.md.
Index
| ADR | Title | What it covers |
|---|---|---|
| 0001 | Project Founding | Why OLP exists (the 2026-06-15 Anthropic billing-split trigger), mission + non-mission boundaries, explicit supersession of OCP ADR 0005 "No Multi-Provider". |
| 0002 | Plugin Architecture for Providers | The lib/providers/<name>.mjs plug-in model, the Provider contract (name / models / auth / spawn / estimateCost / quotaStatus / healthCheck / hints), static in-tree registry, why-not full external plugin discovery. |
| 0003 | Intermediate Representation (IR) Design | The OLP-internal canonical request/response shape between the OpenAI-compat entry surface and each provider plugin. v1.0 IR fields, IR-vs-OpenAI-vs-native-provider three-shape model, IR is not exposed externally. |
| 0004 | Fallback Engine Semantics & Safety | 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 Layer Cross-Provider Design | Cache key composition over (provider, model, messages, …), per-model isolation, D1+D2+D3+D4 port from OCP v3.13.0, cross-provider fallback cache behaviour (correct miss). |
| 0006 | Provider Inclusion / Exclusion + Risk-Tier Framework | The 4-tier classification (A excluded by default / B explicit consent / C opt-in / D eligible-for-default-enabled), Candidate-vs-Enabled distinction, current v0.1 candidate inventory (0 Enabled), Antigravity exclusion rationale (named prohibition + no cost advantage + reinstatement friction; pending primary-source pin), consent UX, future provider addition procedure. |
When to write a new ADR
Open one whenever:
- A structural rule is being added or changed (e.g., new IR field, new trigger type, new cache-key composition).
- A new provider plugin is being proposed (per ADR 0002 + ADR 0006 — ADR is a hard prerequisite for plugin merge).
- A decision encodes a lesson from an external event (provider policy change, drift incident).
- A future contributor reading the code alone could plausibly undo or re-litigate the choice.
Skip ADRs for routine implementation choices (algorithm pick, naming) — those belong in commit messages.
Format
Keep ADRs short — Context / Decision / Consequences / Alternatives is the standard skeleton. Cite incidents, PRs, or commits where useful. Length per ADR ~150–250 lines; longer than that, split.