ReferenceOperatorsworkspace.bind

workspace.bind

Generated by docs/scripts/generate-docs.mjs from operators/workspace-bind/operator.md and operators/workspace-bind/operator.json. Edit the source, not this page.

Binding

FieldValue
idworkspace.bind
domainworkspace
resources.profileluna
resources.requires
policy.webSearch
policy.grammarBound
policy.imageGeneration

Job

Resolve one project and role into a verified checkout identity, its exact source head, and the closed runtime binding it may consume, and return that as one typed route receipt.

Declaration is the only authority

A route exists because a portable declaration in @workspaces/projects/<project>/<role> says so and a hydrated local route projects it onto this machine’s disk. Four things routinely resemble a route and are none of it: a directory whose name matches the project, a sibling checkout that sits next to the Source, the current working directory, and the origin open in a browser. Those are hints, they carry no authority at all, and this operator has nowhere to put them: Requirements declares no hint field, so a request that carries one fails the gate with INVALID_INPUT at step 1 rather than being weighed. Refusing them at the gate is the point, because a hint that survives into the body of a run is a hint that gets followed the moment the declared route looks inconvenient. The two route halves must agree on project, role, Git repository, and branch; a source repository kind carries no directory and resolves to the Source root itself, a sibling kind carries a safe relative directory and resolves beside it, and a hydrated route that names another Source belongs to another machine and is refused.

Endpoint binding is a closed projection

An endpoint is never a URL somebody chose. It is the workspace-route-port-projection: the verified frontend and backend routes, the project offset and slot step from @workspaces/ports/<project>, the application slot, and the routed backend’s declared port services, folded into one fingerprint. The binding carries that fingerprint, and a stale one is refused rather than recomputed into agreement. Only origin-only http://localhost:<canonical-port> values are endpoints; a free URL, 127.0.0.1, a remote host, an alternate application, an undeclared service, or a port that merely happens to be listening establishes nothing. Step 5 runs only when runtimeNeed is not none: a caller that binds no runtime binds no endpoints either, and a route that carries endpoints nobody asked for has consumed a shared resource on its own initiative.

One registry entry per project route

The runtime registry holds one entry for each <project>/<role>, and a binding consumes the entry of its own route: its endpoints, its head, its generation, its status, the evidence behind that status and the identity provider an account for it would be created at. One machine serves several products at once, so a registry read as a single block answers for whichever route happens to be recorded in it, and every other route binds as not ready while the service it needs is listening. The binding records the entry key it read, which is what makes that mistake visible instead of merely likely.

The runtime is bound by ancestry, not by equality

One machine serves one integration branch per product, and that branch carries the work of every session that asked for it, so the head a binding pinned is almost never the head that is serving. A binding therefore does not compare the two for equality — that would fail on arithmetic the moment a second session existed — it establishes that its own head is inside the served one: present among the commits the entry records as contained, and an ancestor of the served head. Both are recorded, because a reader who cannot see the pinned head beside the served one cannot check the claim.

A served head that does not contain this route’s head is not a runtime this binding may consume. RUNTIME_NOT_READY then names the commit that must be served and the operation that would serve it, so the coordination request that follows is a specific request rather than a report that something is wrong; and while another session holds the lease and is merging, the answer is RUNTIME_BUSY with the holder and the queue position, which is a wait rather than a failure. Neither is permission to serve it here: this operator still starts nothing.

The caller is a consumer, never an owner

The shared local frontend, api, and identity processes belong to exactly one delegated owner task. This operator binds the caller to that owner’s endpoints as a consumer. It does not start, stop, restart, replace, or kill a process, and it claims no port, no PID, and no runtime lifecycle. A registry that is missing, stale, or not ready yields a typed stop so the caller can raise one coordination request; it never authorises a replacement. An address already in use, an unexpected authenticated session, and a failing probe are all evidence to report, and none of them is permission.

Nothing is repaired here

No credential is read, copied, or recorded; only the sealed roster reference is bound, and IDENTITY_ROSTER_SEALED states that. A declaration that does not exist is repaired by the workspace owner, never by this operator, so ROUTE_UNDECLARED and ROUTE_UNHYDRATED are expected outcomes of an unprepared workspace rather than defects in the request. CHECKOUT_DIRTY never falls back to anything: this operator does not stash, clean, or reset a working tree to make a binding possible. The businesses authority root is derived as <git root>/.worktrees/businesses when that worktree exists on a source checkout and is absent otherwise; it is never accepted from the person, because a typed authority root is how a second business tree is born. Provenance and freshness are not a step of their own: they are written inside the emit, next to the binding they describe. The head a hydrated route recorded is a record of the hydration and never route authority: the observed head wins, and a hydration head two commits behind the checkout is not a stop.

A blocked branch emits no receipt and no route: response.json is the whole record, and reason carries the observation that justified the stop, including the registry generation, the endpoints probed and what each answered.

Boundary

Context is read-only apart from the machine-local hydrated route state, which Git ignores. The operator writes only response/ of its own branch: response.md, response/data/route.json and response.json. It never accepts a similar name, a sibling directory, the working directory, or a browser URL as route authority, never accepts a chosen URL, a loopback alias, or a merely listening port as an endpoint, never starts, stops, restarts, replaces, or kills a shared runtime process, never claims a port, a PID, or a runtime lifecycle for a feature task, never creates or switches to a task, feature, or worktree branch under a forbidden worktree policy, never writes a credential, token, cookie, or password into the receipt or any evidence, and never repairs a missing route, initializes a workspace, or provisions an account. It makes no product decision and carries no verdict.

Context

