Parent
Part of #25 (Screen Solver v1: implementation spec), itself under the map #1.
What to build
The end-to-end "solve now" behavior over HTTP, wiring the capture session and the provider seam together and broadcasting the result live. This is the tracer bullet for the whole app — after this ticket, a raw HTTP client can trigger a real solve against a real target window and watch it stream.
POST /solve performs only synchronous validation (is a target window configured) — 400 if not, 202 on accept, 503 if the server itself isn't ready. It always interrupts whatever solve is currently in flight; there is no busy rejection. Before spending a call, it runs the pre-flight guards from ticket 3's exposed signals (window gone/unresolvable, minimized/off-desktop, black/zero-size frame) — a failing guard is a silent no-spend with no SSE event. Once a call is actually attempted, GET /events (Server-Sent Events, one shared broadcast, no per-client filtering, no auth) emits start, then delta{text} as the provider streams, then a terminal done{usage} or error{kind}. The server holds the in-flight accumulated text in memory. Interrupting a solve in progress ends its stream as interrupted for downstream consumers (ticket 6 persists this; this ticket only needs to produce the signal).
Acceptance criteria
Blocked by
#26 (Host process bootstrap & API key gate), #28 (Target window config surface), #30 (Live window capture session), and #27 (Provider seam) — the frame signals and the model call this ticket orchestrates.
Parent
Part of #25 (Screen Solver v1: implementation spec), itself under the map #1.
What to build
The end-to-end "solve now" behavior over HTTP, wiring the capture session and the provider seam together and broadcasting the result live. This is the tracer bullet for the whole app — after this ticket, a raw HTTP client can trigger a real solve against a real target window and watch it stream.
POST /solveperforms only synchronous validation (is a target window configured) —400if not,202on accept,503if the server itself isn't ready. It always interrupts whatever solve is currently in flight; there is no busy rejection. Before spending a call, it runs the pre-flight guards from ticket 3's exposed signals (window gone/unresolvable, minimized/off-desktop, black/zero-size frame) — a failing guard is a silent no-spend with no SSE event. Once a call is actually attempted,GET /events(Server-Sent Events, one shared broadcast, no per-client filtering, no auth) emitsstart, thendelta{text}as the provider streams, then a terminaldone{usage}orerror{kind}. The server holds the in-flight accumulated text in memory. Interrupting a solve in progress ends its stream asinterruptedfor downstream consumers (ticket 6 persists this; this ticket only needs to produce the signal).Acceptance criteria
POST /solvewith no target window configured returns400and produces no SSE traffic.POST /solveagainst a correctly configured, capturable target returns202, andGET /eventsobservesstart→ one or moredelta→done{usage}in order.POST /solvewhile a previous solve is still streaming interrupts it immediately — the new solve's events begin, and the prior solve is markedinterruptedrather than left dangling.error{kind}(from ticket 4's seam) reachesGET /eventsas a terminalerror{kind}event.GET /eventsconnections observe an identical event sequence for the same solve.Blocked by
#26 (Host process bootstrap & API key gate), #28 (Target window config surface), #30 (Live window capture session), and #27 (Provider seam) — the frame signals and the model call this ticket orchestrates.