Skip to content

feat: workstreams W3 — read fence, scoped recall, references and moves by share policy - #464

Merged
saucam merged 11 commits into
feat/workstreams-w1from
feat/workstreams-w3
Nov 12, 2026
Merged

saucam merged 11 commits into
feat/workstreams-w1from
feat/workstreams-w3

Conversation

@saucam

@saucam saucam commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #463 (W2) → #462 (W1) → #461 (W0). Design: docs/workstreams-design.md §5.3, §6.4, §7, and the new §12.1 "W3 as built". User guide: docs/CONFIGURATION.md → "Reading another workstream, and moving a session".

Everything enforced here is on only with workstreams.isolateGlobalServers, the same switch W1 uses.

What

  • Read fence.
    • What it covers: every other workstream's folders. These are its path rules expanded on disk (workstreams/folders.ts; bounded, links followed only where a rule names them) plus the folders recorded as its sessions' homes. The fence is not "everything outside the home", which would refuse toolchains and system headers.
    • Nested folders: the more specific rule decides. Inside a foreign folder, only WorkstreamFence.readable stays open: the session's own workdir, its home's folders, and exactly what its references grant.
    • Sandbox: the project roots become the home's and references' folders, never the bare @parent. Everything else in a foreign folder is hidden (extraHidden, carveOut), and an added dir inside a foreign folder is never kept.
    • Gate: workstreams/gate.ts checks every tool call's read, search and write paths in every mode. Claude's PreToolUse hook refuses the same calls, because the CLI skips canUseTool for some of them.
  • Memory.
    • Episodes record workstream_id when written.
    • recall, timeline and get_episode keep to the home and its references (MemoryEngine.scopeSession).
    • The session-map seed is filtered the same way. The frozen index is left out where the repository's memory is mixed.
  • References and moves.
    • Protocol: session.create.references, session.add_reference, session.remove_reference and session.set_workstream.
    • The share policy of the workstream being read decides. open is granted; ask needs the owner's own undelegated approval, or a confirm on the session that is judged again when answered; closed is refused.
    • A move follows the policy of its old home and of every workstream it has read from (readFrom, kept for the session's lifetime). A home inside another workstream's folder counts as reading that folder.
    • Changes are saved at once and applied at the next turn.
    • A stored binding that can't be read back seals the session: no home, and it never moves.
  • CLI: codeoid new --ref, codeoid workstream ref add|rm, codeoid workstream move.
  • Web:
    • The new-session dialog has an "Also reads" picker.
    • The session header's chip opens a panel to read another workstream, remove a reference, or move the session.

Audits

Two deep rounds, folded in:

  • Round 1 (fix: … round 1):
    • nested foreign folders weren't fenced;
    • path-limited references granted their whole folder;
    • a dotfiles ~ became a repository top;
    • moved sessions were fenced out of their own workdir;
    • path extraction missed Glob, ACP, pi;
    • added dirs could open the fence;
    • inputs could seal a binding;
    • the prompt-side memory wasn't filtered.
  • Round 2 (fix: … round 2):
    • monorepo tops widened access;
    • reference paths could leave their workstream through .. or links;
    • changes applied mid-turn;
    • a move forgot the old home;
    • a home in a closed workstream's folder was allowed.

Known limits are listed in §12.1:

  • recall isn't filtered by a reference's folders;
  • remote-only workstreams are fenced once a session has been opened there;
  • Bash in an unsandboxed session isn't asked about;
  • the conductor's fleet reads are W4's.

Verification

  • bun run typecheck and bun run lint: clean.
  • Unit: 6147 pass, 1 fail. The failure is inside real bubblewrap > conversations are the session's own, which also fails on main on this machine.
  • Web: build clean. 39 failures in auth-refresh, embed-refresh, handoff and prompt-drafts, the same set that was failing before; the new chip and dialog tests pass.
  • e2e: 22/22, including workstreams-w3.spec.ts (CLI references and moves; the web UI's dialog and chip panel). It has screenshots at 375×667, 390×844, 768×1024, 1280×800, 1470×830, 1920×1080 and 1280×300, with the geometry asserted, and I opened and checked each one.
  • CI runs only on PRs into main, so these checks run here once the stack below merges.

🤖 Generated with Claude Code

saucam and others added 11 commits November 12, 2026 11:06
…, share verdicts for references and moves

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… from the session's workstream, file tools refused in another workstream's folders

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…line and get_episode keep to the home and its references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…policy, moving a session to another workstream

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- codeoid new --ref, codeoid workstream ref add|rm, workstream move
- the session header's chip opens what the session reads and a move; the
  new-session dialog offers what it may read (under isolation)
- a fresh session's sandbox no longer says its directories changed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s fenced, with what the session reads carved out; its own workdir always readable

- fenceFor: no foreign folder is dropped for being nested in the home's
  or holding it; WorkstreamFence.readable (workdir, home, exactly what a
  reference grants) is carved out — the gate checks it, the sandbox hides
  every other entry on the way (carveOut), else the folder whole
- the home is never a workstream's folder, nor a repository top above it
- a session's own workdir stays readable after a move or an override
- extraction: a glob's partial last segment, ACP locations, pi's @ and
  find/ls; a relative path with an unknown cwd is refused
- an added sandbox dir inside a fenced folder is refused and never kept
- reference inputs held to the binding's own bounds; an unreadable stored
  binding seals the session (no home, never moved)
- the session map and the frozen memory index keep to the workstream
- the chip's panel takes focus and follows the window

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…anted paths where they really are, changes at the turn boundary

- a session's repository top is its own only where no other workstream's
  folder holds it; a recorded home widens to its top only where no other
  workstream has a folder inside it; folders are no longer collapsed
- reference paths resolved at grant, and counted only where they really
  are inside the granting workstream (no .., no link planted later)
- Session.setWorkstream saves at once and applies at the next turn's start
- a move adds the old home to readFrom; a home in another workstream's
  folder is judged by that one's share (closed refuses); the claim and the
  bounds are judged again when an ask is answered
- the gate's folder forms are prepared once per fence; a session's own
  folder may be written though a reference holds it
- globs judged at their start and their dir; pi's @ only before a path

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a closed one's text, and acts through workers homed where the work belongs (#465)

* feat: workstreams W4 — the conductor reads across workstreams without a closed one's text, and acts through workers homed where the work belongs

- fleet tools name each result's workstream and take a workstream filter;
  a closed workstream's episode text never reaches fleet_find, fleet_summary,
  fleet_recall or a worker's digest (status only)
- fleet_spawn takes workstream: judged before queueing as a create is
  (a folder with none needs one; an exclusive folder is an override the
  approval card says; a closed one's folder never), stored on the task and
  judged again at claim
- every send-class approval card (web, Telegram) names the workstream it
  acts in, with the override warning approving it confirms
- egress stub: planEgress (a message is sent by a worker homed in its workstream)
- the conductor recalls its own episodes under isolation
- the fleet rail docks from md up, so a phone's approval card has the width

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W4 audit round 1 — untagged text judged by its session, confirmations tied to the card the owner approved

- untagged episodes are withheld by their session's workstream (live or
  stored), unknown ones while any workstream is closed; a closed one's hits
  are dropped from fleet_find / fleet_recall before anything is counted;
  machine_map leaves out its branches
- a worker's digest is withheld from the conductor only; the conductor's
  tenant-wide task board withholds an orchestrator's closed results
- an override or an ask folder counts as confirmed only by the owner's own
  approval of that very card, once, at that config revision (checked again
  at claim); a delegated token can't approve such a card
- an unnamed spawn keeps the remote-changed guard; a closed workstream's
  worker works only in its own folders
- codeoid approve and the terminal show the card's workstream

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W4 audit round 2 — a confirmation is a one-time token on the approved call, checked against exactly what its card showed

- the owner's own approval of a card that needed confirming adds a token to
  that call's input; its handler accepts it once, for the tenant, workdir,
  workstream, conflicting workstream, ask folders and revision the card was
  judged on (no leftover, stale or denied approval confirms anything)
- only the session owner's own token confirms (not another member's)
- the warning names the workstream it passes; the TUI shows it too
- conductor tasks known by the routing rule (no goal), not today's URI
- a stored session judged by its recorded home; answers memoised briefly
- an unnamed spawn's claim judged as when queued; a closed workstream with
  only remote rules may work in a folder it matches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the W4 e2e follows the warning that names the workstream passed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@saucam
saucam force-pushed the feat/workstreams-w3 branch from 1e01258 to 4386654 Compare November 12, 2026 03:06
@saucam
saucam changed the base branch from feat/workstreams-w2 to feat/workstreams-w1 November 12, 2026 03:06
@saucam
saucam merged commit 20d8794 into feat/workstreams-w1 Nov 12, 2026
2 checks passed
saucam added a commit that referenced this pull request Nov 12, 2026
…nd credentials, and no backend holds a secret (#462)

* feat: workstreams W1 — named credentials behind a pluggable secret store; a home's env and git identity

- credentials[] declares a credential's id and kind (bearer, header, env,
  oauth) — never its value; every reference across the config is checked at
  load (declared once, of a kind that fits)
- credentialStore picks where values live: env-file (CODEOID_CRED_<ID>, kept
  out of every agent subprocess by the CODEOID_ prefix) or dir (one file per
  id: a mounted Kubernetes Secret, a Vault sink; read-only, non-blocking,
  registered as a secret location)
- a session gets its home workstream's env (values read from the store when
  it is built) and git identity; GIT_AUTHOR_*/GIT_COMMITTER_* pass the
  extra-env GIT_ deny, nothing else does; a workstream env name codeoid owns
  for the backend fails the config load

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W1 — isolation: a session reaches only its home's MCP servers, and no backend holds a secret

