docs: D30 — README env vars correctness (F7) + openai-spec-pin.md v0.1 baseline (F20)

cold-audit catch from 2026-05-24 (round 3)

Round-3 P3 docs batch. Two unrelated docs items grouped per IDR cleanup
convention (D19/D20/D25 precedent).

**F7** — README Environment Variables table drift.

Pre-D30 table documented `OLP_HOME` and `OLP_LOG_LEVEL` — neither is
read by any code in the repo. The table missed `OLP_CLAUDE_BIN`,
`OLP_CODEX_BIN`, `OLP_VIBE_BIN` which the 3 provider plugins DO read
to override the path to each provider's CLI binary.

`grep -rn "process\.env\.OLP_" server.mjs lib/` returns exactly 4 reads:
- `OLP_PORT` (server.mjs:49)
- `OLP_CLAUDE_BIN` (anthropic.mjs:69)
- `OLP_CODEX_BIN` (codex.mjs:143)
- `OLP_VIBE_BIN` (mistral.mjs:240)

Fix: README env-vars table now lists exactly these 4 active vars with
their actual defaults. `OLP_HOME` + `OLP_LOG_LEVEL` moved to a
"📋 Planned (Phase 2)" callout block beneath the table, with honest
explanations linking to the actual code state (`loadFallbackConfigSync`
hardcodes the config path; `logEvent` writes unconditionally).

**F20** — Author docs/openai-spec-pin.md v0.1 baseline.

ALIGNMENT.md Authority 2 + Annual Alignment Audit § Scope both
referenced `docs/openai-spec-pin.md` as the artifact the annual audit
diffs against. Pre-D30 the file didn't exist (D20 marked it 📋 Planned).
This left the entry-surface audit without a diff baseline — v0.1 ships
with no provable "the OpenAI spec was THIS on the day OLP implemented
its entry surface" anchor.

Fix: author a 175-line minimal v0.1 baseline. Every field claim is
verified against source (openai-to-ir.mjs + ir-to-openai.mjs + server.mjs).
Structure:
- POST /v1/chat/completions: 14 supported request fields (model,
  messages + 6 message-level subfields, stream, temperature, max_tokens,
  top_p, stop, tools, tool_choice, response_format) + 11 NOT-yet-supported
  fields (n, seed, frequency_penalty, presence_penalty, logit_bias,
  logprobs, top_logprobs, user, service_tier, parallel_tool_calls,
  stream_options)
- Response shapes: chat.completion (non-stream), chat.completion.chunk
  (streaming) — verified against irResponseToOpenAINonStream and
  irChunkToOpenAISSE
- `finish_reason` enum: verified against OPENAI_FINISH_REASON_ENUM
  constant in ir-to-openai.mjs (post-D19 + D26)
- Error response shape: HTTP 4xx/5xx + `{error: {message, type}}` —
  verified against `sendError` in server.mjs
- GET /v1/models: verified against handleModels (post-D18 + D27 F15)
- Streaming SSE semantics: framing, terminator, post-D26 F19
  truncation marker

The pin also documents the v0.1 → v1.0 forward-looking expansion plan:
the "NOT yet supported" fields are explicit candidates for v1.0+
implementation via openai-to-ir.mjs amendments + ADR 0003 updates.

ALIGNMENT.md + README Implementation status table both flip the marker
from 📋 Planned to  Shipped (D30) with the 2026-05-24 timestamp.

Changes (3 files, +180 / -8):
- ALIGNMENT.md +2/-2 (2 markers updated: Authority 2 + Annual Audit)
- README.md +11/-6 (env-vars table delta + status table marker flip +
  Planned callout for OLP_HOME/OLP_LOG_LEVEL)
- docs/openai-spec-pin.md (new, 175 lines)

Tests: 400/400 unchanged — pure docs change.

Authority:
- F7 → process.env reads verified by direct grep
- F20 → OpenAI Chat Completions spec
  https://platform.openai.com/docs/api-reference/chat/create
  https://platform.openai.com/docs/api-reference/chat/streaming
  https://platform.openai.com/docs/api-reference/chat/object
  https://platform.openai.com/docs/api-reference/models/list
- F20 internal source-of-truth: openai-to-ir.mjs + ir-to-openai.mjs +
  server.mjs (all field claims traced)
- ALIGNMENT.md § Authority 2 + § Annual Alignment Audit
- CC 开发铁律 v1.6 § 10.x — Round-3 Cold Audit caught both items

