Part of the "SQLite migration is half-done" parent issue.
Background
The orchestrator runs a designer/critic loop per section and saves each result to the SQLite section_designs table (backend/app/agents/orchestrator.py, step 3 in _drive_run_inner, writing SectionDesign.record). The build agent turns an approved design into a sandbox topology.
Problem / Goal
backend/app/agents/build.py:210 (converse_build) resolves the design it is about to build by calling get_design(design_id), imported from app.agents.loop. That function (backend/app/agents/loop.py:92) only reads data/designs/<id>.json from disk. Orchestrator designs live in SQLite, not on disk, so there is no way to build a sandbox from an orchestrator run — the core v2 workflow dead-ends.
Goal: let the build flow accept a SectionDesign (SQLite integer id) and read its record from the DB.
Where to look
backend/app/agents/build.py:190-306 — converse_build, where get_design is called and final_design = iterations[-1]["design"] is extracted.
backend/app/agents/loop.py:92 — the current flat-file get_design.
backend/app/db/models.py:82 — SectionDesign; its .record column already holds the exact dict shape the build agent expects ({"iterations": [...], ...}).
backend/app/api/build.py:37-67 — BuildConverseRequest.design_id and the converse endpoint.
backend/app/api/runs.py:112 — GET /api/runs/{run_id}/sections/{section_id} shows how a section record is fetched from SQLite.
Suggested approach
- Add a DB-backed lookup that, given a
SectionDesign.id, opens a session (from app.db.session import engine), fetches the row, and returns sd.record (or None).
- Change
converse_build to use that lookup instead of loop.get_design.
- Decide how
design_id is typed on the request: accept the integer SectionDesign.id. Keep backward tolerance for the legacy hex id only if a flat-file fallback is still desired (optional; the parent issue's direction is SQLite-first).
- Guard the "no iterations" / "no design" error paths that already exist (
build.py:215-219).
Acceptance criteria
- Given an approved
SectionDesign, POST /api/build/converse produces a topology and a saved build.
- Building an orchestrator-run section no longer requires any file under
data/designs/.
- Missing/invalid section ids return a clean error event (
{"type":"error", ...}), not a 500 traceback.
Testing
Local backend (cd backend && uvicorn app.main:app --reload --port 8080):
- Run intake + a run until one section is
approved (see parent issue Testing).
- Note the section id from
GET /api/runs/{id}.
POST /api/build/converse with that id; confirm it streams to build_saved.
Add a unit test that stubs the DB session and asserts converse_build reads from SectionDesign.record.
Out of scope
- Where the build record is saved (that's the BuildRecord sub-issue).
- Frontend wiring of a "Build this section" button (backend contract only here).
Part of the "SQLite migration is half-done" parent issue.
Background
The orchestrator runs a designer/critic loop per section and saves each result to the SQLite
section_designstable (backend/app/agents/orchestrator.py, step 3 in_drive_run_inner, writingSectionDesign.record). The build agent turns an approved design into a sandbox topology.Problem / Goal
backend/app/agents/build.py:210(converse_build) resolves the design it is about to build by callingget_design(design_id), imported fromapp.agents.loop. That function (backend/app/agents/loop.py:92) only readsdata/designs/<id>.jsonfrom disk. Orchestrator designs live in SQLite, not on disk, so there is no way to build a sandbox from an orchestrator run — the core v2 workflow dead-ends.Goal: let the build flow accept a
SectionDesign(SQLite integer id) and read its record from the DB.Where to look
backend/app/agents/build.py:190-306—converse_build, whereget_designis called andfinal_design = iterations[-1]["design"]is extracted.backend/app/agents/loop.py:92— the current flat-fileget_design.backend/app/db/models.py:82—SectionDesign; its.recordcolumn already holds the exact dict shape the build agent expects ({"iterations": [...], ...}).backend/app/api/build.py:37-67—BuildConverseRequest.design_idand theconverseendpoint.backend/app/api/runs.py:112—GET /api/runs/{run_id}/sections/{section_id}shows how a section record is fetched from SQLite.Suggested approach
SectionDesign.id, opens a session (from app.db.session import engine), fetches the row, and returnssd.record(orNone).converse_buildto use that lookup instead ofloop.get_design.design_idis typed on the request: accept the integerSectionDesign.id. Keep backward tolerance for the legacy hex id only if a flat-file fallback is still desired (optional; the parent issue's direction is SQLite-first).build.py:215-219).Acceptance criteria
SectionDesign,POST /api/build/converseproduces a topology and a saved build.data/designs/.{"type":"error", ...}), not a 500 traceback.Testing
Local backend (
cd backend && uvicorn app.main:app --reload --port 8080):approved(see parent issue Testing).GET /api/runs/{id}.POST /api/build/conversewith that id; confirm it streams tobuild_saved.Add a unit test that stubs the DB session and asserts
converse_buildreads fromSectionDesign.record.Out of scope