Development record
abcd-ships-an-openai-compatible-api-oracle-adapter-the-first
Summary
The design record for itd-2609081951381895: the OpenAI-compatible API adapter with per-provider allowlists that alone decide which models a provider serves, and the optional oracle.denylist the configuration writes (adr-2609221009491186, superseded by adr-2609300107513982, which retires the bundled vendor denylist under ruling H9 of 2026-09-29).
Scope
- Configuration (
internal/core/oracle/config.go): the provider blocks, the optionaloracle.denylist(abcd bundles no entry), the resolver that validates every role and judgement route at read time and returns the refusal (criteria 1 to 3). - The adapter (
internal/adapter/openaiapi): one client over the chat completions protocol, the request rendered from the same brief the host gets, the response validated against the same output contract, the transcript captured (criterion 1). - The credential: by name from the machine configuration or the environment; a name that resolves to nothing is a refusal (criterion 4).
- The record: provider, requested and reported model per call in the run record (criterion 5).
- The setup (
ahoygap → itd-63's mode): detect no provider block; explain; on yes ask the home (three choices, keychain recommended in prose); write the key by the chosen method (external: store the variable or config name only;abcd:~/.abcd/credentials.jsonmode 0600;keychain: the platform keychain under abcd's service name via thesecuritycommand on macOS and the secret-service API on Linux, the configuration holding the name); write the provider block with the first allowlist; one verification call (criteria 7, 8). - Review: security reviewer on the lane (criterion 9).
Out of scope
- Provider routing; learned routers; the tier's judgement.
Approach
The adapter implements the same validator/runner interface the host path and the RepoPrompt route (itd-6) implement, so the loop does not know which ran; OpenRouter and a local server differ only in configuration.
Footprint
- packages: internal/core/oracle, internal/surface/cli, and a new adapter package for the OpenAI-compatible client
- tests: the resolver's refusals (unlisted, a configured denylist entry, no key) and a listed model of any vendor admitted; the call against a fake server; the record fields
How the criteria are satisfied
| Criterion | Where |
|---|---|
| 1 the call; unconfigured unchanged | scope 2 |
| 2 unlisted refused at read | scope 1 |
| 3 the list alone decides; a configured denylist entry refuses | scope 1 |
| 4 key by name | scope 3 |
| 5 the record | scope 4 |
| 7 ahoy explains and offers | scope 5 |
| 8 three homes, keychain in prose, nothing in the harness | scope 5 |
| 9 security review | scope 6 |
Frontmatter
| id | spc-2609221011153746 |
|---|---|
| type | spec |
| lifecycle | closed |
.abcd/development/specs/closed/spc-2609221011153746-abcd-ships-an-openai-compatible-api.md
open on GitHub ↗ · commit history ↗
References1
- itd-2609081951381895abcd ships an OpenAI-compatible API adapter, and a provider serves only the models it listsimplements
mentions5
- adr-2609221009491186A provider adapter serves only the models it lists, under a vendor denylist no listing overrides; everything else runs on the host
- adr-2609300107513982A provider adapter's allowlist alone decides which models it serves, and abcd bundles no vendor denylist
- dcn-26093000000000062026-09-30 — The technical facilitator's ruling H9 of 2026-09-29 is applied (recorded by lane denyRetire of autonomous run A; the answer is kept verbatim in the local-tier file `.abcd/.work.local/scratch/reports/rulings-answered-2609-29-b.md`, dated 2026-09-29, and restated here because that file is not committed): the bundled `anthropic/*` vendor denylist is retired and a provider's allowlist alone decides which models it serves. adr-2609300107513982 supersedes adr-2609221009491186, revising its decision 2 and carrying decisions 1, 3, 4 and 5 forward word for word; itd-2609081951381895's criterion 3 and spc-2609221011153746 are amended to the allowlist-alone reading, with the old wording in the intent's Audit Notes (iss-2609300110451242). This answers the follow-up the 2026-09-29 entry above left to the technical facilitator under AA(a), whether the bundled entry stays as a backstop: it does not. Ruling Y (added 2026-09-26, from lane apiadapter: may a person remove a bundled entry on their own machine) closes as moot, since no bundled entry remains to remove. The detail H9 left to the lane, whether `oracle.denylist` survives as an optional repository or machine extension, is decided as keep: the setting already existed in both layers, and removing the bundled list leaves it working with no added code, which is the ruling's condition.
- itd-6RepoPrompt over MCP is one opt-in reviewer route, beside the host's own agents
- itd-63The Amateur Coder Is Told What Is Being Installed And Why, Not Just Asked To Run A Command