Repository: gsd-build/get-shit-done (2026-04-11T13:20:47Z)

Issue:
Feature Request: Cross-AI Execution Hook

### Pre-submission checklist
- [x] I have searched existing issues and discussions — this has not been proposed and declined before
- [x] I have read CONTRIBUTING.md and understand that I must wait for `approved-feature` before writing any code
- [x] I have read the existing GSD commands and workflows and confirmed this feature does not duplicate existing behavior
- [x] This feature solves a problem for solo developers using AI coding tools, not a personal preference or workflow I happen to like

### Feature name

Cross-AI execution hook (step 2.5 in execute-phase workflow)

### Type of addition

New workflow

### The solo developer problem

GSD already runs on both Claude Code and Codex (since v1.26.0), and many solo developers have both installed. But these runtimes are silos: a phase orchestrated by Claude Code can only spawn Claude Code executors. If the developer wants Codex to execute a specific plan — because it has a larger context window, a different model's strengths, or simply to get a second perspective on complex code — they must manually copy the plan, run Codex in a separate terminal, then reconcile the output back into GSD's state system by hand (updating STATE.md, ROADMAP.md, and producing a GSD-format SUMMARY.md). This copy-paste reconciliation is slow, error-prone, and defeats the purpose of GSD's automated state management.

### What this feature adds

An optional step 2.5 in the `execute-phase` workflow that, before spawning the normal executor agent, checks whether a plan should be delegated to an external AI command for execution.

The feature:
1. Checks if cross-AI execution is active: `--cross-ai` forces all plans, or plan frontmatter contains `cross_ai: true` and config `workflow.cross_ai_execution` is `true`
2. `--no-cross-ai` disables cross-AI for the run
3. Reads the configured command from `workflow.cross_ai_command` and timeout from `workflow.cross_ai_timeout`
4. Constructs a task prompt from the plan's objective and task list, a brief project context block (from PROJECT.md if available), and an instruction that the external command must modify the working tree directly and print valid `SUMMARY.md` content to stdout
5. Runs the configured command from the project root and writes the prompt to the subprocess stdin so prompt content is never interpolated into the shell command
6. Captures stdout as the candidate execution summary after the external command has attempted the requested implementation in the working tree
7. On success (exit code `0` and stdout validates against the `templates/summary.md` structure): writes `SUMMARY.md` and updates `STATE.md` and `ROADMAP.md` normally
8. On failure (non-zero exit code, invalid summary output, or timeout): offers retry with normal executor, skip plan, or abort phase; if the working tree changed during the failed run, GSD warns that partial edits may exist and requires explicit confirmation before retrying with the normal executor
9. Skips normal executor spawning for plans handled by cross-AI

Example usage:
```text
/gsd:execute-phase 5 --cross-ai
> Delegating to cross-AI execution: 05-01 API endpoints
> ✓ 05-01 API endpoints completed via cross-AI execution
> Spawning executor for: 05-02 Frontend integration (cross_ai not set in frontmatter)
```

The external command runs from the project root where GSD was invoked. This matches existing executor behavior and lets the command read and modify the target source tree without extra path handling.

### Full scope of changes

Files that would be created:
- `tests/cross-ai-execution.test.cjs` — tests for config key validation, activation logic, missing command handling, invalid summary output, timeout handling, dirty-working-tree warning on failure, and fallback behavior

Files that would be modified:
- `get-shit-done/workflows/execute-phase.md` — insert step 2.5 between step 2 (plan display) and step 3 (spawn executor agents)
- `get-shit-done/bin/lib/config.cjs` — add `workflow.cross_ai_execution`, `workflow.cross_ai_command`, and `workflow.cross_ai_timeout` to `VALID_CONFIG_KEYS` and `buildNewProjectConfig` defaults
- `get-shit-done/templates/config.json` — add three new config defaults

Systems affected:
- Execute-phase workflow (new optional step; if cross-AI is not active for a plan, step 3 proceeds exactly as before)
- Execute-phase flag handling for `--cross-ai` and `--no-cross-ai`
- Config system (3 new keys, backward compatible)
- SUMMARY.md handling (validate external stdout against the existing template before writing)
- STATE.md and ROADMAP.md updates (uses existing update logic, no schema changes)

