Skip to content

v1.7.3

Choose a tag to compare

@marmutapp marmutapp released this 28 May 09:30
· 16 commits to main since this release

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-IdThread-Id
X-Client-Request-IdX-Session-IdSessionResolver. 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:

  1. 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-runtime and
    OBSERVER_HOOK_MAX_RUNTIME. Covers ALL hang causes — DB contention,
    stuck syscall, network — not just one hypothesis.
  2. db.Options.SkipIntegrityCheck: hook subprocesses now skip the
    PRAGMA quick_check probe that contended with the daemon's WAL holder
    (the contention the existing feedback_hook_db_open_no_timeout memory
    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.3

The 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.com

Supply 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