v1.7.3
Headline: a sweep of fixes uncovered while live-validating compression A/B
benchmarks against codex+ChatGPT-Plus auth on WSL — every codex turn through
the proxy was effectively unusable pre-fix (~120 s wall per turn with 5
spurious Reconnecting... retries, and the resulting api_turn rows
dropping silently). After this release, the same trivial turn runs in
~10 s with zero retries and lands a fully-attributed row. Plus the Claude
Code 20-minute Windows hook deadlock V3 caught, the antigravity log spam
that drowned post-mortem diagnostics, and a release-notes tie-off for
slsa-verifier version.
fix(proxy): force streaming path on chatgpt-auth so codex stops retrying
chatgpt.com/backend-api/codex/responses returns SSE bodies with an
empty/missing Content-Type header. The proxy's isStream gate checked only
Content-Type: text/event-stream, so chatgpt-auth requests fell into
io.ReadAll and were buffered single-lump before any byte returned to the
client. Codex's inner app-server has a ~15 s "wait for first byte" timeout
on its stdio pipe; the buffered path tripped it ~5× per turn, codex emitted
Reconnecting... N/5 (timeout waiting for child process to exit), and each
turn ballooned from ~10 s to ~2 min wall. One-line gate change: force
isStream = true when the request transited the chatgpt-auth route, so the
existing teeStream flushes each 4 KiB chunk through to codex as it arrives.
The looksLikeSSE content-sniff at proxy.go:642 stays as defensive
belt-and-suspenders. Pinned by TestProxy_ChatGPTAuthStreamsBytesIncrementally
(TTFB < upstream gap). Refs docs/observer-platform-issues-v4.md V4-1.
fix(proxy): resolve openai session_id from body + canonical hyphen headers
OpenAI requests landed api_turn rows with session_id = "" because the
insert site only ran an Anthropic body extractor, then fell through to
X-Session-Id (which codex doesn't set). Codex traffic showed up as
<unattributed> in observer cost --group-by session, blocking per-cell
benchmark aggregation. New resolveAPITurnSessionID helper: for OpenAI,
extractOpenAISessionID(body) → Session-Id → Thread-Id →
X-Client-Request-Id → X-Session-Id → SessionResolver. Header names are
HYPHEN-separated canonical (codex 0.130+ emits Session-Id, not
session_id; textproto.CanonicalMIMEHeaderKey treats them as distinct).
Live-verified 2026-05-28 against codex 0.133 + chatgpt auth — the thread_id
from codex output matches the captured session_id byte-for-byte. Refs
docs/observer-platform-issues-v4.md V4-4.
fix(proxy): detach api_turn insert from r.Context() so client-close doesn't drop turns
Live-test side-effect: codex closes its read side the instant the final SSE
event lands, but SQLite's BEGIN IMMEDIATE + WAL write under contention takes
a few ms. The race cancelled r.Context() mid-insert and every codex
chatgpt-auth turn lost its api_turn row with
store.InsertAPITurn: context canceled. Pre-existing provider-agnostic
mechanism bug at all 5 insert sites in proxy.go (594, 620, 657, 672, 688)
— masked previously by the buffered + retry-storm path which never gave the
client a chance to close cleanly. New insertTurnDetached(turn, label)
helper derives a 10 s timeout from context.Background(), mirroring the
established shutdown idiom at proxy.go:325. The helper takes no
context.Context argument — regression-proof. Pinned by
TestInsertTurnDetached_IgnoresAlreadyCancelledOuterContext +
TestProxy_InsertContextSurvivesClientClose.
fix(hook): process watchdog + skip quick_check so hook can't pin the host
V3 batch caught observer hook subprocesses hanging 20+ minutes on
post-end_turn for Claude Code sessions on Windows. Both pair 4 OFF
(opus-4-7[1m] xhigh) and pair 14 ON (opus-4-6 low) exhibited the same
pattern — model finished cleanly with stop_reason = end_turn, then .out
stayed 0 bytes while the Stop/SessionEnd hook subprocess blocked
indefinitely. The "observer hook MUST NEVER block the host" invariant was
documented but unenforced. Two changes:
- Process-level watchdog wired into
newHookCmd:
time.AfterFunc(maxRuntime, os.Exit(2))armed at the start of every
observer hook ...Run;defer stop()disarms on a clean return.
Default 30 s, overridable via--max-runtimeand
OBSERVER_HOOK_MAX_RUNTIME. Covers ALL hang causes — DB contention,
stuck syscall, network — not just one hypothesis. db.Options.SkipIntegrityCheck: hook subprocesses now skip the
PRAGMA quick_checkprobe that contended with the daemon's WAL holder
(the contention the existingfeedback_hook_db_open_no_timeoutmemory
identified). Daemons, tests, and backfill keep the probe.
Refs docs/observer-platform-issues-v3.md V3-1 + v4.md V4-3.
chore(watcher): dedup adapter warnings by (adapter, path, message)
V3 batch caught the antigravity adapter dominating ~96% of all stderr lines
on Windows — every poll cycle (~30 s per file) re-emitted the same OSCrypt /
unrecoverable WARN for the lifetime of each unrecoverable .pb file. The
~469 KB observer.err on pair 4 OFF contained literally zero usable
diagnostic data about the 20-min hook deadlock above. New per-process
warningDeduper keyed on adapter|path|message; first occurrence per
TTL window logs at WARN, identical repeats drop silently. Default 5-minute
TTL via Options.AdapterWarningTTL (0 → default, negative → disable for
diagnostics). Map growth capped by a sweep threshold (1024 entries).
Provider-agnostic — covers every adapter, not just antigravity. Refs
docs/observer-platform-issues-v3.md V3-3.
docs(release): name slsa-verifier v2.7.0+ in the release notes template
v1.7.2's release body advertises a slsa-verifier verify-artifact command,
but doesn't say the older v2.6.0 fails with
unexpected tlog entry type: expected intoto:0.0.2, got dsse:0.0.1. The
runbook and teams-operations.md note v2.7.0+; release notes now do too.
Applies from this release onward (workflow runs as-of-tag, so v1.7.2's
release body stays frozen per maintainer's "leave older tags" call).
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.3-linux-x64.tar.gz |
| Linux arm64 | observer-v1.7.3-linux-arm64.tar.gz |
| macOS x86_64 (Intel) | observer-v1.7.3-darwin-x64.tar.gz |
| macOS arm64 (Apple Silicon) | observer-v1.7.3-darwin-arm64.tar.gz |
| Windows x86_64 | observer-v1.7.3-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.3
Org server (Docker)
The self-hosted org server ships as a Docker image and as per-platform observer-org-v1.7.3-* archives (attached below).
docker pull ghcr.io/marmutapp/observer-org:v1.7.3The image is keyless-signed with cosign. Verify it:
cosign verify ghcr.io/marmutapp/observer-org:v1.7.3 \
--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