Skip to content

refactor(dir-config): single source of truth for workspace routing#318

Merged
khustup2 merged 1 commit into
mainfrom
fix/dir-config-single-source
Jul 17, 2026
Merged

refactor(dir-config): single source of truth for workspace routing#318
khustup2 merged 1 commit into
mainfrom
fix/dir-config-single-source

Conversation

@khustup2

@khustup2 khustup2 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

The problem, stated once

.hivemind routing kept breaking because it was wired writer-by-writer. Every code path that built a DeeplakeApi from a raw loadConfig() silently used the global workspace instead of the directory's routed one. We shipped it, then found the gap in reads (#316), then skillify (#317), and the audit found the same latent bug in goals, rules, and the Codex/Cursor/Hermes memory hooks. Patching per-report is a treadmill — the next unrouted writer is always one report away.

This makes the whole class impossible to reintroduce.

The fix: one entry point + a guard

loadRoutedConfig(cwd) in src/dir-config.ts is now the single source of truth. It folds the nearest .hivemind (and the HIVEMIND_* env locks, via resolveDirConfig) in one place, and returns Config | null — a drop-in for loadConfig(). Every workspace-touching site now goes through it:

  • CLI writers/readers: goal, rules, context, skillify (push/pull), flush-memory
  • Other-agent memory hooks: codex / cursor / hermes pre-tool-use (only Claude's was routed before — the multi-agent product routed for exactly one agent)
  • session-start-setup, skillify auto-pull, skillopt-worker
  • mcp/server, graph deeplake-pull (symmetric with the already-routed push)

The guardtests/shared/dir-config-single-source.test.ts — enumerates every new DeeplakeApi( site and fails the build unless it routes or is explicitly allow-listed with a reason. Adding a new construction site now forces a choice: route it, or justify why not. A silent unrouted writer can't merge. (Verified: reverting any migrated file to raw loadConfig trips it.)

Five sites are allow-listed with documented reasons: session-prune (account-level), docs (separate per-(org,repo) consent model), cowork-ingest (desktop, no directory context), and the two creds-based display reads resume-brief / open-goals.

Supersedes #317

This folds in the skillify-only fix. Close #317 in favor of this.

Deliberately not routed (tracked follow-up)

resume-brief and open-goals build from creds.workspaceId, not loadConfig — routing them means threading a resolved workspace into fetchOpenGoals, a different change from a loadConfig swap. They read/display global data (low severity), and they're allow-listed so the guard stays honest about it. docs is a separate routing question tied to its consent model.

Verification

  • Behavioral: skillify-dir-routing.test.ts proves loadRoutedConfig routes end-to-end (asserts the workspace the DeeplakeApi is built against — the URL path where writes land). Fails against unrouted code.
  • Guard: catches a reverted file with an actionable message.
  • Typecheck clean; no new suite failures (the 14 pre-existing are the tree-sitter-python graph suites + the flush-memory network test, all green in CI); touched files clear their per-file coverage gates (auto-pull branch coverage raised with a default-path test).

What DevOps sees after this

.hivemind routes consistently across sessions, skills, goals, rules, and every agent — one identity, reads and writes alike. collect:false still gates capture only.

🤖 Generated with Claude Code

https://claude.ai/code/session_017SDmiDuUn7Wqhi7qdn1p1f

Summary by CodeRabbit

  • Bug Fixes
    • Commands and integrations now automatically use the workspace-specific configuration found in the nearest .hivemind directory.
    • Improved reliability for context, memory, goals, rules, skills, snapshots, hooks, and MCP operations when working across multiple workspaces.
    • Skill synchronization now correctly targets the active workspace instead of falling back to a global configuration.
    • Added coverage to verify configuration routing, including nested workspace directories and fallback behavior.

`.hivemind` routing was wired writer-by-writer, so every new path that built
a DeeplakeApi from a raw loadConfig() silently used the GLOBAL workspace
instead of the directory's routed one. Skillify was the latest to bite
(DevOps: "saved a skill, it landed in the default workspace"); goals, rules,
and the Codex/Cursor/Hermes memory hooks had the identical latent bug.

Introduce ONE entry point — loadRoutedConfig(cwd) in dir-config.ts — that
folds the nearest .hivemind (and the HIVEMIND_* env locks, via
resolveDirConfig) in one place, and migrate every workspace-touching site to
it:

  - CLI writers/readers: goal, rules, context, skillify (push/pull),
    flush-memory
  - other-agent memory hooks: codex/cursor/hermes pre-tool-use
  - session-start-setup, skillify auto-pull + skillopt-worker
  - mcp/server, graph deeplake-pull (symmetric with the already-routed push)

A guard test (dir-config-single-source.test.ts) enumerates every
`new DeeplakeApi(` site and fails the build unless it routes or is
allow-listed with a reason — so an unrouted writer can no longer be added
silently. Five sites are allow-listed (session-prune, docs, cowork-ingest,
and the creds-based banner/resume-brief reads) each with a documented reason.

This supersedes and folds in PR #317 (skillify-only fix).

Deliberately NOT routed, tracked as follow-up: the notification reads
(resume-brief, open-goals) build from creds.workspaceId and need a resolved
workspace threaded in — a different change from a loadConfig swap; and docs,
which use a separate per-(org,repo) consent model.

Verified: loadRoutedConfig routes end-to-end (skillify push/pull behavioral
test, fails against unrouted code); the guard catches a reverted file;
typecheck clean; no new suite failures; touched files clear their coverage
gates.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0ee61b50-c75d-4440-b30c-ba05d42f97e9

📥 Commits

Reviewing files that changed from the base of the PR and between 0f1d412 and 01b829a.

📒 Files selected for processing (17)
  • src/commands/context.ts
  • src/commands/flush-memory.ts
  • src/commands/goal.ts
  • src/commands/rules.ts
  • src/commands/skillify.ts
  • src/dir-config.ts
  • src/graph/deeplake-pull.ts
  • src/hooks/codex/pre-tool-use.ts
  • src/hooks/cursor/pre-tool-use.ts
  • src/hooks/hermes/pre-tool-use.ts
  • src/hooks/session-start-setup.ts
  • src/mcp/server.ts
  • src/skillify/auto-pull.ts
  • src/skillify/skillopt-worker.ts
  • tests/claude-code/skillify-auto-pull.test.ts
  • tests/claude-code/skillify-dir-routing.test.ts
  • tests/shared/dir-config-single-source.test.ts
 __________________________________________________________
< My fitness tracker wants me to tackle 10,000 bugs a day. >
 ----------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dir-config-single-source

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Scope: files changed in this PR. Enforced threshold: 90% per metric (per file via vitest.config.ts).

Status Category Percentage Covered / Total
🟢 Lines 91.39% (🎯 90%) 1030 / 1127
🟢 Statements 90.01% (🎯 90%) 1180 / 1311
🔴 Functions 89.47% (🎯 90%) 119 / 133
🔴 Branches 85.04% (🎯 90%) 790 / 929
File Coverage — 14 files changed
File Stmts Branches Functions Lines
src/commands/context.ts 🟢 100.0% 🟢 100.0% 🟢 100.0% 🟢 100.0%
src/commands/flush-memory.ts 🟢 95.7% 🟢 92.0% 🟢 100.0% 🟢 97.7%
src/commands/goal.ts 🟢 96.2% 🟢 92.5% 🟢 100.0% 🟢 98.8%
src/commands/rules.ts 🟢 96.1% 🟢 95.8% 🟢 100.0% 🟢 96.6%
src/commands/skillify.ts 🟢 90.3% 🔴 82.6% 🟢 96.3% 🔴 88.5%
src/dir-config.ts 🟢 100.0% 🟢 100.0% 🟢 100.0% 🟢 100.0%
src/graph/deeplake-pull.ts 🔴 82.5% 🔴 71.0% 🔴 80.0% 🔴 84.0%
src/hooks/codex/pre-tool-use.ts 🟢 99.5% 🟢 91.1% 🟢 100.0% 🟢 99.4%
src/hooks/cursor/pre-tool-use.ts 🔴 88.0% 🟢 92.8% 🟢 100.0% 🔴 85.7%
src/hooks/hermes/pre-tool-use.ts 🟢 92.1% 🔴 88.5% 🟢 100.0% 🟢 90.0%
src/hooks/session-start-setup.ts 🟢 100.0% 🟢 90.9% 🟢 100.0% 🟢 100.0%
src/mcp/server.ts 🟢 91.3% 🔴 87.5% 🔴 85.7% 🟢 94.3%
src/skillify/auto-pull.ts 🟢 97.5% 🔴 77.8% 🟢 90.9% 🟢 100.0%
src/skillify/skillopt-worker.ts 🔴 0.0% 🔴 0.0% 🔴 0.0% 🔴 0.0%

Generated for commit 4a50229.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/claude-code/skillify-auto-pull.test.ts (1)

297-298: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert on the specific success state.

Instead of a negative assertion that the pull didn't fail due to being "not-logged-in", explicitly assert that the pull was successful. This ensures the test fails if it errors out for another reason (e.g., returning "error"). As per path instructions, prefer asserting on specific values.

♻️ Proposed fix
-    expect(result.reason).not.toBe("not-logged-in");
-    expect(calls.length).toBeGreaterThan(0);
+    expect(result.skipped).toBe(false);
+    expect(calls.length).toBeGreaterThan(0);
🤖 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 `@tests/claude-code/skillify-auto-pull.test.ts` around lines 297 - 298, Update
the assertions in the auto-pull test to check that result.reason equals the
expected successful state instead of only asserting it is not "not-logged-in";
retain the existing calls.length assertion.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@tests/claude-code/skillify-auto-pull.test.ts`:
- Around line 297-298: Update the assertions in the auto-pull test to check that
result.reason equals the expected successful state instead of only asserting it
is not "not-logged-in"; retain the existing calls.length assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0ee61b50-c75d-4440-b30c-ba05d42f97e9

📥 Commits

Reviewing files that changed from the base of the PR and between 0f1d412 and 01b829a.

📒 Files selected for processing (17)
  • src/commands/context.ts
  • src/commands/flush-memory.ts
  • src/commands/goal.ts
  • src/commands/rules.ts
  • src/commands/skillify.ts
  • src/dir-config.ts
  • src/graph/deeplake-pull.ts
  • src/hooks/codex/pre-tool-use.ts
  • src/hooks/cursor/pre-tool-use.ts
  • src/hooks/hermes/pre-tool-use.ts
  • src/hooks/session-start-setup.ts
  • src/mcp/server.ts
  • src/skillify/auto-pull.ts
  • src/skillify/skillopt-worker.ts
  • tests/claude-code/skillify-auto-pull.test.ts
  • tests/claude-code/skillify-dir-routing.test.ts
  • tests/shared/dir-config-single-source.test.ts

@khustup2
khustup2 merged commit dd080b7 into main Jul 17, 2026
11 checks passed
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