feat: add plugin_version column to memory + sessions tables#120
Conversation
Track the hivemind version that produced each row. The column is TEXT NOT NULL DEFAULT '' on both tables (memory + sessions) so older clients that omit it from their INSERT column list keep working — the backend fills '' from the DEFAULT. Migration is handled the same way as the agent column (added 2026-04-11): CREATE TABLE includes plugin_version on greenfield tables, and ensureColumn() ALTERs existing tables on first contact with the new client. The marker-store TTL caches the ensure() result so steady-state calls cost zero round-trips. Schema-scenario tests cover all 7 combinations (greenfield, full legacy, half-legacy mem/sessions, fully migrated, mixed) plus the cross-cutting invariants (race-tolerated "Column already exists" via re-SELECT, no silent swallow of unrelated ALTER errors).
Wire the installed hivemind version into every INSERT and UPDATE the plugin produces, across all five agents (claude-code, codex, cursor, hermes, pi). Sources of the value differ by call site, all resolved through getInstalledVersion(__bundleDir, manifestDir): - capture hooks resolve once at module load and reuse for the lifetime of the hook process - session-start hooks resolve once at the top of main() and thread the value into createPlaceholder() - wiki-worker reads pluginVersion from the spawn-config JSON written by the parent process, since the worker runs detached from a bundle layout that may differ from its spawner upload-summary.ts gains a pluginVersion UploadParams field and writes it on both the UPDATE (refresh) and INSERT (new summary) paths. Kept atomic with the summary column to preserve the single-statement invariant (Deeplake silently drops one of two rapid UPDATEs on the same row — see CLAUDE.md). session-queue.ts (currently unused but tested) carries the column through QueuedSessionRow + buildSessionInsertSql so the shape stays consistent if and when the queue path is re-enabled. plugin-version-resolution.test.ts is a per-agent guard that: 1. resolves getInstalledVersion against each shipped bundle's actual on-disk layout and asserts a semver-shaped non-empty result, and 2. scans each capture.js / session-start*.js bundle to confirm plugin_version is in the INSERT column list. A silent regression in either dimension (renamed manifest dir, dropped .hivemind_version stamp, esbuild dropping the helper, source-edit not re-bundled) would now fail in CI instead of landing empty strings in production. Side finding worth a separate fix: codex/.codex-plugin/plugin.json is stuck at 0.6.7 (the bump script appears to only update the top-level package.json and claude-code's manifest). The per-agent test asserts non-empty semver rather than strict equality with package.json so this unrelated release-process bug doesn't block this change.
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR adds comprehensive plugin version tracking across the HiveMind codebase. It introduces a ChangesPlugin Version Tracking Across All Agents
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Coverage ReportScope: files changed in this PR. Enforced threshold: 90% per metric (per file via
File Coverage — 21 files changed
Generated for commit 6af4f8e. |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
hermes/bundle/capture.js (1)
1112-1124:⚠️ Potential issue | 🟠 Major | ⚡ Quick winResolve Hermes from its own manifest in both write paths.
Both the summary-worker config and the capture hook still read
.claude-plugin, so Hermes can persist the wrongplugin_versionto bothmemoryandsessions.Also applies to: 1489-1490
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@hermes/bundle/capture.js` around lines 1112 - 1124, The code currently calls getInstalledVersion(bundleDir, ".claude-plugin") when writing config.json (via writeFileSync3) and in the other write path; change both calls to resolve Hermes from its own manifest instead of ".claude-plugin" — e.g., replace the manifest name with Hermes' manifest identifier (the Hermes plugin/manifest constant or filename) when calling getInstalledVersion(bundleDir, ...), so the pluginVersion variable used in the config write (configFile / writeFileSync3) and the summary-worker write path is derived from Hermes' manifest rather than ".claude-plugin".src/hooks/upload-summary.ts (1)
73-87:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAvoid clearing
plugin_versionon UPDATE whenpluginVersionis omitted.Because
pluginVersiondefaults to""(Line 73) and UPDATE always writes it (Line 86), callers that don’t passpluginVersionwill erase previously stored values.Suggested conditional UPDATE clause
- const pluginVersion = params.pluginVersion ?? ""; + const pluginVersion = params.pluginVersion; + const pluginVersionForInsert = params.pluginVersion ?? ""; ... - `plugin_version = '${esc(pluginVersion)}', ` + + (pluginVersion === undefined ? "" : `plugin_version = '${esc(pluginVersion)}', `) + `last_update_date = '${ts}' ` + ... - `${sizeBytes}, '${esc(project)}', E'${esc(desc)}', '${esc(agent)}', '${esc(pluginVersion)}', '${ts}', '${ts}')`; + `${sizeBytes}, '${esc(project)}', E'${esc(desc)}', '${esc(agent)}', '${esc(pluginVersionForInsert)}', '${ts}', '${ts}')`;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/upload-summary.ts` around lines 73 - 87, The UPDATE currently always writes plugin_version because pluginVersion defaults to "" which will overwrite stored values; change the logic around the pluginVersion variable and the SQL construction in the existing-branch where the UPDATE string is built (the code that defines pluginVersion and the SQL variable) so that plugin_version is only included in the SET clause when the caller actually provided a pluginVersion (e.g., params.pluginVersion !== undefined/null/empty), by conditionally appending `, plugin_version = '...'"` to the SQL string only when present and leaving it out otherwise; ensure you still use esc(pluginVersion) when you include it and that the rest of the SET fields (summary, summary_embedding, size_bytes, description, last_update_date) remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@claude-code/tests/plugin-version-resolution.test.ts`:
- Line 55: The test currently asserts expect(version).toMatch(/^\d+\.\d+\.\d+$/)
which rejects valid semver prerelease/build strings; update the assertion on the
version variable (the expect(...).toMatch call in
plugin-version-resolution.test.ts) to use a regex that allows optional
prerelease and build metadata (e.g. permit a trailing -prerelease and +build
segment) so strings like 1.2.3-beta.1 and 1.2.3+exp.sha.5114f85 pass.
In `@src/hooks/cursor/capture.ts`:
- Around line 44-45: PLUGIN_VERSION is being resolved from ".claude-plugin"
which pulls the wrong manifest; update the call to getInstalledVersion so it
resolves Cursor’s own manifest instead (use the Cursor manifest identifier
rather than ".claude-plugin"), keeping __bundleDir and PLUGIN_VERSION the same
so the hook stamps session rows with the correct plugin_version; modify the
getInstalledVersion call invoked where PLUGIN_VERSION is defined (refer to
__bundleDir and getInstalledVersion) to point at the Cursor manifest.
In `@src/hooks/cursor/session-start.ts`:
- Around line 155-157: Replace the hard-coded ".claude-plugin" manifest when
calling getInstalledVersion so SessionStart uses the Cursor manifest like the
rest of the codebase; update the call getInstalledVersion(__bundleDir,
".claude-plugin") to pass the same Cursor manifest identifier used elsewhere
(e.g. the constant or string used in other hooks), and keep assigning
pluginVersion = current ?? "" so placeholder rows and the injected version
notice report Cursor’s version instead of Claude’s.
---
Outside diff comments:
In `@hermes/bundle/capture.js`:
- Around line 1112-1124: The code currently calls getInstalledVersion(bundleDir,
".claude-plugin") when writing config.json (via writeFileSync3) and in the other
write path; change both calls to resolve Hermes from its own manifest instead of
".claude-plugin" — e.g., replace the manifest name with Hermes' manifest
identifier (the Hermes plugin/manifest constant or filename) when calling
getInstalledVersion(bundleDir, ...), so the pluginVersion variable used in the
config write (configFile / writeFileSync3) and the summary-worker write path is
derived from Hermes' manifest rather than ".claude-plugin".
In `@src/hooks/upload-summary.ts`:
- Around line 73-87: The UPDATE currently always writes plugin_version because
pluginVersion defaults to "" which will overwrite stored values; change the
logic around the pluginVersion variable and the SQL construction in the
existing-branch where the UPDATE string is built (the code that defines
pluginVersion and the SQL variable) so that plugin_version is only included in
the SET clause when the caller actually provided a pluginVersion (e.g.,
params.pluginVersion !== undefined/null/empty), by conditionally appending `,
plugin_version = '...'"` to the SQL string only when present and leaving it out
otherwise; ensure you still use esc(pluginVersion) when you include it and that
the rest of the SET fields (summary, summary_embedding, size_bytes, description,
last_update_date) remain unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d90e6193-9cdd-4966-9d07-ac043606addf
📒 Files selected for processing (58)
bundle/cli.jsclaude-code/bundle/capture.jsclaude-code/bundle/commands/auth-login.jsclaude-code/bundle/pre-tool-use.jsclaude-code/bundle/session-end.jsclaude-code/bundle/session-start-setup.jsclaude-code/bundle/session-start.jsclaude-code/bundle/shell/deeplake-shell.jsclaude-code/bundle/wiki-worker.jsclaude-code/tests/deeplake-api.test.tsclaude-code/tests/plugin-version-resolution.test.tsclaude-code/tests/schema-scenarios.test.tscodex/bundle/capture.jscodex/bundle/commands/auth-login.jscodex/bundle/pre-tool-use.jscodex/bundle/session-start-setup.jscodex/bundle/session-start.jscodex/bundle/shell/deeplake-shell.jscodex/bundle/stop.jscodex/bundle/wiki-worker.jscursor/bundle/capture.jscursor/bundle/commands/auth-login.jscursor/bundle/pre-tool-use.jscursor/bundle/session-end.jscursor/bundle/session-start.jscursor/bundle/shell/deeplake-shell.jscursor/bundle/wiki-worker.jshermes/bundle/capture.jshermes/bundle/commands/auth-login.jshermes/bundle/pre-tool-use.jshermes/bundle/session-end.jshermes/bundle/session-start.jshermes/bundle/shell/deeplake-shell.jshermes/bundle/wiki-worker.jsmcp/bundle/server.jspi/bundle/autopull-worker.jspi/bundle/wiki-worker.jssrc/deeplake-api.tssrc/hooks/capture.tssrc/hooks/codex/capture.tssrc/hooks/codex/session-start-setup.tssrc/hooks/codex/spawn-wiki-worker.tssrc/hooks/codex/stop.tssrc/hooks/codex/wiki-worker.tssrc/hooks/cursor/capture.tssrc/hooks/cursor/session-start.tssrc/hooks/cursor/spawn-wiki-worker.tssrc/hooks/cursor/wiki-worker.tssrc/hooks/hermes/capture.tssrc/hooks/hermes/session-start.tssrc/hooks/hermes/spawn-wiki-worker.tssrc/hooks/hermes/wiki-worker.tssrc/hooks/pi/wiki-worker.tssrc/hooks/session-queue.tssrc/hooks/session-start.tssrc/hooks/spawn-wiki-worker.tssrc/hooks/upload-summary.tssrc/hooks/wiki-worker.ts
| const __bundleDir = dirname(fileURLToPath(import.meta.url)); | ||
| const PLUGIN_VERSION = getInstalledVersion(__bundleDir, ".claude-plugin") ?? ""; |
There was a problem hiding this comment.
Resolve Cursor’s own manifest here.
This hook is still sourcing PLUGIN_VERSION from .claude-plugin, so Cursor session rows can be stamped with the wrong version. That undercuts the new plugin_version tracking on one of the primary write paths.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/hooks/cursor/capture.ts` around lines 44 - 45, PLUGIN_VERSION is being
resolved from ".claude-plugin" which pulls the wrong manifest; update the call
to getInstalledVersion so it resolves Cursor’s own manifest instead (use the
Cursor manifest identifier rather than ".claude-plugin"), keeping __bundleDir
and PLUGIN_VERSION the same so the hook stamps session rows with the correct
plugin_version; modify the getInstalledVersion call invoked where PLUGIN_VERSION
is defined (refer to __bundleDir and getInstalledVersion) to point at the Cursor
manifest.
There was a problem hiding this comment.
Rejecting — .cursor-plugin/plugin.json does not exist in this repo. Cursor doesn't ship its own per-plugin manifest; it has only cursor/bundle/.
getInstalledVersion(__bundleDir, manifestDir) is documented as having three fallback paths (see src/utils/version-check.ts):
<bundle>/../<manifestDir>/plugin.json— only used by claude-code and codex, which DO have manifest dirs in the repo.<bundle>/../.hivemind_version— the actual source of truth for cursor/hermes/pi, written bywriteVersionStamp(PLUGIN_DIR, getVersion())insrc/cli/install-cursor.tsat install time.- Walk-up
package.jsonwith a hivemind name — dev fallback when running--plugin-dir ./cursorfrom the repo (resolves the repo-level@deeplake/hivemindversion).
So the .claude-plugin argument is effectively a no-op for cursor (the first lookup misses, falls through). Both real paths (installed via hivemind cursor install → .hivemind_version stamp, or running from the repo → walk-up package.json) resolve correctly.
Verifiable locally: ls cursor/.claude-plugin cursor/.cursor-plugin — neither exists.
There was a problem hiding this comment.
@efenocchi, thank you for the detailed explanation — that's clear and I was wrong to flag this. The .claude-plugin argument is effectively a no-op for cursor because the first lookup (<bundle>/../.claude-plugin/plugin.json) simply misses, and the function correctly falls through to the .hivemind_version stamp (the install-time source of truth) or the walk-up package.json in dev. No change needed here.
(ᵔ.ᵔ)?
✏️ Learnings added
Learnt from: efenocchi
Repo: activeloopai/hivemind PR: 120
File: src/hooks/cursor/capture.ts:44-45
Timestamp: 2026-05-12T00:24:10.966Z
Learning: In `src/utils/version-check.ts`, `getInstalledVersion(bundleDir, manifestDir)` has three ordered fallback paths:
1. `<bundleDir>/../<manifestDir>/plugin.json` — only meaningful for claude-code and codex, which have actual manifest directories in the repo.
2. `<bundleDir>/../.hivemind_version` — the real source of truth for cursor, hermes, and pi agents; written at install time by `writeVersionStamp` in `src/cli/install-cursor.ts` (and equivalent install scripts).
3. Walk-up `package.json` matching known hivemind package names — dev fallback when running from the repo root.
For cursor/hermes/pi agents, passing `.claude-plugin` as `manifestDir` is a no-op (the first lookup misses), and version resolution correctly falls through to paths 2 or 3. No `.cursor-plugin`, `.hermes-plugin`, or `.pi-plugin` manifest directories exist in the repo.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| // Resolve plugin version once — also stamped on the placeholder row. | ||
| const current = getInstalledVersion(__bundleDir, ".claude-plugin"); | ||
| const pluginVersion = current ?? ""; |
There was a problem hiding this comment.
Use the Cursor manifest for SessionStart as well.
This path also resolves from .claude-plugin, so placeholder rows and the injected version notice can report Claude’s version instead of Cursor’s.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/hooks/cursor/session-start.ts` around lines 155 - 157, Replace the
hard-coded ".claude-plugin" manifest when calling getInstalledVersion so
SessionStart uses the Cursor manifest like the rest of the codebase; update the
call getInstalledVersion(__bundleDir, ".claude-plugin") to pass the same Cursor
manifest identifier used elsewhere (e.g. the constant or string used in other
hooks), and keep assigning pluginVersion = current ?? "" so placeholder rows and
the injected version notice report Cursor’s version instead of Claude’s.
There was a problem hiding this comment.
Rejecting — same reason as the cursor capture comment above. .cursor-plugin/plugin.json does not exist; cursor's installer writes ~/.cursor/hivemind/.hivemind_version at install time and getInstalledVersion reads that via its second fallback. Passing .claude-plugin as the first arg is a no-op because cursor has no manifest dir under that name either.
E2E confirmed: a cursor session in this branch's plugin lands the right hivemind version in plugin_version via the .hivemind_version stamp path.
There was a problem hiding this comment.
@efenocchi, understood — thanks for the detailed explanation. The .claude-plugin argument is a no-op for cursor since that manifest directory doesn't exist in the cursor layout, so getInstalledVersion naturally falls through to the .hivemind_version stamp, which is the correct source. The review comment was mistaken. Acknowledged.
✏️ Learnings added
Learnt from: efenocchi
Repo: activeloopai/hivemind PR: 120
File: src/hooks/cursor/session-start.ts:155-157
Timestamp: 2026-05-12T00:24:22.657Z
Learning: In `src/hooks/cursor/session-start.ts` (and other cursor hooks), `getInstalledVersion(__bundleDir, ".claude-plugin")` is intentional: cursor has no `.claude-plugin` or `.cursor-plugin/plugin.json` manifest, so the first argument is a no-op. The version is resolved via the second fallback — the `.hivemind_version` stamp written by cursor's installer to `~/.cursor/hivemind/.hivemind_version`. The call correctly reports the hivemind version for cursor sessions.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
PR #120 coverage report flagged eight files I touched below the 90 % threshold. Two new test files address the worst gaps directly. spawn-wiki-worker.test.ts — functional tests for the four spawn helpers (claude-code, codex, cursor, hermes). Each asserts the spawn-config JSON written to disk contains the resolved `pluginVersion`, plus the agent-specific config fields (claudeBin / codexBin / cursorBin / cursorModel / hermesBin / hermesProvider / hermesModel). Mocks the child_process.spawn boundary so no subprocess actually launches. Coverage impact for the four spawn-* variants: - src/hooks/spawn-wiki-worker.ts : 0 % → 95 % - src/hooks/codex/spawn-wiki-worker.ts : 0 % → 90 % - src/hooks/cursor/spawn-wiki-worker.ts : 30 % → 95 % - src/hooks/hermes/spawn-wiki-worker.ts : 95 % → 95 % (branches +) wiki-worker-plugin-version.test.ts — parametrized across the three sibling workers (cursor/hermes/pi). Each variant gets exercised through main() with mocks at the upload-summary / execFileSync / fetch boundaries, hitting four scenarios: 1. cfg.pluginVersion → uploadSummary params.pluginVersion (happy path) 2. cfg without pluginVersion → "" fallback (legacy spawner compat) 3. LLM-spawn failure → no upload, releaseLock still fires 4. Empty summary file → no upload, releaseLock still fires 5. No session events → early exit, no LLM spawn Coverage impact for the three worker variants: - src/hooks/cursor/wiki-worker.ts : 0 % → 78 % lines (54 % branches) - src/hooks/hermes/wiki-worker.ts : 0 % → 78 % lines (54 % branches) - src/hooks/pi/wiki-worker.ts : 0 % → 78 % lines (54 % branches) The remaining ~22 % uncovered in those files is the embed-daemon path-resolution branch and the resume-from-existing-summary branch, neither of which is touched by this PR. They're left for a follow-up. cursor/capture.ts and hermes/capture.ts also crossed the 90 % bar as a side effect of vi.resetModules() in the new file forcing a fresh hook trace through their module-level constants. Total: +24 tests, 2206 → 2230 passing.
…nches Follow-up to the previous coverage push. The cursor/hermes/pi worker files were at 78 % lines / 54 % branches — the remaining gap was three specific branches we hadn't exercised: 1. **Resume path** (lines 150–155): worker reads an existing summary row and parses `**JSONL offset**: N` for the LLM prompt. 2. **Embeddings disabled**: `HIVEMIND_EMBEDDINGS=false` skips the daemon hop; uploadSummary still fires with embedding === null. 3. **API retry** (lines 89–100): each worker has its own inline retry loop for transient 401/403/429/5xx. setTimeout is stubbed to advance the exponential-backoff windows without burning real wall-clock. Each scenario is parametrized across cursor / hermes / pi so a regression on one variant doesn't slip past. Coverage impact: - src/hooks/cursor/wiki-worker.ts : 78 % → 92.6 % lines, 54 % → 87.2 % branches - src/hooks/hermes/wiki-worker.ts : 78 % → 92.6 % lines, 54 % → 87.2 % branches - src/hooks/pi/wiki-worker.ts : 78 % → 92.5 % lines, 54 % → 87.2 % branches All three now over the 90 % line threshold flagged in the PR coverage report. +9 tests, 2230 → 2239 passing.
… gap Three changes, two driven by CodeRabbit review on PR #120: 1. **upload-summary UPDATE no longer overwrites plugin_version with '' when caller omits the field** (CodeRabbit comment #5). Previously `pluginVersion ?? ""` collapsed undefined to "", and the UPDATE always wrote the column. A future caller that doesn't pass `pluginVersion` would silently erase a previously-stored real version. Fix: keep the column OUT of the SET clause when pluginVersion is undefined; INSERT still defaults to '' to match the column DEFAULT. Passing an explicit "" still clears the value (escape hatch). New regression test in upload-summary.test.ts asserts both branches and the negative pattern. 2. **plugin-version-resolution regex widened to accept full SemVer 2.0** (CodeRabbit comment #1). The original `/^\d+\.\d+\.\d+$/` rejected prerelease and build metadata strings like `0.8.0-rc.1` or `1.2.3+exp.sha.5114f85`. We don't ship those today but a future RC tag shouldn't silently fail this guard. 3. **pi/extension-source/hivemind.ts now stamps plugin_version on its capture INSERT and threads it into the wiki-worker spawn config.** Pi runs a TS extension instead of compiled hook bundles, so the shared `src/hooks/capture.ts` change didn't propagate. Without this fix, pi sessions would land rows with `plugin_version = ''` even on tables migrated by other agents. The extension reads the version stamp at `~/.pi/agent/.hivemind/.hivemind_version` (written by `hivemind pi install`); if the stamp is missing, falls back to '', which matches the column DEFAULT — schema-compatible. CodeRabbit comments #2, #3, #4 (cursor/hermes "use own manifest") are not addressed because the referenced manifest directories (.cursor-plugin / .hermes-plugin) do not exist in the repo. Cursor / Hermes / Pi rely on the fallback chain inside getInstalledVersion: `<bundle>/../.hivemind_version` (written by their installers) → walk-up package.json (in-repo dev). Both paths resolve to the right version today; passing `.claude-plugin` as the first arg is a no-op for these agents. 2244 tests pass.
Response to CodeRabbit reviewAddressed the actionable comments in 3c8356e + 2daea7f. Summary: ✅ Accepted#1 — Semver regex too strict ( #5 — upload-summary UPDATE overwrites stored plugin_version with ❌ Rejected (CodeRabbit hallucination)#2, #3, #4 — "resolve Cursor/Hermes from its own manifest". These comments claim there's a For cursor/hermes/pi, the version resolves through Verifiable: Bonus fix (discovered while preparing e2e)Pi runs a TS extension ( 2244 tests pass. Coverage report on the next CI run. |
openclaw was the only agent whose capture path still wrote the sessions row without `plugin_version` — the PR added the column to every other agent (claude-code, codex, cursor, hermes, pi) but missed openclaw's INSERT at openclaw/src/index.ts:1130. Production rows from openclaw landed with an empty `plugin_version` while all other agents stamped the real installed version. This patch: - adds `plugin_version` to the INSERT column list - threads `getInstalledVersion()` (already wired via the esbuild `__HIVEMIND_VERSION__` define) through the VALUES clause - extends the bundle-level regex guard in claude-code/tests/plugin-version-resolution.test.ts to also scan openclaw/dist/index.js, so a future build that drops the column fails the test instead of regressing silently Live e2e against the test_plugin/default tables is blocked right now by a backend outage on api.deeplake.ai (plain `SELECT 1` returns HTTP 000 with a 10s timeout). Bundle test passes; the runtime behavior matches the codex/cursor/hermes pattern verified earlier during this PR.
|
Follow-up: openclaw plugin_version fix (29663d7) Caught a gap during a final e2e sweep across all agents — openclaw's session-capture INSERT at
E2E status note: live verification against |
|
Correction on the backend-status claim Pushed back on my own previous comment after the user rightly asked "is it a backend issue?" — re-probed and it isn't:
Re-ran the openclaw live e2e against PASS — openclaw is now verified live against the test table, same as the other 4 agents. The 10s timeouts and |
|
Update: openclaw real-e2e ✅ + main merged openclaw end-to-end finalmente verificato in produzioneDopo aver tracciato i log di openclaw runtime, trovate due gate di security non documentate che bloccavano
Una volta sistemate entrambe nel mio Scoreboard finale 6/6
Merge da main
Pronto per merge. |
The per-agent matrix and step-by-step release checklist have been superseded by the end-to-end verification flow validated in PR #120 (real e2e against every shipped agent — claude_code, codex, cursor, hermes, pi, openclaw — including openclaw via Telegram). The file was a snapshot of pre-release manual steps that no longer reflects the current process and was last edited before the openclaw security gates (plugins.allow + plugins.entries.hivemind.hooks.allowConversationAccess) were documented.
# Conflicts: # RELEASE_CHECKLIST.md # tests/claude-code/plugin-version-resolution.test.ts # tests/claude-code/spawn-wiki-worker.test.ts # tests/claude-code/wiki-worker-plugin-version.test.ts
Summary
plugin_version TEXT NOT NULL DEFAULT ''column to bothmemoryandsessionstables, idempotently migrated on first contact viaensureColumn.plugin-version-resolution.test.ts) verifies each shipped bundle's on-disk layout resolves a non-empty semver and that every capture / session-start INSERT actually listsplugin_versionin its column list.Why
Today, rows landing in
memory/sessionshave no record of which plugin version wrote them. This blocks (a) telemetry that needs to attribute regressions to a specific release, (b) backend-side compatibility decisions when a schema or write pattern changes across versions, and (c) debugging cases where stale plugins keep writing alongside upgraded ones.Schema and migration
Both tables get one new column:
CREATE TABLEincludes the column, no extra round-trip.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''")performs the same SELECT-then-ALTER flow already used foragent(added 2026-04-11). Marker-store TTL caches the result so steady-state startup pays nothing.''from the DEFAULT. Verified end-to-end (see below).The race-tolerated "Column already exists" path is preserved: if SELECT reports missing but ALTER fails with
already exists, re-SELECT confirms the column landed before declaring success. All other ALTER failures propagate.Wire-through
Each call site resolves the version through
getInstalledVersion(__bundleDir, manifestDir):const PLUGIN_VERSION = ...main()createPlaceholder()codex/stop.tspluginVersioninto spawn-config JSONcfg.pluginVersionupload-summary.tsUploadParams.pluginVersionsummaryto preserve the single-UPDATE invariant (Deeplake silently drops one of two rapid UPDATEs on the same row)session-queue.ts(currently unused, tested)buildQueuedSessionRow({pluginVersion})buildSessionInsertSqlTests
npm test→ 113 files, 2206 tests pass.New / updated coverage:
schema-scenarios.test.ts— extended to cover all 7 table-state combinations × the newplugin_versionmigration (greenfield, full legacy, half-legacy mem, half-legacy sessions, fully migrated, mixed).deeplake-api.test.ts—ensureTable/ensureSessionsTabletests updated to assert the extra SELECT info_schema + (when missing) ALTER forplugin_version.plugin-version-resolution.test.ts(new):getInstalledVersionagainst every agent's actual shipped bundle layout, asserts non-null semver.capture.js/stop.js/session-start*.jsconfirmsplugin_versionis in the INSERT column list. Mirrors the spirit ofwiki-worker-upload-sql.test.ts.End-to-end verification
Ran a real session against
test_plugin/default(the sandbox per CLAUDE.md), with the memory + sessions tables already populated and without the new column:''plugin_version="0.7.17"plugin_version="0.7.17"''via DEFAULT — no error, no regressionTest plan
test_pluginworkspace, confirm CREATE includes the columnplugin_version), confirm ALTER ADD COLUMN fires once then markers cacheplugin_version=''and the new plugin writes the actual versionOut of scope (filed as side findings)
codex/.codex-plugin/plugin.jsonis stuck at 0.6.7 while the repo is 0.7.18. The version-bump scripts appear to only touchpackage.jsonandclaude-code/.claude-plugin/plugin.json. The per-agent test in this PR asserts non-empty semver rather than strict equality so this pre-existing release-process bug doesn't block the change — but it should be fixed separately.Summary by CodeRabbit
Release Notes
New Features
Tests