Skip to content

Request config lives in the sidecar, not Org properties

Status: accepted

Current decision

The only top-level gptel Org property mevedel transcripts may carry is GPTEL_BOUNDS; a transcript with no persisted bounds carries none. Every other GPTEL_* property — backend, model, reasoning effort, preset, system prompt, tools, temperature, token limits — is stripped from the top-level drawer at save time, at chat-buffer init, and at agent hydration (mevedel-session-artifacts-strip-gptel-config-properties). The session sidecar (:model-provider, :reasoning-effort, :preset-name) and the retained-agent registry are the only mevedel-managed durable sources of request configuration, and the buffer-locals rebuilt from them are the canonical live source.

Rationale and consequences

The alternative — keeping the drawer in sync by writing it from mevedel-model-set-session-provider and every other config mutation — was rejected: it preserves two sources of truth whose divergence is silent and only observable as a wrong provider on the wire. Removing one source is strictly smaller. Storing config once, in the sidecar, was already the design (mevedel-model-apply-session-policy and mevedel-preset-restore-session rebuild buffer-locals on init); the drawer copies were redundant the whole time.

Consequences: gptel's top-level persisted request configuration is absent, so it cannot override the live buffer-locals. Resume paths enable gptel-mode with gptel--restore-state disabled — mevedel restores GPTEL_BOUNDS itself — which avoids duplicate bounds restoration and the "Could not activate gptel backend" noise a config-free drawer would produce. The strip matches by GPTEL_ prefix with an explicit GPTEL_BOUNDS exemption, so a config property added by a future gptel is stripped automatically instead of reintroducing the override. The strip applies to every initialized or saved transcript.

Decision history

ADR 0110 addressed the following failure: gptel advises gptel-send with gptel-org--send-with-props, which in an org buffer prefers the drawer's properties over the buffer-local values for the request. Mevedel's failed-turn autosave wrote those properties from the buffer-locals current at save time, so a 401 turn pinned the failing backend into the drawer; the user then switched models through the cockpit — which correctly updated the buffer-locals and the sidecar — and the next send silently went to the old backend anyway, because the stale drawer outranked both. The freshly chosen reasoning effort leaked through in the same request only because no effort property existed yet, which is what identified the mechanism. The failure healed itself one failed turn later when the next autosave rewrote the drawer, making the bug look like "changing the model needs two attempts".