v0.10.10
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"forsdl.slice.build, plus optionalwireFormatonsdl.symbol.searchandsdl.context. Line-oriented text format with#PACKED/1header, legend-interned path prefixes, single-char tagged CSV tables, and__tables/__stypesself-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 assl1(slice),ss1(symbol-search),ctx1(context),gen1(generic fallback). Schema-free decoder (decodePacked) reads__tables/__stypesso 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.packedsection insdl.usage.statsreportsencodings,fallbacks,bytesSaved, and per-encoder breakdown. Migrationm014addspackedEncodings,packedFallbacks,packedBytesSaved,packedByEncoderJsoncolumns toUsageSnapshot. - Config: new
wire.packed.{enabled,threshold,defaultFormat,encoders}block underAppConfigSchema. Env varsSDL_PACKED_ENABLED,SDL_PACKED_THRESHOLD,SDL_PACKED_DEFAULT_FORMAToverride config. - New
estimatePackedTokens(text)insrc/util/tokenize.ts(length / 3.2). - New agent helper
unpackWireResult(result)/tryUnpackPayload(payload)insrc/agent/wire-utils.ts.
Token-aware gate
- Two-axis auto-gate for
packedemission. The dispatcher emits packed when EITHER byte savings clearwire.packed.threshold(default 0.15) OR estimated-token savings clearwire.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 varSDL_PACKED_TOKEN_THRESHOLDoverrides the token axis. - New
decideFormatDetailed(wireFormat, metrics, byteThreshold, tokenThreshold)returns{ decision, axisHit, bytesSavedRatio, tokensSavedRatio }. Legacy single-axisdecideFormatandshouldEmitPackedretained for back-compat. _packedStatsresponse field gainsjsonTokens,packedTokens,tokenSavedRatio,axisHit("bytes"|"tokens").- New optional dispatcher option
packedTokenThresholdparallelspackedThreshold.
Fixed
sdl-mcp initnow respectsCLAUDE_CONFIG_DIRand resolves the home directory cross-platform.detectInstalledClientspreviously used the Windows-onlyprocess.env.USERPROFILE, so detection on macOS/Linux silently produced CWD-relative garbage paths and never matched anything; it also ignoredCLAUDE_CONFIG_DIR, so install instructions always pointed at~/.claudeeven when the user had redirected it. Falls back toos.homedir()whenUSERPROFILEis unset and honorsCLAUDE_CONFIG_DIR. (#17)- Concurrent
serve --stdiono 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.tsandsrc/cli/commands/serve.ts) now resolve the graph DB path, runfindExistingProcess, andwritePidfileto atomically claim the singleton beforeinitGraphDb.writePidfileadditionally writes withflag: "wx"and recovers fromEEXISTby 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
writePidfileahead ofinitGraphDb, startup throwing later (initGraphDb, watcher start, etc.) left the pidfile on disk because thecatchhandlers inmain.tsandserve.tsexit without invokingshutdownMgr.shutdown().pidfilePathis now hoisted so the catch handler canremovePidfile()on failure, eliminating the operator-confusion + PID-reuse window. (#19)
Breaking Changes
- Compact wire format versions 1 and 2 retired. Calling
sdl.slice.buildwithwireFormatVersion: 1or2now throwsWireFormatRetiredError(codeWIRE_FORMAT_RETIRED) at runtime. Migration: replacewireFormatVersion: 1or2withwireFormatVersion: 3(the default since v0.7.x), or opt in towireFormat: "packed"for the new line-oriented format. - Deleted:
toCompactGraphSliceV1,toCompactGraphSliceV2,decodeCompactGraphSliceV3ToV2,decodeCompactEdgesV2ToV1,decodeCompactEdgesV3ToV1,CompactGraphSlice(v1 alias),CompactGraphSliceV2type aliases. The downgrade decoders are gone because the encoders that fed them are gone. serializeSliceForWireFormat()now returns a discriminatedWireFormatResultunion ({format, payload, ...}) instead of a raw payload object. Direct callers in user-side code must read.payload.SliceBuildResponse.sliceschema gainedz.string()to admit packed payloads.
Full Changelog: v0.10.9...v0.10.10