# Main Agent Runtime Flow Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add the first production-ready main-agent run layer so StoryForge can create confirmable runs, display runtime progress in the floating OneLiner window, and unify “handoff to main agent” actions. **Architecture:** Keep existing `OneLiner session/message` as the conversation layer, add `agent_runs` plus `agent_run_events` inside `oneliner_features.py` as a separate runtime layer, then extend the existing floating OneLiner UI to render a run header area above the chat stream and wire selected page actions into run creation. **Tech Stack:** FastAPI, SQLite, existing StoryForge Web V4 vanilla JS, Node test runner, Python unittest --- ### Task 1: Save docs for the runtime flow **Files:** - Create: `docs/superpowers/specs/2026-03-29-main-agent-runtime-flow-design.md` - Create: `docs/superpowers/plans/2026-03-29-main-agent-runtime-flow.md` - [ ] **Step 1: Save the approved design** Write the runtime flow design into the spec file above. - [ ] **Step 2: Save this implementation plan** Write this plan file and keep it committed with the implementation. - [ ] **Step 3: Commit docs checkpoint** ```bash git add docs/superpowers/specs/2026-03-29-main-agent-runtime-flow-design.md docs/superpowers/plans/2026-03-29-main-agent-runtime-flow.md git commit -m "docs: add main agent runtime flow spec" ``` ### Task 2: Add failing backend tests for the run layer **Files:** - Modify: `tests/test_main_agent_governance.py` - [ ] **Step 1: Write the failing test for run creation** Add a test that creates a run through `POST /v2/oneliner/runs` and asserts: ```python response = self.client.post( "/v2/oneliner/runs", headers=self.ctx["member_headers"], json={ "project_id": self.ctx["project_id"], "source_screen": "dashboard", "source_action_key": "homepage-primary-action", "title": "跟进重点账号", "summary": "先由主 Agent 评估优先级", "intent_key": "track_account", "platform": "douyin", "platform_scope": "single_platform", "plan_request": { "goal": "跟进重点账号", "steps": ["读取当前项目上下文", "检查重点账号变化", "决定下一步"] }, }, ) payload = response.json() self.assertEqual(response.status_code, 200, response.text) self.assertEqual(payload["run_status"], "needs_confirmation") self.assertEqual(payload["source_screen"], "dashboard") self.assertEqual(payload["platform"], "douyin") self.assertTrue(payload["plan"]["steps"]) ``` - [ ] **Step 2: Write the failing test for confirm flow** Add a test that confirms a run and asserts: ```python confirm = self.client.post( f"/v2/oneliner/runs/{run_id}/confirm", headers=self.ctx["member_headers"], json={"reason": "user confirmed"}, ) payload = confirm.json() self.assertEqual(confirm.status_code, 200, confirm.text) self.assertIn(payload["run_status"], {"queued", "running"}) ``` - [ ] **Step 3: Write the failing test for projectless governance snapshot** Add a test that creates a run for an approved user with no project id and verifies no default project is auto-created while the run still stores a blank governance snapshot. - [ ] **Step 4: Run the backend tests and confirm they fail** Run: ```bash python3 -m unittest tests.test_main_agent_governance -v ``` Expected: FAIL because the run tables and endpoints do not exist yet. ### Task 3: Add backend run schema and payload helpers **Files:** - Modify: `collector-service/app/oneliner_features.py` - [ ] **Step 1: Add the new tables** Add schema creation for: ```sql CREATE TABLE IF NOT EXISTS agent_runs ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, user_id TEXT NOT NULL, project_id TEXT NOT NULL DEFAULT '', source_type TEXT NOT NULL, source_screen TEXT NOT NULL, source_action_key TEXT NOT NULL, title TEXT NOT NULL, summary TEXT NOT NULL DEFAULT '', intent_key TEXT NOT NULL DEFAULT '', platform TEXT NOT NULL DEFAULT '', platform_scope TEXT NOT NULL DEFAULT 'single_platform', delivery_mode TEXT NOT NULL DEFAULT 'hybrid', run_status TEXT NOT NULL, scheduling_mode TEXT NOT NULL DEFAULT 'queued', active_executor_key TEXT NOT NULL DEFAULT '', plan_json TEXT NOT NULL DEFAULT '{}', result_json TEXT NOT NULL DEFAULT '{}', status_summary TEXT NOT NULL DEFAULT '', needs_user_input INTEGER NOT NULL DEFAULT 0, blocked_reason TEXT NOT NULL DEFAULT '', active_admin_override_notice_json TEXT NOT NULL DEFAULT '{}', created_at TEXT NOT NULL, updated_at TEXT NOT NULL, started_at TEXT NOT NULL DEFAULT '', finished_at TEXT NOT NULL DEFAULT '' ); CREATE TABLE IF NOT EXISTS agent_run_events ( id TEXT PRIMARY KEY, run_id TEXT NOT NULL, event_type TEXT NOT NULL, summary TEXT NOT NULL DEFAULT '', details_json TEXT NOT NULL DEFAULT '{}', created_at TEXT NOT NULL ); ``` - [ ] **Step 2: Add helper payload builders** Implement focused helpers for: ```python def _agent_run_payload(row: dict[str, Any]) -> dict[str, Any]: ... def _agent_run_event_payload(row: dict[str, Any]) -> dict[str, Any]: ... def _list_agent_run_events(run_id: str) -> list[dict[str, Any]]: ... ``` - [ ] **Step 3: Add governance snapshot helper** Add a helper that returns: ```python { "project_id": project_id, "platform": platform, "effective_policy": ..., "layers": ..., "active_admin_override_notice": ..., } ``` for use at run creation time. - [ ] **Step 4: Run backend tests** Run: ```bash python3 -m unittest tests.test_main_agent_governance -v ``` Expected: tests still fail, but schema-related failures move forward. ### Task 4: Add backend run endpoints and state transitions **Files:** - Modify: `collector-service/app/oneliner_features.py` - Test: `tests/test_main_agent_governance.py` - [ ] **Step 1: Add request models** Add minimal request models for: ```python class AgentRunCreateRequest(BaseModel): project_id: str = "" source_screen: str source_action_key: str title: str summary: str = "" intent_key: str = "" platform: str = "" platform_scope: str = "single_platform" plan_request: dict[str, Any] = Field(default_factory=dict) class AgentRunConfirmRequest(BaseModel): reason: str = "" class AgentRunCancelRequest(BaseModel): reason: str = "" ``` - [ ] **Step 2: Add `POST /v2/oneliner/runs`** Create a run that: - resolves or creates the current session via the existing OneLiner session helper - stores `run_status = "needs_confirmation"` - snapshots current governance - logs `run.created` - [ ] **Step 3: Add read endpoints** Implement: ```python @app.get("/v2/oneliner/runs") @app.get("/v2/oneliner/runs/{run_id}") @app.get("/v2/oneliner/runs/{run_id}/events") ``` The list endpoint should return the latest 20 runs for the current user and project, newest first. - [ ] **Step 4: Add confirm and cancel endpoints** Implement: ```python @app.post("/v2/oneliner/runs/{run_id}/confirm") @app.post("/v2/oneliner/runs/{run_id}/cancel") ``` Confirm should: - move `needs_confirmation -> queued` - set `status_summary = "等待主 Agent 执行"` or `"主 Agent 正在执行"` - create `run.confirmed` - for the first version, immediately promote to `running` when no other active run exists for that user/project Cancel should: - move `needs_confirmation -> cancelled` or `queued -> cancelled` - create `run.cancelled` - [ ] **Step 5: Add one backend test for event sequencing** Assert that creation + confirm writes at least: - `run.created` - `run.confirmed` - one of `run.queued` / `run.started` - [ ] **Step 6: Re-run backend tests** Run: ```bash python3 -m unittest tests.test_main_agent_governance -v ``` Expected: PASS. ### Task 5: Add failing frontend tests for the floating runtime header **Files:** - Modify: `web/storyforge-web-v4/tests/workbench-pages.test.mjs` - [ ] **Step 1: Add a failing test for the run header** Assert the OneLiner UI now includes a dedicated runtime header zone: ```javascript const source = extractBetween(APP, "function ensureOneLinerUi()", "function renderOneLinerSessionTabs()"); assert.match(source, /data-role="oneliner-runs"/); ``` - [ ] **Step 2: Add a failing test for the run list loader** Assert the app state loader hits the new endpoints: ```javascript const source = extractBetween(APP, "async function loadAgentControlSurfaces(projectId = \"\")", "async function loadOneLinerMessages(sessionId)"); assert.match(source, /\/v2\/oneliner\/runs/); ``` - [ ] **Step 3: Add a failing test for the handoff entrypoint** Assert the click handler routes a main-agent handoff to a dedicated action instead of only opening the chat: ```javascript const actions = extractBetween(APP, "document.addEventListener(\"click\", async (event) => {", "document.addEventListener(\"submit\", async (event) => {"); assert.match(actions, /handoff-to-main-agent/); ``` - [ ] **Step 4: Run the frontend tests and confirm failure** Run: ```bash node --test web/storyforge-web-v4/tests/workbench-pages.test.mjs ``` Expected: FAIL on missing run UI and handoff actions. ### Task 6: Extend app state and OneLiner UI with the runtime header **Files:** - Modify: `web/storyforge-web-v4/assets/app.js` - Modify: `web/storyforge-web-v4/assets/styles.css` - Test: `web/storyforge-web-v4/tests/workbench-pages.test.mjs` - [ ] **Step 1: Extend app state** Add state fields: ```javascript onelinerRuns: [], selectedOnelinerRunId: "", onelinerRunEvents: [], ``` - [ ] **Step 2: Render the runtime header area** Inside `ensureOneLinerUi()`, add a dedicated block: ```html
``` above the message list. - [ ] **Step 3: Add render helpers** Implement focused helpers: ```javascript function pickPrimaryRun(runs) { ... } function renderOneLinerRunSummary(primaryRun, runs) { ... } function renderOneLinerRunEventChips(primaryRunEvents) { ... } ``` Behavior: - prefer `needs_confirmation` - then `blocked` - then `running` - then latest `done` - [ ] **Step 4: Add CSS for the runtime header** Add styles for: - `.oneliner-runs` - `.oneliner-run-card` - `.oneliner-run-title` - `.oneliner-run-summary` Keep the current OneLiner visual language. Do not redesign the shell. - [ ] **Step 5: Re-run frontend tests** Run: ```bash node --test web/storyforge-web-v4/tests/workbench-pages.test.mjs node --check web/storyforge-web-v4/assets/app.js ``` Expected: PASS. ### Task 7: Wire run loading, confirmation, and page handoff **Files:** - Modify: `web/storyforge-web-v4/assets/app.js` - Test: `web/storyforge-web-v4/tests/workbench-pages.test.mjs` - [ ] **Step 1: Load runs into app state** Extend `loadAgentControlSurfaces(projectId)` to fetch: ```javascript storyforgeFetch(`/v2/oneliner/runs?project_id=${encodeURIComponent(normalizedProjectId)}`) ``` and store it in `appState.onelinerRuns`. - [ ] **Step 2: Add API helpers** Implement: ```javascript async function createMainAgentRun(input) { ... } async function confirmMainAgentRun(runId, reason = "") { ... } async function cancelMainAgentRun(runId, reason = "") { ... } ``` - [ ] **Step 3: Add the dedicated handoff action** In the click handler, add: ```javascript if (name === "handoff-to-main-agent") { ... } ``` This action should: - create a `needs_confirmation` run - refresh run state - open the OneLiner panel - [ ] **Step 4: Replace the first batch of entrypoints** Update these buttons to use `handoff-to-main-agent`: - homepage primary action handoff - homepage secondary action handoff - strategy page `交给 OneLiner 调整` - agent governance handoff tags - [ ] **Step 5: Add confirmation controls in the run header** For `needs_confirmation` runs, show: - `确认执行` - `取消` For `running/queued/blocked/done`, show compact status only. - [ ] **Step 6: Re-run frontend tests** Run: ```bash node --test web/storyforge-web-v4/tests/workbench-pages.test.mjs ``` Expected: PASS. ### Task 8: Full verification, deploy, and publish **Files:** - Modify only files already touched above - [ ] **Step 1: Run full verification** ```bash python3 -m unittest tests.test_main_agent_governance tests.test_platform_contracts tests.test_production_baseline -v node --test web/storyforge-web-v4/tests/dashboard-home.test.mjs web/storyforge-web-v4/tests/workbench-pages.test.mjs python3 -m compileall collector-service/app tests git diff --check bash scripts/check_repo_baseline.sh ``` Expected: all pass. - [ ] **Step 2: Deploy to NAS** ```bash STORYFORGE_FNOS_COLLECTOR_DEPLOY_MODE=remote_build bash /Users/kris/code/StoryForge-gitea/scripts/deploy_fnos_storyforge_collector.sh bash /Users/kris/code/StoryForge-gitea/scripts/deploy_fnos_storyforge_web.sh bash /Users/kris/code/StoryForge-gitea/scripts/smoke_fnos_storyforge_lan.sh ``` Expected: web, collector, cutvideo tunnel, compat smoke all pass. - [ ] **Step 3: Commit and push** ```bash git add collector-service/app/oneliner_features.py tests/test_main_agent_governance.py web/storyforge-web-v4/assets/app.js web/storyforge-web-v4/assets/styles.css web/storyforge-web-v4/tests/workbench-pages.test.mjs docs/superpowers/specs/2026-03-29-main-agent-runtime-flow-design.md docs/superpowers/plans/2026-03-29-main-agent-runtime-flow.md git commit -m "feat: add main agent runtime flow" git push gitea codex/storyforge-live-orchestrator-sync-20260323 ```