## Context Host Agent executes Cloud assignments in-process through the shared `runtime.TaskRunner`. Its `HostAgentApplication` already constructs a Host-local `TaskMetadataStore` and `Timeline` and injects them into the runner factory. The missing link is task creation: `AssignmentExecutor` constructs a `Task`, calls `runner.run(task)`, and `TaskRunner` only issues updates. SQLite therefore receives updates for a row that does not exist, so the Host Agent `/tasks` UI is empty. The separate `api.rest` process was never on this execution path. It owned a different database and artifact root, so its REST endpoints and UI could only show tasks submitted directly to that unrelated process. Running it alongside a Host Agent created two competing operator entry points without transferring any history between them. ## Goals / Non-Goals **Goals:** - Make durable task-row creation an invariant of `TaskRunner.run()` whenever a metadata store is configured. - Correlate a Host-local goal execution with the Cloud task ID and attempt that caused it, without making shared Runtime or storage packages import Cloud models. - Make the authenticated Host Agent console at port `8765` the authoritative web view for actual task execution: task list, detail, live status, before screenshot, operation, after screenshot, OCR observations, and normalized UI-tree results. - Preserve the shared Runtime Timeline as the evidence model and keep its before/after screenshot, OCR, and UI-tree capture behavior. - Retire the standalone Runtime REST service/UI and its Host Agent supervisor configuration while retaining Runtime, storage, MCP, and skill-sync library modules. - Retain the existing Cloud latest-progress and Cloud-proxy planner-decision history behavior. Cloud remains a fleet-level view and never stores screenshots. **Non-Goals:** - No SSE/WebSocket push; Host and Cloud consoles keep their existing polling. - No duplicate full evidence upload to Cloud. Screenshots and Timeline artifacts remain Host-local. - No new Host/Cloud dependencies in `runtime/`, `storage/`, `driver/`, or `device/`. - No replacement general-purpose device-control REST API. Operators use Host Agent device management and task pages for the managed execution workflow. ## Decisions ### D1: TaskRunner owns idempotent task metadata creation `TaskRunner.run()` will call `TaskMetadataStore.create_task(task)` before the first running-state update. `create_task()` will use idempotent insert semantics so callers that already created a direct Runtime task remain compatible and workflow-created tasks are captured automatically. This places the invariant at the shared execution boundary rather than relying on every caller to remember an out-of-band persistence call. It fixes goal assignments and prevents the same failure for `WorkflowRunner` planned-goal steps. ### D2: Host assignment correlation stays in the Host adapter Before executing a Cloud goal assignment, `AssignmentExecutor` creates the local task record with optional generic `source_task_id` and `source_attempt` metadata. Shared storage uses generic names and does not import Cloud types. The Host Agent task list/detail renders those fields as the Cloud task ID and attempt. The local `Task.id` remains generated by the Runtime. Reusing the Cloud task ID as an artifact directory name would make retries overwrite each other and would admit unsafe path characters from an external identifier. ### D3: Host Agent console is the authoritative evidence UI The Host Agent already owns the device manager, assignment executor, metadata store, Timeline, local account, session, and CSRF boundary. Its same-origin `/tasks` and `/tasks/{task_id}` pages therefore render the shared Timeline directly. The task list is named for Host executions rather than "Local Runtime tasks", and submission feedback tells the operator that the submitted Cloud task appears there when this Host begins execution. No browser needs to point a separate frontend at the Host Agent. This keeps the conservative local-account/session model and does not add CORS. ### D4: Complete per-step evidence is rendered by the Host Agent Timeline records retain distinct pre-action and post-action screenshot paths, the action description and arguments, the execution result, raw OCR observations, and the existing normalized UI-tree result. The Host Agent page creates data URIs only for available local artifacts and supports legacy records where the single `screenshot_path` is the post-action image. OCR is rendered when present. UI-tree output is rendered only for `get_ui_tree` and `ui_tree` records that contain normalized nodes; it uses a collapsible structured view while retaining the JSON result. No tool contract or duplicate persistence field is introduced. ### D5: Host-local retention remains bounded The existing Host retention pass continues to remove metadata rows, Timeline records, and artifacts according to the configured count and age thresholds. The new task creation invariant must use that same store so it cannot create an unbounded second history source. ### D6: Cloud progress remains a latest snapshot on lease renewal The Host execution thread writes a bounded latest-progress holder. The lease renewal path optionally carries its step index, status, and summary; Cloud stores only the latest snapshot for an active assignment and stops exposing it after terminal completion. No screenshot or scene payload enters this protocol. ### D7: Cloud-proxy planner decisions remain durable Cloud history For `AI_PLANNER_TRANSPORT=cloud`, Cloud's existing planner-decision endpoint persists successful system/user prompts, tool calls, arguments, and a task/attempt-scoped step index. The direct transport intentionally produces no such Cloud history. This log has bounded terminal-task retention and never persists request screenshot bytes. ### D8: Retire the standalone Runtime REST service and UI Remove `api/rest.py`, `api/console.py`, `api/console_web.py`, their templates/static assets, their package-data declarations, and their service/UI tests. Preserve `api/mcp.py`, `api/errors.py`, skill-sync, and skill-catalog modules because they are independent library integrations. Remove `HOST_AGENT_RUNTIME_SUPERVISED`, `HOST_AGENT_RUNTIME_HOST`, and `HOST_AGENT_RUNTIME_PORT`. The optional dependency supervisor continues to support Appium only. Configuration with a removed Runtime-supervision variable fails with an actionable migration error instead of silently doing nothing. ### D9: Documentation points operators to Host Agent Operator documentation no longer instructs users to start `uvicorn api.rest:create_app` or browse port `8000`. It identifies the Host Agent console at `http://127.0.0.1:8765/tasks` as the execution-history authority, explains its local authentication, and documents that the Cloud Console is a fleet/progress and Cloud-proxy LLM-history surface rather than a screenshot store. ## Risks / Trade-offs - Per-step metadata writes add small SQLite I/O. This is the same local store already selected for Host history, and bounded retention limits growth. - A Host-local evidence record is only available while retained on that Host. This is intentional: it reflects the actual device execution and avoids sending screenshots to Cloud. - Removing the unauthenticated Runtime REST service is a breaking operator change. Clear configuration errors and documentation avoid a silent fallback to a nonexistent inspection surface. - Full Cloud-proxy prompts can contain visible screen text. This is the previously accepted Cloud troubleshooting trade-off; screenshot bytes remain excluded. ## Migration Plan 1. Upgrade the Host Agent code. Existing task databases gain nullable source correlation columns on startup; legacy Timeline records remain readable. 2. Remove any `HOST_AGENT_RUNTIME_*` environment variables and stop any `api.rest` process. Start or browse only the Host Agent console for local execution evidence. 3. Confirm a completed Host assignment appears at `/tasks` with its Cloud task/attempt correlation and complete Timeline evidence. 4. Roll back only by restoring the prior release. The retired REST/UI routes are deliberately not kept as a compatibility alias because their storage was not connected to Host execution. ## Open Questions - Manual verification still requires a real Host Agent, Appium, and device. Automated coverage verifies persistence, correlation, rendering, and service removal; real hardware validates screenshots and OCR availability.