Files
storyforge/docs/superpowers/plans/2026-03-29-main-agent-runtime-flow.md
2026-03-29 18:05:30 +08:00

13 KiB

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
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:

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:

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:

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:

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:

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:

{
    "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:

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:

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:

@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:

@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:

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:

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:

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:

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:

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:

onelinerRuns: [],
selectedOnelinerRunId: "",
onelinerRunEvents: [],
  • Step 2: Render the runtime header area

Inside ensureOneLinerUi(), add a dedicated block:

<div class="oneliner-runs" data-role="oneliner-runs"></div>

above the message list.

  • Step 3: Add render helpers

Implement focused helpers:

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:

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:

storyforgeFetch(`/v2/oneliner/runs?project_id=${encodeURIComponent(normalizedProjectId)}`)

and store it in appState.onelinerRuns.

  • Step 2: Add API helpers

Implement:

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:

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:

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

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
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
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