Spec Studio

A cap on concurrent turns holds across every Claude session on the machine

spec:cc-agents.cross-session-slotsUse Case / Behavior · featureready floor ready

Packs: none

Design: 4 design entries · 2 pinned declarations · no decisions

Findings: no errors, no warnings

specs/runner/cross-session-slots.sdp.md

1 open question and no blocking questions; implementation binding present, verifier binding present, and expected-outcome oracle none.

The slots are plugin/sidecar/slots.mjs, used by the sidecar. For each cap key a directory holds, for each index below the cap, a lock file and a record. A turn holds an index by holding an exclusive lock on its lock file through a small perl process, because Node has no flock; the kernel drops the lock when the holder dies, even by SIGKILL.

Intent
actor
Every sidecar on one machine.
problem
Several Claude sessions start turns of the same account at once, and a cap kept inside one process cannot hold, nor can capacity be handed back while a dead holder's work still runs.
outcome
At most the cap of turns of one key run on the machine, and capacity returns only when the turn's work is confirmed gone.
value
A busy machine and a provider's concurrency limit are protected from several sessions each trusting its own count.

Open questions

  • non-blocking · question.harnessWideCap line 25 · specs/runner/cross-session-slots.sdp.md:25 · The caps option also accepts a bare harness name, which the code applies to every key of that harness not named itself; no test pins that fallback.Decider: delegatedProvisional: As built.
Behavior
rule.capHolds line 31 · specs/runner/cross-session-slots.sdp.md:31
At most the cap's number of turns of one cap key run on the machine across all sessions and sidecars, and two sidecars taking the last slot at once, one of them stalled, never both get it.

no example exercises it

rule.lockDiesWithHolder line 32 · specs/runner/cross-session-slots.sdp.md:32
A slot is held by an exclusive lock that the kernel releases when its holder dies, and a paused holder keeps its slot, so nobody can take it from a live holder.

no example exercises it

rule.holderByStart line 33 · specs/runner/cross-session-slots.sdp.md:33
A holder is told apart by its process id and its process start time, so a slot whose id now belongs to another process is not held.

no example exercises it

rule.releaseWhenEnded line 34 · specs/runner/cross-session-slots.sdp.md:34
A turn's end reports its outcome to the agent and frees no capacity: the slot is released only when the turn's work is confirmed ended, and a sidecar that stops mid-turn leaves the slot taken until the work has ended.

no example exercises it

rule.orphansAreReaped line 35 · specs/runner/cross-session-slots.sdp.md:35
A record whose holder is dead and whose work still runs counts against the cap, and a reaper, holding the index's lock so that only one acts, ends or confirms the work and then marks the record released, asking the adapter when the work is not a process group.

no example exercises it

rule.reserveAtSpawn line 36 · specs/runner/cross-session-slots.sdp.md:36
A spawn reserves a slot before its agent starts, a spawn stopped while it waits takes none, an agent stopped before its first turn gives its slot back, and a reservation no turn binds within a minute is released without being taken back from a turn that got its slot.

no example exercises it

rule.capsOption line 37 · specs/runner/cross-session-slots.sdp.md:37
The caps option is a list such as codex=2, opencode:xai=1, and a key not named takes the default its adapter gives.

no example exercises it

0 unkeyed rules.

Design
slotFiles line 41 · specs/runner/cross-session-slots.sdp.md:41
<state>/slots/<cap key>/slot-<i>.lock and slot-<i>.json.

no citation recorded

recordFields line 42 · specs/runner/cross-session-slots.sdp.md:42
The holder's session, agent, harness, model, effort, mode, process id and start, the turn's work, the cap, and a released flag.
reservationTtl line 43 · specs/runner/cross-session-slots.sdp.md:43
60 seconds, changed for tests by CC_AGENTS_RESERVATION_MS.
slotStates line 44 · specs/runner/cross-session-slots.sdp.md:44
held for a live holder and orphaned for a dead holder whose work has not ended.

no citation recorded

Verification

