diff --git a/README.md b/README.md index 8714c2b..2d69645 100644 --- a/README.md +++ b/README.md @@ -458,7 +458,7 @@ cstack inspect cstack inspect --interactive ``` -By default, `cstack` uses `danger-full-access` and allows direct source execution for `build`, `ship`, and `deliver`. Use `--safe` on a run when you want that invocation to fall back to `workspace-write` plus clean-worktree execution for defaulted dirty-worktree settings. +By default, `cstack` uses `workspace-write` and isolated execution checkouts for `build`, `ship`, and `deliver`. Use `--allow-dirty` when you explicitly want direct source execution for a run. While a run is active in a normal terminal, `cstack` renders a bounded ANSI dashboard instead of endlessly appending log lines. @@ -551,8 +551,8 @@ Notes: - `command` can point at the installed `codex` binary or a script path for testing. - `sandbox`, `profile`, `model`, and `extraArgs` are passed through to Codex launches. -- By default, `sandbox` resolves to `danger-full-access`, and `workflows.build.allowDirty`, `workflows.ship.allowDirty`, and `workflows.deliver.allowDirty` resolve to `true`. -- Use `--safe` when you want one run to fall back to `workspace-write` and clean-worktree execution for defaulted `allowDirty` values. +- By default, `sandbox` resolves to `workspace-write`, and `workflows.build.allowDirty`, `workflows.ship.allowDirty`, and `workflows.deliver.allowDirty` resolve to `false`. +- Use `--allow-dirty` when you want one run to execute directly in the source checkout. - If repo or user config explicitly sets `sandbox` or `allowDirty`, that explicit config wins over `--safe`. - `--allow-all` is deprecated and currently accepted as a temporary no-op. - `workflows.build.mode` selects `interactive` or `exec`; interactive is the default for build runs. diff --git a/src/commands/build.ts b/src/commands/build.ts index e861ef0..7a86a3d 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -259,7 +259,7 @@ export async function runBuild(cwd: string, args: string[] = []): Promise 0 && !allowDirty diff --git a/src/commands/deliver.ts b/src/commands/deliver.ts index f8e0525..91d32a9 100644 --- a/src/commands/deliver.ts +++ b/src/commands/deliver.ts @@ -300,7 +300,7 @@ export async function runDeliver(cwd: string, args: string[] = [], hooks: Delive `Mode: requested=${requestedMode} observed=${execution.buildExecution.observedMode}`, `Status: ${finalRunRecord.status}`, buildSession.sessionId ? `Build session: ${buildSession.sessionId}` : "Build session: not observed", - `Execution policy: ${policy.safe ? "dangerous default disabled via --safe" : "default dangerous execution"}`, + `Execution policy: ${policy.safe ? "safe overrides applied via --safe" : "default execution policy"}`, `Execution checkout: ${executionCheckout.record.execution.kind} @ ${executionCheckout.record.execution.cwd}`, `Source snapshot: ${executionCheckout.record.source.branch} ${executionCheckout.record.source.commit}`, executionCheckout.record.source.dirtyFiles.length > 0 && !allowDirty diff --git a/src/commands/ship.ts b/src/commands/ship.ts index 2ca9e33..d6f7dcd 100644 --- a/src/commands/ship.ts +++ b/src/commands/ship.ts @@ -252,7 +252,7 @@ export async function runShip(cwd: string, args: string[] = [], hooks: ShipRunHo `Run: ${runId}`, "Workflow: ship", `Status: ${finalRunRecord.status}`, - `Execution policy: ${policy.safe ? "dangerous default disabled via --safe" : "default dangerous execution"}`, + `Execution policy: ${policy.safe ? "safe overrides applied via --safe" : "default execution policy"}`, `Ship readiness: ${execution.shipRecord.readiness}`, `GitHub mutation: ${execution.githubMutationRecord.summary}`, `GitHub delivery: ${execution.githubDeliveryRecord.overall.status}`, diff --git a/src/config.ts b/src/config.ts index 3a23288..61186a9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,7 +7,7 @@ import type { ConfigProvenance, ConfigValueSource, CstackConfig } from "./types. const DEFAULT_CONFIG: CstackConfig = { codex: { command: process.env.CSTACK_CODEX_BIN || "codex", - sandbox: "danger-full-access", + sandbox: "workspace-write", extraArgs: [] }, workflows: { @@ -40,7 +40,7 @@ const DEFAULT_CONFIG: CstackConfig = { build: { mode: "interactive", verificationCommands: [], - allowDirty: true, + allowDirty: false, maxCodexAttempts: 3, timeoutSeconds: 900, capabilities: { @@ -69,7 +69,7 @@ const DEFAULT_CONFIG: CstackConfig = { ship: { mode: "exec", verificationCommands: [], - allowDirty: true, + allowDirty: false, timeoutSeconds: 600, capabilities: { allowed: ["shell", "github"], @@ -83,7 +83,7 @@ const DEFAULT_CONFIG: CstackConfig = { deliver: { mode: "interactive", verificationCommands: [], - allowDirty: true, + allowDirty: false, timeoutSeconds: 900, capabilities: { allowed: ["shell", "github", "browser"], diff --git a/src/inspector.ts b/src/inspector.ts index 2f860cd..d6353d5 100644 --- a/src/inspector.ts +++ b/src/inspector.ts @@ -1221,7 +1221,7 @@ export function renderInspectionSummary(cwd: string, inspection: RunInspection): ? `- source snapshot: ${inspection.executionContext.source.branch} ${inspection.executionContext.source.commit}` : undefined, inspection.executionContext - ? `- execution policy: ${run.inputs.safe ? "dangerous default disabled via --safe" : "default dangerous execution"}` + ? `- execution policy: ${run.inputs.safe ? "safe overrides applied via --safe" : "default execution policy"}` : undefined, inspection.executionContext?.source.localChangesIgnored ? "- local dirty changes: ignored by default; execution used committed HEAD" diff --git a/src/runtime-config.ts b/src/runtime-config.ts index bc7af03..96c9b33 100644 --- a/src/runtime-config.ts +++ b/src/runtime-config.ts @@ -58,11 +58,11 @@ export function resolveSourceExecutionReason(options: { if (options.configuredAllowDirtySource && options.configuredAllowDirtySource !== "default") { return `Direct source execution was enabled by configured allowDirty for ${options.workflow}.`; } - return `Direct source execution used the default dangerous execution policy for ${options.workflow}.`; + return `Direct source execution used the default execution policy for ${options.workflow}.`; } export function emitDeprecatedAllowAllWarning(command: string): void { process.stderr.write( - `[cstack] Warning: \`--allow-all\` is deprecated for \`${command}\` and has no effect because dangerous execution is now the default.\n` + `[cstack] Warning: \`--allow-all\` is deprecated for \`${command}\` and has no effect.\n` ); } diff --git a/test/build.test.ts b/test/build.test.ts index cb11b36..52e5969 100644 --- a/test/build.test.ts +++ b/test/build.test.ts @@ -292,7 +292,7 @@ describe("runBuild", () => { expect(await fs.readFile(path.join(executionContext.execution.cwd, "codex-generated-change.txt"), "utf8")).toContain("generated"); }, 60_000); - it("uses the source checkout and danger-full-access by default when sandbox and allowDirty are not configured", async () => { + it("uses an isolated checkout and workspace-write by default when sandbox and allowDirty are not configured", async () => { await fs.writeFile(path.join(repoDir, "local-only.txt"), "uncommitted\n", "utf8"); const configPath = path.join(repoDir, ".cstack", "config.toml"); await fs.writeFile( @@ -322,15 +322,14 @@ describe("runBuild", () => { codexCommand: string[]; }; - expect(run.inputs.allowDirty).toBe(true); + expect(run.inputs.allowDirty).toBe(false); expect(executionContext.source.dirtyFiles).toContain("local-only.txt"); - expect(executionContext.source.localChangesIgnored).toBe(false); - expect(executionContext.execution.kind).toBe("source"); - expect(executionContext.execution.cwd).toBe(repoDir); - expect(executionContext.execution.notes.join(" ")).toContain("default dangerous execution policy"); + expect(executionContext.source.localChangesIgnored).toBe(true); + expect(executionContext.execution.kind).toBe("git-worktree"); + expect(executionContext.execution.cwd).not.toBe(repoDir); expect(session.codexCommand).toContain("--sandbox"); - expect(session.codexCommand).toContain("danger-full-access"); - expect(await fs.readFile(path.join(repoDir, "codex-generated-change.txt"), "utf8")).toContain("generated"); + expect(session.codexCommand).toContain("workspace-write"); + await expect(fs.access(path.join(repoDir, "codex-generated-change.txt"))).rejects.toThrow(); }, 60_000); it("treats --allow-all as a deprecated no-op", async () => { diff --git a/test/config.test.ts b/test/config.test.ts index a51406e..a0c27b1 100644 --- a/test/config.test.ts +++ b/test/config.test.ts @@ -166,18 +166,18 @@ blockSeverities = ["medium", "high", "critical"] const { config, sources, provenance } = await loadConfig(repoDir); expect(sources).toHaveLength(0); - expect(config.codex.sandbox).toBe("danger-full-access"); + expect(config.codex.sandbox).toBe("workspace-write"); expect(config.workflows.spec.timeoutSeconds).toBe(600); expect(config.workflows.discover.timeoutSeconds).toBe(600); - expect(config.workflows.build.allowDirty).toBe(true); + expect(config.workflows.build.allowDirty).toBe(false); expect(config.workflows.build.timeoutSeconds).toBe(900); expect(config.workflows.review.mode).toBe("exec"); expect(config.workflows.review.allowDirty).toBe(true); expect(config.workflows.review.timeoutSeconds).toBe(600); expect(config.workflows.ship.mode).toBe("exec"); - expect(config.workflows.ship.allowDirty).toBe(true); + expect(config.workflows.ship.allowDirty).toBe(false); expect(config.workflows.ship.timeoutSeconds).toBe(600); - expect(config.workflows.deliver.allowDirty).toBe(true); + expect(config.workflows.deliver.allowDirty).toBe(false); expect(config.workflows.deliver.timeoutSeconds).toBe(900); expect(config.workflows.deliver.stageTimeoutSeconds?.build).toBe(900); expect(config.workflows.deliver.stageTimeoutSeconds?.validation).toBe(600); diff --git a/test/deliver.test.ts b/test/deliver.test.ts index 22b4c23..053d7ea 100644 --- a/test/deliver.test.ts +++ b/test/deliver.test.ts @@ -406,7 +406,7 @@ describe("runDeliver", () => { expect(lineage.stages.find((stage) => stage.name === "validation")?.status).toBe("deferred"); }, 60_000); - it("uses the source checkout and danger-full-access by default when sandbox and allowDirty are not configured", async () => { + it("uses an isolated checkout and workspace-write by default when sandbox and allowDirty are not configured", async () => { const configPath = path.join(repoDir, ".cstack", "config.toml"); const configBody = await fs.readFile(configPath, "utf8"); await fs.writeFile( @@ -451,15 +451,14 @@ describe("runDeliver", () => { codexCommand: string[]; }; - expect(run.inputs.allowDirty).toBe(true); + expect(run.inputs.allowDirty).toBe(false); expect(executionContext.source.dirtyFiles).toContain("src-change.txt"); - expect(executionContext.source.localChangesIgnored).toBe(false); - expect(executionContext.execution.kind).toBe("source"); - expect(executionContext.execution.cwd).toBe(repoDir); - expect(executionContext.execution.notes.join(" ")).toContain("default dangerous execution policy"); + expect(executionContext.source.localChangesIgnored).toBe(true); + expect(executionContext.execution.kind).toBe("git-worktree"); + expect(executionContext.execution.cwd).not.toBe(repoDir); expect(buildSession.codexCommand).toContain("--sandbox"); - expect(buildSession.codexCommand).toContain("danger-full-access"); - expect(await fs.readFile(path.join(repoDir, "codex-generated-change.txt"), "utf8")).toContain("generated"); + expect(buildSession.codexCommand).toContain("workspace-write"); + await expect(fs.access(path.join(repoDir, "codex-generated-change.txt"))).rejects.toThrow(); }, 60_000); it("treats --allow-all as a deprecated no-op when source execution is otherwise disabled", async () => {