refactor(dir-config): single source of truth for workspace routing#318
Conversation
`.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.
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (17)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Coverage ReportScope: files changed in this PR. Enforced threshold: 90% per metric (per file via
File Coverage — 14 files changed
Generated for commit 4a50229. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/claude-code/skillify-auto-pull.test.ts (1)
297-298: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAssert 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
📒 Files selected for processing (17)
src/commands/context.tssrc/commands/flush-memory.tssrc/commands/goal.tssrc/commands/rules.tssrc/commands/skillify.tssrc/dir-config.tssrc/graph/deeplake-pull.tssrc/hooks/codex/pre-tool-use.tssrc/hooks/cursor/pre-tool-use.tssrc/hooks/hermes/pre-tool-use.tssrc/hooks/session-start-setup.tssrc/mcp/server.tssrc/skillify/auto-pull.tssrc/skillify/skillopt-worker.tstests/claude-code/skillify-auto-pull.test.tstests/claude-code/skillify-dir-routing.test.tstests/shared/dir-config-single-source.test.ts
The problem, stated once
.hivemindrouting kept breaking because it was wired writer-by-writer. Every code path that built aDeeplakeApifrom a rawloadConfig()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)insrc/dir-config.tsis now the single source of truth. It folds the nearest.hivemind(and theHIVEMIND_*env locks, viaresolveDirConfig) in one place, and returnsConfig | null— a drop-in forloadConfig(). Every workspace-touching site now goes through it:goal,rules,context,skillify(push/pull),flush-memorycodex/cursor/hermespre-tool-use(only Claude's was routed before — the multi-agent product routed for exactly one agent)The guard —
tests/shared/dir-config-single-source.test.ts— enumerates everynew 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 rawloadConfigtrips 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 readsresume-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-briefandopen-goalsbuild fromcreds.workspaceId, notloadConfig— routing them means threading a resolved workspace intofetchOpenGoals, a different change from aloadConfigswap. They read/display global data (low severity), and they're allow-listed so the guard stays honest about it.docsis a separate routing question tied to its consent model.Verification
skillify-dir-routing.test.tsprovesloadRoutedConfigroutes end-to-end (asserts the workspace theDeeplakeApiis built against — the URL path where writes land). Fails against unrouted code.tree-sitter-pythongraph suites + theflush-memorynetwork 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
.hivemindroutes consistently across sessions, skills, goals, rules, and every agent — one identity, reads and writes alike.collect:falsestill gates capture only.🤖 Generated with Claude Code
https://claude.ai/code/session_017SDmiDuUn7Wqhi7qdn1p1f
Summary by CodeRabbit
.hiveminddirectory.