From 9a1b21860b190d17e47446af90db3c5d1309cdf0 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Tue, 19 May 2026 15:48:11 +0800 Subject: [PATCH] {"schema":"decodex/commit/1","summary":"Require explicit concurrency limits","authority":"manual"} --- apps/decodex/src/workflow.rs | 22 ++++++---------------- docs/runbook/self-dogfood-pilot.md | 2 +- docs/spec/runtime.md | 2 +- docs/spec/workflow-file.md | 5 ++--- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/apps/decodex/src/workflow.rs b/apps/decodex/src/workflow.rs index bc13685c..259d02a3 100644 --- a/apps/decodex/src/workflow.rs +++ b/apps/decodex/src/workflow.rs @@ -228,7 +228,6 @@ pub struct WorkflowExecution { max_attempts: u32, max_turns: u32, max_retry_backoff_ms: u64, - #[serde(default)] max_concurrent_agents: WorkflowConcurrencyLimit, canonicalize_commands: Vec, verify_commands: Vec, @@ -568,10 +567,9 @@ impl<'de> Visitor<'de> for WorkflowConcurrencyLimitVisitor { } /// Project-level concurrent agent limit. -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum WorkflowConcurrencyLimit { /// No project-level concurrent-agent cap. - #[default] Unlimited, /// A positive project-level concurrent-agent cap. Limited(u32), @@ -1406,6 +1404,11 @@ transport = "stdio://" "agent", ), ("missing max_attempts", Remove("max_attempts = 3\n"), "max_attempts"), + ( + "missing max_concurrent_agents", + Remove("max_concurrent_agents = 1\n"), + "max_concurrent_agents", + ), ( "empty terminal states", Replace( @@ -1757,19 +1760,6 @@ Then validate the lane. ); } - #[test] - fn defaults_missing_global_concurrency_limit_to_unlimited() { - let document = parse_valid_workflow_with(|markdown| { - *markdown = markdown.replace("max_concurrent_agents = 1\n", ""); - }) - .expect("missing global concurrency should parse"); - - assert_eq!( - document.frontmatter().execution().max_concurrent_agents(), - WorkflowConcurrencyLimit::Unlimited - ); - } - #[test] fn rejects_zero_global_concurrency_limit() { let result = WorkflowDocument::parse_markdown( diff --git a/docs/runbook/self-dogfood-pilot.md b/docs/runbook/self-dogfood-pilot.md index 2f2434e2..04ca3fd0 100644 --- a/docs/runbook/self-dogfood-pilot.md +++ b/docs/runbook/self-dogfood-pilot.md @@ -142,7 +142,7 @@ At minimum, the target repo should define: - `[execution] max_attempts` - `[execution] max_turns` - `[execution] max_retry_backoff_ms` -- optional `[execution] max_concurrent_agents`; omit it, or set it to `"unlimited"`, to run without a project-level concurrent-agent cap +- `[execution] max_concurrent_agents`; set it explicitly to `"unlimited"` to run without a project-level concurrent-agent cap - optional `[context] read_first = [...]` only when the repo truly needs extra repo-local files loaded in addition to the `WORKFLOW.md` body; treat this as a Decodex-local extension, not as the primary policy surface Child-run execution policy is not part of the project-owned `WORKFLOW.md` contract. `decodex` must let `codex app-server` inherit sandbox and approval behavior from the active Codex runtime instead of pinning repo-local overrides. diff --git a/docs/spec/runtime.md b/docs/spec/runtime.md index b3afc1af..56bc56f6 100644 --- a/docs/spec/runtime.md +++ b/docs/spec/runtime.md @@ -109,7 +109,7 @@ Optional future expansion: Current runtime note: -- Project-level concurrency is unlimited by default; a project may opt into a finite cap with `[execution] max_concurrent_agents = `. +- Project-level concurrency must be explicit; set `[execution] max_concurrent_agents = "unlimited"` for no project-level cap, or use a positive integer for a finite cap. - Active leases are the service-local claim set for running lanes, and shared dispatch-slot locks coordinate cross-process capacity when a finite cap is configured. ## Lane model diff --git a/docs/spec/workflow-file.md b/docs/spec/workflow-file.md index c6dbd2fa..a507359d 100644 --- a/docs/spec/workflow-file.md +++ b/docs/spec/workflow-file.md @@ -139,9 +139,8 @@ Supported keys: - note: caps control-plane-owned failure retry backoff in milliseconds; clean continuation retries use a separate short fixed delay in runtime policy - `max_concurrent_agents` - type: integer or string `"unlimited"` - - optional - - default: `"unlimited"` - - note: when set to a positive integer, upper-bounds concurrent `decodex` runs per repository; when omitted or set to `"unlimited"`, Decodex does not apply a project-level concurrent-agent cap; Decodex does not apply separate per-state concurrency caps + - required + - note: set to `"unlimited"` to run without a project-level concurrent-agent cap; when set to a positive integer, upper-bounds concurrent `decodex` runs per repository; Decodex does not apply separate per-state concurrency caps - `canonicalize_commands` - type: array of string - required