v1.7.5
Headline: three follow-on fixes for docs/observer-platform- issues-v6.md findings surfaced during v1.7.4 verification against
the v4 codex compression A/B benchmark. V6-1 is a real bug in
v1.7.4's post-flight capture-rate check (envelope-type mismatch
silenced every warning). V6-2 and V6-3 are observer-side
mitigations for two codex-cli design choices that prevent capture
even after the v1.7.4 --exclusive mitigation handles V5-1. None
of the three is a true fix — V6-2 and V6-3 need upstream codex
changes (forward -c overrides to the inner app-server; raise the
inner-pipe TTFB timeout) — but the combined v1.7.5 mitigations
should make the v4 batch runnable once the operator updates
$CODEX_HOME/config.toml per the V6-2 pre-flight warning. (#22)
fix(codex): match session_meta envelope in post-flight capture-rate check (V6-1)
parseRolloutForCapture in cmd/observer/codex_capture_check.go
only matched top-level envelope type session_configured with
payload.session_id. Real codex 0.130.0-alpha.5 rollouts use
top-level session_meta with payload.id. The mismatch meant the
post-flight check silently dropped every codex 0.130+ rollout —
sessionID stayed empty, the file was rejected, the helper returned
"" on the "nothing to validate" path, and the V6-2/V6-3 capture
failures the helper was designed to surface stayed invisible to the
operator. v1.7.4 shipped this bug end-to-end (the test fixture
matched the wrong assumption, not real codex).
Fix: envelope-type matcher now accepts BOTH session_meta (codex
0.130+) AND session_configured / session_start / turn_context
(legacy / co-existing per internal/adapter/codex/adapter.go:945-957),
and reads either payload.id or payload.session_id — preferring
id when both are present (mirrors sessionContext at
internal/adapter/codex/adapter.go:161 which declares both fields).
Test fixture updated to emit the real codex 0.130 shape; a sibling
fixture pins the legacy shape so the dual-tolerance is
table-covered. Pinned by TestParseRolloutForCapture_SessionMetaPrimary,
TestParseRolloutForCapture_LegacySessionConfigured,
TestParseRolloutForCapture_PrefersIDOverSessionID. Refs
docs/observer-platform-issues-v6.md §V6-1.
feat(codex): pre-flight config.toml openai_base_url warning (V6-2)
Codex 0.130+ silently drops the wrapper's argv-injected
-c openai_base_url=… override. The outer codex exec parses
the override into its own TOML state but does NOT forward it to the
inner spawned codex app-server child that makes the HTTP call.
The inner reads only $CODEX_HOME/config.toml, so capture
fails silently UNLESS the operator has manually added
openai_base_url = "<proxy>/v1" to that file.
v1.7.5 adds a pre-flight warning that reads
$CODEX_HOME/config.toml (cross-mount aware via
crossmount.AllHomes(), mirrors the codex adapter's WatchPaths)
and emits ONE stderr line per misconfigured CODEX_HOME root, naming
the exact openai_base_url = "<proxy>/v1" line to add. Three
warning branches: file absent ("does not exist; create with …"),
key absent ("no openai_base_url; add …"), wrong URL ("sets
openai_base_url=<got> but the proxy is <want>"). Trailing-slash
and <proxy> vs <proxy>/v1 shapes both accepted. Honors the
existing --no-app-server-check escape hatch. Pinned by 7 branch
tests at cmd/observer/codex_config_check_test.go. The warning
makes the otherwise-silent capture loss loudly visible; the actual
fix is the operator editing config.toml once. An opt-in
--write-config-toml flag (surgical edit + sidecar-marker crash
recovery) is tracked as a v1.7.6 follow-up if the warning-only
path proves insufficient. Refs
docs/observer-platform-issues-v6.md §V6-2.
feat(proxy): TLS pre-warm at observer start + flusher.Flush hygiene (V6-3)
The V4-1 retry pattern from v1.7.2 recurs in v1.7.4 even with
the chatgpt-auth streaming gate: codex's inner-pipe TTFB timeout
(~15s) fires before the proxy can deliver chatgpt.com's first SSE
event, because the proxy pays a 500ms–1.5s TLS handshake on a cold
connection pool. Each request opens a fresh TCP+TLS connection
from the proxy to chatgpt.com (codex's direct calls reuse a
persistent connection from a single codex exec invocation, but
the proxy splits each request into a fresh upstream call). Total
TTFB pushes past codex's timeout; codex retries 5×; api_turn
never lands.
Two layers in v1.7.5:
-
Pre-warm at
observer start: newProxy.Prewarmmethod
fires HEAD requests against[proxy].prewarm_targets(defaults:
https://chatgpt.com/,https://api.openai.com/) via the
proxy's upstreamhttp.Client. This populates the
http.Transportconnection pool with warm TLS sessions, so the
first real codex request reuses an established connection.
Non-blocking goroutine fromListenAndServe; per-target timeout
5s; all errors logged at info-level, never abort startup. Operators
on egress-restricted networks can disable via
[proxy].prewarm_targets = []. -
flusher.Flush()afterWriteHeader(resp.StatusCode)on
the streaming path only (gated byisStream). Defense-in-depth:
pushes theHTTP/1.1 200 OKstatus line + response headers to
codex's inner pipe within RTT instead of waiting for the first
body byte. Doesn't fix the upstream TTFB insidedoWithRetry
(that fires BEFORE WriteHeader), but ensures that when WriteHeader
IS called on an SSE response, no buffering layer adds extra
latency. Gated on isStream so the non-stream path keeps its
single-write semantics — flushing in that path would let the
client return before the synchronousinsertTurnDetachedlands
the api_turn row, exposing a pre-existing race.
The pre-warm is the primary fix; the flush is hygiene. Together they
should keep total TTFB under codex's ~15s timeout for cell smoke
tests. The long-reasoning-model edge case still depends on upstream
codex raising its inner-pipe TTFB timeout. Pinned by
TestProxy_PrewarmFiresHEADPerTarget,
TestProxy_PrewarmTolerates5xxAndTimeouts,
TestProxy_PrewarmEmptyTargetsSilent,
TestProxy_PrewarmNilTargetsUsesDefault,
TestProxy_ServeFlushesAfterWriteHeader. Refs
docs/observer-platform-issues-v6.md §V6-3.
docs(v6): track observer-platform-issues-v6.md + Resolution blocks + gotcha sections
docs/observer-platform-issues-v6.md promoted from untracked
persistent doc to tracked, with Resolution blocks under each
finding (V6-1 / V6-2 / V6-3) and an updated summary table. Mirrors
the v1.7.3 treatment of v3/v4 docs and the v1.7.4 treatment of v5.
The doc stays private-only on the public release (in
PRIVATE_ONLY_PATHS = (docs) per scripts/release.sh:81); the
operator-facing guidance ships in docs/codex-shared-app-server- gotcha.md (public carve-out) where two new sections cover V6-2's
manual config.toml fix and V6-3's pre-warm config knob.
docs/release-runbook.md gains a v1.7.5 follow-up subsection
covering the new behavioral defaults (pre-warm fires 2 HEAD requests
at startup; operators on egress-restricted networks set
[proxy].prewarm_targets = []).
Downloads
Pre-built binaries for each supported platform are attached below. Linux variants bundle antigravity-bridge.exe next to the observer binary for WSL2 users of the Antigravity adapter.
| Platform | Asset |
|---|---|
| Linux x86_64 | observer-v1.7.5-linux-x64.tar.gz |
| Linux arm64 | observer-v1.7.5-linux-arm64.tar.gz |
| macOS x86_64 (Intel) | observer-v1.7.5-darwin-x64.tar.gz |
| macOS arm64 (Apple Silicon) | observer-v1.7.5-darwin-arm64.tar.gz |
| Windows x86_64 | observer-v1.7.5-win32-x64.zip |
Verify with sha256sum -c SHA256SUMS (or shasum -a 256 -c SHA256SUMS on macOS) from the directory containing the downloads.
Also available via npm: npm install -g @superbased/observer@1.7.5
Org server (Docker)
The self-hosted org server ships as a Docker image and as per-platform observer-org-v1.7.5-* archives (attached below).
docker pull ghcr.io/marmutapp/observer-org:v1.7.5The image is keyless-signed with cosign. Verify it:
cosign verify ghcr.io/marmutapp/observer-org:v1.7.5 \
--certificate-identity-regexp 'https://github.com/marmutapp/superbased-observer-private/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comSupply chain
CycloneDX SBOMs are attached: observer.cdx.json and observer-org.cdx.json.
SLSA Level 3 build provenance for the binaries is attached below as a *.intoto.jsonl attestation. The build runs on the private origin repo, so pass that as the source when verifying an extracted binary with slsa-verifier v2.7.0 or newer (older versions fail with unexpected tlog entry type: expected intoto:0.0.2, got dsse:0.0.1):
slsa-verifier verify-artifact ./observer \
--provenance-path *.intoto.jsonl \
--source-uri github.com/marmutapp/superbased-observer-private