-
Notifications
You must be signed in to change notification settings - Fork 2
plat 296
Update 2026-09-30 (PLAT-371): the private per-session runtime folder stays for workflow Builder/Run chats. Crew and Code run in the project folder and no longer overwrite or delete a project's instruction files: adapters add a marked, session-counted block and
mcpagent's cleanup removes only what a session projected. The collisions and "restore or remove" behavior described below for a shared folder are superseded there. Moving workflows to the shared folder too is planned: plan, design.
| Coordination | Value |
|---|---|
| Assigned agent | Codex |
| Ticket state | implemented on main — concurrent Builder and Run chats use separate prompts, tools, skills and private CLI runtimes while sharing the guarded workflow workspace; scheduled runs use the Run surface. Cross-provider cleanup and real two-user authorization remain follow-up acceptance work |
| Last synchronized | 2026-09-07 |
- Related: PLAT-262 (server-enforced Run permissions), PLAT-297) (Codex retained-session, resume and structured-event reliability).
Owner/Workshop and Run sessions can work on the same workflow, with different prompts, tools and skills. The server selects these per session, but main workflow chats give their CLI processes the same workflow working directory. The CLI adapters project session-specific instructions and skills into fixed paths in that directory. Separate conversations therefore do not guarantee separate instruction files.
For example, an Owner session projects its instructions, then a Run session projects different instructions into the same path. A subsequent file read, startup or continuation can see the other session's content. Closing one session can also remove or restore a file another session still needs. Same-mode sessions can collide too; Owner versus Run makes the difference in intended guidance particularly visible.
The adapter-level collision is now reproduced (see the test record below). It is not yet a reproduced live two-user incident or a demonstrated permission bypass. Server-side tool grants are separate from projected prompt text. The complete filesystem and user isolation boundary still needs verification.
-
agent_go/cmd/server/server.go: workflow-phase chats setchatWorkingFolder = workflowPhaseFolder, derivechatWorkingDir, and pass it asCodingAgentWorkingDir. Read-only users are forced into Run mode. -
agent_go/cmd/server/coding_agent_modes.go:codingAgentWorkspaceWorkingDirresolves the workflow folder beneath the workspace documents root. -
agent_go/cmd/server/workflow_phase_tools.go: Workshop mode gates the available mutation tools and guidance. Guidance materialization inguidance/materialize.goattaches mode-specific content under stable skill identities, includingbuilder-reference. - The locally replaced
mcpagentdependency,agent/coding_agent_options.go, uses the configured working directory unless isolated-session workspace mode is enabled. It requests project-instruction-only delivery, so a successful projection can be the CLI's sole system-instruction carrier. - The locally replaced
multi-llm-provider-godependency's Codex adapter,pkg/adapters/codexcli/codexcli_interactive_adapter.go, writesAGENTS.mdbeneath that working directory. Its cleanup restores or removes the path without checking whether another session now owns its contents.codexcli_skills.goprojects skills into.agents/skillsbeneath the same directory. - The Claude adapter also projects project instructions and attached skills into the working directory. Audit each supported provider's actual paths, discovery rules and cleanup behavior during implementation.
-
mcpagent/agent/coding_agent_options.goalready provides an isolated temporary directory keyed by session ID for opted-in agents. This is a useful precedent, but it can fall back to the shared working directory if directory creation fails. It is not a complete main-chat migration. -
mcpagent/agent/session_handle.goprioritizes the persisted working directory when resolving continuations. Existing CLI sessions therefore need an explicit resume/migration strategy.
Dependency paths above refer to sibling repositories selected by
agent_go/go.mod; changes may span all three repositories.
- Give each chat a private runtime directory scoped to the authenticated user/tenant, workflow and session. Keep its identity stable across turns and supported server restarts. Prompt, skill and provider configuration writes must not affect another live session, including during mode changes.
- Keep the workflow's plan, reports, database, outputs and learnings in the authoritative shared workflow workspace. Do not copy the entire workflow into independent per-user folders. Separate the CLI runtime directory from the workspace tools' workflow root.
- Audit relative paths, shell execution, provider instruction discovery and skill references. Use explicit workflow-root resolution or a controlled mapping so moving the CLI runtime does not break workflow file access or accidentally discover another mode's generated instructions.
- Make cleanup ownership-aware. Cancellation, failures and shutdown must remove only the terminating session's generated files. Preserve user-maintained project instructions.
- Persist the runtime location needed by native CLI resume. Define migration for existing sessions: resume safely or start a new native conversation with application history preserved and an explicit handoff. Do not silently resume against a different workflow or mode.
- Retain server-side authorization independently of prompt isolation. Run users must not acquire Owner tools or unauthorized filesystem access by changing a client mode value, path or prompt. Folder separation alone is not a permission boundary.
- Fail with an actionable error if required isolation cannot be established; do not silently fall back to shared instruction files.
- Check Codex, Claude and other supported CLI provider paths, plus main chats, scheduled execution and step agents. Reuse existing isolation where it meets this contract and avoid breaking provider session coordination.
Concurrent workflow edits remain a separate concern: verify compatibility with PLAT-290)'s execution snapshots and retained run revisions. Private instruction folders must not change which plan revision an active run executes or widen workflow mutation permissions.
- Run two same-provider chats on one workflow as Owner and Run, with distinct prompt/skill markers. Test both startup orders, continued turns and skill reads. Each session consistently receives its own guidance.
- Repeat with two same-mode sessions and relevant cross-provider combinations.
- Close, cancel or fail one session while the other continues. Its instruction and skill files remain intact and usable.
- Switch one chat's mode while another is active; verify both content and actual server tool grants. Forged Run-to-Owner requests remain denied.
- Verify normal workflow file reads, permitted writes, report generation, database access and outputs resolve to the correct shared workspace after changing the runtime directory.
- Restart and resume chats, including a pre-migration session. Check native conversation identity, history, mode and working-directory association.
- Change a plan as Owner while a Run session executes; confirm the existing run-revision behavior remains correct.
- Force private-directory creation failure and assert an actionable failure with no shared projection fallback.
- Use a fixture workflow with no public side effects for concurrency tests.
Workflow-chat isolation is implemented as the server default, with
AGENTWORKS_ISOLATE_WORKFLOW_CLI=false retained as an explicit rollback. The
separate-instance launcher also defaults to isolation. Both initial agent
construction and workflow-phase setup select private directories; the workspace
bridge retains the shared workflow root. Resume checks reject mismatched cwd and
Codex project-directory overrides. Read-only mode identity is pinned to Run.
The implementation is on main. Full cross-provider cleanup and two-user authorization acceptance remain open. Codex restart/resume behavior and its structured transcript boundary were subsequently hardened and tested under PLAT-297). The current policy replays application history into a fresh native conversation when resuming into a different app chat; only the same private chat identity retains native continuation. Runtime directory garbage collection is deferred.
Standard process: Isolated workflow testing.
Runner: scripts/test-workflow-isolation.py; projection fixture:
scripts/testdata/plat296_projection_test.go.tmpl.
Used an allowlisted, sanitized copy of the existing Workflow/testing. Tested
real Codex and Claude prompt/skill projection functions through Go overlays,
without modifying the provider checkout or invoking any native CLI/model.
Both providers reproduced prompt, skill body, supporting-reference and cleanup
collisions with overlapping simulated Owner/Run lifetimes. Both startup orders
and both cleanup modes were exercised. Private-directory controls passed while
retaining a shared workflow plan. Source workflow input hashes were unchanged.
16 cases passed in reproduction mode (8 per provider, including controls).
The --expect isolated --providers codexcli negative check failed as expected
on the shared-directory cases. The existing isolated-instance launcher test
also passed using its fake runner.
Local retained evidence:
.local/workflow-tests/plat-296-5wrfgb8_/receipt.json and the provider logs.
The receipt records the source hashes and scope. No running AgentWorks server,
browser, workflow or native session was started/stopped/reconfigured. Zero model
calls. This does not yet verify the server's directory choice, real two-user
permissions, native continuation, restart or mode switching.
- Production selector:
agent_go/cmd/server/workflow_cli_isolation.go, backed byagent_go/pkg/cliruntime/workspace.go. Private storage is required outside workspace documents and rejects symlinked managed directories and obstructions. - Server restore tests cover matching private continuation, old/shared or other-chat rejection, provider/mode isolation, and both representations of Codex's cwd override. Claude, Pi and Cursor handle checks are exercised; their full native CLIs are not invoked.
-
python3 scripts/test-workflow-isolation.py --server-isolationtests the production selector, then feeds its paths to the real Codex/Claude projection and cleanup functions. Both startup orders and cleanup modes pass, with shared workflow instructions/data retained. Shared-directory cases still reproduce the original collision, so the probe retains its negative control. -
bash scripts/test-local-instance.shverifies flag forwarding with a fake runner. No existing server, browser, tmux pane or source workflow was changed.
Evidence: .local/workflow-tests/plat-296-azko23d8/receipt.json,
server-isolation.log, server-runtime-directories.json, codexcli.log, and
claudecode.log. These are local retained artifacts, not committed fixtures.
The separate test instance at http://127.0.0.1:52733/ was restarted with the
user's existing /Users/mipl/.codex login, as requested. The earlier fresh Codex
authentication directory had caused the CLI to wait at sign-in; it was a test
setup issue. No credentials were copied and the production backend remained
running as the same process.
The real Codex chat now completes a response, reads builder-reference and its
file-layout reference, and lists the shared testing workflow's planning/plan.json,
planning/step_config.json, soul.md, workflow.json, and conversation files.
The CLI launches in the private runtime selected before the restart, while
workspace shell commands still run in the shared testing workflow folder.
Listing the private CLI directory through the workspace shell is denied by its workspace-root boundary. This is recorded separately, not counted as a successful private-directory read. This smoke check does not complete concurrent Owner/Run, cleanup, mode-change, two-user authorization, or native-resume acceptance.
Local evidence: .local/agentworks-instances/plat296-live/live-file-access-retry.json
and that instance's server log; chat 87999abb-c50e-4e8b-a26b-b58f3dc9ed3a.
Tested the existing Codex chat as a builder, using its normal workflow APIs and file tools. The outer test driver only submitted chat requests and inspected results; it did not implement the plan or Python changes for the builder.
Verified on disk:
- Read the existing plan and builder-reference skills.
- Renamed the existing step through
update_scripted_step. - Added
builder-write-probethroughadd_scripted_step, including JSON output validation, and authoredlearnings/builder-write-probe/main.pythrough the workspace patch tool. - Passed
validate_plan_changewith no issues. - On a subsequent chat turn, updated both code and plan description and repaired the test fixture's file layout through the same normal tools.
- Executed only the new scripted step using
execute_step(fast_path_only=true). Executionexec-builder-write-probe-1788679689541286000finished successfully. Its real output atruns/iteration-0/execution/builder-write-probe/builder-write-probe.jsoncontainsmarker: PLAT296_BUILDER_OK,soul_readable: true, andshared_workflow_marker: PLAT296_SHARED_WORKFLOW.
The first execution failed because the test fixture placed soul.md at the
workflow root. Execution steps are granted read access to the canonical soul/
folder. The builder copied the fixture content into soul/soul.md and corrected
its script and description; the retry passed without widening permissions.
The native CLI cwd remained the private per-chat runtime throughout these turns.
Production testing-workflow input hashes were unchanged.
The entire automatic builder flow does not pass yet. On both the failed and
successful step completions, the notification dispatcher cleared a supposedly
stale busy flag and attempted a synthetic turn while the retained Codex turn was
still active. AskWithHistory returned
mcpagent: a turn is already in flight on this agent. The dispatcher then logged
the synthetic turn as completed, and chat stayed at “waiting for automatic
completion.” The successful JSON was independently verified; automatic return
to the builder and readback were not credited as passing. Investigate the busy
check and notification retry handling in background_agents.go before claiming
end-to-end readiness. This evidence does not establish that directory isolation
caused the notification race.
Evidence: .local/agentworks-instances/plat296-live/builder-acceptance/receipt.json,
its before/ and after/ artifacts, and server log entries at 12:52:48 and
12:58:09 local time. Concurrent Owner/Run, mode-change, two-user permissions and
the full native-resume/restart matrix remain pending.
Opened the blank Builder tab in the isolated test UI and verified a new backend
chat (07fdef59-20d2-4e9d-8ead-fd76d88d00eb) and private CLI runtime. The test
used only two ordinary messages, with no tool names, paths or acceptance markers:
- “What does this workflow do right now?”
- “Can you add a step at the end that writes a short, readable summary of what this workflow checks? Run that new step once and show me the summary.”
The builder independently read skills and canonical files, explained the existing
workflow, added summarize-workflow-checks as a message-sequence step, validated
the plan and launched it. The first execution failed because the new step lacked
read permission for planning/plan.json. Its failure notification reached the
builder, which added scoped additional_read_paths, validated and retried without
another user message. Execution exec-summarize-workflow-checks-1788680544501472000
succeeded. The success notification also reached the builder, which read and
displayed the actual workflow-checks-summary.md; the file was independently
verified on disk. Production testing-workflow input hashes were unchanged.
This fresh-chat builder flow passes after autonomous recovery. It does not erase
the earlier retained-chat notification race or establish concurrent Owner/Run,
two-user permissions, or restart/resume acceptance. Local receipt and artifact
copies: .local/agentworks-instances/plat296-live/natural-chat-acceptance/.
Added a Run chat action in the workflow composer controls. It creates an
independent workflow chat whose tab metadata pins workshop_mode=run; normal
Builder chats continue to use workshop. Submission and command discovery now
read the mode from the owning tab, so changing tabs cannot make two concurrent
chats inherit one workflow-global mode.
Live-tested both chats at the same time in the isolated testing instance:
- Run chat
5d413cc2-f374-4599-b93a-234d21606f81received therunoverride, had 67 registered workflow tools, and used private runtimecli-runtimes/v1/7c0acb.... - A Builder chat received the
workshopoverride with 128 registered workflow tools while the Run chat was active. A second Builder conversation created directly from the permanent Builder launcher repeated that result as fresh session926dba2c-f472-4b24-9549-c46947108154, using private runtimecli-runtimes/v1/d3e469db.... - Both read the same shared testing workflow while running concurrently.
- After switching away and back, the Run chat refused a requested plan type conversion and directed the user to Workshop. No workflow files changed.
This completes same-user concurrent Builder/Run prompt and tool separation. Two-user authorization and restart/native-resume acceptance remain pending.
Normal cron and calendar schedule messages now execute with
execution_options.workshop_mode=run. This gives unattended work the same
constrained prompt and tool policy as an interactive Run chat and selects a
private CLI runtime directory by default, derived from the schedule session,
provider, user, workflow and Run mode.
Explicitly selected workflow business skills remain attached so the scheduled
job can perform its purpose.
The scheduler chooses authority per turn. Contract-upgrade and answered-decision preflights use Workshop mode because those bounded turns may update workflow artifacts. Pulse Gate, review/fix and finalization also use Workshop mode. The request helper clones nested execution options, preventing one maintenance turn's elevation from leaking into the normal scheduled run that follows.
Run's projected reference bundle no longer contains Workshop slash-command procedures, Pulse repair/drift material, secret management, or backup/publish authoring guides. Focused scheduler, tool-policy, guidance materialization and prompt tests pass.
Conversation and cost continuity were audited after introducing these per-turn mode changes. The canonical conversation JSON continues to merge the full pre-switch transcript with each new exchange while excluding the synthetic conversation-file pointer used to ground the relaunched CLI. A disk fallback now loads that prior transcript when the process-local history cache is empty, so the overwrite guard does not preserve the old file at the expense of dropping the new turn.
Cost attribution now uses pulse_lifecycle_turn as the authoritative Pulse
signal; Pulse can use the Builder model without the retired
llm_config_source=scheduled_pulse marker. Phase-token cumulative bookkeeping is
also keyed by native CLI session epoch. A Run→Workshop relaunch therefore records
the new process's complete first-turn usage instead of subtracting the prior
process's counters. Non-native phase turns use their query/turn id as the epoch.
Tests cover cumulative history across mode changes, disk-history recovery,
native-runtime counter resets, explicit Pulse scope, and the existing immutable
cost-ledger behavior.
The private-directory design remains necessary: the offline negative control reproduces prompt, skill, reference and cleanup collisions when two modes share one directory. The review found additional provider lifecycle defects, now covered by failing-before/fixed-after regressions:
- Cursor compared Git-root strings, misidentifying case aliases on macOS. Its
fallback removed existing
.gitmetadata. Root checks now use filesystem identity, marker creation refuses existing files/directories, setup fails explicitly instead of loading the parent project's configuration, and cleanup checks that it still owns the marker directory. - Cursor used one textual cwd hash for transcripts. Physical spelling now drives discovery; exact native-session lookup also accepts legacy path hashes. Missing known transcripts no longer fall through to a different chat's newest database. Resumed streaming follows the same native-ID rule and primes old messages before submitting the next prompt.
- Cursor cleanup deleted the entire
.cursortree, overriding even the explicit restore option. It now cleans generated files individually, preserves unrelated content, and honors restore mode for configuration and projected instructions. The existing default remains non-restoring for the specific generated files. - Pi's workspace MCP lease and retained-launch comparison used literal paths; aliases could bypass the conflicting-configuration check. They now use physical identity/canonical keys. Claude trust, MCP approvals and transcript candidates now include physical spellings alongside legacy keys.
- AgentWorks containment, resume and Pi cleanup matching now account for physical filesystem identity. The existing v1 runtime digest input is deliberately unchanged, so this repair does not migrate saved chats to new directories. User, session, provider and mode boundaries remain enforced.
A shared pathidentity package in the provider library implements these rules.
It does not blindly lowercase paths or change the process working directory.
Case-sensitive filesystems must continue distinguishing genuinely different
folders. The test suite exercises symlinks everywhere and real case aliases on
case-insensitive filesystems.
From the AgentWorks repository:
python3 scripts/test-workflow-isolation.py --server-isolation --expect collisioncollision is the expectation for the deliberately shared-folder negative
control. Every private-directory case must remain isolated. The harness now
covers Codex, Claude, Cursor and Pi using production prompt/skill writers and
server-selected runtime directories (32 projection cases). Pi always restores
prior projected-file contents; the two restore parameter cases exercise the
same Pi behavior. It copies only testing-workflow design inputs, disables
schedules/capabilities, verifies source hashes afterward, and launches no server,
CLI session or model. All four providers and server selection passed in
.local/workflow-tests/plat-296-p3q25_68/.
The full four provider adapter unit packages and shared path tests pass, as do focused AgentWorks resume/mode/schedule, prompt, conversation/cost-continuity and mcpagent continuation tests. Provider lint reports zero issues.
Remaining acceptance is explicit: live cross-provider restart/resume and real two-user authorization were not rerun during this audit. Cursor's first-ever turn still uses time-based transcript discovery until a native ID is known. Changing the workflow path spelling itself can still select a different v1 runtime digest; a future identity migration must preserve existing sessions. The case-alias resume fix covers different spellings of the same runtime, not an automatic migration between different runtime directories.
The audit also found that mcpagent compiled inside the local go.work, but
failed with GOWORK=off: its published provider pin did not define
llmtypes.WithCodingAgentReleaseSession, already used by its code. The provider
pin is now updated, with the required transitive module versions selected by Go.
Focused continuation, session-handle and isolated-workspace tests pass against
the published module with the workspace disabled. Future acceptance must include
this check; local replacement modules alone cannot prove a deployable build.
AgentWorks also passes focused isolation, resume, prompt and conversation/cost
checks with GOWORK=off and both provider/mcpagent local replacements removed
from a temporary module file. That verifies the published dependencies rather
than sibling checkouts. The tested pins are provider 62c1b6e and mcpagent
6073b0a. The check uses published dependency modules and deletes its temporary
module file afterward. Focused path/Pi-lease race tests also pass.
The AgentWorks repository-wide commit hook still reports 18 existing non-blocking
lint findings (one reflect.Ptr vet suggestion and 17 spelling findings), all in
files outside this patch. This audit does not claim that repository-wide lint is
clean; provider and mcpagent lint are clean.
The social-media run reproduced a same-mode session ownership collision: a
cancelled execution completed cleanup after its replacement had started, and
both used the same deterministic message-sequence session ID. The old cleanup
removed the new execution's shell cwd/guard, producing WORKFLOW STEP CONFIG ERROR: ... refusing workspace-root fallback.
New runtimes now receive a unique owner suffix; only turns in the same live sequence reuse it. Saved history cannot reclaim an old runtime identity. The regression test failed before the fix and passes after it. This addition is local source, not yet deployed; no live retry or server restart was done. See incident and verification record.
The follow-up lifecycle review also fixes failed-creation rollback, final MCP connection cleanup for the owned runtime, and inconsistent DB tool selection between default and explicit lists. Shared browser sessions remain separately owned. Permanent tests cover those cases, restricted parent tool pools, and partially initialized agent cleanup. These changes remain local, not deployed. See the session/tool lifecycle review.
Auto-synced from docs/ on main. Edit there, not here.