### User stories

1. As a solo developer using both Claude Code and Codex, I want to route computationally heavy plan execution to Codex while keeping GSD as my orchestrator, so that I get better results without losing GSD's state management and verification pipeline.
2. As a solo developer, I want to mark specific plans for cross-AI execution via frontmatter (`cross_ai: true`) so that I can selectively delegate plans that benefit from a different AI tool without routing everything externally.
3. As a solo developer, I want cross-AI execution failures to offer fallback to the normal executor so that a flaky external tool doesn't block my entire phase.

### Acceptance criteria

- [ ] `--cross-ai` flag on `/gsd:execute-phase` forces all plans to use cross-AI execution
- [ ] Plan frontmatter `cross_ai: true` combined with `workflow.cross_ai_execution: true` activates cross-AI for that specific plan
- [ ] `--no-cross-ai` disables cross-AI execution regardless of config or frontmatter
- [ ] If `workflow.cross_ai_command` is empty or `null` when cross-AI is active, an error with setup instructions is displayed and the plan falls back to the normal executor
- [ ] Task prompt is constructed from the plan's objective, task list, and a brief project context block (from PROJECT.md if available), and is passed to the external command without shell expansion of prompt content
- [ ] The external command is expected to modify the working tree directly and print `SUMMARY.md` content to stdout
- [ ] The external command runs from the project root
- [ ] On successful execution, stdout is validated and written as `SUMMARY.md` matching `templates/summary.md`
- [ ] Invalid or incomplete summary output is treated as a failure and does not update state files
- [ ] On successful execution, `STATE.md` and `ROADMAP.md` are updated using existing update logic
- [ ] On failed execution, the user is offered: retry with normal executor, skip plan, or abort phase
- [ ] If a failed cross-AI run changed the working tree, GSD warns that partial edits may exist and requires explicit confirmation before retrying with the normal executor
- [ ] Plans handled by cross-AI do not also spawn a normal executor
- [ ] Plans not marked for cross-AI proceed through step 3 unchanged
- [ ] Timeout: if the external command does not exit within `workflow.cross_ai_timeout` seconds (default `300`), it is killed and treated as a failure
- [ ] All existing tests still pass
- [ ] New tests cover: config key validation, activation logic matrix, missing command handling, per-plan vs all-plans behavior, invalid summary output, timeout handling, and dirty-working-tree warning on failure

### Which area does this primarily affect?

Core workflow

### Applicable runtimes

- [x] All runtimes

### Breaking changes assessment

None. This is an optional step that defaults to inactive (`cross_ai_execution: false`, `cross_ai_command: null`, `cross_ai_timeout: 300`). Existing execute-phase behavior is unchanged unless the user explicitly enables cross-AI execution and configures a command. Existing `config.json` files without these keys will use the defaults.

### Maintenance burden

- No new external dependencies
- Three new config keys to maintain in `config.cjs` and `config.json`
- Summary validation must stay aligned with the existing `SUMMARY.md` template if that template changes
- The step uses existing `STATE.md` and `ROADMAP.md` update functions
- Error handling remains simple: non-zero exit, invalid summary output, or timeout equals failure and offers fallback

### Alternatives considered

1. **Structured API integration** (JSON-RPC, HTTP, or MCP protocol to external AI tools) — rejected because it adds protocol complexity, external dependencies, and only works with tools that implement the protocol. A CLI command interface works with any compatible tool.
2. **Built-in multi-model support** (GSD spawns agents with different model configurations) — rejected because GSD is runtime-agnostic and cannot assume which models or runtimes are available. The external command approach leaves that decision to the user.
3. **Post-execution reconciliation** (run externally, then import results) — rejected because it breaks the execute-phase flow and reintroduces manual STATE.md and ROADMAP.md updates.

### Prior art and references

- GSD Discussion #697 ("Integrate GSD with Codex CLI") — users describe wanting to run GSD tasks through Codex; one user manually internalized GSD into Codex sessions, demonstrating demand for cross-runtime execution
- GSD Discussion #520 ("Codex support") — Codex was added as a first-class runtime in v1.26.0, establishing the multi-runtime foundation. This feature bridges the gap between runtimes that currently operate as silos
- `codex exec --full-auto` is an example of an external CLI tool that accepts a prompt and executes work, matching this feature's stdin interface
- No prior feature request or declined proposal exists for this functionality

