Skip to content

v0.10.10

Choose a tag to compare

@GlitterKill GlitterKill released this 28 Apr 17:52
· 165 commits to main since this release

Highlights

packed line-oriented wire format ships across slice/search/context with a token-aware auto-gate (~45% token savings on representative slices, 8/8 emitting packed). Concurrent serve --stdio no longer corrupts the LadybugDB WAL. Cross-platform fix for sdl-mcp init. Breaking: compact wire formats v1 and v2 retired.

Added — packed wire format

  • New wireFormat: "packed" and "auto" for sdl.slice.build, plus optional wireFormat on sdl.symbol.search and sdl.context. Line-oriented text format with #PACKED/1 header, legend-interned path prefixes, single-char tagged CSV tables, and __tables / __stypes self-describing schema. Median ~45% byte savings vs JSON across slice-shaped responses; gate-protected to fall back to compact-JSON below the 0.15 savings threshold (configurable). Encoders ship as sl1 (slice), ss1 (symbol-search), ctx1 (context), gen1 (generic fallback). Schema-free decoder (decodePacked) reads __tables / __stypes so new encoders ship without bumping the decoder version. Header is intentionally #PACKED/1 (not #MUNCH/1) to namespace cleanly from the upstream jcodemunch-mcp dialect.
  • Telemetry: new wire.packed section in sdl.usage.stats reports encodings, fallbacks, bytesSaved, and per-encoder breakdown. Migration m014 adds packedEncodings, packedFallbacks, packedBytesSaved, packedByEncoderJson columns to UsageSnapshot.
  • Config: new wire.packed.{enabled,threshold,defaultFormat,encoders} block under AppConfigSchema. Env vars SDL_PACKED_ENABLED, SDL_PACKED_THRESHOLD, SDL_PACKED_DEFAULT_FORMAT override config.
  • New estimatePackedTokens(text) in src/util/tokenize.ts (length / 3.2).
  • New agent helper unpackWireResult(result) / tryUnpackPayload(payload) in src/agent/wire-utils.ts.

Token-aware gate

  • Two-axis auto-gate for packed emission. The dispatcher emits packed when EITHER byte savings clear wire.packed.threshold (default 0.15) OR estimated-token savings clear wire.packed.tokenThreshold (default 0.30). Tokens are the dominant axis on slice-shaped LLM payloads — empirically 40–54% saved across 8 representative live slices (6–60 cards), versus 5–23% byte savings. Pre-tune, only 1/8 fixtures cleared the byte gate; post-tune, 8/8 emit packed. New env var SDL_PACKED_TOKEN_THRESHOLD overrides the token axis.
  • New decideFormatDetailed(wireFormat, metrics, byteThreshold, tokenThreshold) returns { decision, axisHit, bytesSavedRatio, tokensSavedRatio }. Legacy single-axis decideFormat and shouldEmitPacked retained for back-compat.
  • _packedStats response field gains jsonTokens, packedTokens, tokenSavedRatio, axisHit ("bytes" | "tokens").
  • New optional dispatcher option packedTokenThreshold parallels packedThreshold.

Fixed

  • sdl-mcp init now respects CLAUDE_CONFIG_DIR and resolves the home directory cross-platform. detectInstalledClients previously used the Windows-only process.env.USERPROFILE, so detection on macOS/Linux silently produced CWD-relative garbage paths and never matched anything; it also ignored CLAUDE_CONFIG_DIR, so install instructions always pointed at ~/.claude even when the user had redirected it. Falls back to os.homedir() when USERPROFILE is unset and honors CLAUDE_CONFIG_DIR. (#17)
  • Concurrent serve --stdio no longer corrupts the LadybugDB WAL. Two processes starting simultaneously (e.g. two Claude Code windows) could both open the WAL before the pidfile singleton guard fired, corrupting it on the next checkpoint. Both call sites (src/main.ts and src/cli/commands/serve.ts) now resolve the graph DB path, run findExistingProcess, and writePidfile to atomically claim the singleton before initGraphDb. writePidfile additionally writes with flag: "wx" and recovers from EEXIST by re-reading the file, closing the check-then-write TOCTOU window if call-site ordering ever regresses. (#19)
  • Pidfile is removed when startup fails. Code-review follow-up to the WAL race fix: after reordering writePidfile ahead of initGraphDb, startup throwing later (initGraphDb, watcher start, etc.) left the pidfile on disk because the catch handlers in main.ts and serve.ts exit without invoking shutdownMgr.shutdown(). pidfilePath is now hoisted so the catch handler can removePidfile() on failure, eliminating the operator-confusion + PID-reuse window. (#19)

Breaking Changes

  • Compact wire format versions 1 and 2 retired. Calling sdl.slice.build with wireFormatVersion: 1 or 2 now throws WireFormatRetiredError (code WIRE_FORMAT_RETIRED) at runtime. Migration: replace wireFormatVersion: 1 or 2 with wireFormatVersion: 3 (the default since v0.7.x), or opt in to wireFormat: "packed" for the new line-oriented format.
  • Deleted: toCompactGraphSliceV1, toCompactGraphSliceV2, decodeCompactGraphSliceV3ToV2, decodeCompactEdgesV2ToV1, decodeCompactEdgesV3ToV1, CompactGraphSlice (v1 alias), CompactGraphSliceV2 type aliases. The downgrade decoders are gone because the encoders that fed them are gone.
  • serializeSliceForWireFormat() now returns a discriminated WireFormatResult union ({format, payload, ...}) instead of a raw payload object. Direct callers in user-side code must read .payload.
  • SliceBuildResponse.slice schema gained z.string() to admit packed payloads.

Full Changelog: v0.10.9...v0.10.10