Skip to content

Add sandbox and approval run profile controls#100

Merged
slashdevcorpse merged 4 commits into
mainfrom
feature/sandbox-run-profiles
May 31, 2026
Merged

Add sandbox and approval run profile controls#100
slashdevcorpse merged 4 commits into
mainfrom
feature/sandbox-run-profiles

Conversation

@slashdevcorpse
Copy link
Copy Markdown
Owner

@slashdevcorpse slashdevcorpse commented May 31, 2026

Summary\n- add named Codex run profiles for read-only inspect, workspace write, and elevated manual review\n- show effective sandbox and approval flags before send and require confirmation for write/elevated profiles\n- persist the selected default profile per workspace and pass the selected profile through the send flow\n- record the profile, sandbox, and approval policy on session history messages\n\n## Testing\n- pnpm -C apps/codex-claw lint\n- pnpm -C apps/codex-claw build\n- pnpm -C apps/codex-claw test\n\nCloses #87


Summary by cubic

Adds named Codex run profiles with sandbox and approval controls across chat and settings. Profiles sync with the workspace sandbox by default; write and elevated runs require confirmation and are recorded in history. Implements Linear #87.

  • New Features
    • Run profiles: Inspect (read-only), Write (workspace-write), Elevated (danger-full-access). Write/Elevated require confirmation.
    • Profile–sandbox sync: profile defaults derive from sandbox; changing either keeps sandbox, approval, and profile aligned.
    • Chat composer: profile selector shows “sandbox · approval”; confirm checkbox gates send; selection is saved per workspace and sent with the message.
    • Settings: Run profile and Approval fields; defaults applied for new/duplicated workspaces.
    • API/Server: /api/send accepts runProfile and confirmedRisk; workspaces accept codexApproval and runProfile. codex-cli passes -s and -a, includes the profile in the prompt, stores profile/sandbox/approval on messages, and rejects risky sends without confirmation. Tests cover the confirmation guard and workspace defaults.

Written for commit 442c29f. Summary will update on new commits.

Review in cubic

Copilot AI review requested due to automatic review settings May 31, 2026 08:12
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/codex-claw/src/server/codex-cli.ts">

<violation number="1" location="apps/codex-claw/src/server/codex-cli.ts:407">
P1: `runProfile` fallback is derived from the default workspace instead of the workspace sandbox, which can silently change execution permissions for existing or partially updated workspaces.</violation>
</file>

<file name="apps/codex-claw/src/screens/chat/components/chat-composer.tsx">

<violation number="1" location="apps/codex-claw/src/screens/chat/components/chat-composer.tsx:155">
P2: Run profile changes can be saved to a stale workspace id. `activeWorkspaceId` is captured at mount and reused later, so after switching workspaces this PATCH may update the wrong workspace.</violation>

<violation number="2" location="apps/codex-claw/src/screens/chat/components/chat-composer.tsx:156">
P2: `updateWorkspace` result discarded with `void` — local state updates before it, so a failure silently leaves UI and server out of sync. Add `.catch()` that reverts the optimistic state or surfaces an error.</violation>
</file>

<file name="apps/codex-claw/src/screens/chat/components/settings-dialog.tsx">

<violation number="1" location="apps/codex-claw/src/screens/chat/components/settings-dialog.tsx:102">
P2: Approval dropdown is missing the supported `on-failure` policy, causing UI/backend mismatch for workspace approval settings.</violation>

<violation number="2" location="apps/codex-claw/src/screens/chat/components/settings-dialog.tsx:432">
P2: Changing sandbox or approval individually leaves `runProfile` stale — the saved profile won't match the actual sandbox/approval values if they were overridden after profile selection.

Either reset `runProfile` to a sentinel (e.g. `'custom'` or `undefined`) when sandbox/approval are changed independently, or disable the individual sandbox/approval selects when a named profile is selected so the two don't drift.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread apps/codex-claw/src/server/codex-cli.ts Outdated
codexApproval: normalizeApproval(
value.codexApproval || fallback.codexApproval,
),
runProfile: normalizeRunProfile(value.runProfile || fallback.runProfile),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: runProfile fallback is derived from the default workspace instead of the workspace sandbox, which can silently change execution permissions for existing or partially updated workspaces.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/server/codex-cli.ts, line 407:

<comment>`runProfile` fallback is derived from the default workspace instead of the workspace sandbox, which can silently change execution permissions for existing or partially updated workspaces.</comment>