### Additional context

The hook invokes a user-configured subprocess from the project root, and the workflow changes live in `execute-phase.md`, which is interpreted by all runtimes.

This feature is complementary to the Plan Bounce Hook (#1874): plan bounce refines plans via cross-AI review before execution, while cross-AI execution delegates the implementation itself to an external AI. Together they create a full cross-AI pipeline across the planning and execution lifecycle stages.




Discussion:
This tackles a real pain point — the manual reconciliation of external AI output back into GSD's state system is exactly the kind of toil that should be automated.

**Security design is good:** Piping the prompt via stdin rather than interpolating into shell commands avoids injection. The SUMMARY.md structure validation before accepting output prevents malformed results from corrupting state.

**The retry/skip/abort UX on failure is well-designed.** Falling back to the normal executor means cross-AI failures don't block the phase — they just degrade gracefully.

**Consideration: context transfer.** The task prompt constructed from plan objective + task list may not carry enough context for a cold-start external AI. GSD executors benefit from `<project_context>`, `<files_to_read>`, and CLAUDE.md — the external AI gets none of this. Might be worth including a brief project context block (from PROJECT.md) in the prompt template to improve cold-start quality.

**Interaction with #1874 (plan bounce):** These two features are complementary — #1874 bounces plans for refinement, this one bounces execution for diversity. Together they create a full cross-AI pipeline. Worth noting the interaction in both specs.

Strong spec overall.
Good feedback — addressed both items:

**Context transfer:** The task prompt now includes a brief project context block from PROJECT.md when available, helping cold-start external AIs. Added to both the feature description and acceptance criteria.

**Dirty working tree on failure:** New acceptance criterion — if a failed cross-AI run changed the working tree, GSD warns that partial edits may exist and requires explicit confirmation before retrying with the normal executor. This prevents the normal executor from starting on a corrupted state.

**Complementary with #1874:** Added a cross-reference in Additional Context noting that plan bounce refines plans pre-execution while this feature delegates execution itself. Together they create a full cross-AI pipeline across planning and execution.

Spec updated in the issue body above.
✅ **Approved.** The dirty-working-tree warning on failed cross-AI execution is exactly the right safety detail, and the failure → retry/skip/abort UX prevents the feature from ever blocking a phase. The SUMMARY.md validation before writing state is solid. You may begin coding. Use the Feature PR template and link this issue with `Closes #1875`.



Patch:
diff --git a/get-shit-done/bin/lib/config.cjs b/get-shit-done/bin/lib/config.cjs
index 40f14527f4..93ca845ee3 100644
--- a/get-shit-done/bin/lib/config.cjs
+++ b/get-shit-done/bin/lib/config.cjs
@@ -31,6 +31,7 @@ const VALID_CONFIG_KEYS = new Set([
   'workflow.plan_bounce_passes',
   'git.branching_strategy', 'git.base_branch', 'git.phase_branch_template', 'git.milestone_branch_template', 'git.quick_branch_template',
   'planning.commit_docs', 'planning.search_gitignored',
+  'workflow.cross_ai_execution', 'workflow.cross_ai_command', 'workflow.cross_ai_timeout',
   'workflow.subagent_timeout',
   'hooks.context_warnings',
   'features.thinking_partner',
diff --git a/get-shit-done/templates/config.json b/get-shit-done/templates/config.json
index 03378389d0..e77dda0ba2 100644
--- a/get-shit-done/templates/config.json
+++ b/get-shit-done/templates/config.json
@@ -15,7 +15,10 @@
     "code_review_command": null,
     "plan_bounce": false,
     "plan_bounce_script": null,
-    "plan_bounce_passes": 2
+    "plan_bounce_passes": 2,
+    "cross_ai_execution": false,
+    "cross_ai_command": "",
+    "cross_ai_timeout": 300
   },
   "planning": {
     "commit_docs": true,
diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md
index 70cf394530..8f49afb7df 100644
--- a/get-shit-done/workflows/execute-phase.md
+++ b/get-shit-done/workflows/execute-phase.md
@@ -57,6 +57,8 @@ Parse `$ARGUMENTS` before loading any context:
 - First positional token → `PHASE_ARG`
 - Optional `--wave N` → `WAVE_FILTER`
 - Optional `--gaps-only` keeps its current meaning
+- Optional `--cross-ai` → `CROSS_AI_FORCE=true` (force all plans through cross-AI execution)
+- Optional `--no-cross-ai` → `CROSS_AI_DISABLED=true` (disable cross-AI for this run, overrides config and frontmatter)
 
 If `--wave` is absent, preserve the current behavior of executing all incomplete waves in the phase.
 </step>
@@ -243,6 +245,77 @@ Report:
 ```
 </step>
 
+<step name="cross_ai_delegation">
+**Optional step 2.5 — Delegate plans to an external AI runtime.**
+
+This step runs after plan discovery and before normal wave execution. It identifies plans
+that should be delegated to an external AI command and executes them via stdin-based prompt
+delivery. Plans handled here are removed from the execute_waves plan list so the normal
+executor skips them.
+
+**Activation logic:**
+
+1. If `CROSS_AI_DISABLED` is true (`--no-cross-ai` flag): skip this step entirely.
+2. If `CROSS_AI_FORCE` is true (`--cross-ai` flag): mark ALL incomplete plans for cross-AI execution.
+3. Otherwise: check each plan's frontmatter for `cross_ai: true` AND verify config
+   `workflow.cross_ai_execution` is `true`. Plans matching both conditions are marked for cross-AI.
+
+```bash
+CROSS_AI_ENABLED=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.cross_ai_execution --default false 2>/dev/null)
+CROSS_AI_CMD=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.cross_ai_command --default "" 2>/dev/null)
+CROSS_AI_TIMEOUT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.cross_ai_timeout --default 300 2>/dev/null)
+```
+
+**If no plans are marked for cross-AI:** Skip to execute_waves.
+
+**If plans are marked but `cross_ai_command` is empty:** Error — tell user to set
+`workflow.cross_ai_command` via `gsd-tools.cjs config-set workflow.cross_ai_command "<command>"`.
+
+**For each cross-AI plan (sequentially):**
+
+1. **Construct the task prompt** from the plan file:
+   - Extract `<objective>` and `<tasks>` sections from the PLAN.md
+   - Append PROJECT.md context (project name, description, tech stack)
+   - Format as a self-contained execution prompt
+
+2. **Check for dirty working tree before execution:**
+   ```bash
+   if ! git diff --quiet HEAD 2>/dev/null; then
+     echo "WARNING: dirty working tree detected — the external AI command may produce uncommitted changes that conflict with existing modifications"
+   fi
+   ```
+
+3. **Run the external command** from the project root, writing the prompt to stdin.
+   Never shell-interpolate the prompt — always pipe via stdin to prevent injection:
+   ```bash
+   echo "$TASK_PROMPT" | timeout "${CROSS_AI_TIMEOUT}s" ${CROSS_AI_CMD} > "$CANDIDATE_SUMMARY" 2>"$ERROR_LOG"
+   EXIT_CODE=$?
+   ```
+
+4. **Evaluate the result:**
+
+   **Success (exit 0 + valid summary):**
+   - Read `$CANDIDATE_SUMMARY` and validate it contains meaningful content
+     (not empty, has at least a heading and description — a valid SUMMARY.md structure)
+   - Write it as the plan's SUMMARY.md file
+   - Update STATE.md plan status to complete
+   - Update ROADMAP.md progress
+   - Mark plan as handled — skip it in execute_waves
+
+   **Failure (non-zero exit or invalid summary):**
+   - Display the error output and exit code
+   - Warn: "The external command may have left uncommitted changes or partial edits
+     in the working tree. Review `git status` and `git diff` before proceeding."
+   - Offer three choices:
+     - **retry** — run the same plan through cross-AI again
+     - **skip** — fall back to normal executor for this plan (re-add to execute_waves list)
+     - **abort** — stop execution entirely, preserve state for resume
+
+5. **After all cross-AI plans processed:** Remove successfully handled plans from the
+   incomplete plan list so execute_waves skips them. Any skipped-to-fallback plans remain
+   in the list for normal executor processing.
+</step>
+
 <step name="execute_waves">
 Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZATION=true`, sequential if `false`.
 


