feat(hub): a Locks tab that says what it does not know - #57
Merged
Conversation
Completes P4. Cross-project lease view plus the one human override: force-release, which bumps the fencing counter so a holder that comes back is stale by construction. That stays out of the agent-facing c3_locks tool on purpose -- breaking someone else's lease is a decision for a person. Two honesty rules the view keeps, both from the spec. A project whose lock state could not be READ is badged UNREADABLE rather than rendered with zero leases: 'all clear' is a different claim from 'we don't know', and the second one is what an unreadable repo actually supports. And the coverage caveat stays on screen, because a lease gates C3 tool surfaces only -- a raw shell redirect, a non-Claude agent, or a human in an editor is not covered. Verified in a real browser rather than by unit test alone: the tab renders live leases with holder, intent and a draining TTL bar, and a force-release round trip removed the lease, moved fencing 2 to 3, and wrote a ledger entry naming the previous owner. Worth recording that the first attempt clicked nothing -- I used coordinates read off a screenshot, which is image space, not CSS space. Querying the element position fixed it. Also caught the stale-server trap on the way: port 3330 was held by a hub started hours earlier still serving 2.63.0, so the new route 404'd and looked missing. Restarted from the branch before believing anything. 1805 pass, ruff clean. Claude-Session: https://claude.ai/code/session_01Sbd9NbQfDcoJvvURD7KCVf
drknowhow
added a commit
that referenced
this pull request
Jul 30, 2026
…off (#58) Four merged PRs (#54-#57) had accumulated past the v2.64.0 tag, so the version no longer identified the code: the installed build reported 2.64.0 while running leases, a new tool, a new CLI command and a new Hub tab. MINOR, not patch. I earlier called this 2.64.1; that was wrong. It adds c3_locks, `c3 locks`, a Hub tab and a config section — new surface, not a fix. What ships: - Agent Locks. Two mechanisms for two different problems. A cross-process file lock stops two c3-mcp processes tearing each other's writes (and now covers create mode, which ran outside the lock entirely). Leases stop two agents doing overlapping work, carrying the intent from the edit summary so the blocked agent is told who holds the file and why. TTL is the real release mechanism, so a crashed agent cannot wedge a repo. - c3_locks tool, `c3 locks` CLI with the human-only force-release, and a Hub Locks tab that badges an unreadable project UNREADABLE rather than showing it with zero leases. - Access Guard built-in opt-out, two-key: a config entry AND a keyring attestation. Either alone leaves the built-in enforcing, so an agent that writes config.json cannot grant itself write access to ~/.claude/settings.json. The credential vault stays absolute. The changelog states coverage honestly rather than implying it: leases gate C3's own tool surfaces, and a raw c3_shell redirect, a non-Claude agent, or a human in an editor is not covered. 1805 pass, ruff clean, both version constants bumped (test_version_sync guards the pair). Claude-Session: https://claude.ai/code/session_01Sbd9NbQfDcoJvvURD7KCVf
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.
Completes P4 of
docs/agent-locks.md. The FleetDeck reader landed in #56; this is the Hub UI half.Why a separate PR: this commit was pushed to
feat/agent-locks-p3-p4after #56 had already merged, so it was stranded — GitHub never fired asynchronizeevent, no CI run was created for it, andgh pr checkskept reporting #56's older green as if it covered this code. Cherry-picked onto the updatedmaininstead.What it is
A cross-project lease view plus the one human override: force-release, which bumps the fencing counter so a holder that comes back is stale by construction. That deliberately stays out of the agent-facing
c3_lockstool — breaking someone else's lease is a person's decision.cli/hub_ui/components/hub_locks.js— the viewGET /api/hub/locks/overview— cross-project snapshot, per-row isolatedPOST /api/projects/locks/force-release— audited on the target projectTwo honesty rules the view keeps
Both from §4.4 and §9 of the spec:
UNREADABLE, not rendered with zero leases. "All clear" is a different claim from "we don't know", and only the second is supportable from an unreadable file. Same reasoning as the FleetDeck reader'savailable: false.Projects that cannot coordinate at all (no
.c3/, locks disabled, unreadable state) are listed explicitly under "Not coordinating" rather than silently omitted, because silence there reads as all-clear.Verified in a browser, not only by test
The tab renders live leases with holder, intent, and a draining TTL bar. A full force-release round trip:
Two traps hit on the way, both worth knowing:
Tests
tests/test_hub_locks_routes.py— 11 tests. The two that matter most pin the honesty rules: an uninitialized project is flagged rather than reported empty, and force-release must bump fencing (without it a returning holder still looks current and the override achieved nothing). Also assertshub_locks.jsis in the served bundle list, because a component missing from it renders as a blank tab with no error — the failure mode is silence.1805 pass, ruff clean. The same commit was verified green on all 11 CI jobs via
workflow_dispatch(run 30580000933) before this PR existed.https://claude.ai/code/session_01Sbd9NbQfDcoJvvURD7KCVf