<file context>
@@ -317,6 +401,10 @@ function normalizeWorkspaceRecord(
+    codexApproval: normalizeApproval(
+      value.codexApproval || fallback.codexApproval,
+    ),
+    runProfile: normalizeRunProfile(value.runProfile || fallback.runProfile),
     codexWorkdir: normalizePath(value.codexWorkdir, fallback.codexWorkdir),
     stateDir: normalizePath(value.stateDir, fallback.stateDir),
</file context>
Suggested change
runProfile: normalizeRunProfile(value.runProfile || fallback.runProfile),
runProfile: normalizeRunProfile(
value.runProfile || profileFromSandbox(value.codexSandbox || fallback.codexSandbox),
),

setRunProfile(nextProfile.id)
setConfirmedRisk(false)
if (activeWorkspaceId) {
void updateWorkspace({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: updateWorkspace result discarded with void — local state updates before it, so a failure silently leaves UI and server out of sync. Add .catch() that reverts the optimistic state or surfaces an error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/screens/chat/components/chat-composer.tsx, line 156:

<comment>`updateWorkspace` result discarded with `void` — local state updates before it, so a failure silently leaves UI and server out of sync. Add `.catch()` that reverts the optimistic state or surfaces an error.</comment>

<file context>
@@ -89,6 +141,28 @@ function ChatComposerComponent({
+      setRunProfile(nextProfile.id)
+      setConfirmedRisk(false)
+      if (activeWorkspaceId) {
+        void updateWorkspace({
+          id: activeWorkspaceId,
+          runProfile: nextProfile.id,
</file context>

<WorkspaceField label="Approval">
<select
value={draft.codexApproval ?? 'untrusted'}
onChange={(event) =>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Changing sandbox or approval individually leaves runProfile stale — the saved profile won't match the actual sandbox/approval values if they were overridden after profile selection.

Either reset runProfile to a sentinel (e.g. 'custom' or undefined) when sandbox/approval are changed independently, or disable the individual sandbox/approval selects when a named profile is selected so the two don't drift.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/screens/chat/components/settings-dialog.tsx, line 432:

<comment>Changing sandbox or approval individually leaves `runProfile` stale — the saved profile won't match the actual sandbox/approval values if they were overridden after profile selection.

Either reset `runProfile` to a sentinel (e.g. `'custom'` or `undefined`) when sandbox/approval are changed independently, or disable the individual sandbox/approval selects when a named profile is selected so the two don't drift.</comment>

<file context>
@@ -376,6 +413,34 @@ export function SettingsDialog({
+              <WorkspaceField label="Approval">
+                <select
+                  value={draft.codexApproval ?? 'untrusted'}
+                  onChange={(event) =>
+                    updateDraft('codexApproval', event.target.value)
+                  }
</file context>


const newWorkspaceId = '__new__'
const sandboxOptions = ['read-only', 'workspace-write', 'danger-full-access']
const approvalOptions = ['untrusted', 'on-request', 'never']
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Approval dropdown is missing the supported on-failure policy, causing UI/backend mismatch for workspace approval settings.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/screens/chat/components/settings-dialog.tsx, line 102:

<comment>Approval dropdown is missing the supported `on-failure` policy, causing UI/backend mismatch for workspace approval settings.</comment>

<file context>
@@ -97,6 +99,27 @@ function WorkspaceField({ label, children }: WorkspaceFieldProps) {
 
 const newWorkspaceId = '__new__'
 const sandboxOptions = ['read-only', 'workspace-write', 'danger-full-access']
+const approvalOptions = ['untrusted', 'on-request', 'never']
+const runProfileOptions = [
+  {
</file context>
Suggested change
const approvalOptions = ['untrusted', 'on-request', 'never']
const approvalOptions = ['untrusted', 'on-request', 'on-failure', 'never']

Comment on lines +155 to +162
if (activeWorkspaceId) {
void updateWorkspace({
id: activeWorkspaceId,
runProfile: nextProfile.id,
codexSandbox: nextProfile.sandbox,
codexApproval: nextProfile.approval,
})
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Run profile changes can be saved to a stale workspace id. activeWorkspaceId is captured at mount and reused later, so after switching workspaces this PATCH may update the wrong workspace.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/codex-claw/src/screens/chat/components/chat-composer.tsx, line 155:

<comment>Run profile changes can be saved to a stale workspace id. `activeWorkspaceId` is captured at mount and reused later, so after switching workspaces this PATCH may update the wrong workspace.</comment>

<file context>
@@ -89,6 +141,28 @@ function ChatComposerComponent({
+        runProfiles.find((profile) => profile.id === value) ?? runProfiles[0]
+      setRunProfile(nextProfile.id)
+      setConfirmedRisk(false)
+      if (activeWorkspaceId) {
+        void updateWorkspace({
+          id: activeWorkspaceId,
</file context>
Suggested change
if (activeWorkspaceId) {
void updateWorkspace({
id: activeWorkspaceId,
runProfile: nextProfile.id,
codexSandbox: nextProfile.sandbox,
codexApproval: nextProfile.approval,
})
}
void fetchWorkspaces()
.then((data) => {
if (!data.activeWorkspaceId) return
return updateWorkspace({
id: data.activeWorkspaceId,
runProfile: nextProfile.id,
codexSandbox: nextProfile.sandbox,
codexApproval: nextProfile.approval,
})
})
.catch(() => {
// ignore
})

@slashdevcorpse slashdevcorpse merged commit 7787887 into main May 31, 2026
5 checks passed
@slashdevcorpse slashdevcorpse deleted the feature/sandbox-run-profiles branch May 31, 2026 08:18
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.

2 participants