Verifier bindings

  • test:cc-agents.sidecar-suite · the sidecar, the Codex adapter, the slots and the guard against a fake Codex app-server · anchored · test anchor binding present · tests/sidecar.test.mjs:4
Design view

Requirement drawing

  • Declared verifying examples: no declared verifying example.
Requirement drawing for spec:cc-agents.cross-session-slots
The diagram as text
  • spec:cc-agents.cross-session-slots: A cap on concurrent turns holds across every Claude session on the machine · declared Spec
  • spec:cc-agents.runner: Other model families run as real Claude Code agents · declared Spec
  • impl:cc-agents.sidecar: the per-session sidecar: owns each harness turn, its events and its slot, and drives every adapter through the contract · anchored code · plugin/sidecar/server.mjs:12
  • impl:cc-agents.slots: the machine-wide cap on concurrent turns, held with OS locks that die with their holder · anchored code · plugin/sidecar/slots.mjs:16
  • test:cc-agents.sidecar-suite: the sidecar, the Codex adapter, the slots and the guard against a fake Codex app-server · anchored test binding · binding present · tests/sidecar.test.mjs:4
  • spec:cc-agents.cross-session-slots refines spec:cc-agents.runner · declared
  • impl:cc-agents.sidecar satisfies spec:cc-agents.cross-session-slots · anchored
  • impl:cc-agents.slots satisfies spec:cc-agents.cross-session-slots · anchored
  • test:cc-agents.sidecar-suite verifies spec:cc-agents.cross-session-slots · anchored
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred and chosen syntax: requirementDiagram; exact refines, satisfies and verifies, with claim words in nodes and per-edge comments.

Decisions

No decision record named.

Runtime
  • impl:cc-agents.sidecar · the per-session sidecar: owns each harness turn, its events and its slot, and drives every adapter through the contract · plugin/sidecar/server.mjs:12

    Uses

    None.

  • impl:cc-agents.slots · the machine-wide cap on concurrent turns, held with OS locks that die with their holder · plugin/sidecar/slots.mjs:16

    Uses

    None.

Runtime observation: not tracked.

Evidence

Runtime observation: not tracked.

Build attestations: not tracked.

Deployments: not tracked.

Bills of materials: not tracked.

Impact
Relations of spec:cc-agents.cross-session-slots
The diagram as text
  • spec:cc-agents.cross-session-slots: A cap on concurrent turns holds across every Claude session on the machine
  • spec:cc-agents.runner: Other model families run as real Claude Code agents
  • spec:cc-agents.sidecar: One sidecar per Claude session owns the session's harness turns
  • spec:cc-agents.work-containment: A guard ends the groups a harness turn started and it has seen, even when the sidecar is killed
  • spec:cc-agents.cross-session-slots dependsOn spec:cc-agents.work-containment · declared — a slot is free only once the work recorded in it has ended
  • spec:cc-agents.cross-session-slots refines spec:cc-agents.runner · declared
  • spec:cc-agents.sidecar dependsOn spec:cc-agents.cross-session-slots · declared — a turn starts only with a slot of its cap key
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred syntax: flowchart. Chosen syntax: labelled flowchart, preserving exact relations, claims and folds.

Rests on

Rested on by

Distance 1

  • spec:cc-agents.sidecar

    Implementing units

    • impl:cc-agents.launch · plugin/sidecar/launch.mjs:7
    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.sidecar-suite

    Packs: None

Distance 2

  • spec:cc-agents.mod-layer

    Implementing units

    • impl:cc-agents.mod · plugin/hooks/register.js:9

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite

    Packs: None

  • spec:cc-agents.pane

    Implementing units

    • impl:cc-agents.harness-options · plugin/hooks/harnesses.js:5
    • impl:cc-agents.pane · plugin/hooks/pane.js:13

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite, test:cc-agents.pane-suite

    Packs: None

  • spec:cc-agents.relay-job-type

    Implementing units

    • impl:cc-agents.mod · plugin/hooks/register.js:9
    • impl:cc-agents.relay-header · plugin/sidecar/relay-header.mjs:9
    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite, test:cc-agents.sidecar-suite

    Packs: None

  • spec:cc-agents.runner

    Implementing units

    None.

    Referencing units

    None.

    Enabled verifiers: None

    Packs: None

  • spec:cc-agents.steering-and-stop

    Implementing units

    • impl:cc-agents.mod · plugin/hooks/register.js:9
    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite, test:cc-agents.sidecar-suite

    Packs: None

