-
Notifications
You must be signed in to change notification settings - Fork 3
plat 262
PLAT-262 — Read-only workflow user: restore the read permission tier with real enforcement (not the removed dead gate)
| Coordination | Value |
|---|---|
| Assigned agent | Claude Code |
| Ticket state |
implemented, live-verified locally — mechanism keyed on WorkshopMode (single source of truth for tools/prompt/skills), with WorkflowAccessLevel doing exactly one thing: pinning a read-only identity's session to WorkshopMode="run" server-side, permanently, with no client-side toggle. See RCA #1 (why access-level, not mode), RCA #2 (why that was corrected back toward mode), and the Live reverify section (two real folder-guard bugs found and fixed during local live testing, one pre-existing dead security check removed) |
| Last synchronized | 2026-08-31 |
- Type: platform feature (design only at filing time, no code changed yet). Filed at the user's explicit request immediately after the design converged, to record the full negotiated shape before implementation starts.
-
Origin: the user asked directly whether AgentWorks has an admin-vs-
read-only user model. It does not: authentication supports multiple users
(
AUTH_USERS=user:pass,..., or full multi-user mode via Cognito/Supabase), but there is no authorization boundary once someone is logged in — every authenticated identity can do everything today. The user wants a second, read-only user: same chat/UI experience, but everything is read-only.
Commit 5db05c9af, deployed in RTS release bb7ac6d-20260912135437:
workflow selection now refreshes from the authorized manifest API on opening and
identity changes, without falling back to stale cached presets. Each query
validates source workflow paths, current access and workflow allowlists before
session/tool initialization. Missing, malformed, revoked or unreadable sources
fail closed; accepted attachments remain read-only.
Backend authorization and picker reopen/failure regressions passed. This is a check on each new request, not a claim of immediate revocation inside an already running turn or an audit of every unrelated filesystem tool. Admin-only global secret promotion is a separate explicit capability: PLAT-308.
-
A read-only permission tier already existed once and was deliberately
removed (
cmd/server/workflow_permissions.golines 14-24, removed 2026-08-17): it was never actually configured in any deployment (every caller resolved toowner, sinceWORKFLOW_READ_USERS/WORKFLOW_WRITE_USERS/WORKFLOW_OWNER_USERS/WORKFLOW_USER_PERMISSIONSwere never set), and its removal also cleaned up "a query-access gate full of unreachable checks against workshop modes that no longer exist," which cost real debugging time (PLAT-125). This plan restores the tier but wires it to concrete, currently-real enforcement points instead of the old dead workshop-mode gate — the mistake to avoid repeating is adding a permission check that nothing actually reads. -
"Run mode" is currently prompt-only, not enforced.
WorkflowInteractiveWorkshopAgent.Execute(pkg/orchestrator/agents/workflow/step_based_workflow/interactive_workshop_manager.go) callsregisterFullWorkshopAgentToolsunconditionally regardless of theWorkshopModetemplate variable — a Run-mode session today has every mutating tool available; the system prompt just asks the agent not to use them. The read-only user's whole safety property depends on Run mode becoming a real, enforced boundary, so this ticket makes that real as part of building the role, not as a side effect. -
Shell (
execute_shell_command) is already folder-guarded, not unrestricted —createInteractiveWorkshopAgentalready scopes it viaSetWorkspacePathForFolderGuard/WrapWorkspaceToolsWithFolderGuard. Today's write paths (workshopWritePaths) includevariables/,evaluation/,db/, which already lets shell bypass several typed-tool-level restrictions (update_variable,update_evaluation_plan,mutate_workflow_db) by writing the underlying files/DB directly — so gating the typed tools alone would not actually close the loop. Run mode must also get its own narrower (here: empty) shell write-path list. -
request_workflow_folder_accessnever grants anything itself — it only creates a pending request a human must explicitly approve via the desktop UI toolbar. Confirmed inert without that approval step, so it stays available in Run mode.
Superseded in one respect by the RCA below — kept here for the historical
record of what was first agreed, corrected there, not deleted. The
corrected version: tool/shell restriction is keyed on WorkflowAccessLevel
(read vs write/owner), not on WorkshopMode (workshop vs run stays
prompt-only focus, exactly as today, freely available to every user
regardless of access level). Read that section for why.
- Read-only user can chat (not locked out of the chat surface — chosen explicitly over blocking chat entirely).
-
Read-only user is server-side forced into Run mode— superseded; see RCA.WorkshopModeis not touched by this ticket at all. - The mutating tool set becomes genuinely restricted for a read-access identity: everything that mutates plan structure, step config, variables, schedules, secrets, LLM config, or skills is excluded. Execution monitoring/triggering, read-only inspection, and human communication stay available regardless of access level. Full tool-by-tool classification recorded in the plan file used to build this (see Implementation below).
- Shell gets zero write access for a read-access identity (reads stay full — same broad read set as everyone else). Actual step execution runs through its own separately folder-guarded process regardless of the chat session's own shell grant.
-
request_workflow_folder_accessstays available regardless of access level (inert without human approval). -
get_workflow_command_guidancestays available; its existing per-kindModesgating incmd/server/guidance/guidance.go'sallKindsgoverns the focus axis only (workshop vs run) and is unrelated to this ticket's access-level axis — left as-is, not audited as part of this ticket. -
No new user-creation mechanism needed. Creating a read-only user is:
add them via
AUTH_USERS(or their normal login provider), then an owner grants themreadaccess via the existingWorkflowAccessPopup.tsxUI (gets a third'read'option) orWORKFLOW_USER_PERMISSIONS/PUT /workflow/user-permissions— infrastructure that already exists and is already wired end to end forwrite/owner.
-
cmd/server/workflow_permissions.go: restoreWorkflowAccessRead, stop collapsingread/reader/run/runner/view/vieweraliases intowrite. (Done — landed ahead of this ticket's filing.) -
cmd/server/server.go/cmd/server/workflow_phase_tools.go: insideinstallWorkflowPhaseTools(already re-run every turn fromhandleQuery's live request), resolveworkflowAccessForClaims(GetUserFromContext(ctx))and pass the resulting access level down into tool registration.WorkshopMode/SetWorkshopModeOverrideare untouched. -
pkg/orchestrator/agents/workflow/step_based_workflow/interactive_workshop_manager.go: gate the mutating subset ofregisterFullWorkshopAgentTools/registerInteractiveWorkshopTools(plan-mod tools + the ~41-tool Workshop-only subset) on access level, modeled on the existingprepareReadOnlyBackgroundAgentTools/FilterCustomToolsByCategorypattern rather than a new mode-branch; pass empty shell write paths toSetWorkspacePathForFolderGuardfor a read-access identity, independent ofWorkshopMode. -
cmd/server/secrets_tools.go: skip the four secret-mutation tools for a read-access identity;list_secretsstays. -
frontend/src/components/workflow/WorkflowAccessPopup.tsx: add the'read'option to the access-level dropdown. -
cmd/server/guidance/guidance.go: out of scope for this ticket (governs the focus axis, not access level — see RCA).
Full tool-by-tool RUN-safe/WORKSHOP-only classification, exact line numbers, and the verification plan are recorded in the approved implementation plan this ticket was filed alongside (not duplicated here — see the commits that follow this filing for the executed version).
Mid-implementation, re-examination found docs/design/agent_tool_surface_single_source.md
— a dedicated design doc recording a deliberate, largely-executed decision to
remove mode-based tool filtering from AgentWorks entirely, with three
documented production incidents it fixed (Video Studio secrets tools,
list_llm_capabilities, a Builder background-child mismatch). Its core
argument: a coding CLI caches its tool catalog once at launch; removing a
tool the agent already knows about degrades gracefully (it calls, gets an
error, adapts), but adding one after launch is invisible (the agent can't
ask for something it was never told exists, so it silently shells out
instead). GetToolsForWorkshopMode filtered the registered-tool catalog
itself by the WorkshopMode request parameter — a value the same live,
continuously-cached chat session could freely toggle turn to turn — and that
mismatch between what the CLI's cache believed existed and what was actually
registered on a later turn was the recurring bug. installWorkflowPhaseTools's
own doc comment already states the fix landed: "registers one complete tool
surface and applies no workshop-mode narrowing... Mode is a focus rule now
and lives in the Builder prompt." Confirmed live: GetToolsForWorkshopMode
has zero callers today — dead code, not wired to anything.
This looked, at first read, like it invalidated the whole "restrict Run mode's tool set" mechanism this ticket depends on. Two follow-up questions resolved it instead of scrapping it:
-
Does the doc's objection apply to a role-based split, not just a
mode-toggle split? No — traced to
prepareReadOnlyBackgroundAgentTools/runReviewPlanAgent(interactive_workshop_manager.go), an already-shipped precedent that creates a separate, dedicated agent instance (its ownsessionID, its ownconfigureWorkshopToolAgentSessioncall) with a permanently reduced tool set decided once at that agent's creation (orchestrator.FilterCustomToolsByCategory, onlyexecute_shell_commandread-only-folder-guarded +human_tools:*+query_workflow_db). The design doc's own table names exactly this pattern as the one to keep: "tool-agent registration...paired with writePaths — authority — Reader vs Writer — keep", distinct from the deleted "focus — BUILD vs DEBUG" gate. A read-only user's chat session and an admin's chat session never share asessionID— the specific bug (one session's catalog drifting across its own turns) cannot occur between two people who never share a session in the first place. -
Could a user's access level change mid-session and leave a stale,
over-privileged registration in place? Traced the actual call path:
handleQuery(server.go:3045, the/api/queryHTTP handler, invoked once per message) derivesisWorkflowPhase := req.AgentMode == "workflow_phase"from that request's own payload (server.go:3460), and the tool-registration block containinginstallWorkflowPhaseToolsruns inside that same per-request condition — confirmed by reading the handler top to bottom, not inferred from a comment. SoinstallWorkflowPhaseToolsre-runs, and re-resolves the caller's access level fresh from that turn's own JWT claims viaGetUserFromContext(ctx), on every conversational turn, not once at session creation. A demotion takes effect on the person's very next message; if the agent still believes an excluded tool exists from an earlier turn and tries to call it, the MCP bridge returns a normal "unknown tool" error — exactly the degrade-gracefully path the design doc accepts as safe, not the silent-shell-fallback failure mode it warns against. This closed the one gap that would have justified an extra per-executor live-recheck layer; that layer was dropped as unnecessary once this was confirmed, not merely simplified away.
Net design correction from the original filing above: tool-registration
exclusion is keyed on WorkflowAccessLevel (the authority axis — read vs
write vs owner), resolved fresh per turn from the live request's own claims —
not on WorkshopMode (the focus axis — workshop vs run), which stays
exactly as it is today: prompt-only, freely togglable by any user regardless
of role, per the design doc's own rule that conflating focus and authority
in code is "the same one-decision-two-places failure" the doc exists to
remove. Forcing WorkshopMode="run" server-side for read-only users — part
of the original design above — is dropped for the same reason: it would
smuggle an authority decision through a variable the codebase has already
deliberately committed to treating as advisory-only. The shell folder-guard
write-path restriction is gated the same way: on access level, not on
WorkshopMode.
After the implementation above shipped and was verified live, the user
reviewed a real read-only session's chat response and pushed back hard on
one specific consequence of RCA #1's split: the agent's system prompt was
identical for a read-only user and a full admin (RCA #1 deliberately left
WorkshopMode/the prompt untouched), so a read-only user asking "what can
you do?" got a generic Builder self-description — "I can design the
workflow..." — that was true of the prompt but false of the actual tool
catalog. That is a real UX defect RCA #1's reasoning did not address (RCA #1
was entirely about tool-catalog correctness, not prompt correctness).
The fix path went through several iterations, each one narrowing on a wrong premise before landing on the final design:
- First fix: add a
ReadOnlyAccesstemplate variable, separate fromWorkshopMode, so the prompt could say "you're read-only" without touching the mode axis. This worked but produced two overlapping variables in the same prompt (WorkshopModeandReadOnlyAccess), which the user immediately flagged as confusing on sight. - The user asked to key the prompt check on
WorkshopMode == "run"instead of the separateReadOnlyAccessvariable. This was correctly flagged back as a correctness risk, not just a style question: RCA #1 established that a normal write/owner user can legitimately be in Run mode too (Bot Connector WhatsApp/Slack routes explicitly setworkshop_mode: "run"per route, with full account access) — keying the "your tools are absent" claim on mode alone would have made that claim false for those sessions. - Investigating that objection surfaced the actual resolving fact: there
is no live UI path for a normal human user to toggle
WorkshopModeat all. Grepping the entire frontend for every call site of the store'ssetWorkshopModeaction found exactly one, gated behind a slash command'srequiredWorkshopMode— and no built-in command requires"run"alone. The only real sources ofWorkshopMode="run"are: Bot Connector's per-route config, the scheduler, and the agent-profile runtime — none of them a human toggling mode mid-chat. This meant RCA #1's own justification (protecting against the documented same-session tool-catalog-cache bug fromdocs/design/agent_tool_surface_single_source.md) does not actually apply to a human chat session pinned to Run mode at creation and never switched — there is no live toggle to reopen that bug through. - With that confirmed, the user made the final call explicitly and
repeated it after seeing the Bot Connector tradeoff spelled out with a
concrete before/after preview:
WorkshopModebecomes the single gate for tools, prompt, and skills — full stop, for every caller, not just read-only ones. A Bot Connector/scheduled/agent-profile session sitting in Run mode now gets the exact same reduced tool set a read-only human does. This is treated as intentional, not a regression: Run mode's prompt guidance already told every such caller not to use these tools ("Workshop-Owned Tools — Visible But Not Yours"); this change makes that guidance literally true (tool absent) instead of merely requested (tool present, please don't call it).
What WorkflowAccessLevel still does, and nothing more: it decides, once
per turn, whether this session's WorkshopMode gets force-pinned to "run"
(server-side, in server.go, before both the prompt render and
installWorkflowPhaseTools) — and nothing downstream ever reads
WorkflowAccessLevel/readOnlyAccess again. WorkshopChatSession.readOnlyAccess,
InteractiveWorkshopManager.readOnlyAccess, SetReadOnlyAccess, the
ReadOnlyAccess prompt template variable, and the now-fully-unused
pkg/common/WorkflowReadOnlyAccessKey context-key plumbing were all removed
as part of this correction — not deprecated, deleted, since nothing consumed
them once WorkshopMode became the sole gate.
Explicitly out of scope for this correction (confirmed with the user):
the shell/file folder-guard write-path restriction in server.go (the
workflow-phase chat session's own write grant) stays keyed on
WorkflowAccessLevel directly, not WorkshopMode — switching that too would
also cut file-write access for live Bot Connector sessions sitting in Run
mode, a materially different and larger blast radius than the tool-catalog
change above, and the user's instruction was scoped to "tool and prompt
skills," not folder guard.
Implementation complete (RCA #2 is the final, current state). Results:
-
go build ./...: clean (only the pre-existing, unrelatedlibonnxruntime.dyliblinker version warning). -
gofmt -lon every touched Go file: clean. -
go test ./cmd/server/ ./pkg/orchestrator/agents/workflow/step_based_workflow/...: all pass.cmd/server/virtual-toolsfails atgo vet/go testsetup on a pre-existing missing-module import (mcpagent/agentreview), unrelated to this ticket, present before this work started. - Go tests:
-
cmd/server/workflow_permissions_test.go:read/reader/run/runner/view/vieweraliases resolve toWorkflowAccessRead(notWorkflowAccessWrite);write/owneraliases unaffected;workflowPermissionInfo(WorkflowAccessRead)yieldsCanRunWorkflows=true,CanWriteWorkflows=false,CanManageWorkflowAccess=false. Still valid post-RCA#2 — this tier's parsing is unrelated to which mechanism gates tools. -
pkg/orchestrator/agents/workflow/step_based_workflow/workshop_readonly_gate_test.go:TestMutatingWorkshopToolsAreGatedByRunMode— an AST-based test (matching this file's existingworkshop_tool_allowlist_test.gopattern, sinceregisterInteractiveWorkshopToolshas too many construction-time dependencies to invoke with fakes) proving all 18 mutating tools (debug_step,update_step_config,review_plan,review_step_code,update_variable,add_group,update_group,delete_group,update_workflow_config,set_workflow_contract_version,create_schedule,create_calendar_schedule,update_schedule,delete_schedule,import_skill,uninstall_skill,install_skill,set_workflow_llm_config) are registered only inside anif iwm.isRunModeRestricted() {...} else if err := mcpAgent.RegisterCustomTool(...)guard, and thatexecute_step/query_step/run_full_workflow/list_secretsare never gated.TestRunModeZeroesBackgroundAgentWritePathsprovesrunBackgroundTaskAgentSequence's write-path narrowing zeroes shell write grants whenworkshopModeOverride == "run", whileworkshopWritePathsitself still returns a non-empty list for a normal (Workshop-mode) caller (guards against the test passing vacuously).
-
- Frontend:
npx tsc --noEmit -p .clean;npm run buildclean (pre-existing bundle-size warning only, not a failure).
Enforcement points, all keyed on WorkshopMode (per RCA #2 — WorkflowAccessLevel
now does exactly one thing: force-pins a read-only identity's WorkshopMode to
"run", server-side, every turn):
-
RegisterPlanModificationTools(both call sites ininstallWorkflowPhaseTools, plus insideregisterFullWorkshopAgentToolsfor background sub-agents) — skipped entirely in Run mode. - The 18 Workshop-only tools inside
registerInteractiveWorkshopTools, plusmark_changelog_artifact_reviewed— skipped in Run mode, viaiwm.isRunModeRestricted()(canonicalWorkshopMode(iwm.workshopModeOverride) == "run"). -
registerSecretManagementTools—set_user_secret,delete_user_secret,set_workflow_secret,delete_workflow_secretskipped in Run mode;list_secretsalways registered. (The separate multi-agent-chat call site inserver.go— an unrelated surface with noWorkshopModeconcept — still keys onWorkflowAccessLeveldirectly, unchanged.) -
reorganize_knowledgebase/consolidate_knowledgebase— skipped in Run mode. -
phaseTemplateVars["WorkshopMode"]is force-set to"run"inserver.gofor a read-only identity, on every turn, before both the system-prompt render andinstallWorkflowPhaseTools— the single point of truth both read.workshopSession.SetWorkshopModeOverride(phaseTemplateVars["WorkshopMode"])is then called unconditionally (not just on session reuse) ininstallWorkflowPhaseTools, closing a gap where a freshly-created session's first turn could have missed the pin. - The system prompt's
## Run Mode — Execute and Monitor Onlysection (merged from two previously-separate, now-deleted sections) states plainly that mutating tools are genuinely absent, not just discouraged — keyed only on{{if eq .WorkshopMode "run"}}, with no separateReadOnlyAccesstemplate variable. - Shell/file write paths:
server.go's main chat-session folder guard grant andrunBackgroundTaskAgentSequence's background-agent folder guard both still key onWorkflowAccessLeveldirectly (deliberately not moved toWorkshopMode— see the out-of-scope note above RCA #2); read paths stay full and unchanged in both cases. -
agent_profile_workflow.go's agent-profile-driven workflow runtime (already hardcodedWorkshopMode="run") now gets its Workshop-only-tool restriction automatically, with no separate access-level thread needed —SetReadOnlyAccess/isReadOnlyAccesswere removed from this file entirely. -
WorkshopChatSession.readOnlyAccess,InteractiveWorkshopManager.readOnlyAccess,SetReadOnlyAccess, theReadOnlyAccessprompt template variable, andpkg/common/workflow_access_context.go(WorkflowReadOnlyAccessKey,IsWorkflowReadOnlyAccess,WithWorkflowReadOnlyAccess) were all deleted as part of RCA #2 — none had any remaining consumer onceWorkshopModebecame the sole gate.
Also removed as dead code at the user's explicit request, found while tracing
this ticket's live tool-registration paths: InteractiveWorkshopOnly
(zero callers anywhere in the codebase), and everything reachable only from
it — createInteractiveWorkshopAgent, enableWorkflowMainCodingAgentKeepAlive,
newWorkflowInteractiveWorkshopAgent, the WorkflowInteractiveWorkshopAgent
struct and its Execute method, and the two now-orphaned unit tests that
covered enableWorkflowMainCodingAgentKeepAlive directly
(controller_agent_factory_test.go). interactiveWorkshopSystemTemplate/
interactiveWorkshopUserTemplate were kept — confirmed still live via
planning_exports.go's RegisterWorkshopChatTools, the real path that
builds this same system prompt today. Initially misclassified
registerFullWorkshopAgentTools/workshopWritePaths/
prepareBackgroundWorkshopToolDefinitions as part of the same dead chain;
corrected before deleting anything after tracing them to the live
run_in_background background-sub-agent path
(runBackgroundTaskAgentSequence).
Ran a real local server (WORKFLOW_USER_PERMISSIONS="default=read", the
default single-user local-dev identity flipped to read-only) against a live
workflow (confida-login) and confirmed via server logs the read-only
identity's RegisterPlanModificationTools/reorganize_knowledgebase calls
were actually skipped for that exact session/turn, and the folder guard
logged no whole-workflow write grant. The agent's own greeting correctly
self-identified as "in Run mode" (the RCA #2 prompt fix working as intended,
replacing the earlier misleading generic Builder self-description).
This surfaced two real bugs, both fixed and reverified live before this commit:
-
Shell reads of
Workflow/...paths were incorrectly denied, not just writes. Root cause:wrapExecutorsWithWorkflowPhaseFolderGuard(tool_setup.go) had its own separate, earlier-stage guard — a shell-command text scan (hasWorkflowAccess) that decided "may this command referenceWorkflow/at all" from the same write-folder parameter used to grant writes. Emptying that parameter to correctly deny writes for a read-only identity also emptiedhasWorkflowAccess, incorrectly blocking reads too — a bug the real, path-based folder guard (SetSessionFolderGuard'sreadPaths, unaffected) never had. -
Browser tools bypassed the read-only restriction entirely — a separate
folder-guard closure (feeding
registerCodingBrowserTools) was never gated bycurrentUserIsReadOnlyat all, and would have granted a read-only session's browser tools full write access. Found while tracing bug 1; unrelated to it, but same fix shape.
Investigating bug 1 led to removing its root cause rather than working
around it: the hasWorkflowAccess text-scan check was traced via git log -S to the commit that introduced tool_setup.go (c2c7cba47, 2026-03-09,
not a targeted fix for a prior incident) and confirmed to have zero test
coverage anywhere in the codebase. Its only sensible purpose — blocking a
general multi-agent chat session from touching Workflow/ unless the user
explicitly attached one via the #workflow picker — belongs to
wrapExecutorsWithChatModeFolderGuard, a sibling wrapper over the same
shared low-level function that, per a full grep, has zero live callers
in the actual request-handling flow today (only exported for a test file).
The only caller that ever reaches this check in production is
wrapExecutorsWithWorkflowPhaseFolderGuard, where it is structurally
pointless — a workflow-builder session is by definition always working
inside its own Workflow/<name> folder — so it silently passed 100% of the
time for every session type that existed before read-only access did, and
broke the instant a legitimately-empty write-folder parameter appeared.
Deleted the check (hasWorkflowAccess computation + its guard block) from
wrapExecutorsWithFolderGuard entirely, and the now-dead workflowAccessDenyMode
parameter from all three functions in its call chain, rather than leave a
workaround patched around a check that protected nothing live. go test ./cmd/server/ (including tool_setup_test.go's wrapExecutorsWithChatModeFolderGuard
write-blocking tests) stayed green throughout.
Confirmed via live retest after the fix: the same cat/sed commands
against Workflow/confida-login/... paths that previously failed with
access denied: shell commands cannot reference 'Workflow/' folder in workflow builder now succeed for the read-only session.
Not yet done: verifying this same read-only flow against a real second
account in actual multi-user mode (MULTI_USER_MODE=true + distinct
AUTH_USERS identities) rather than the single local-dev identity flipped
to read-only — the local dev script hardcodes single-user mode, so this
session's live testing exercised the permission/gating logic paths
correctly but not the separate per-account chat-history isolation path
(verified instead by code inspection: chatHistoryRoot(userID) is
genuinely per-userID-keyed, confirmed no shared state).
Live testing surfaced several UI controls that either promised a capability
the read-only account doesn't have (misleading, since the click would just
fail or silently do nothing after the fact), or exposed operational actions
the user decided a read-only account specifically shouldn't see even though
the underlying tool (stop_step, trigger_schedule) stays server-side
available in Run mode for everyone by design (see the Run-mode tool-safe
bucket above). All of these are pure isWorkflowReadOnly(user, isMultiUserMode)
UI hides — none change server-side enforcement, which was already correct
either way:
-
ModePresetBar.tsx: the "Edit automation" gear icon hidden, both next to the active automation name and inside the preset-picker dropdown list. -
WorkflowChatTabs.tsx: the in-tab Stop button (busy-tab control) and the "Interact in Automation Builder" icon (converts a view-only scheduled/bot-run tab into an interactive Builder chat) both hidden. -
WorkflowScheduleRunsPanel.tsx(the Schedules popup): per-schedule Stop/Run now/Resume buttons hidden (both the compact and expanded list layouts); in the "⋮" action menu, "Pause schedule" and "Delete schedule" items hidden individually ("Show details" stays visible for everyone); the global scheduler pause/resume control and the per-workflow-group bulk pause/resume control both hidden. -
WorkflowCapabilitiesPanel.tsx(opened via the canvas toolbar's skills/MCP/secrets/browser/LLM-config icons — a separate entry point from thePresetModalthe gear icon opens): the Save footer button hidden: nothing in that panel can actually persist for a read-only account.
Verified: npx tsc --noEmit -p . and npm run build clean after each
change (pre-existing bundle-size warning only, not a failure).
The search input and document keydown listener both handled the same arrow event, advancing selection twice. With rts-latency, rts-aws and automation-testing, one ArrowDown selected automation-testing. Input arrow/Escape handling now stops propagation, preserving document handling when focus has not moved into search. A DOM regression reproduced the skip before the fix and covers down/up selection and single Escape dismissal. This changes navigation only, not list permissions. Implementation verified locally; RTS deployment verification pending.
The latest follow-up above is included in RTS release 3a37a1c-20260912143119
(app 3a37a1c75). Source revisions and all three services were verified after
activation. The configured-environment Linux sandbox regression passed.
Earlier pending-deployment notes are superseded; feature-specific live acceptance
limits remain as documented. No user accounts/sharing or notification recipients
were changed during verification.
State: implemented and regression-tested locally; deployment and Confida live reverify pending.
Confida testing session be5087d3-b2c4-4fe1-a114-007a3fe12dcd was built in Run mode. Logs at 14:19:14 server time show mode=run; calls to update_workflow_config at 14:21:10 and 14:31:21 failed because the tool was not registered. The account subsequently has owner access, but warm delivery bypassed definition reconstruction. The attempted mcp_servers:[] payload was also unsupported: workflow detachment uses remove_servers with configured server names. Neither failed call removed an integration.
The existing ChatPolicyKey already hashes effective mode, origin, admitted capabilities, product chat definition and MCP configuration. Do not add a parallel permission fingerprint or change the durable chat ID. workflow_retained_policy.go revalidates current workflow access and compares that key before eligible /api/query retained delivery and before /live-input delivery. Unknown or mismatched old admission falls back to construction; revoked access fails closed. The early comparison does not publish the new key, so the existing reconnect/handoff path can still recognize the old policy.
A mismatch cancels the old foreground turn, closes its actual native provider and routes the message through normal serialized setup. Explicit new turns and auto-notifications do not perform early interruption merely to attempt retained delivery. Existing tool execution context checks still revalidate permissions per invocation; the fingerprint is lifecycle coordination, not replacement authorization.
Verification covers promotion, demotion, revoked access, unchanged warm admission, unchanged previous-key visibility to reconnect, live-input next-turn dispatch and preservation of the current message/workflow. Explicit new-turn admission regression protects the foreground turn. This is revalidation at the next message/invocation, not a claim of instant revocation of every already-started external action.
The separate bounded suggestion exception is PLAT-330.
Confida release confida-f75a304b-20260918150257 correctly admitted Builder tools, but a restored request still carried execution_options.workshop_mode=run. Session metadata, reconnect notices and the private native CLI directory continued using that stale value. Normalize the workflow-builder conversational request from resolved current access before those consumers run, preserving other execution options and leaving Crew profiles and headless execution unchanged. Regression checks cover owner promotion, reader demotion, missing options, private directory mode selection and the exclusive authoring guard.
Auto-synced from docs/ on main. Edit there, not here.