AliasBindRequired
@workspaces/projects/<project>/<role>the portable route declaration, read by fingerprint; the only route authorityyes
@workspaces/local/routes/<project>/<role>the hydrated route this machine projects the declaration onto, and the checkout it resolves toyes
@workspaces/device-statemachine identity and the sealed credential roster, bound by name and never readyes
@workspaces/ports/<project>the port projection, read only when the caller consumes the runtimeno
@worktrees/sessions/central-runtimethe runtime owner registry: the entry of this route, the head it serves, what that head contains, its lease, generation and health evidence, bound only when the caller consumesno

Inputs

KindFromRequired
this operator opens the chain, so it consumes no earlier branchno

Requirements

FieldTypeDefaultAsk
projectidThe project to bind
rolechoicefe or be: the role of that project to bind
gitPolicylist of {worktreeBranches, mutationBranch}the policy the route declaration carries; a declaration that carries none is INVALID_INPUT at step 1, never a guessed policyThe branch law this binding is verified against; forbidden keeps every write on the mutation branch
declaredWriteRootslistemptyThe only paths later work may write; anything dirty outside them is CHECKOUT_DIRTY, and so is anything dirty at all when the checkout sits on the mutation branch rather than a session/<sessionId> branch
runtimeNeedchoicenonenone binds no runtime and skips step 5; consume binds the owner’s endpoints as a consumer
resumetokennullThe blocked branch’s token when re-entering after a stop

Steps

#StepParamsReadsWritesStops with
1Validate the gate and resume, and refuse every hint it carriesresumerequest/request.json, its requirements and its frozen headINVALID_INPUT, SOURCE_DRIFT, NO_PROGRESS
2Bind bootstrap and identity@workspaces/device-state, the machine identity and the sealed credential roster, @tools/secretsIDENTITY_UNVERIFIED
3Resolve the routeproject, role@workspaces/projects/<project>/<role> for exactly this project and role, @workspaces/local/routes/<project>/<role>, @tools/gitROUTE_UNDECLARED, ROUTE_UNHYDRATED, ROUTE_MISMATCH
4Verify the checkout: branch policy, a clean tree, and the write rootsgitPolicy, declaredWriteRoots@workspaces/local/routes/<project>/<role>, the resolved checkout, its branch, its head and its working tree, @tools/git, @tools/shell<br />ANCH_POLICY_VIOLATION, CHECKOUT_DIRTY
5Bind the runtime the caller consumes from the registry entry of this project route, only when runtimeNeed is not noneruntimeNeed@worktrees/sessions/central-runtime, the entry of this <project>/<role> with its served head, what that head contains, its lease, its generation, health evidence and identity declaration, @workspaces/ports/<project>, @tools/httpENDPOINT_AUTHORITY_STALE, RUNTIME_NOT_READY, RUNTIME_BUSY
6Bind provenance and freshness, then emiteverything above, @workspaces/device-stateresponse/response.md, response/data/route.json, response/response.json

Step 5 recomputes nothing: the endpoint fingerprint either matches the closed projection or the branch stops. Under worktreeBranches set to forbidden, a route binds only on the mutation branch and records WORKTREE_<br />ANCH_FORBIDDEN; under session-only it binds on the mutation branch or on a session/<sessionId> worktree branch, the only shape a source-writing operator may commit to, and records WORKTREE_<br />ANCH_SESSION_ONLY, because a policy that opens a write path is exactly the finding a later reader looks for; a redacted conversation head records PROVENANCE_HEAD_BOUND, and a cached receipt matching the same identity tuple and fingerprints records CACHED_ROUTE_REUSED. mutationReadiness is ready when the observed branch is one the routed policy permits a write on — the mutation branch, or a session/<sessionId> branch under session-only — and the working tree carries nothing this step must refuse; it is read-only in every other case, including a route bound with no declared write roots. The declared write roots exempt dirt only on a session/<sessionId> branch, where it is a session’s expected work in progress; the mutation branch has no in-progress state of its own to exempt, so any dirt observed there — inside a declared write root or outside it — is source that was written with no session to account for it, and step 4 stops with CHECKOUT_DIRTY rather than reporting a readiness that would carry the violation forward unrecorded. It is derived here and never accepted from the request, because a readiness a caller can assert is a readiness nobody measured. A resume begins again at step 1, reuses only unchanged fingerprinted observations, and consumes the exact delta; a republished route arrives as a new route fingerprint, because the same fingerprint cannot yield a different binding.

Outputs

KindFileTypeRequired
workspace-route-bindingresponse/response.mdmdyes
routeresponse/data/route.jsondatayes

Stops

CodeDisposition
INVALID_INPUTterminate
SOURCE_DRIFTterminate
NO_PROGRESSterminate
IDENTITY_UNVERIFIEDterminate
ROUTE_UNDECLAREDterminate
ROUTE_UNHYDRATEDterminate
ROUTE_MISMATCHterminate
<br />ANCH_POLICY_VIOLATIONterminate
CHECKOUT_DIRTYterminate
ENDPOINT_AUTHORITY_STALEterminate
RUNTIME_NOT_READYterminate
RUNTIME_BUSYterminate

Next

WhenOperator
the route is bound and the checkout carries a publication to pushgit.publish
the route is bound and a promise must be decided against its sourcebusiness.decide
the route is bound and a backend contract must be filled inside itbackend.source.apply
the route is bound and a frontend surface must be written inside itfrontend.source.apply
the runtime owner is missing or not ready and one coordination request must be raisedplatform.operate
the route is bound and a frontend surface must be decided inside itfrontend.direction.decide
the route is bound and a published head must be verified before it shipsquality.verify
the route is bound and a served surface must be observedfrontend.surface.audit

Source: operators/workspace-bind/operator.md.