Test patch:
diff --git a/tests/cross-ai-execution.test.cjs b/tests/cross-ai-execution.test.cjs
new file mode 100644
index 0000000000..2870ed103f
--- /dev/null
+++ b/tests/cross-ai-execution.test.cjs
@@ -0,0 +1,173 @@
+const { test, describe } = require('node:test');
+const assert = require('node:assert/strict');
+const fs = require('fs');
+const path = require('path');
+
+const CONFIG_PATH = path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'config.cjs');
+const EXECUTE_PHASE_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md');
+const CONFIG_TEMPLATE_PATH = path.join(__dirname, '..', 'get-shit-done', 'templates', 'config.json');
+
+describe('cross-AI execution', () => {
+
+  describe('config keys', () => {
+    test('workflow.cross_ai_execution is in VALID_CONFIG_KEYS', () => {
+      const { VALID_CONFIG_KEYS } = require(CONFIG_PATH);
+      assert.ok(VALID_CONFIG_KEYS.has('workflow.cross_ai_execution'),
+        'VALID_CONFIG_KEYS must include workflow.cross_ai_execution');
+    });
+
+    test('workflow.cross_ai_command is in VALID_CONFIG_KEYS', () => {
+      const { VALID_CONFIG_KEYS } = require(CONFIG_PATH);
+      assert.ok(VALID_CONFIG_KEYS.has('workflow.cross_ai_command'),
+        'VALID_CONFIG_KEYS must include workflow.cross_ai_command');
+    });
+
+    test('workflow.cross_ai_timeout is in VALID_CONFIG_KEYS', () => {
+      const { VALID_CONFIG_KEYS } = require(CONFIG_PATH);
+      assert.ok(VALID_CONFIG_KEYS.has('workflow.cross_ai_timeout'),
+        'VALID_CONFIG_KEYS must include workflow.cross_ai_timeout');
+    });
+  });
+
+  describe('config template defaults', () => {
+    test('config template has cross_ai_execution default', () => {
+      const template = JSON.parse(fs.readFileSync(CONFIG_TEMPLATE_PATH, 'utf-8'));
+      assert.strictEqual(template.workflow.cross_ai_execution, false,
+        'cross_ai_execution should default to false');
+    });
+
+    test('config template has cross_ai_command default', () => {
+      const template = JSON.parse(fs.readFileSync(CONFIG_TEMPLATE_PATH, 'utf-8'));
+      assert.strictEqual(template.workflow.cross_ai_command, '',
+        'cross_ai_command should default to empty string');
+    });
+
+    test('config template has cross_ai_timeout default', () => {
+      const template = JSON.parse(fs.readFileSync(CONFIG_TEMPLATE_PATH, 'utf-8'));
+      assert.strictEqual(template.workflow.cross_ai_timeout, 300,
+        'cross_ai_timeout should default to 300 seconds');
+    });
+  });
+
+  describe('execute-phase.md cross-AI step', () => {
+    let content;
+
+    test('execute-phase.md has a cross-AI execution step', () => {
+      content = fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      assert.ok(content.includes('<step name="cross_ai_delegation">'),
+        'execute-phase.md must have a step named cross_ai_delegation');
+    });
+
+    test('cross-AI step appears between discover_and_group_plans and execute_waves', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      const discoverIdx = content.indexOf('<step name="discover_and_group_plans">');
+      const crossAiIdx = content.indexOf('<step name="cross_ai_delegation">');
+      const executeIdx = content.indexOf('<step name="execute_waves">');
+      assert.ok(discoverIdx < crossAiIdx, 'cross_ai_delegation must come after discover_and_group_plans');
+      assert.ok(crossAiIdx < executeIdx, 'cross_ai_delegation must come before execute_waves');
+    });
+
+    test('cross-AI step handles --cross-ai flag', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      assert.ok(content.includes('--cross-ai'),
+        'execute-phase.md must reference --cross-ai flag');
+    });
+
+    test('cross-AI step handles --no-cross-ai flag', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      assert.ok(content.includes('--no-cross-ai'),
+        'execute-phase.md must reference --no-cross-ai flag');
+    });
+
+    test('cross-AI step uses stdin-based prompt delivery', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      // The step must describe piping prompt via stdin, not shell interpolation
+      assert.ok(content.includes('stdin'),
+        'cross-AI step must describe stdin-based prompt delivery');
+    });
+
+    test('cross-AI step validates summary output', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      // The step must describe validating the captured summary
+      const crossAiSection = content.substring(
+        content.indexOf('<step name="cross_ai_delegation">'),
+        content.indexOf('</step>', content.indexOf('<step name="cross_ai_delegation">')) + '</step>'.length
+      );
+      assert.ok(
+        crossAiSection.includes('SUMMARY') && crossAiSection.includes('valid'),
+        'cross-AI step must validate the summary output'
+      );
+    });
+
+    test('cross-AI step warns about dirty working tree', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      const crossAiSection = content.substring(
+        content.indexOf('<step name="cross_ai_delegation">'),
+        content.indexOf('</step>', content.indexOf('<step name="cross_ai_delegation">')) + '</step>'.length
+      );
+      assert.ok(
+        crossAiSection.includes('dirty') || crossAiSection.includes('uncommitted') || crossAiSection.includes('working tree'),
+        'cross-AI step must warn about dirty/uncommitted changes from external command'
+      );
+    });
+
+    test('cross-AI step reads cross_ai_command from config', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      const crossAiSection = content.substring(
+        content.indexOf('<step name="cross_ai_delegation">'),
+        content.indexOf('</step>', content.indexOf('<step name="cross_ai_delegation">')) + '</step>'.length
+      );
+      assert.ok(
+        crossAiSection.includes('cross_ai_command'),
+        'cross-AI step must read cross_ai_command from config'
+      );
+    });
+
+    test('cross-AI step reads cross_ai_timeout from config', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      const crossAiSection = content.substring(
+        content.indexOf('<step name="cross_ai_delegation">'),
+        content.indexOf('</step>', content.indexOf('<step name="cross_ai_delegation">')) + '</step>'.length
+      );
+      assert.ok(
+        crossAiSection.includes('cross_ai_timeout'),
+        'cross-AI step must read cross_ai_timeout from config'
+      );
+    });
+
+    test('cross-AI step handles failure with retry/skip/abort', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      const crossAiSection = content.substring(
+        content.indexOf('<step name="cross_ai_delegation">'),
+        content.indexOf('</step>', content.indexOf('<step name="cross_ai_delegation">')) + '</step>'.length
+      );
+      assert.ok(crossAiSection.includes('retry'), 'cross-AI step must offer retry on failure');
+      assert.ok(crossAiSection.includes('skip'), 'cross-AI step must offer skip on failure');
+      assert.ok(crossAiSection.includes('abort'), 'cross-AI step must offer abort on failure');
+    });
+
+    test('cross-AI step skips normal executor for handled plans', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      const crossAiSection = content.substring(
+        content.indexOf('<step name="cross_ai_delegation">'),
+        content.indexOf('</step>', content.indexOf('<step name="cross_ai_delegation">')) + '</step>'.length
+      );
+      assert.ok(
+        crossAiSection.includes('skip') && (crossAiSection.includes('executor') || crossAiSection.includes('execute_waves')),
+        'cross-AI step must describe skipping normal executor for cross-AI handled plans'
+      );
+    });
+
+    test('parse_args step includes --cross-ai and --no-cross-ai', () => {
+      content = content || fs.readFileSync(EXECUTE_PHASE_PATH, 'utf-8');
+      const parseArgsSection = content.substring(
+        content.indexOf('<step name="parse_args"'),
+        content.indexOf('</step>', content.indexOf('<step name="parse_args"')) + '</step>'.length
+      );
+      assert.ok(parseArgsSection.includes('--cross-ai'),
+        'parse_args step must parse --cross-ai flag');
+      assert.ok(parseArgsSection.includes('--no-cross-ai'),
+        'parse_args step must parse --no-cross-ai flag');
+    });
+  });
+});