With workstreams.isolateGlobalServers on (off by default: nothing changes):
- a session's MCP view lists built-ins, the servers its home selects by name,
  and the folder's own servers only when the folder is the home's; no home,
  the conductor's, or an orphaned one: built-ins only — on every backend
- Claude runs with a strict MCP config (no ~/.claude.json, .mcp.json, plugin
  or agent servers of its own); every server it may reach is an in-process
  bridge to the daemon's hub, so it holds no server config, command or secret
- other CLI backends mount natively only servers that need no secret; HTTP
  servers called with a credential go through the daemon's relay, which now
  takes the session's view; a stdio server needing env values is withheld
- a server a workstream selects with a named credential carries a reference;
  the hub and relay read its value from the secret store per request and
  refuse without it
- every hub list and call, and every relay request, is checked against the
  grant as it is now (G4): a removed server fails mid-turn with the reason
- under isolation a user's create in a folder with no home is refused with
  workstream_required; sessions the daemon starts there get built-ins only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W1 — confirmed overrides, the folder record, a home required under isolation

- a create choosing a workstream past another's exclusive claim is refused
  with what an override would change and, when the claimant allows it, a
  token: single use, five minutes, bound to the caller, folder, choice and
  config revision (G3); the confirming retry needs session:approve on an
  undelegated token and is audited (workstream.override); codeoid new shows
  the warning and asks, or --override confirms it
