-
Notifications
You must be signed in to change notification settings - Fork 2
crew_shared_root
Update 2026-09-30: "Run mode" readers and guest calls now share one Crew prompt with the owner; the reader role is a block in front of each message, enforced by tools and folder guards, and refusals name
submit_crew_suggestion. A guest call is a reader. See project instruction files, PLAT-371.
Status: design, 2026-09-26. Not started.
A crew lives in its owner's private tree, _users/<owner>/Chats/Work/projects/<slug>-<id8>,
and so has two names:
-
The owner's UI sends the user-relative
Chats/Work/projects/<id>. The workspace service silently expandsChats/…under the caller (workspace/utils/path.go:136). -
Readers (Crew Run mode) must send the physical
_users/<owner>/…form, because the short one would expand into their own tree.
Every endpoint has to translate between the two, and each does it by hand. There are about 17
helpers and 15 hand-written _users/ checks. Each new feature breaks on whichever form its author
did not test. On 2026-09-26, window.report.run returned 400 for the owner, and the live feed
drops every crew notice.
Workflows never had this problem. They live at a shared Workflow/<name> with owners and readers
in workflow.json, so there is one name and access comes from the manifest rather than the
location.
Crews move to a shared root, Crew/<slug>-<id8>, keeping the folder name they have today.
-
Owner: comes from the crew manifest (
product.jsongainsowner_id), never from the path. -
Access stays as it is today (Crew Run mode):
- the owner gets full access;
- any other user with the Crew product gets Run mode;
- users without the Crew product get nothing.
- Old paths, both logical and physical, keep resolving through one alias resolver indefinitely. Old links, stored references we missed, and open tabs keep working, and a warning is logged.
- Other products (SparkQuill
Chats/SparkQuill/activities, Video Studio) are not moved. They have no cross-user readers.
// crewref.Resolve(callerID, anyPath) -> (root "Crew/<id>", ownerID, ok)This accepts Crew/<id>, Chats/Work/projects/<id> (resolved under the caller) and
_users/<o>/Chats/Work/projects/<id>. Every API entry point and every stored-reference reader
calls it. After the migration, the old helpers are deleted:
-
crewProjectOwnerID,isCrewProjectPath,canonicalCrewWorkspaceRoot; -
reportRunPhysicalCrewRoot,isOtherOwnerCrewPath,crewReaderSharedAsset's segment checks; -
pkg/commonClassifySessionWorkspace's crew branch, and the frontend's prefix regexes.
A guard test fails if any new non-test code in cmd/server matches Chats/Work/projects or
"_users/" for crews.
Today the only thing protecting crews is that foreign _users/* is blocked. A shared Crew/ has
no such protection, so these gates are needed:
-
cleanAgentProfileWorkspace(agent_profile_runtime.go:97) must checkCrew/<id>against the manifest owner or the reader policy. Otherwise any user could chat in someone else's crew in full mode. -
The raw workspace proxy (
workspace_proxy.go:96,184,481) must gateCrew/the way it gatesWorkflow/: the owner reads and writes; readers get no raw access and go through the/shared-projectsendpoints as today. -
The live feed's
liveFeedAccess.visiblemust use crew visibility forCrew/notices.
These currently take the owner from the path and must switch to the manifest:
-
services/bot_scope.go:20ValidateBotScope; -
pkg/workflowtypes/crew_attachments.go:74ValidateCrewAttachmentBinding(it also requires aprojectssegment, so it rejectsCrew/<id>today); -
crew_access.go:41crewProjectOwnerIDand its about 10 callers (schedules, triggers, webhooks, reader chat mirror); -
services/bot_connector.go:3705routeWorkspaceUserID.
Model it on crew_bot_scope_migration.go + RewriteSlackScopes: keep a record, and log crews
where it cannot find exactly one owner.
For each _users/<owner>/Chats/Work/projects/<p> with a valid work product.json:
-
Stop live coding-CLI panes for the crew (tmux
mlp-*for that workspace). A deploy restarts everything anyway. -
Write
owner_idintoproduct.json. -
Rename the folder to
Crew/<p>. It is on the same filesystem, so this is instant. IfCrew/<p>already exists, stop that crew and log it; never merge. -
Record the alias
old logical,old physical→Crew/<p>in_system/crew-path-aliases.json(read by the resolver). -
Rewrite stored references. Store by store:
Store Field <crew>/builder/conversation/session-*.jsonand readers'_users/<r>/chat_history/…workspace_path,runtime.workspace_pathsame runtime.agent_session_handle.provider.working_dir,project_dir_id(absolute paths)_users/<u>/chat_history/product-conversations.jsonworkspace_path_users/<u>/chat_history/submissions/*.jsonproject_users/<u>/chat_history/crew-creation/*.jsonworkspace_pathevery Workflow/*/workflow.jsoncrew_attachments[].crew_workspace_path,workflow_context_pathsevery crew workflow.jsonworkflow_context_pathsSlack config (connections + channel routes), bot allowed_channelsworkspace_pathWhatsApp channel routes (meta table) WorkspacePathcost ledger sqlite workflow_id(UPDATE old → new; logical owner rows gain the owner)Path-independent, so no change is needed: triggers and callers (keyed by manifest ID), Slack thread bindings, structured-chat-events sqlite,
product-schedules.json, work-folder access. Webhook and schedule discovery rescan the root; they must scanCrew/instead. -
Coding-CLI native context. This is the highest risk. mcpagent replaces the working directory with the saved
handle.WorkingDiron resume, andclaude --resumeonly searches~/.claude/projects/<slug(cwd)>/. For every account home ($HOMEand eachCLAUDE_CONFIG_DIRfromprovider_connections.go:150), copyprojects/<slug(old)>/<sid>.jsonl(and its sidecar dir) toprojects/<slug(new)>/. Codex's--cd/project_dir_idis fixed by step 5. Cursor, Pi and Muse only needworking_dir(step 5). Without this, AgentWorks history survives but each crew silently starts a fresh native session. -
Browser profile: rename the profile dir keyed by
hash(old physical)tohash(Crew/<p>), so saved browser logins survive (pkg/common/types.go:659). -
Projected files (
.claude/skills,.agents/skills,.pi/skills,CLAUDE.md,AGENTS.md) move with the folder and are regenerated on the next turn. No action is needed.
-
internal/workproduct/product.yaml:162:projects_root: Crew. The generic product-project store (product_conversation_registry.go:750,774) scans<root>/*/product.json. It must treatCrewas shared (not per-user) and filter by manifest owner for "my crews". -
Owner transcripts: allowed conversation paths (
chat_history_persistence.go:3399,3691) and submission discovery roots (chat_submission_journal.go:594) should useCrew/. -
Cost overview (
cost_overview.go:86): the crew branch keys onCrew/<id>and the manifest owner. -
Live feed:
livefeed.WorkflowRootand the frontendliveFeedWorkflowRootacceptCrew/<id>. Crew runs publish report and human-input notices. This fixes crew dashboards never auto-refreshing. -
window.report.run: drop the crew path special cases; resolve through the resolver. -
Frontend:
-
WORK_PROJECTS_ROOTbecomesCrew. -
productProjects.ts:242must stop creating crew folders client-side; creation goes through the server only (crew_creation.go), which also writesowner_id. -
sharedCrewFilesandChatInput's_users/shared-client switch become "owned by me?" from the crew summary. - Report page,
ReportDocumentSwitcher,CostsOverviewandslackWorkflowConnectionstop parsing prefixes. -
activitySessions.tsregex coversCrew/.
-
-
Folder Guard: crew roots are
Crew/<id>. Reader read-only is unchanged, and so are workflow crew attachments (WORKFLOW_CREW_*).
- Access gates and the resolver first, accepting all three forms. This ships with no behaviour change.
-
Switch readers and writers to the resolver and
Crew/, and add the migration. Put the guard test in. -
Verify on an isolated server (per the P0-gate recipe) with a copy of RTS's 4 crews (18 GB, all
one owner) and their
~/.claudestores:- owner chat resumes the same native CLI session, for Claude, Cursor and Codex;
- a reader gets Run mode and cannot write;
- Slack DM and channel for a crew bot;
- a crew trigger and schedule;
- a workflow crew step and the attachment env;
- dashboard
window.report.runand live refresh; - Costs for the crew;
- browser logins kept;
- an old
Chats/Work/projects/…link still opens.
- Deploy in a quiet window. The migration runs once on startup.
- After a stable period, remove the reader copies of old-path handling. The alias resolver stays.
About 17 Go files and 8 frontend files of real changes, plus the migration. 74 Go + 25 frontend test files hardcode the old path, mostly mechanically. Roughly 2–4 days including isolated verification.
- Folder naming: keep
<slug>-<id8>(proposed; renames are cheap) or use the bare manifest ID. - Should
Crew/be listable by everyone through the raw proxy (likeWorkflow/), or stay behind/shared-projects? Proposed: stay behind/shared-projectsfor readers, so Crew Run mode does not change.
Auto-synced from docs/ on main. Edit there, not here.