Distance 3

  • spec:cc-agents.adapter-contract

    Implementing units

    • impl:cc-agents.codex · plugin/sidecar/adapters/codex.mjs:15
    • impl:cc-agents.opencode · plugin/sidecar/adapters/opencode.mjs:26
    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.opencode-suite, test:cc-agents.sidecar-suite

    Packs: None

  • spec:cc-agents.codex-adapter

    Implementing units

    • impl:cc-agents.codex · plugin/sidecar/adapters/codex.mjs:15

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.sidecar-suite

    Packs: None

  • spec:cc-agents.exact-answer

    Implementing units

    • impl:cc-agents.mod · plugin/hooks/register.js:9

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite

    Packs: None

  • spec:cc-agents.hq-links

    Implementing units

    None.

    Referencing units

    None.

    Enabled verifiers: None

    Packs: None

  • spec:cc-agents.installation

    Implementing units

    None.

    Referencing units

    • impl:cc-agents.harness-options · plugin/hooks/harnesses.js:5

    Enabled verifiers: None

    Packs: None

  • spec:cc-agents.machine-guard

    Implementing units

    • impl:cc-agents.machine · plugin/sidecar/machine.mjs:20

    Referencing units

    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12

    Enabled verifiers: test:cc-agents.machine-suite, test:cc-agents.sidecar-suite

    Packs: None

  • spec:cc-agents.model-templates

    Implementing units

    • impl:cc-agents.harness-options · plugin/hooks/harnesses.js:5
    • impl:cc-agents.mod · plugin/hooks/register.js:9

    Referencing units

    • impl:cc-agents.sync-templates · scripts/sync-templates.mjs:10

    Enabled verifiers: test:cc-agents.mod-suite

    Packs: None

  • spec:cc-agents.opencode-adapter

    Implementing units

    • impl:cc-agents.opencode · plugin/sidecar/adapters/opencode.mjs:26

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.opencode-suite

    Packs: None

  • spec:cc-agents.work-containment

    Implementing units

    • impl:cc-agents.groups · plugin/sidecar/groups.mjs:13
    • impl:cc-agents.guard · plugin/sidecar/guard.mjs:16

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.sidecar-suite

    Packs: None

Relations

Relation and directionOther endClaimReasonExercised keys
dependsOn · outgoingA guard ends the groups a harness turn started and it has seen, even when the sidecar is killed
spec:cc-agents.work-containment · resolves
declareda slot is free only once the work recorded in it has endedNone
refines · outgoingOther model families run as real Claude Code agents
spec:cc-agents.runner · resolves
declaredNoneNone
dependsOn · incomingOne sidecar per Claude session owns the session's harness turns
spec:cc-agents.sidecar · resolves
declareda turn starts only with a slot of its cap keyNone

Bindings

Implementation binding: present

  • impl:cc-agents.sidecar · the per-session sidecar: owns each harness turn, its events and its slot, and drives every adapter through the contract · plugin/sidecar/server.mjs:12
  • impl:cc-agents.slots · the machine-wide cap on concurrent turns, held with OS locks that die with their holder · plugin/sidecar/slots.mjs:16

Design reference: none

Verifier binding: present

  • test:cc-agents.sidecar-suite · the sidecar, the Codex adapter, the slots and the guard against a fake Codex app-server · anchored · tests/sidecar.test.mjs:4 · test anchor

Expected-outcome oracle: none

Runtime observation: not tracked.

Run results: not recorded in the graph.

Findings

None.

Composing (0)

Composing needs the script.