- how each folder last resolved is recorded per tenant: a repository rule that
  now matches another workstream (an edited origin) makes the next create
  choose, and leaves a session the daemon starts there without a home
- under isolation a user's create in a folder with no home is refused with
  the workstreams to choose from

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W1 — OAuth credential slots: a second sign-in to one server, one per workstream

- mcp_oauth_credentials gains a credential slot in its primary key, rebuilt
  once in one transaction; every existing row is the default slot ('')
- the OAuth engine reads, refreshes, stamps and signs out per slot; a pending
  sign-in carries its slot, so a completion always lands in the slot it began
  for
- a workstream's server with an oauth credential is called with that slot's
  sign-in (on the hub, the Claude bridge and the relay), never the default
- mcp.oauth.begin / disconnect and codeoid mcp signin --credential take a slot,
  refused unless it is declared as an oauth credential for that server

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W1 — codeoid workstream import, an e2e journey, and the docs

- codeoid workstream import proposes workstreams from the repositories on
  disk (grouped by remote owner) and lists Claude's global MCP servers to
  assign; it writes nothing
- skillDirs are accepted but not applied yet, and the daemon says so
- e2e: under isolation a create with no home is refused with the choices; an
  exclusive folder takes only a confirmed override; credentials show by name
- docs/CONFIGURATION.md: credentials, the secret store, what a workstream
  gives its sessions, overrides, moving an existing setup; the design's
  backlog and status

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W1 audit round 1 — CLI backends kept to codeoid's servers, no daemon variable in a server config, an honest folder record

