feat(docs): branch-aware and visibility-scoped wiki docs#303
Conversation
📝 WalkthroughWalkthroughDocumentation now supports branch-scoped reads and writes, source-fingerprint freshness tracking, private branch storage, overlay promotion after merges, stale-page indicators, and CLI/VFS scope propagation. ChangesBranch-scoped documentation
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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 9429c03. |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
src/docs/wiki-refresh.ts (1)
427-455: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win
pendingPublishis incremented even when nothing was written privately.
updateWikiPagereturns"no_change"or"failed"via early returns that never reach theprivateSinkcall, yetif (priv) pendingPublish++;here fires regardless ofout.action. For"failed"this is harmless (failures already forceincomplete), but for"no_change"it needlessly marks the cycle as having pending work, which stalls cursor advancement and causes this page to be re-diffed and re-run through the LLM on every subsequent cycle for as long as the branch stays unpushed — even though nothing about it ever changes.Consider only incrementing
pendingPublishwhenout.actionis"patched"or"mechanics_refreshed"(the cases whereprivateSinkactually wrote).🤖 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/docs/wiki-refresh.ts` around lines 427 - 455, Only increment pendingPublish in the priv branch when updateWikiPage returns an action indicating a private write occurred: "patched" or "mechanics_refreshed". Use the returned out.action from updateWikiPage, while preserving the existing branchName cleanup behavior.src/commands/docs.ts (1)
864-876: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
docs wikishould block dirty, detached, or unpushed checkouts.generateWikiPageswrites directly to Deeplake withscope: currentScope(defaultGit(cwd)), so this manual command can publish docs for uncommitted or unpushed code without the safeguardswiki-refreshapplies. If manual generation is meant to be allowed, document that explicitly; otherwise add the same hold check here.🤖 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/commands/docs.ts` around lines 864 - 876, The docs wiki command currently invokes generateWikiPages without validating repository state, allowing dirty, detached, or unpushed checkouts to publish documentation. Before the generateWikiPages call in the docs wiki handler, invoke the same hold-check logic used by wiki-refresh, using the existing cwd/defaultGit context, and abort with the matching error behavior when the checkout is unsafe.src/docs/write.ts (1)
217-232: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winLanded-check scope filter doesn't mirror insertDoc's own scope default.
insertDocdefaults an omittedscopeto"main"when writing the row (Line 164:input.scope ?? "main"), but the resilient wrapper's post-timeout "landed" check passesscope: input.scope(Line 227) unmodified — so when scope is omitted, noscopefilter is applied ingetDocLatest/buildProjectFilter(read.tsonly adds the clauseif (opts.scope !== undefined)). If a branch overlay for the same(project, doc_id)already exists (a scenario this PR explicitly supports coexisting), the check can match that overlay's row instead of the just-inserted main row, silently returning the wrong version/content and masking whether the intended write actually landed.🐛 Proposed fix
- const landed = await getDocLatest(query, tableName, input.doc_id, { project: input.project, scope: input.scope }).catch(() => null); + const landed = await getDocLatest(query, tableName, input.doc_id, { project: input.project, scope: input.scope ?? "main" }).catch(() => null);🤖 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/docs/write.ts` around lines 217 - 232, Ensure the landed-check in the resilient insert flow uses the same scope default as insertDoc: pass scope: input.scope ?? "main" to getDocLatest within the retry loop. Update the call associated with insertDocResilient so omitted scopes cannot match an overlay row.src/docs/read.ts (1)
174-204: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winInconsistent dedup-key delimiter can silently drop docs in reader-scoped
listDocs.Line 182 builds the per-key dedup map using a NUL (
\u0000) delimiter specifically to avoid ambiguity between project/doc_id boundaries. The scoped-winner write-back at Line 202 instead joins with a plain space, reintroducing exactly the collision the NUL delimiter was chosen to prevent — two distinct(project, doc_id)pairs whose concatenation with a space is identical (plausible since paths with spaces are explicitly supported elsewhere, e.g.tests/shared/docs-fingerprint.test.tstests spaces in paths) will overwrite each other inlatest, silently dropping a document from the branch-scoped listing.🐛 Proposed fix
if (scoped) { for (const [, list] of candidates) { const winner = pickByScopePrecedence(list, opts.readerScope!); - if (winner) latest.set(`${winner.project} ${winner.doc_id}`, winner); + if (winner) latest.set(`${winner.project}\u0000${winner.doc_id}`, winner); } }🤖 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/docs/read.ts` around lines 174 - 204, Use the same NUL delimiter consistently for scoped winner write-back: update the key construction in the scoped loop of listDocs to `${winner.project}\u0000${winner.doc_id}`, matching the key used when populating candidates and preventing collisions between distinct project/doc_id pairs.src/hooks/pre-tool-use.ts (1)
462-483: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winThread
readerScope/gitthrough the/docslisting path
src/hooks/pre-tool-use.ts:475-483still callshandleDocsVfsFnwith միայնproject, so/docslistings can fall back to scope-agnostic latest-version resolution and surface a foreign branch overlay. Pass the samereaderScope/gitoptions used by the direct-read branch here too.🤖 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/pre-tool-use.ts` around lines 462 - 483, The /docs listing branch omits readerScope and git context, allowing scope-agnostic document resolution. In the lsDir === "/docs" or "/docs/" block, create docsCwd and docsGit consistently with the direct-read branch and pass readerScope: currentScope(docsGit) and git: docsGit alongside project to handleDocsVfsFn.
🧹 Nitpick comments (1)
tests/shared/docs-wiki-refresh.test.ts (1)
186-208: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the
heldreason message, not just the action.The sibling "dirty working tree" test asserts the full
outcomesarray includingreasons. This test only checksaction === "held", leaving the specific reason message unverified.As per path instructions, "Prefer asserting on specific values (paths, messages) over generic substrings."🔧 Suggested tightening
- expect(report.outcomes[0].action).toBe("held"); + expect(report.outcomes[0]).toEqual({ + doc_id: "wiki/pkg/core", + action: "held", + reasons: ["private page needs regeneration — publishes on push"], + });🤖 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/shared/docs-wiki-refresh.test.ts` around lines 186 - 208, Strengthen the test for the held private page by asserting the specific reason message in report.outcomes, alongside the existing action assertion. Update the expectations in the test case “private page with no usable diff is HELD, never regenerated to the cloud (codex r3 leak)” to verify the full outcomes details, matching the precise held reason used by the refresh cycle.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.
Inline comments:
In `@src/docs/vfs-handler.ts`:
- Around line 121-143: Update the private-document guard in the VFS document
lookup to treat an empty-string project as valid, while still excluding missing
projects and MAIN_SCOPE. Use an explicit undefined/null check for opts.project
alongside the existing readerScope condition before calling readPrivateDoc.
In `@src/docs/wiki-refresh.ts`:
- Around line 323-344: Compute the detached state before the promotion logic,
then update the trunk promotion condition to require both branchName === null
and !detached. Use the existing detached variable and promoteMergedOverlays
block so detached HEADs cannot promote overlays into main.
---
Outside diff comments:
In `@src/commands/docs.ts`:
- Around line 864-876: The docs wiki command currently invokes generateWikiPages
without validating repository state, allowing dirty, detached, or unpushed
checkouts to publish documentation. Before the generateWikiPages call in the
docs wiki handler, invoke the same hold-check logic used by wiki-refresh, using
the existing cwd/defaultGit context, and abort with the matching error behavior
when the checkout is unsafe.
In `@src/docs/read.ts`:
- Around line 174-204: Use the same NUL delimiter consistently for scoped winner
write-back: update the key construction in the scoped loop of listDocs to
`${winner.project}\u0000${winner.doc_id}`, matching the key used when populating
candidates and preventing collisions between distinct project/doc_id pairs.
In `@src/docs/wiki-refresh.ts`:
- Around line 427-455: Only increment pendingPublish in the priv branch when
updateWikiPage returns an action indicating a private write occurred: "patched"
or "mechanics_refreshed". Use the returned out.action from updateWikiPage, while
preserving the existing branchName cleanup behavior.
In `@src/docs/write.ts`:
- Around line 217-232: Ensure the landed-check in the resilient insert flow uses
the same scope default as insertDoc: pass scope: input.scope ?? "main" to
getDocLatest within the retry loop. Update the call associated with
insertDocResilient so omitted scopes cannot match an overlay row.
In `@src/hooks/pre-tool-use.ts`:
- Around line 462-483: The /docs listing branch omits readerScope and git
context, allowing scope-agnostic document resolution. In the lsDir === "/docs"
or "/docs/" block, create docsCwd and docsGit consistently with the direct-read
branch and pass readerScope: currentScope(docsGit) and git: docsGit alongside
project to handleDocsVfsFn.
---
Nitpick comments:
In `@tests/shared/docs-wiki-refresh.test.ts`:
- Around line 186-208: Strengthen the test for the held private page by
asserting the specific reason message in report.outcomes, alongside the existing
action assertion. Update the expectations in the test case “private page with no
usable diff is HELD, never regenerated to the cloud (codex r3 leak)” to verify
the full outcomes details, matching the precise held reason used by the refresh
cycle.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 440ef446-ee98-449a-a8cf-84b2db5be608
📒 Files selected for processing (22)
src/commands/docs.tssrc/deeplake-schema.tssrc/docs/branch-scope.tssrc/docs/candidates.tssrc/docs/fingerprint.tssrc/docs/private-store.tssrc/docs/promote.tssrc/docs/read.tssrc/docs/vfs-handler.tssrc/docs/wiki-generate.tssrc/docs/wiki-refresh.tssrc/docs/wiki-update.tssrc/docs/write.tssrc/hooks/pre-tool-use.tstests/shared/docs-branch-scope.test.tstests/shared/docs-fingerprint.test.tstests/shared/docs-private-store.test.tstests/shared/docs-promote.test.tstests/shared/docs-vfs-handler.test.tstests/shared/docs-wiki-refresh.test.tstests/shared/docs-wiki-update.test.tstests/shared/docs.test.ts
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/shared/docs-wiki-freshness-hint.test.ts (1)
14-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPrefer full-message equality over generic substring assertions.
Lines 14–36 use generic substring/regex checks (
toContain("graph init"),not.toMatch(/NOT stay fresh/),not.toContain("docs auto on")). Per thetests/**path instruction, prefer asserting on specific message values. UsingtoBewith the full expected string would catch any unintended wording drift in these user-facing hints.♻️ Proposed refactor for lines 14–20
it("auto ON, no hook → offers per-commit upgrade, never claims it's stale", () => { const h = wikiFreshnessHint({ autoEnabled: true, hookInstalled: false, subject: "This wiki" }); - expect(h).not.toBeNull(); - expect(h!).toContain("graph init"); - expect(h!).not.toMatch(/NOT stay fresh/); - expect(h!).not.toContain("docs auto on"); + expect(h).toBe("Auto refresh is ON (updates on session start). For instant per-commit refresh: hivemind graph init"); });🤖 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/shared/docs-wiki-freshness-hint.test.ts` around lines 14 - 36, Replace the broad substring and regex assertions in the wikiFreshnessHint tests with exact full-message equality checks using toBe. For each autoEnabled/hookInstalled scenario, assert the complete expected string returned by wikiFreshnessHint, while retaining the null check only if needed.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/shared/docs-wiki-freshness-hint.test.ts`:
- Around line 14-36: Replace the broad substring and regex assertions in the
wikiFreshnessHint tests with exact full-message equality checks using toBe. For
each autoEnabled/hookInstalled scenario, assert the complete expected string
returned by wikiFreshnessHint, while retaining the null check only if needed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ce4dd4cc-9c32-4906-96d3-edf82efe1e27
📒 Files selected for processing (5)
src/commands/docs.tssrc/docs/vfs-handler.tssrc/docs/wiki-refresh.tstests/shared/docs-wiki-freshness-hint.test.tstests/shared/docs-wiki-refresh.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/docs/vfs-handler.ts
- src/docs/wiki-refresh.ts
What
Makes the wiki-docs corpus branch-aware and visibility-scoped: a doc is never more visible than the code it describes, branches are isolated, and unchanged pages are inherited from
maininstead of duplicated.Builds on the shared
hivemind_docscloud table by reusing the existingscopecolumn as the identity dimension:main= canonical/public corpus,b:<branch>= a per-branch overlay.Behaviour
main.main(seeded from the trunk merge-base so the first cycle overlays just the branch's own changes, not the whole corpus).~/.hivemind/docs-private/), readable only on this machine/branch,main→ everyone.source_fp); reads carry a staleness banner naming changed files.main, its overlay is re-stamped asmain(reused, not regenerated) when the fingerprint matches the current membership, and the overlay is archived.Key files
src/docs/branch-scope.ts(identity + precedence resolver),src/docs/fingerprint.ts(blob-sha fingerprint + publish/clean gates),src/docs/private-store.ts(local private store),src/docs/promote.ts(merge promotion), plus threading throughread.ts/write.ts/wiki-refresh.ts/wiki-update.ts/wiki-generate.ts/vfs-handler.ts/commands/docs.ts.Testing
tscclean. New suites:docs-branch-scope,docs-fingerprint,docs-promote,docs-private-store, plus branch/visibility cases acrossdocs,docs-wiki-refresh,docs-wiki-update,docs-vfs-handler.main).upsertDocdelete, timeout-safe scoped-read fallback, working-tree/detached holds, promotion membership guard, private-store isolation + no cloud leak on escalate, cursor-hold so private pages publish after push).Known limitation
A brand-new subsystem authored on an unpushed branch is held from the cloud until pushed (private materialization currently covers updates to existing pages, not first-generation). A full LLM-pipeline e2e (the real doc generation) is the remaining end-to-end sign-off; component and read-path e2e are done.
Summary by CodeRabbit