Skip to content

A lease says who - #411

Merged
VeryComplexAndLongName merged 1 commit into
mainfrom
a-lease-says-who
Sep 11, 2026
Merged

A lease says who#411
VeryComplexAndLongName merged 1 commit into
mainfrom
a-lease-says-who

Conversation

@VeryComplexAndLongName

Copy link
Copy Markdown
Owner

A workspace lease records a host kind, a hostname and a pid. Two people
on one machine cannot tell whose run holds it, and the only way to ask
who does was to try to start a run and read the refusal.

The lease says who took it

WorkspaceLeaseDocument gains an optional git identity — user.email,
falling back to user.name.

Attribution, never authentication. Anybody can set that value to
anything. The failure mode of recording it is not that it is wrong; it
is that a later reader treats a self-declared label as an audit trail.
So it is called "git author" wherever it is shown, never "user", and
nothing is permitted or refused on the strength of it.

It is read once, where a host starts up — the CLI run, the
standalone server's recovery service, the extension's activation — and
never inside acquireOrRenew, which runs every five seconds while a run
is active for a value that cannot change mid-run.

A lease taken where no identity is configured is valid and records none,
exactly like every lease written before this existed.

Asking who holds a workspace

openspec-ui-cli lease [--format text|json]

Exits 0 held or free: the question was answered either way, and a
script asking "is it free" should read the output rather than infer it
from a failure code.

Clearing one

openspec-ui-cli lease release

There is deliberately no --force, and working out why shaped this
change.

A holder that died stops renewing, and the next acquirer reclaims
the lease on its own once the heartbeat is stale. That case already
heals.

A holder that is alive and still renewing has the workspace open. It
may be stuck, but taking its lease would let a second mutating run start
against files the first still holds — the exact thing the lease exists
to prevent. A stuck holder is stopped, not robbed.

So release clears only where it can establish the holder is gone: the
heartbeat is already stale, or the holder is on this machine and its pid
is not running. process.kill(pid, 0) answers the second without
signalling anything. A holder on another machine cannot be checked from
here at all, and the refusal says so rather than guessing. Exits 0
cleared, 1 refused, 2 could not look.

Verification

npm run verify — exit 0. Typecheck and lint clean across all five
packages; tests cli 107, core 1075, vscode 327, server 80, webui 379 —
1968 across 155 files, 0 failed. openspec validate --strict --changes
passes.

A live smoke found the backward-compatible path for real: the workspace
was held by a VS Code extension built before this change, so its lease
carries no identity. lease described it (exit 0) with no git author
line; lease release refused it (exit 1) because its pid was running.
What that did not show is a lease written by this build, which is what
the change's one delegated item covers.

Change: openspec/changes/a-lease-says-who/

🤖 Generated with Claude Code

The workspace lease records the git identity of the working directory
that took it, and the CLI can be asked who holds a workspace without
attempting to start a run and reading the refusal.

Attribution, never authentication. Anybody can set user.email to
anything, so the value is called "git author" wherever it is shown and
nothing is permitted or refused on the strength of it. It is read once
where a host starts up, never in the heartbeat, which renews every five
seconds for a value that cannot change during a run. A lease taken
where no identity is configured is valid and records none, exactly like
every lease written before this.

"lease release" clears a lease only where it can establish the holder is
gone: the heartbeat is already stale, or the holder is on this machine
and its process is not running, checked with a signal that delivers
nothing. There is deliberately no --force. A holder that died already
self-heals once its heartbeat goes stale; a holder that is alive still
has the workspace open, and taking its lease would permit a second
mutating run against files it is still holding, which is what the lease
exists to prevent. A stuck holder is stopped, not robbed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@VeryComplexAndLongName
VeryComplexAndLongName merged commit 394d426 into main Sep 11, 2026
9 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