Skip to content

fix(orchestrator): version-drift + path-hashing leading-dash strip in mcp/server.ts#10

Open
evannadeau wants to merge 1 commit into
SpawnBox-dev:mainfrom
evannadeau:fix/mcp-server-version-drift-and-path-hashing
Open

fix(orchestrator): version-drift + path-hashing leading-dash strip in mcp/server.ts#10
evannadeau wants to merge 1 commit into
SpawnBox-dev:mainfrom
evannadeau:fix/mcp-server-version-drift-and-path-hashing

Conversation

@evannadeau
Copy link
Copy Markdown

Summary

Two trivial bug fixes in plugins/orchestrator/mcp/server.ts, plus rebuilt dist/server.js. Independent fixes, bundled because they touch the same file and have minimal blast radius.

Fix 1 — Version-drift (mcp/server.ts:655)

system_status reports a hardcoded 0.30.28 instead of the dynamically-read ${PLUGIN_VERSION}. The 0.30.31 consolidation that introduced PLUGIN_VERSION (read from package.json at module load, lines 32-37) explicitly aimed to fix this kind of drift — "the version isn't hardcoded in multiple spots and forgotten on every other version bump" per the comment — but missed this one site. Every release since (0.30.32 → 0.30.38) has shipped system_status returning 0.30.28 while the manifest, package.json, and startup banner all correctly show the current version.

Before:

lines.push(`- **Version**: orchestrator MCP server **0.30.28** (pid ${process.pid})`);

After:

lines.push(`- **Version**: orchestrator MCP server **${PLUGIN_VERSION}** (pid ${process.pid})`);

Cosmetic to user-visible, but operators reading system_status to confirm they're on the expected version are currently misled.

Fix 2 — Path-hashing leading-dash strip (mcp/server.ts:2184)

projectHash strips leading dashes after replacing path separators:

const projectHash = projectDir.replace(/[\\/:]/g, "-").replace(/^-+/, "");

On POSIX, every absolute path produces a leading dash after the replace(/[\\/:]/g, "-") (e.g., /home/user/project-home-user-project). The subsequent .replace(/^-+/, "") strips it, yielding home-user-project. But Claude Code preserves the leading dash when it names project directories: the actual directory created at ~/.claude/projects/ is -home-user-project, with the dash.

The runtime consequence: projectsHashDir resolves to a non-existent directory. listJsonlFiles() reads readdirSync(projectsHashDir) and the existsSync() guard returns false, yielding []. The agent-channel filewatcher processes zero JSONL files, never writes offsets, never parses @SA-<id8> channel addresses, and never emits routing notifications. Every multi-paragraph PA→SA dispatch on POSIX silently drops until an operator notices and symlinks the stripped path to the real one as a workaround.

Other agent-channel paths (stateDir = join(projectDir, ".orchestrator-state") for sessions.json, system_events DB, etc.) are correctly relative to cwd — only projectsHashDir, which has to match CC's directory naming, is affected.

Before:

const projectHash = projectDir.replace(/[\\/:]/g, "-").replace(/^-+/, "");

After:

// Hash the project directory the same way Claude Code does: replace path separators
// and colons with dashes, preserving any leading dash (POSIX absolute paths produce
// a leading "-home-..." form, and CC keeps the leading dash in ~/.claude/projects/).
const projectHash = projectDir.replace(/[\\/:]/g, "-");

The original strip was over-correction for the Windows drive-letter case (C:\fooC--foo) — a cosmetic double-dash, not broken routing. Removing the strip is harmless on Windows; on POSIX it's load-bearing.

Diagnostic recipe for anyone reproducing the bug:

  1. ls <project>/.orchestrator-state/agent-channel/ — absence of offsets-<id8>.json files means JSONL is not being processed.
  2. grep -c '<channel source' <project_jsonl_dir>/<sa_session_id>.jsonl — zero means no events were ever delivered to that SA.
  3. Compare paths: actual ls ~/.claude/projects/ shows -home-... with leading dash; plugin-computed projectsHashDir (instrumented via process.env.DEBUG=1 or similar) shows home-... without it.

Test plan

  • bun install (98 packages, clean).
  • bun run build against the updated source → dist/server.js 0.94 MB, 249 modules bundled. Output regenerated to match source (no stale dist).
  • bun test516 pass / 0 fail / 1207 expect() calls across 38 test files.
  • Verified PLUGIN_VERSION resolves correctly at module load (already in use at server.ts:464 for version: PLUGIN_VERSION in the MCP serverInfo and at server.ts:2900 for the startup banner — same identifier).
  • Re-read both diffs against cb40771 (current main) — line numbers verified directly against main source, not against stale notes.
  • Reviewer may want to add an explicit unit test for projectHash against POSIX vs Windows inputs — happy to follow up if requested.

Files changed

  • plugins/orchestrator/mcp/server.ts — 2 surgical edits (1 line for version, 1 line + 3 comment lines for path-hashing)
  • plugins/orchestrator/dist/server.js — regenerated via bun run build (only 4 lines changed, bundler-deterministic)

Out of scope

Two larger upstream candidates still queued from the same operator's KB:

  • Agent-channel filewatcher silently truncating PA-to-SA multi-paragraph assistant_text events at the first colon-after-blank-line — substantive change to the truncation logic, separate PR.
  • Concurrent stop-hook deadlock between parallel Claude Code sessions against the orchestrator SQLite DB — architectural fix, separate PR.

Both will ship after maintainer review of the trivial-fix bundle here.

Related

🤖 Generated with Claude Code (Admiral PA orchestrating an upstream-cleanup batch)

… mcp/server.ts

Two trivial fixes in mcp/server.ts plus rebuilt dist/server.js (bun build,
249 modules; 516/516 tests pass).

1. Version-drift (mcp/server.ts:655)
   `system_status` reported a hardcoded `0.30.28` instead of the
   dynamically-read `${PLUGIN_VERSION}` (set from package.json at module
   load, lines 32-37). The 0.30.31 consolidation that introduced
   PLUGIN_VERSION explicitly aimed to fix this kind of drift but missed
   this one site. Symptom: every release since (0.30.32 → 0.30.38) has
   shipped `system_status` reporting `0.30.28`.

2. Path-hashing leading-dash strip (mcp/server.ts:2184)
   `projectHash` stripped leading dashes after replacing path separators:
       projectDir.replace(/[\\/:]/g, "-").replace(/^-+/, "");
   On POSIX, every absolute path produces a leading dash after the
   replace, which the strip then removes. But Claude Code preserves the
   leading dash when it names project directories under
   `~/.claude/projects/-<...>`. The mismatch silently breaks the
   agent-channel filewatcher: `projectsHashDir` resolves to a
   non-existent directory, `listJsonlFiles()` returns `[]`, and every
   `@SA-<id8>` channel address gets silently dropped.

   Symlinking the stripped name to the real one restores routing (validated
   workaround); the proper fix is to stop stripping.

   On Windows, the prior leading-dash strip was over-correction for the
   drive-letter case (`C:\foo` → `C--foo`) — a cosmetic double-dash, not
   broken routing. Removing the strip is harmless there.

dist/server.js regenerated via `bun run build` against the same source
to guarantee the bundled artifact matches the source (per the long-stale
PR rebuild-after-rebase rule).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant