The controller ran silently and surfaced errors as opaque/empty bodies,
making it impossible to diagnose failures from either the UI or the server.
- Configure Python logging (timestamps, levels) and log job lifecycle events
(created / dispatched / done / failed) plus startup and dispatch warnings.
- Add a catch-all error handler so unhandled exceptions return a JSON error
with the exception type+message instead of an empty response.
- Remove the frontend submit bug that hand-set multipart Content-Type without
a boundary (and built an opts object it never used); post the FormData
directly so the browser sets the correct content type + boundary.
- Add ctrl/ package: Flask app (waitress), SQLite job store, background
scheduler that probes configured workers, dispatches via /upload and
/checkout_git, fetches artifacts, and serves a single-page UI
- Supporting API: /api/jobs (create/list/detail/cancel), /api/workers,
/api/capabilities, artifact download, and SSE log stream
- Workers keep the existing HTTP API; add /progress/<job_id> endpoint and
per-step build tracking for live status
- Retire agent_manager.py and the cross-py-builder CLI; controller UI is
the primary frontend
- Record design in DESIGN.md; add waitress dependency
Note: /progress is exposed on the worker but the synchronous /upload and
/checkout endpoints block until a build completes, so per-step worker
progress is not yet relayed live in the UI. Live streaming needs an
async-start worker endpoint as a follow-up.