Conversation
…(BEA-256)
A Claude Code Stop hook now runs `bdrive sync . --hook-stop claude-code`:
one blocking cycle under the volume flock, then — only if this session's own
ops (matched on Op.Session or the session note) are still past PushedOps —
one {"decision":"block"} JSON naming them and why (offline, the hub's
refusal, paused, reverted). Fails open, and stop_hook_active stops a loop.
The Stop group is identified by --hook-stop, so a user's own
`bdrive sync .` Stop hook is neither converged over on install nor removed
on uninstall. syncer.DriftPaths reports a paused mount's unscanned files
without resuming it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
x.html🔗" when the file had never left the laptop. Now a Claude CodeStophook checks first.stop_hook_active). A user's ownbdrive sync .Stop hook is never overwritten or removed..bdriveignoreare never journaled, so they're invisible to it. A read-only-folder revert usually happens in the daemon before Stop runs, so it's rarely reported.Closes BEA-256.
flowchart LR A["Stop fires"] --> B{"stop_hook_active?"} B -- yes --> Z["exit 0, silent"] B -- no --> C["per mount: one Cycle under the volume flock<br/>(paused mount: DriftPaths only, never resumed)"] C --> D{"own ops past PushedOps<br/>with Session or note == this session?"} D -- none --> Z D -- some --> E["one JSON on stdout:<br/>decision: block, reason names the files"]What it prints (from
TestHookStopMultipleMounts):What can't break
os.Stdout, not a cobra buffer, so a strayPrintfwould fail them.hookStopCommandismountGuard()+bdrive sync . --hook-stop claude-code, andTestSec_Hooks_EveryHookCommandIsGuardedcovers it.Cycleunder the flock, so it waits behind any in-flight async push. That wait is the point. The cost: if it runs past the 30s timeout, the turn ends with no receipt (fails open).syncer.DriftPathsis a pure read, and the paused-mount test assertsmounts.jsonis byte-identical afterwards.How it's built
cmd/bdrive/hookstop.go(new). It parses the Stop event and runs each target. Ops are matched onOp.Session == idorOp.Note == "<label> session <id>", because mid-turn writes come from the async push hook and daemon scans, which only carry the note. It dedupes by path (last op wins) and renders grouped by reason, capped at 20.hooksync.go: the session-stamping block moved intostampHookSession, so the turn-start and turn-end hooks stamp identically. This is a pure refactor.agenthooks:Stopis added toourEvents.mergeJSONHooksgains astopCmdslot, used by Claude only. Codex and Gemini pass"". UnderStop, "ours" is judged by--hook-stopalone, both on install convergence and in uninstall'sownHook.syncer/drift.go:Driftnow wraps a callback walker (walkDrift).DriftPathsreturns the added and modified paths.Drift's counts are unchanged.INSTALL_FOR_AGENTS.md§5,web/docshooks page and CLI reference.Tests run
go test ./...: all green.go vet ./...: clean.cmd/bdrive/hookstop_test.go: active stop is silent and runs no cycle; offline blocks withb.mdcommitted through the note-only push path; another session's and a human's ops aren't listed; pushed is silent and the op is stamped; refused push carries the hub's 403 text; paused mount; two mounts produce one object withprojA/andprojB/prefixes.agenthooks: ClaudeStopis present and not async, and Codex/Gemini have none. NewTestInstallStopKeepsUsersOwnStopHookcovers install twice plus uninstall.TestUninstallRemovesOnlyOurHookscovers our Stop.syncer:TestDriftSeesUnscannedWorkassertsDriftPaths.Architecture changes
architecture/cli-sync.md:Driftgains+DriftPaths;AgentHooksgains theturn-end: sync --hook-stop (claude)hook; new edgeCommands --> Drift(the--hook-stoppaused-mount path).flowchart TB Commands["<div style='text-align:left'><b>Commands</b><br/>cmd/bdrive</div>"] Drift["<div style='text-align:left'><b>Drift</b><br/>+Drift(folder, include, accepted, cache) added, modified, removed<br/><span style='background:#22c55e55;padding:0 4px;border-radius:3px'>✅ +DriftPaths(folder, include, accepted, cache) paths</span></div>"] walkFolder["walkFolder"] Filter["Filter"] AgentHooks["<div style='text-align:left'><b>AgentHooks</b><br/>Detect / Install / Uninstall / Registered<br/>ConfigPath = USER config<br/>turn-start: sync --hook<br/>post-edit: sync --note<br/>post-read: read-log<br/><span style='background:#22c55e55;padding:0 4px;border-radius:3px'>✅ turn-end: sync --hook-stop (claude)</span></div>"] syncBlocked["<div style='text-align:left'><b>syncBlocked</b><br/>enrolled in mounts.json?<br/>volume not paused?</div>"] Drift -- "same predicate" --> walkFolder Drift -- "own fresh instance" --> Filter Commands -- "hooks install/uninstall" --> AgentHooks Commands -- "sync and read-log gate first" --> syncBlocked Commands -- "<span style='background:#22c55e55;padding:0 5px;border-radius:3px'>✅ sync --hook-stop names a paused mount's unscanned paths</span>" --> Drift classDef added fill:#22c55e22,stroke:#22c55e,stroke-width:2px linkStyle 4 stroke:#22c55e,stroke-width:2pxBuild session
(only works on the build machine)
🤖 Generated with Claude Code