Security:
- Codex, the Gemini CLI and Qwen loaded MCP servers of their own under
  isolation. Codex now has every server its user and project configs declare
  turned off (with a placeholder transport, so an untrusted project file still
  starts; measured on codex 0.166) and its apps off; the Gemini CLI and Qwen
  get only codeoid's server names allowed; opencode's turns are refused under
  isolation until its project config can be kept out
- ${CODEOID_*}, ${ZEROID_*}, ${TELEGRAM_*} in a server config resolve empty, and
  such a bearerTokenEnv is ignored: a repo's .mcp.json could read any
  workstream's credential from the daemon's environment
- secretFree also reads the URL (a token in its path or query, user:pass@)
  and the command line: such servers go through the relay or are withheld
- Claude under isolation bridges only HTTP servers; a stdio one needing no
  secret is the CLI's to run inside the session's sandbox, as before (the hub
  would have run a repo's script on the host)
- a delegated token can't edit an origin and then choose the workstream it
  now matches, and never writes the folder record
- the hub's per-call check refuses an unknown session under isolation; a
  relay whose credential changed is closed (the backend initializes again);
  a slot sign-in or sign-out drops that slot's hub clients too
- override tokens go only to callers who can redeem them

Correctness:
- the folder record holds what the folder matched, never a chosen home, and
  is written once the session exists: an explicit choice no longer makes the
  next plain create in that folder refuse
- an override token is spent only once its session exists
- not isolated, the relay reaches exactly what it did before
- a server address change rebuilds the Claude query
- servers withheld from a backend are said in the session, once per set
- --override without --workstream is refused with why

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W1 audit round 2 — codex plugins off, rejecting a changed origin sticks, the daemon-variable rule narrowed

- codex plugins bring MCP servers that can't be turned off by name: under
  isolation plugins are off whole (and /etc/codex layers are read too)
- choosing a workstream other than the folder's match (rejecting a changed
  origin included) no longer records the new match; the record changes only
  when the owner accepts the match
- codex's home config.toml is read through a link (account overlays link it;
  isolation failed to start for every codex account); a project's is not
- a codeoid server named like one of codex's own entries is refused: codex
  merges the two, lending codeoid's server that entry's headers or env
- the rule against naming daemon variables in a server config is narrowed to
  workstream credentials (CODEOID_CRED_*): a user's own CODEOID_* variable in
  their own config resolves as before
- an override token is spent up front again: single use under concurrent creates
- tests: codex plugins/links/merged names, the Gemini/Qwen allowlist, the
  opencode refusal, the record after a rejected change

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: codex isolation holds a config to well-formed table headers, never to the runtime parser's leniency

Bun 1.3.13 (the pinned runtime) reads an unclosed header such as
`[mcp_servers.x` as a table where 1.4 refuses it, so on CI a malformed
codex config started codex instead of being refused. tomlStructureProblem
checks every table header (outside strings and open values) and that
nothing is left open, before the parse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W2 — edit workstreams and credentials live, in the web UI (#463)

* feat: workstreams W2 — edit workstreams and credentials live

- workstream.upsert / delete / settings and credential.list / upsert /
  delete / set_value: settings:write on an undelegated token; written to
  config.json through the settings store (the whole config validated, nothing
  written otherwise) and applied to the running daemon at once — new sessions
  and every session's grant follow it, live sessions keep their homes;
  workstreams.changed announces it
- a credential's value goes to .env (0600) and the daemon's environment and
  is never sent back: only whether it is set, and where; an OAuth one points
  at codeoid mcp signin; deleting one a workstream uses is refused
- a session's MCP view fingerprint includes its grant: a workstream edited
  rebuilds that session's backend at its next turn, on every backend

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W2 — the web UI: Workstreams and Credentials in Settings, a picker and override confirm in New session, a chip on every session

- Settings → Workstreams: isolation switch, default, the list (claim, share,
  rules, servers, env), an editor that keeps what it doesn't show (the item
  as configured, not bases merged — WorkstreamSummary.config), remove, and
  'check a folder'