Reviewer (Iron Rule v1.6 § 10.x Mode A, fresh-context opus, independent
of drafter): APPROVE. Independent verification:
- Grep confirmed exactly 4 process.env.OLP_* reads — matches new env
  vars table
- Each new var's default value verified against the plugin code
  (anthropic.mjs:69 → 'claude'; codex.mjs:143 → 'codex'; mistral.mjs:240
  → 'vibe')
- All 14 spec-pin supported request fields traced to openAIToIR line
  references (model L129, messages L45-89, stream L141, temperature
  L160, max_tokens L152, top_p L168, stop L176, tools L183, tool_choice
  L187, response_format L191)
- All 11 NOT-supported fields confirmed absent via grep
- Response shape claims (chat.completion + chat.completion.chunk +
  /v1/models) all match source code line-by-line
- ALIGNMENT.md markers — pure markup flip, no rule changes
- 400/400 tests pass

3 non-blocking suggestions noted (function_call finish_reason caveat;
README slug rendering; ADR 0003 cross-link in spec-pin) — all cosmetic,
not folded.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 16:56:01 +10:00
co-authored by Claude Opus 4.7
parent de9f3ca7c9
commit 5119b427fd
3 changed files with 186 additions and 6 deletions
+2 -2
View File
@@ -65,7 +65,7 @@ OLP exposes a single external HTTP surface: `/v1/chat/completions` in OpenAI Cha
Citation format for entry-surface PRs: OpenAI spec URL + the specific field, parameter, or behaviour. Example: `OpenAI Chat Completions, response_format parameter (https://platform.openai.com/docs/api-reference/chat/create#chat-create-response_format)`.
The spec pin lives in `docs/openai-spec-pin.md`deferred, not yet authored; will land before the first annual audit (target: v1.0). Not required for v0.1 governance bootstrap.
The spec pin lives in `docs/openai-spec-pin.md`✅ shipped as of D30 (2026-05-24); v0.1 baseline pinning the entry-surface fields OLP currently implements. Re-snapshot annually per § Annual Alignment Audit.
### Authority 3 — IR contract (internal interop scope)
@@ -156,7 +156,7 @@ In addition to the recurring 14 May audit below, the following one-shot audits a
- **Date:** 14 May each year (the anniversary of the Anthropic 2026-05-14 announcement, which is the structural trigger for OLP).
- **Scope (per-provider plugin):** For each enabled provider, re-audit the provider CLI version against the pin in the Authorities table above. Re-verify that every spawn invocation, flag, and output-parser expectation in `lib/providers/<name>.mjs` still matches that CLI version's actual behaviour. Update the pin row in this file.
- **Scope (entry surface):** Snapshot OpenAI's `/v1/chat/completions` specification and diff against the pin in `docs/openai-spec-pin.md` (📋 deferred — not yet authored; must be created before the first annual audit). For each field OLP implements, verify the spec still defines it the same way. Update the pin.
- **Scope (entry surface):** Snapshot OpenAI's `/v1/chat/completions` specification and diff against the pin in `docs/openai-spec-pin.md` (✅ v0.1 baseline shipped D30). For each field OLP implements, verify the spec still defines it the same way. Update the pin.
- **Scope (IR):** Diff the IR documented in ADR 0003 against the implementation in `lib/ir/`. Any drift triggers an amendment or a deletion PR.
- **Scope (Risk Tier reclassifications):** For each provider, re-evaluate the risk tier against current ToS, FAQ language, and observed enforcement events from the past year. Reclassifications land as ADR 0006 amendments.
- **Output:** A signed audit note committed to `docs/alignment-audits/YYYY-05-14.md` (📋 `docs/alignment-audits/` directory does not exist yet; it is created when the first audit is conducted). Inline pin updates go into this file.
+9 -4
View File
@@ -114,10 +114,15 @@ _placeholder — full table lands per-phase as variables are introduced._
| Variable | Default | Description |
|---|---|---|
| `OLP_PORT` | `3456` | HTTP listener port. |
| `OLP_HOME` | `~/.olp` | Config, providers, keys, cache, logs root. |
| `OLP_LOG_LEVEL` | `info` | One of `error`, `warn`, `info`, `debug`. |
| `OLP_CLAUDE_BIN` | `claude` (from PATH) | Override path to the `claude` binary (Anthropic provider). Useful when multiple `claude` installs are present. |
| `OLP_CODEX_BIN` | `codex` (from PATH) | Override path to the `codex` binary (OpenAI provider). |
| `OLP_VIBE_BIN` | `vibe` (from PATH) | Override path to the `vibe` binary (Mistral provider). |
Further variables (per-provider auth path overrides, cache size limits, fallback-engine knobs) land with the relevant phase.
> **📋 Planned (Phase 2) — not yet read by the codebase:**
> - `OLP_HOME` (`~/.olp`) — Config, providers, keys, cache, logs root. Currently hardcoded to `~/.olp/config.json` in `loadFallbackConfigSync`; the env override path is a Phase 2 config-layer deliverable.
> - `OLP_LOG_LEVEL` (`info`) — Log level filter (`error`/`warn`/`info`/`debug`). `logEvent` currently writes unconditionally; level filtering is a Phase 2 observability deliverable.
Further variables (per-provider auth path overrides, cache size limits, fallback-engine knobs) land with the relevant phase. See also the [Implementation status](#implementation-status-as-of-2026-05-24) table.
---
@@ -155,7 +160,7 @@ Phase 1 is in progress. This table reflects what is currently shipped vs. what i
| `lib/keys.mjs` | 📋 Planned (Phase 2) | Multi-key auth, per-key namespacing, audit log |
| `dashboard.html` | 📋 Planned (Phase 6) | Owner-only multi-provider dashboard |
| `docs/provider-caveats.md` | 📋 Planned (Phase 3+) | Lossy-translation reference; for now documented inline in each plugin header |
| `docs/openai-spec-pin.md` | 📋 Planned (Phase 1 gate) | OpenAI spec snapshot for annual audit; deferred from v0.1 bootstrap |
| `docs/openai-spec-pin.md` | ✅ Shipped (D30) | OpenAI spec snapshot for annual audit; v0.1 baseline pinned 2026-05-24 |
| `docs/alignment-audits/` | 📋 Planned | Output directory for annual alignment audits (first audit: 2027-05-14) |
| `scripts/migrate-from-ocp.mjs` | 📋 Planned (Phase 7) | OCP → OLP migration tool |
| `setup.mjs` | 📋 Planned | Setup wizard / initial config |
+175
View File
@@ -0,0 +1,175 @@
# OpenAI Spec Pin (v0.1 baseline)
- **Date pinned:** 2026-05-24 (D30)
- **Status:** v0.1 baseline — annual audit per ALIGNMENT.md § Annual Alignment Audit
- **Authority:** OpenAI Chat Completions API + Models API
This document is the spec-diff baseline against which OLP's annual audit will compare
future OpenAI spec changes. It enumerates the specific spec sections OLP currently
implements as the entry surface, verified against `lib/ir/openai-to-ir.mjs` and
`lib/ir/ir-to-openai.mjs` at the time of pinning.
---
## Endpoints implemented
### POST /v1/chat/completions
- Spec section: https://platform.openai.com/docs/api-reference/chat/create
- Retrieval timestamp: 2026-05-24
**Request body fields supported** (translated into IR by `openAIToIR` in
`lib/ir/openai-to-ir.mjs`):
| Field | Type | Required | Notes |
|---|---|---|---|
| `model` | string | required | Passed through to IR; validated non-empty |
| `messages` | array | required | Non-empty; each element translated via `translateMessage` |
| `messages[i].role` | string | required | `system` / `user` / `assistant` / `tool`; deprecated `function` normalized to `tool` |
| `messages[i].content` | string\|null | required | `null` normalized to `''` |
| `messages[i].name` | string | optional | Passed through to IR |
| `messages[i].tool_call_id` | string | optional | Passed through to IR |
| `messages[i].tool_calls` | array | optional | `{id, type:'function', function:{name, arguments}}` |
| `messages[i].function_call` | object | optional | Deprecated field; mapped to a single `tool_calls` entry |
| `stream` | boolean | optional | Default `false`; `true` triggers SSE path |
| `temperature` | number | optional | Range `[0, 2]`; passed to IR |
| `max_tokens` | integer | optional | Must be a positive integer; passed to IR |
| `top_p` | number | optional | Range `[0, 1]`; passed to IR |
| `stop` | string \| array | optional | Passed to IR as-is |
| `tools` | array | optional | Only `type:'function'` tools supported; translated via `translateTools` |
| `tools[i].function.name` | string | required (in tool) | Passed through |
| `tools[i].function.description` | string | optional | Passed through if present |
| `tools[i].function.parameters` | object | optional | Passed through if present |
| `tool_choice` | `'auto'`\|`'none'`\|`'required'`\|`{type:'function',function:{name}}` | optional | Passed to IR verbatim |
| `response_format` | object | optional | Passed to IR verbatim |
**Request body fields NOT yet supported** (silently dropped by entry surface — not
read in `openAIToIR`):
- `n` — multiple completions; OLP is single-completion only
- `seed` — deterministic sampling
- `frequency_penalty`, `presence_penalty`
- `logit_bias`
- `logprobs`, `top_logprobs`
- `user`
- `service_tier`
- `parallel_tool_calls`
- `stream_options`
**Response shape (non-streaming)**`object: 'chat.completion'`
(assembled by `irResponseToOpenAINonStream` in `lib/ir/ir-to-openai.mjs`):
```json
{
"id": "chatcmpl-<base64url>",
"object": "chat.completion",
"created": <unix-epoch-seconds>,
"model": "<model-string-from-request>",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "<string-or-null>",
"tool_calls": [ ... ]
},
"finish_reason": "<stop|length|tool_calls|content_filter|function_call|null>"
}],
"usage": { ... }
}
```
- `usage` is included only when the provider surfaces token counts on the final chunk.
- `message.tool_calls` is included only when tool calls are present.
- `message.content` is `null` when there is no text content and tool calls are present.
**Response shape (streaming)**`object: 'chat.completion.chunk'`
(emitted by `irChunkToOpenAISSE` in `lib/ir/ir-to-openai.mjs`):
```json
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":<ts>,"model":"<m>","choices":[{"index":0,"delta":{"role":"assistant","content":"..."},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":<ts>,"model":"<m>","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
```
- `delta` carries `role` (first chunk only), `content` (text chunks), or `tool_calls` (tool call chunks).
- Final stop chunk has `finish_reason` set and empty `delta`.
- `usage` is included in the stop chunk only when the provider surfaces token counts.
- Stream terminator: `data: [DONE]\n\n`.
**`finish_reason` enum honored** (normalized by `normalizeFinishReason`; non-spec values
normalized to `'stop'`):
- `stop` — natural completion
- `length` — truncated at `max_tokens` or synthesized on truncation (D19/D16)
- `tool_calls` — model stopped to emit a tool call
- `content_filter` — provider-side content filter
- `function_call` — deprecated; preserved for backwards compatibility
- `null` — in-progress (streaming delta chunks)
**Error response shape:**
HTTP 4xx/5xx with body:
```json
{ "error": { "message": "<string>", "type": "<string>" } }
```
No invented top-level `error` field on `chat.completion` objects (per ALIGNMENT.md
Rule 2 — D12 finding). Errors surface exclusively via HTTP status codes with the above
body shape.
---
### GET /v1/models
- Spec section: https://platform.openai.com/docs/api-reference/models/list
- Retrieval timestamp: 2026-05-24
Response shape (`handleModels` in `server.mjs`):
```json
{ "object": "list", "data": [ ... ] }
```
Each entry: `{ "id": "<model-id>", "object": "model", "created": <ts>, "owned_by": "<provider-key>" }`
no invented fields (per D27 F15). Alias entries are also surfaced as separate list members
(per D27 F15 alias surfacing).
---
### GET /health
OLP-specific endpoint (not in OpenAI spec). Returns:
```json
{ "ok": true, "version": "<semver>", "providers": { "enabled": <n>, "available": <n> } }
```
---
## Streaming SSE semantics
- MIME: `text/event-stream`
- Per-chunk framing: `data: <json>\n\n`
- Terminator: `data: [DONE]\n\n`
- Truncation marker: `finish_reason: 'length'` synthesized when the provider generator
exhausts without a natural stop chunk (D26 F19 — mirrors D16 buffered-path semantics)
- Response headers on stream: `Content-Type: text/event-stream`, `Cache-Control: no-cache`,
`Connection: keep-alive`, `X-Accel-Buffering: no` plus OLP diagnostic headers
---
## Audit method
Annual audit (target 2027-05-14): re-fetch each cited URL above, diff against the field
lists above, file an ADR amendment for any newly-shipped OpenAI field or changed semantic
that OLP should implement. Consult `lib/ir/openai-to-ir.mjs` and `lib/ir/ir-to-openai.mjs`
as the implementation source of truth.
---
## Scope note: v0.1 baseline
This is a minimal baseline. v1.0+ should expand coverage to include the "NOT yet
supported" fields above where OLP intends to support them (via `openAIToIR` amendments +
ADR 0003 updates).