This release adds 3 notable features for engineering teams evaluating rollout.
✓ No known CVEs patched in this version
Topics
+12 more
Affected surfaces
Summary
AI summaryBroad release touches Files touched, Summary by CodeRabbit, Test plan, and pid.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Security | Medium |
Runtime validation now rejects non-finite elements in daemon JSON responses. Runtime validation now rejects non-finite elements in daemon JSON responses. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: high |
— |
| Feature | Medium |
Added helper tryEmbedStandalone for agents without own daemon (pi, openclaw). Added helper tryEmbedStandalone for agents without own daemon (pi, openclaw). Source: granite4.1:8b-q6_K@2026-05-20 Confidence: high |
— |
| Feature | Medium |
OpenClaw now embeds message_embedding before INSERT using tryEmbedStandalone. OpenClaw now embeds message_embedding before INSERT using tryEmbedStandalone. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: high |
— |
| Feature | Medium |
Extended maybeCleanupOwnPlaceholder to unlink empty pidfiles after spawn timeout. Extended maybeCleanupOwnPlaceholder to unlink empty pidfiles after spawn timeout. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: high |
— |
| Feature | Medium |
Implemented 11-case embedding edge matrix with unit tests. Implemented 11-case embedding edge matrix with unit tests. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: low |
— |
| Dependency | Medium |
Updated test configuration for code coverage thresholds. Updated test configuration for code coverage thresholds. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: low |
— |
| Performance | Medium |
Added coverage thresholds (90/80/90/90) for client.ts tier. Added coverage thresholds (90/80/90/90) for client.ts tier. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: low |
— |
| Bugfix | Medium |
Fixed pi spawn-on-miss race condition causing second daemon crash. Fixed pi spawn-on-miss race condition causing second daemon crash. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: high |
— |
| Bugfix | Medium |
Resolved empty-pidfile race leading to stale daemon spawning and bind errors. Resolved empty-pidfile race leading to stale daemon spawning and bind errors. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: high |
— |
| Bugfix | Medium |
Fixed pidfile leak when spawn succeeds but daemon never opens socket. Fixed pidfile leak when spawn succeeds but daemon never opens socket. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: high |
— |
| Bugfix | Medium |
Fixed SIGKILL edge case leaving empty pidfiles causing silent NULL embeddings. Fixed SIGKILL edge case leaving empty pidfiles causing silent NULL embeddings. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: high |
— |
| Refactor | Medium |
Injected realSpawn into tryEmbedStandalone for openclaw bundle environments. Injected realSpawn into tryEmbedStandalone for openclaw bundle environments. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: high |
— |
| Other | Medium |
Added 8 new unit tests for embedding client and daemon behavior. Added 8 new unit tests for embedding client and daemon behavior. Source: granite4.1:8b-q6_K@2026-05-20 Confidence: low |
— |
Full changelog
Closes #178. Follow-up to PR #168 — surfaced during review by @kaghni who flagged that pi and openclaw had no/minimal changes despite the "embeddings fully wired across agents" framing.
What this lands
Three pieces of work, separated into focused commits per the repo's "never >3 src files in one commit across layers" rule:
1. src/embeddings/standalone-embed-client.ts + tests — c9478ec
New helper tryEmbedStandalone(text, kind) for agents that don't bundle a daemon of their own (pi extension source, openclaw plugin). Mirrors the spawn-on-miss state machine in src/embeddings/client.ts but stripped:
- No hello/handshake. Read-only consumers never recycle a stuck daemon; recycling is the hot-path client's job, two recycle paths would race.
- No singleton, no notification side-effects.
- No SIGTERM on a live-PID pidfile with a missing socket — same PID-reuse risk PR #168 fixed in
client.ts.
Coverage threshold added at the client.ts tier (90/80/90/90).
2. Pi spawn-on-miss bug fix — 17f9435
Pi's existing embed() called spawn(...) bare — no O_EXCL pidfile lock, no respect for an alive owner. Two concurrent pi turns (or pi racing another agent at SessionStart) both spawned a daemon; the second crashed on bind. The header comment block described the canonical behavior but the code didn't implement it.
Replaces both tryEmbedOverSocket (connect-only) and the inline spawn loop with a single spawn-on-miss state machine mirroring the shared helper. embed() collapses to env-check → empty-check → tryEmbedOverSocket.
3. OpenClaw embedding producer — 8d7df3d
OpenClaw previously omitted message_embedding from every sessions INSERT — semantic recall on openclaw sessions was broken because every row landed NULL.
Now openclaw imports tryEmbedStandalone and embeds the captured message before INSERT. The helper imports spawn from node:child_process at the top level, which the openclaw esbuild config replaces with a no-op stub. Without the real spawn, the auto-spawn-on-miss fallback silently does nothing. Fix: openclaw already has realSpawn from createRequire(import.meta.url); we inject it into the helper at module load via _setSpawnImpl (renamed from _setSpawnForTesting to reflect its two legitimate use cases — tests AND bundle environments stubbing node:child_process).
Bundle-scan regression guard in tests/openclaw/openclaw-embed-bundle.test.ts locks in: exactly one tryEmbedStandalone call site on the auto-capture path, message_embedding in the INSERT column list, _setSpawnImpl(realSpawn) called at module load, and no INSERT that hardcodes literal NULL.
4. Codex pre-merge review fixes — bb9df97
Pre-merge codex review flagged 2 P1 + 1 P2:
- P1 #1 — Empty-pidfile race.
openSync(path, "wx")creates the lock file BEFOREwriteSync(pid)lands. A second caller observing the gap sawNumber("") === 0 → null → "stale", unlinked, and re-opened. Both callers spawned a daemon; the second crashed on bind. Fix:readPidFilenow returns a tristate (number | "empty" | null);trySpawnDaemontreats"empty"as "writer in progress, wait", never unlinks. Pi's inline version also switched fromwriteFileSync(path, ...)towriteSync(fd, ...)so a racing unlink can't clobber. - P1 #2 — Pidfile leak when spawn succeeds but daemon never opens socket. Placeholder PID stayed in the file with our (still-alive) process PID; future callers saw a "live owner" and waited forever. Fix: new
maybeCleanupOwnPlaceholderunlinks ONLY if pidfile still containsprocess.pid. - P2 — Runtime validation at the socket boundary. Daemon JSON is untrusted at runtime even though TypeScript types claim
number[]. Both implementations now reject any non-finite element before returning the vector.
4 new unit tests (empty pidfile = no respawn, retry-after-cleanup recovery, non-finite array → null, NaN/Infinity → null) + 3 source-level regression guards in pi.
5. Codex follow-up: stuck empty pidfile — f04f00a
Codex's second pass confirmed all 3 fixes correct but flagged a residual edge: a process SIGKILL'd exactly between openSync(wx) and writeSync(pid) leaves an empty pidfile that every subsequent caller treats as "writer in progress" — silent NULL embeddings for that uid forever. Extended maybeCleanupOwnPlaceholder to also unlink an empty pidfile after the spawnWaitMs (5s) timeout — orders of magnitude longer than the legitimate openSync→writeSync gap.
11-case edge matrix (all unit-tested)
| # | Scenario | Expected |
|---|---|---|
| 1 | Binary missing | NULL, no spawn |
| 2 | Binary present, no socket / pid | Spawn → wait → embed |
| 3 | Socket alive | Connect → embed |
| 4 | Stale socket, no daemon | Spawn (daemon unlinks on bind) |
| 5 | Dead PID in pidfile | Cleanup → spawn |
| 6 | Live PID, no socket | Wait, no SIGTERM |
| 7 | Two callers race | O_EXCL: one spawns, other waits |
| 8 | spawn() throws | NULL, pidfile rolled back |
| 9 | Daemon never opens socket | 5s timeout → NULL + cleanup |
| 10 | Embed request times out | NULL |
| 11 | Daemon returns unknown-op | NULL |
Test plan
- [x]
npm test— 2741 / 2741 pass (was 2733 before this branch; added 8) - [x]
npm run buildclean - [x]
npx tsc --noEmitclean - [x]
codex review— final pass returned "No new [P1] or [P2] findings" - [x] Per-file coverage on
src/embeddings/standalone-embed-client.ts: 96.52% statements / 84.61% branches / 94.73% functions / 100% lines (≥ 90/80/90/90 threshold added in this PR) - [ ] E2E on
test_plugin/default/sessions_test(NEVER prod) — manual pre-merge step using the/tmp/e2e-embed-check.mjspattern from PR #168 (socket p50=10ms, write p50=402ms, semantic recall TOP-1 @ 0.7409). Will run before merge.
Files touched
src/embeddings/standalone-embed-client.ts(new, 305 LOC)tests/claude-code/standalone-embed-client.test.ts(new, 22 tests)pi/extension-source/hivemind.ts(replacestryEmbedOverSocket+embed()spawn logic)tests/pi/pi-extension-source.test.ts(5 new regression guards)openclaw/src/index.ts(embed call +_setSpawnImplinjection)tests/openclaw/openclaw-embed-bundle.test.ts(new bundle-scan)tests/claude-code/skillify-session-start-injection.test.ts(regex window bump)vitest.config.ts(coverage threshold)
Summary by CodeRabbit
-
New Features
- Added message embedding to the auto-capture pipeline with automatic daemon spawning and graceful NULL fallback on failures.
- Implemented improved daemon lifecycle management with race-condition safety and per-user isolation.
-
Tests
- Added comprehensive test coverage for embedding client functionality and daemon behavior.
- Added integration tests to prevent regressions in embedding wiring.
-
Chores
- Updated test configuration for code coverage thresholds.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
Track Hivemind turns agent traces into skills and shares with your team
Get notified when new releases ship.
Sign up freeAbout Hivemind turns agent traces into skills and shares with your team
All releases →Related context
Related tools
Earlier breaking changes
- v0.7.52 Removes `hivemind tasks` CLI and related code surfaces.
- v0.7.51 Removes `hivemind tasks` CLI and related code surfaces.
- v0.7.19 Module name skilify replaced with skillify; affects all imports
- v0.7.19 CLI command skilify removed; renamed to skillify without deprecation alias
- v0.7.18 CLI subcommand renamed from `skilify` to `skillify`; no deprecation alias.
Beta — feedback welcome: [email protected]