- Settings → Credentials: declare by id and kind, set a value from a password
  field (sent once, cleared, never shown back), clear, remove (refused while
  used), OAuth slots point at codeoid mcp signin
- New session: what the folder resolves to and why, an explicit choice; a
  choice past an exclusive folder is offered with what it would change, and
  Start anyway resends with the override token
- a workstream chip in the session header and the session list (orphaned red,
  override amber); the list filter matches workstream names
- live: workstreams.changed refetches what's shown

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W2 — the UI journey with screenshots at every viewport; fixes they showed

- e2e: Settings lists and adds a workstream, a credential value is set and
  never shown back, the new-session dialog says what the folder resolves to,
  a choice past an exclusive folder is offered and confirmed, and the chip
  shows it — screenshots at 375x667, 390x844, 768x1024, 1280x800, 1470x830,
  1920x1080 and 1280x300, the override's buttons asserted inside the window
- the Default select no longer overflows a phone's width; the override offer
  scrolls into view as it appears and says what the choice gets accurately

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W2 audit round 1 — edits apply both blocks, live env, a header is part of a credential, editor survives refreshes

Daemon:
- an edit applies both the workstreams and the credentials block from the
  file as written: a hand edit since boot is never shadowed by a stale copy
- a session's workstream env is read live: a credential set, swapped or
  cleared, an env entry removed, reaches the backend at its next start, and
  a changed env rebuilds the backend at the next turn like its servers
- a credential's header is part of its hub client and of the per-call check:
  redeclaring it with another header is another credential
- credential.delete takes a strict id and refuses an undeclared one; deleting
  an OAuth slot signs it out; a value set in the daemon's real environment is
  refused (it would win at the next start); an edit's reply needs no
  settings:read; the revision covers credentials too

