Here is the final, concrete architectural recommendation. We are perfectly aligned on the principles: keep the execution engine (`StepRunner`) pure, keep the orchestrator (`ClientSequenceRunner`) workflow-aware, and avoid polluting the core pipeline with client-specific hacks.

### Resolving the Final Disagreements

**1. Grid Exploration State**
**Your lean is correct.** Do not abuse the `ExecutionStore` state machine by pretending a grid is a production keyframe. Keep the "needs grid / has start frame" logic in the orchestrator. 
*The elegant trick:* When you generate a grid, pass `SEQ01_grid` as the `shot_id` to `StepRunner`. `ExecutionStore` will create a lightweight state file for `SEQ01_grid`, track the take, log the cost, and it will even show up in the Dailies tab for easy viewing—all without polluting the actual `SEQ01` video state.

**2. Shot-Level Prompt Tracking on Takes**
**Option (a) is the winner.** `StepRunner.execute_multi_shot` already receives the `multi_prompt_sequence` array as a parameter. Modifying it to simply inject this array into the take record's `metadata` dictionary is a 2-line, highly cohesive change. Reaching into `ExecutionStore` from the orchestrator after the fact (Option b) is an anti-pattern that risks data consistency.

**3. The `generate_grid()` Method**
**Do not create it.** You are exactly right. `execute_keyframe()` already does 100% of the work (calls Gemini, saves image, tracks cost, manages takes). The orchestrator should simply call `execute_keyframe(shot_id="SEQ01_grid", prompt=grid_prompt, ...)` and handle the cropping logic externally. 

***

## Final Deliverable Request

### 1. Files to create
*   **`starsend/client_pipeline.py`** - Contains `ClientSequenceRunner`, handling sequence-level orchestration, the 3-element cap logic, and bridging the JSON plan to `StepRunner`.
*   **`tools/client_generate.py`** - The CLI entry point (e.g., `python tools/client_generate.py --seq SEQ01 --mode grid|multi|single`).

### 2. Files to modify
*   **`starsend/pipeline.py`** - In `StepRunner.execute_multi_shot()`, add `multi_prompt_sequence` (the array) to the take's `metadata` dictionary when saving the take record.
*   **`starsend/client_bridge.py`** - Flesh out the stubbed data loaders to return the exact dictionary shapes expected by `ClientSequenceRunner` and `ElementManager`.

### 3. Files NOT to modify
*   **`starsend/execution_store.py`** - (Confirming) No new states, no changes to transitions.
*   **`starsend/element_manager.py`** - (Confirming) Works perfectly as-is.
*   **`starsend/api_client.py`** - (Confirming) Works perfectly as-is.
*   **`pipeline.py` (Orchestrator part)** - Do not touch the Recoil-specific 13-step orchestrator code.

### 4. Day 1 implementation order
1.  **Update `StepRunner`:** Add the 2 lines in `execute_multi_shot()` to save the prompt array to the take metadata.
2.  **Flesh out `client_bridge.py`:** Ensure `load_client_bible()` and `load_client_storyboard()` return clean, validated dictionaries.
3.  **Build `ClientSequenceRunner` (`client_pipeline.py`):** 
    *   Implement `run_grid_exploration()` (calls `execute_keyframe("SEQ01_grid")`).
    *   Implement `run_sequence_video()` (enforces the 3-element cap, auto-detects clean start frames, calls `execute_multi_shot("SEQ01")`).
4.  **Build `tools/client_generate.py`:** Wire up `argparse` to trigger the runner methods.
5.  **End-to-End Test:** Run `client_generate --seq SEQ01 --mode grid`, manually crop the result into `clean/`, then run `--mode multi` and verify the video generates and appears in the Dailies UI.

### 5. The one thing most likely to go wrong
**The Risk:** `ElementManager` throws silent errors or causes Kling API rejections because the manually written `client_bible.json` is missing fields that the Recoil Narrative Engine usually guarantees (e.g., missing `trigger_word`, wrong image path resolution, or missing `default_weight`).
**The Prevention:** In `client_bridge.py`, write a strict validation function inside `load_client_bible()`. Before returning the bible data, assert that every character/prop entry has exactly the keys `ElementManager.build_fal_elements()` expects. If a key is missing, throw a loud, descriptive `ValueError` immediately on startup, rather than letting it fail deep inside the Fal API call 3 minutes later.