v1.28.0
Added
-
feat(cli):
observer statusline— one-line, fail-open, wordmarked
cost status. Prints a single line — the▞ superbasedwordmark plus
(when available) the current session's cost, today's total observed
spend, and the active model name — for Claude Code'sstatusLine
contract or any host tool that execs a command and reads stdout.
Input precedence is piped stdin JSON, then--session-cost/--model/
--cwdflags, thenOBSERVER_STATUSLINE_SESSION_COST/
OBSERVER_STATUSLINE_MODELenv vars, then nothing — segments are
omitted, never rendered as a fabricated$0.00. The "today" segment
comes from a new, leanGET /api/statuslinedaemon endpoint over a
bounded, loopback-only HTTP call (--timeout, default80ms); when
no daemon is running, or it doesn't answer in time, that segment (and
only that segment) is silently omitted and the command still exits 0.
--no-daemonskips the daemon attempt entirely;--explainreports
which data path was used to stderr;--jsonemits a machine-readable
line. All dollar figures are
estimated list-price totals, not invoiced amounts, stated once at
--helpand at registration time, never per-render. Registration is
opt-in viaobserver init --statusline(writes Claude Code
settings.json's top-level"statusLine"key) — never implied by
--all, never auto-registered byobserver start;--uninstall
removes the key entirely. A Claude Code install detected only on the
Windows side of a cross-OS WSL setup gets an honest WARN, not a
cross-OS bridge. The VS Code status bar's live-state text also picks
up the same▞ superbasedwordmark prefix
(observer.statusBar.wordmark, defaulttrue, to turn it off). The
daemon side adds migration 074 (idx_token_usage_timestamp) so the
endpoint's "today" aggregate is an index scan, not a table scan. See
docs/observer-statusline.md. -
feat(cli):
observer usage— zero-install one-shot cost report (the
npx @superbased/observeractivation path). Scans every detected AI
coding tool's own local session files into a throwaway temp database,
prints one tool × model cost table, and deletes the database again.
Zero configuration, zero network calls: pricing is embedded in the
binary and the default path never reads~/.observer/config.tomlor
anyOBSERVER_*environment variable, never writes any AI tool's
config, and never binds a port. Flags:--since(default30d,
accepts7d|30d|90d|all|RFC3339),--days,--group-by
(tool-model|tool|model|day),--tool,--budget(default30s,
0= unlimited, prints an honestpartial:footer on expiry),
--json,--keep-db,--config(opt back into a real config and its
pricing overrides),--no-progress. Bareobservernow runs this same
report on a machine with no local SuperBased state at all (no
~/.observer/observer.db, noconfig.toml, no daemon listening) — a
state branch, never an invocation-channel branch — and prints the
usual welcome screen otherwise;OBSERVER_ONESHOT=offalways forces
the welcome screen. New pure packageinternal/oneshot. See
docs/one-shot-usage-report.md. -
docs: repo-landing hygiene fixes. README gets a "Try it in one
command" block (thenpx/observer usageactivation path, immediately
above Install) and a plain, no-count "⭐ Star this repo" badge + CTA
line near the top.scripts/release.sh's public-mirror re-staging now
also carves outdocs/deployment-models.md,docs/remote-access.md,
docs/session-handoff.md,docs/cache-tracking.md, and the
docs/guard-{rules,policy-authoring,compliance}.mdtrio, fixing four
confirmed-live 404s on the public repo's README links (all three
re-staging sites — the force-add list, the.gitignorenegation
block, and thedocs_leakssanity-check regex — kept in sync). Both
README hero infographics regenerated from new versioned HTML
sources (docs/assets/infographics/gen/+render.mjs— the old
images were one-offs with no source in the repo): current single-word
lockup, correct counts (29 adapters / 25 MCP tools), and the
proxy-accuracy / cache-attribution / routing differentiators now in
the pixels. The stale13-trajectories.pngscreenshot recaptured from
the web2 admin dashboard's Trajectory Explorer (the node dashboard no
longer hosts that page). -
feat(mcp): droid + Command Code MCP registration.
observer init
can now register the SuperBased MCP server into Factory Droid and
Command Code — one locate row + Installed probe + registryMCPTarget
each, following the grounded droid pattern. Registry rows only; no
new switches (integration-registry discipline). -
feat(store,dashboard,web,cli): session tags/favorites/notes. New
migration 075 backs asessiontagsstore seam behind Execute-class
routes (mutations gated the same way as every other write path), plus
anobserver tag/observer tagsCLI with exact-first-then-index-range
prefix resolution. The dashboard gainsTagPill/TagEditor,
star-favorite toggles, a note field, server-side tag+favorite filters,
and a tags rollup panel; the rollup query is chunked past SQLite's
32766 bind-variable ceiling (cost.SessionRowsByID, capped at 900
session IDs per scope — the pre-fix symptom was cost columns silently
zeroing out, not an error). Remote devices are honestly read-only for
tag mutation: the onlyExecute-class mint is terminal-scoped and
loopback, so a phone or tablet viewing the dashboard sees the
classification controls disabled with a stated reason rather than a
control that quietly fails. Mutations are validate-before-write atomic
and single-flight; a conflicting note edit surfaces a hint instead of
silently overwriting. Privacy pins cover the new columns end to end
(a canary tag proven never pushed underadmin_managed). See
docs/dashboard-walkthrough.md
("Classifying sessions: tags, favorites, notes"). -
feat(announce,web,orgserver,orgclient,web2): dashboard
announcements, three rails. New pure packageinternal/announce
backs R1, aGET /api/announcementsembedded rail with a
dismissibleAnnouncementBanner(ack tracked insb_announce_ack,
never re-shown once dismissed). R2 is a click-only npm-metadata
piggyback — thesuperbased.announcementfield an operator authors
intonpm/observer/package.jsonat publish time and the dashboard
reads passively; an always-on background poll was considered and
rejected for the zero-network postureobserver privacystates, and
the choice is written down on the measurement-honesty page. R3 is
a signed org-distribution rail on the routing-policy pattern: agent
migration 076 (a node-side cache) + server migration 022, a
[dashboard].org_announcementsgate (partial-merge default tested),
and a web2 composer as the first UI consumer of the sign-and-distribute
mechanism. See Fixed below for the security round that hardened R3
before it shipped, and
docs/teams-operations.md§7 for the
publish/retract/trust-model/opt-out operator reference. -
feat(tooltax,web): canonical cross-adapter tool taxonomy. New pure
packageinternal/tooltax(WP-T1) is a 1,074-row table across all 30
tool identities, withMCPIdentityas the one owner of MCP-tool
naming — a corpus-measured slash-form convention was dropped on
zero corpus evidence rather than guessed at, closing a taint
false-positive class;models.IsMCPToolNamenow delegates to it, and
the routing package's independent zero-imports pin correctly refuses
the same delegation, with equivalence proven from the tooltax side
instead. WP-T2 generates a TypeScript mirror (actiontax.gen.json→
actions.tsas a reader) so the dashboard's category/colour data
can't drift from the Go table, aligns the MCP-parser divergence to Go,
and adds ataxonomy-build-driftCI job. A codex gate round then
closed the real hole the first pass left open: the Go-side test suite
stayed green while a hand-written TS reader could still silently
diverge, so the fix is a REAL-TS parity gate that esbuild-compiles the
actualactions.tsagainst 28 Go-oracle vectors, a generated literal
category union that makes a missingCATEGORY_COLORa compile error
instead of a silent runtime fallback, andC:/-drive plus
POSIX-colon path guards. -
feat(mcp,cli,docs): published stable MCP local contract.
docs/mcp-contract.mddocuments the full
25-tool tier table (18 stable, 4 conditional, 3 experimental) and
observer contract --jsonemits it as machine-readable data, backed
by conformance tests that pin it against the real server; the README
gains an Integrations section pointing at it. A codex round then made
the published invariants actually true: adays-parameter clamp
closes a gap between the documented and enforced range, every one of
the 25 tools' required parameters and all 18 stable tools' result keys
are wire-checked, and the markdown drift-gate test now parses BOTH the
doc and the generated mirror instead of one — the prior version could
pass while blind to a real divergence between them. -
feat(plugins): adapter distribution as installable plugins, Phase 1
in-repo — not yet published.plugins/plugingensatisfies the
one-owner registration rule by execution rather than duplication: it
runs the realobserver initregistrars (mcp.Registrar,
hook.Registry) against a sandbox$HOMEand transposes their exact
output intoplugins/claude-code/(a marketplace root plus a plugin
directory —plugin.json,.mcp.json,hooks.jsonwith all 22
events) andplugins/cursor/deeplink.txt(a static, exact-config
base64 deeplink, with its inherent one-click-install caveat stated
rather than hidden). A byte-for-byteverify-plugins-buildplus a
plugins-build-driftCI job keep the generated trees honest, and
sync-npm-version.shnow stamps both plugin manifests with the full
semver (an opaque version pin, not MV3's numeric-core trap). Nothing
is published: thesuperbasedapp/observer-pluginsrepo, the Claude
Code marketplace listing, and the Cursor deeplink distribution all
stay operator-gated pending a decision on a known caveat — installing
the plugin alongsideobserver init --claude-codedouble-registers
hooks, so both would fire on every event until that's deduped. -
feat(orgserver,orgclient,web2): Teams member-invite loop,
default-off.[server].member_invites(defaultfalse) lets an
active admin mint a capped, audited, time-boxed invite link — server
migration 023 backs the monthly cap and theinvite_mintedaudit
trail — with a web2 invite composer page and a node-dashboard nudge on
theExecute-class surface. Conversion is reconstructed by joining
minted_byagainstused_at; nothing new goes on the agent wire. See
Fixed below for the security round that closed eight findings (seven
fixed, one documented) before this shipped. -
feat(plugins): one-command install for 20 AI coding tools, published
as the publicsuperbasedapp/pluginsrepo. Each supported tool can
now wire SuperBased up through its own native plugin or extension
mechanism instead of a hand-edited config file. The repo is assembled
from this one, never hand-maintained:scripts/assemble-plugins-repo.sh
runs the REALobserver initregistrars (mcp.Registrar+
hook.Registry) against a sandboxHOMEand transposes their exact
output, so a plugin manifest can never drift from whatinitactually
writes. Coverage grew across three waves to a 52-file assembled
tree: six native manifests (kimi-code, qoder, devin, droid, openclaw,
antigravity — validated with each vendor's own validator where one
exists), six generated config listings (crush, kiro-cli, copilot-cli,
kilo-code, roo-code, open-interpreter), a Gemini CLI extension, a Goose
listing, a Codex plugin + marketplace, and a Copilot
vscode:mcp/installdeep link.@superbased/opencode-pluginis
published to npm. Cowork's.mcpbbundle is built but deliberately
not claimed as supported until a live install verifies it. -
feat(tooltax): a canonical tool-name taxonomy shared by every
adapter. A single 1,074-row table (30 tools) is now the one owner of
what each tool call is — its canonical name, category, and surface —
replacing per-adapter string matching. Adapters readtooltax.For;
migration 077 backfills historical rows whoseaction_typewas
unknown; the dashboard gains a/api/tools/breakdownendpoint with
real category and surface dimensions; andobserver adaptersgains a
VOCABcolumn reporting, per adapter, whether its vocabulary is
grounded or honestly unknown. -
feat(db): test runs can no longer touch your real database.
db.Open
now refuses any path inside the operator's actual~/.observerwhen
running undergo test, returningErrRealDBInTestbeforesql.Open.
This closes a real incident in which a working-tree test resolved
config.Default()'s live path and ran a migration against the 16 GB
production store. The gate resolves symlinks across the full path and
checks hard links viaos.SameFile, guards both migration entry points
(includinginternal/orgserver/db), and fails loudly rather than
skipping when the home directory can't be resolved. -
feat(orgserver,web2): GitHub Copilot seat subscriptions are finally
priced. The seats API returns COUNTS ONLY, so/api/org/telemetry
reported "50 seats" and the metered overage while the subscription
those seats cost — normally the larger of Copilot's two cost feeds —
appeared nowhere.[copilot_analytics].per_seat_price_usdexisted and
defaulted to $19, but nothing read it: an admin could set it and
change nothing.SeatStatsnow carriesper_seat_price_usdand
monthly_usd(total × price), threaded from config through the
dashboard options, and the org dashboard renders a "Seat subscription"
tile plus a summary stat. Both are omitted entirely when the price is
unconfigured — absent means "price not supplied", never "seats are
free". The unit trap is preserved at every level:monthly_usdis
a point-in-time monthly fee and is never summed intocost_usd
(additive per-day metered spend) — asserted in Go, on the wire, and in
every UI aggregate. A non-finite or negative price is now refused at
config validation: TOML accepts the literalsnanandinfwithout
error, andinfwould otherwise reachencoding/json, which refuses
it after the 200 status is already sent. -
feat(cockpit): burn rate on the session cost strip. A running
total never answers "should I stop this?" — a rate does. The terminal
cockpit's cost strip gains $/h plus a +1h projection. The rate is
measured over the turns the cockpit already polls so it reflects
current spend rather than being diluted by earlier idle time; the
oldest fetched turn is the window boundary, so it sets the denominator
but is excluded from the numerator. Falls back to the session average
over the same elapsed the header shows, and returns nothing — never a
confident $0.00/h — when neither basis exists.
Fixed
-
fix(codex): patch rows no longer land with an empty input. Modern
Codex invokesapply_patchfrom inside anexeccustom_tool_call and
the executor stampspatch_apply_endwith its ownexec-<uuid>id,
while the response_item carriescall_<hash>. Different namespaces, so
the join could never match on current builds and every patch row took
the standalone path with noraw_tool_input. Measured over a
393-rollout corpus: 2,059 exec-uuid vs 416 call_hash. There is no join
to repair, so the standalone path now reconstructs the input from the
producer's ownchangesobject, with the rendering owned in one place
so a live-captured row and abackfill --codex-tool-inputrecovery are
byte-identical. On one live update-heavy session this took empty inputs
from 95 to 0.Authored-byte accounting was deliberately left alone after measurement
proved the obvious "fix" wrong: anupdatechange carries only
unified_diffand scores zero here, which looks like a bug, but every
patch also emits an invocation row that already counts the same bytes
from its own patch text (107 invocation rows / 142,823 B alongside 95
executor rows). Counting the diff as well double-counts every update.
The reasoning is now written into the code so the zero is not "fixed"
again. -
fix(dashboard): Compression tiles show an em dash, not a confident
zero.deriveTotals()returned an all-zero object whenever the
timeseries query had no data, so five tiles rendered "$0.00 saved
across 0 compression events" from the moment the page mounted — and
because the loading flag also clears on error, a failed query settled
that zero in as a confident, wrong answer. The no-data branch now
yields undefined, which the existing null-safe formatters render as an
em dash: loading keeps the pulse, an error settles to a static dash. -
test(terminal): one flaked assertion can no longer kill an entire
package. A cache-cleared-racesweep reportedFAIL cmd/observer 2400s— the full 40-minute timeout. The cause was not slowness:
TestTerminalPidSeederEndToEndfailed an assertion and then deadlocked
in cleanup, because the PTY stub'sKill/Closewere no-ops that never
unblocked the parked reader, soManager.Shutdown's unbounded
wg.Wait()never returned. Being a serial test, it left ~340 parallel
tests in the same package waiting and never run — the package reported
FAIL having executed almost none of itself. The prior "green" sweep
could not have caught this:cmd/observerwas one of the 193/195
cached packages. With the stub honouringClose, the same failure now
takes 6.9s instead of 40 minutes. -
fix(dashboard): cache-savings counterfactual no longer zeroed for
proxy-recorded turns. Rows with a recorded cache read > 0
contributed $0 to the "saved by caching" counterfactual, understating
cache savings roughly 3× on real corpora. Found by the Show HN corpus
mining pass; the memo's savings claims are gated on this fix. -
fix(store): outcome-update seam hardened. Cross-tick
tool_resultsoutcome flips now persist action outcomes through ONE
store-owned seam instead of per-adapter writes, closed across two
adversarial review rounds (evidence-gated 1→0 self-heal included). -
fix(hook,init): settings.json writer classes hardened (9 call
sites). Two pre-existing defect classes shared by the hooks/cursor/
statusline registrars: theany-decode re-indent path could mangle
big integers (float64), reformat numbers, HTML-escape strings, panic
on JSON null, and silently collapse duplicate keys — now re-indents
RAW bytes; and the read-modify-write used a fixed temp name with no
lock — now unique temps + a cross-process advisory lock +
re-read-after-lock. Plus: loose ownership matching tightened, symlink
clobbering refused, a 5 MiB size cap, PID-reuse liveness via
/proccmdline, and oversize-stdin handling on the statusline path. -
fix(hook): registrar write-path hardening, second pass. The
deferred WP9 ledger:hook_checksums.jsonwrites now take the
cross-process advisory lock and rename a unique temp file (was an
unlocked plain write); the codexhooks.json/config.tomlwriters
drop their fixed.tmpnames for unique-temp atomic writes under
per-path locks with re-read-after-lock; and the unregister path
refuses to delete a symlinked config file instead of silently
unlinking the link while the target keeps stale hooks. Each pinned
by a concurrent-writer or symlink regression test. -
fix(adapter): foreign-mount SQLite mirror staging unified under
internal/adapter/mirrorbase. Seven adapters staged cross-mount DB
mirrors into persistent~/.cacheduring parsing through copied
helper code; the new single-owner seam scopes them, and
observer usageredirects mirrors into its throwaway scratch
directory (live-verified: a one-shot run adds zero cache entries). -
fix(integration): npm Binary-row honesty. Dropped impossible
.exespellings from 10 npm-distributed CLI tools' Windows binary
rows — npm shims install.cmd/.ps1wrappers, never raw.exe. -
feat(web): share surfaces + camera-ready wordmark + once-only star
prompt. CommunityCard can share a real personal stat the user opts
into ("$X tracked across N tools this month — superbased.app"), with
the exact text previewed before sharing and a static-link fallback
(never "$0.00 tracked"). MilestonesCard brag moments gain share/copy
actions. The four hero stat surfaces (Cost, Suggestions, Milestones,
Report) carry a muted in-frame▞ superbasedwordmark so a native
screenshot travels with attribution — no export pipeline. A
lazygit-pattern once-only GitHub star prompt appears after a week of
real usage (never in demo mode, never twice). A new open-ended
Feedback issue form joins the GitHub templates. -
feat(cli):
observer privacy— verifiable zero-network posture.
Prints an honest, config-derived egress report: every listening
socket (loopback-only except the opt-in tailnet remote-access
listener) and every outbound path the binary can take, each with its
gate and this config's actual on/off state;--jsonfor scripting.
Paired with two changes that make the claim exact: the dashboard's
npm-registry update check is now user-initiated only (a "Check
for updates" card in Settings → Health; no fetch on tab load, no
background timer), and a new website page,
/docs/reference/measurement-honesty, states the
compression-savings retraction plainly and documents the provable
accuracy claims that replace it. -
feat(website): three new competitor comparison pages (CodeBurn,
Claude Code Usage Monitor, TokenTracker), authored from a dated
2026-07-30 re-verification of every competitor claim, with steelman
sections and the competitors' newest features stated fairly. New
make track-build/verify-track-build+ a CI drift gate; fixed a
track-gen idempotency bug (marker blocks re-appended at EOF instead
of spliced in place, which could permanently redden a byte-for-byte
drift gate). -
fix(website): tool-count drift sweep. Live compare pages said "26
coding tools" against 29 shipped adapters; the four compare pages are
corrected andtrack-gennow derives counts fromlen(roster)so the
class can't recur (new accuracy-check Check B2 guards it). -
fix(orgserver,orgclient,store): org-announcements rail hardened
before release (adversarial review, six findings, all fixed). The
rail was built by cloning the routing-policy distribution rail, and
cloning a mechanism cloned its weaknesses into a context that couldn't
tolerate them. Three HIGHs, all fixable because the rail was still
unreleased: the signature covered the body only — no version, no rail
identity — so a captured signed document replayed at a bumped version
froze a node's cache via its own monotonic short-circuit, an old
retraction replayed cleared every banner, and because the org server
has one shared signing identity, a genuinely-signed routing-policy
document verified as a valid announcement. Fixed with
orgcontract.AnnouncementSigningMessage(a domain tag, the version,
and the body, hashed together) — the routing rail keeps its released
wire shape as-is, and the residual is now tracked as ledger row
ROUTING-SIG-1 (docs/security.md) instead of silently inherited.
Announcement TOFU also consulted only its own rail's pin, so a node
enrolled for months would accept any key on its first announcement
fetch — closed ininternal/orgclient/orgpin.go, one org identity now
enforced across both rails. And unenrolment left a departed org's
cached banner (and its pinned key) live, risking poisoning
re-enrolment into a different org —store.DeleteEnrolmentnow clears
both distribution caches. Three smaller fixes: the per-cycle poll is
now stated inobserver privacyinstead of left implicit; both
endpoints' 1 MiB caps applied to the read only and ignored trailing
bytes, noworgcontract.DecodeCapped; and"[]"/"null"were a third
and fourth silent spelling of a retraction, now one representation.
Every fix carries a regression test proven to fail pre-fix by inline
mutation against a document the org genuinely signed. -
fix(orgserver): member-invite loop hardened before release
(adversarial review, eight findings, seven fixed, one documented).
Two HIGHs: the monthly mint cap was read-then-insert across two
connections with tens of milliseconds of argon2id hashing between
them, so concurrent mints could all read the same pre-mint count and
all land past the cap — now oneBEGIN IMMEDIATEtransaction,
mutation-proved by an 8-goroutine hammer test; and a SCIM-deactivated
admin with a still-live SAML cookie kept minting uncapped because the
admin branch returned before any liveness check — fixed both at the
invite gate and, more broadly, atadminCheckerFor(AND active = 1),
restoring deactivation as an authority revocation across every
RequireAdminSAMLroute. Three MEDs:ttl_dayswas unbounded (a
273-year token was mintable) and is now clamped 1–90 with explicit-zero
rejected rather than treated as "omitted"; the invite-lookup
404-vs-201 response was a membership oracle, now rate-limited to 20
failed lookups/hour per inviter (server migration 024, storing only
the inviter id and timestamp — never the probed address); and the
enrol link is now copy-only text rather than a clickable URL (the SPA
has no/enrol/:tokenroute, so navigability bought nothing and only
cost exposure to link-unfurlers and mail scanners). One MED + one LOW:
inviter attribution now survives SCIM deletion via aminted_by
fallback, and the invite audit'ssource_ipno longer trusts
X-Forwarded-For(new ledger row XFF-1 for two pre-existing,
deliberately-deferred XFF-trusting audit sites elsewhere in the org
server). One LOW documented rather than fixed: target-deletion CASCADE
frees a cap slot early, recorded indocs/teams-operations.md§8.2 as
needing member-lifecycle authority that already outranks the cap. -
fix(adapters): model reasoning no longer files itself as a completed
task. Fifteen adapters minted a separate action row — typed
task_complete— every time the model produced a reasoning block,
inventing 15,734 phantom "task completions" in a live corpus for work
the model never did. Reasoning is not an action; it is something the
model did on the way to one. Every adapter now carries it as
preceding_reasoningon the successor event and mints no row at all.
Threading is consumed-once, last-wins, discarded at a turn boundary,
and never crosses a session id. Opaque or encrypted reasoning
placeholders are never threaded anywhere. Affected: crush, hermes,
cline-cli, antigravity, cursor, opencode, kilo-code-cli, codex,
open-interpreter, openclaw, cline, cowork, pi, copilot, copilot-cli,
gemini. -
fix(dashboard):
/api/statusno longer saturates the daemon.
diag.Snapshotcosts several seconds ofCOUNT(*)s on a large
database, and two always-mounted chrome components polled it every 5
seconds — so a scan was permanently in flight and starved every other
query (measured first-load latencies reached 36 s). The snapshot is now
computed behind a 15-second TTL + singleflight cache. Crucially the
scan runs detached from the caller's request context: the previous
fix was incomplete because the client aborts its prior fetch each tick,
cancelling whichever poller held the gate and serialising the scans
anyway. Degraded snapshots now carry aQueryErrorscount and are
returned but never cached. -
fix(cursor): the reasoning stash is now safe across hook processes.
Cursor is a hook adapter — one process per event — so its pending
reasoning needs an on-disk carrier. The first implementation raced:
under contention, 190 of 200 iterations double-threaded a thought and 8
of 200 reads saw a truncated file. It is now an atomic rename-claim
protocol — writes land at a unique temporary name and rename over the
target, and consumers claim by renaming the target to a nonce first, so
exactly one participant can win a name. Proven under-racewith
multiple concurrent participants. -
fix(gemini): a failed call no longer records itself as a success. A
legacy-shaped failed call persistedsuccess=1forever. The verdict is
now taken from the response body — but only for meaningful errors
(non-blank strings, non-empty objects or arrays).{"error": false},
0,[],{}, and bare booleans or numbers never invent a failure. -
fix(plugins): two wrong-plugin-resolution hazards caught before
publication. Both were reproduced live, not theorised. Factory's
droidresolved./superbasedat the repo root to the Claude Code
plugin directory — hooks and all — so the plugin was relocated to
factory/superbased.qodercli plugins marketplace addlikewise
installed the Claude plugin via its binary's literal search order;
closed with a root.qoder-plugin/marketplace.jsonwhose schema was
extracted from the shipped binary, then verified end to end.
Changed
- Migration 079 deletes ~15,050 content-free reasoning rows on first
run, and your action counts will drop. This is the historical half of
the reasoning fix above: the(reasoning)and(encrypted reasoning, N bytes)placeholder rows written by the retired emit sites, plus their
FTS search excerpts, are removed, and 7 stale cursor rows are rewritten.
Those rows have no content, never had content, and were never threaded
anywhere — deleting them removes rows, not information. Aggregate
action counts on the dashboard will fall accordingly; this is the
intended correction, not data loss. Everything content-bearing is
kept, including the 329 codex reasoning rows carrying real summary text
and all 15 gemini reasoning rows (deleting those was measured to be
lossy). An id high-water marker is recorded inschema_metabefore the
deletes so the result can be re-verified afterwards. Schema golden
moves 78 → 79. scripts/sync-npm-version.shnow stamps 11 plugin-side manifests
in addition to the npm and VS Code packages, with a set-equality test
in both directions so a new manifest cannot be silently missed.
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.28.0-linux-x64.tar.gz |
| Linux arm64 | observer-v1.28.0-linux-arm64.tar.gz |
| macOS x86_64 (Intel) | observer-v1.28.0-darwin-x64.tar.gz |
| macOS arm64 (Apple Silicon) | observer-v1.28.0-darwin-arm64.tar.gz |
| Windows x86_64 | observer-v1.28.0-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.28.0
Org server (Docker)
The self-hosted org server ships as a Docker image and as per-platform observer-org-v1.28.0-* archives (attached below).
docker pull ghcr.io/superbasedapp/observer-org:v1.28.0The image is keyless-signed with cosign. Verify it:
cosign verify ghcr.io/superbasedapp/observer-org:v1.28.0 \
--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