Web:
- workstream rows are keyed by id: an open editor keeps what was typed when
  the list refreshes (another client's edit, a toggle)
- a forced refresh during a fetch refetches after it, so the newer list wins
- the editor checks env and server lines before sending; failed settings saves
  put the control back; remove and clear confirm, with Cancel, busy-guarded;
  the credential field uses new-password
- New session: the last folder's answer never shows for a new one, a failed
  check says so, the override offer names workstreams as the picker does and
  clears on a mode switch, focus moves to create after choosing the folder's,
  and workstream_required is said in the UI's words with the picker focused

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W2 audit round 2 — env changes rebuild a backend without MCP servers, shadowed .env values refused, an OAuth slot's removal signs every tenant out

- A session with no MCP view never rebuilt its backend when its workstream's
  env changed: #applyMcpView returned early. The env check now runs before
  the view check, and #envBuiltWith keeps a sha256, not the values.
- loadDotEnv records the keys the real environment shadowed; set_value
  refuses those too, as it did a key set only in the real environment.
- Removing an OAuth credential forgets its slot for every tenant and URL
  (Store.clearMcpOAuthSlot, McpOAuth.forgetSlot), never the default sign-in.
- Credentials rows keyed by id, so a refresh keeps a typed value.
- New-session dialog: the workstream picker in collaborative mode; pipeline
  runs get UI-worded workstream errors (showCreateError).
- The dialog's deferred focus no longer moves focus already inside it
  (a fast fill typed the workdir into the name: the e2e flake).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W3 — read fence, scoped recall, references and moves by share policy (#464)

* feat: workstreams W3 — a workstream's folders, a session's read fence, share verdicts for references and moves

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W3 — the read fence: sandbox roots and hidden paths from the session's workstream, file tools refused in another workstream's folders

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W3 — episodes record their workstream; recall, timeline and get_episode keep to the home and its references

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W3 — references at create and mid-session by share policy, moving a session to another workstream

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: lint — no assignment in an expression

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W3 — CLI and web UI for references and moves; docs

- codeoid new --ref, codeoid workstream ref add|rm, workstream move
- the session header's chip opens what the session reads and a move; the
  new-session dialog offers what it may read (under isolation)
- a fresh session's sandbox no longer says its directories changed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: lint — plain strings

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: a test's memory stub has the session scope W3 registers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W3 audit round 1 — every other workstream's folder is fenced, with what the session reads carved out; its own workdir always readable

- fenceFor: no foreign folder is dropped for being nested in the home's
  or holding it; WorkstreamFence.readable (workdir, home, exactly what a
  reference grants) is carved out — the gate checks it, the sandbox hides
  every other entry on the way (carveOut), else the folder whole
- the home is never a workstream's folder, nor a repository top above it
- a session's own workdir stays readable after a move or an override
- extraction: a glob's partial last segment, ACP locations, pi's @ and
  find/ls; a relative path with an unknown cwd is refused
- an added sandbox dir inside a fenced folder is refused and never kept
- reference inputs held to the binding's own bounds; an unreadable stored
  binding seals the session (no home, never moved)
- the session map and the frozen memory index keep to the workstream
- the chip's panel takes focus and follows the window

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W3 audit round 2 — monorepos keep to the package, granted paths where they really are, changes at the turn boundary

- a session's repository top is its own only where no other workstream's
  folder holds it; a recorded home widens to its top only where no other
  workstream has a folder inside it; folders are no longer collapsed
- reference paths resolved at grant, and counted only where they really
  are inside the granting workstream (no .., no link planted later)
- Session.setWorkstream saves at once and applies at the next turn's start
- a move adds the old home to readFrom; a home in another workstream's
  folder is judged by that one's share (closed refuses); the claim and the
  bounds are judged again when an ask is answered
- the gate's folder forms are prepared once per fence; a session's own
  folder may be written though a reference holds it
- globs judged at their start and their dir; pi's @ only before a path

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: workstreams W4 — the conductor reads across workstreams without a closed one's text, and acts through workers homed where the work belongs (#465)

* feat: workstreams W4 — the conductor reads across workstreams without a closed one's text, and acts through workers homed where the work belongs

- fleet tools name each result's workstream and take a workstream filter;
  a closed workstream's episode text never reaches fleet_find, fleet_summary,
  fleet_recall or a worker's digest (status only)
- fleet_spawn takes workstream: judged before queueing as a create is
  (a folder with none needs one; an exclusive folder is an override the
  approval card says; a closed one's folder never), stored on the task and
  judged again at claim
- every send-class approval card (web, Telegram) names the workstream it
  acts in, with the override warning approving it confirms
- egress stub: planEgress (a message is sent by a worker homed in its workstream)
- the conductor recalls its own episodes under isolation
- the fleet rail docks from md up, so a phone's approval card has the width

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W4 audit round 1 — untagged text judged by its session, confirmations tied to the card the owner approved

- untagged episodes are withheld by their session's workstream (live or
  stored), unknown ones while any workstream is closed; a closed one's hits
  are dropped from fleet_find / fleet_recall before anything is counted;
  machine_map leaves out its branches
- a worker's digest is withheld from the conductor only; the conductor's
  tenant-wide task board withholds an orchestrator's closed results
- an override or an ask folder counts as confirmed only by the owner's own
  approval of that very card, once, at that config revision (checked again
  at claim); a delegated token can't approve such a card
- an unnamed spawn keeps the remote-changed guard; a closed workstream's
  worker works only in its own folders
- codeoid approve and the terminal show the card's workstream

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: workstreams W4 audit round 2 — a confirmation is a one-time token on the approved call, checked against exactly what its card showed

- the owner's own approval of a card that needed confirming adds a token to
  that call's input; its handler accepts it once, for the tenant, workdir,
  workstream, conflicting workstream, ask folders and revision the card was
  judged on (no leftover, stale or denied approval confirms anything)
- only the session owner's own token confirms (not another member's)
- the warning names the workstream it passes; the TUI shows it too
- conductor tasks known by the routing rule (no goal), not today's URI
- a stored session judged by its recorded home; answers memoised briefly
- an unnamed spawn's claim judged as when queued; a closed workstream with
  only remote rules may work in a folder it matches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: the W4 e2e follows the warning that names the workstream passed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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