← Technical journal

Systems · Python · SSE · 6 min read

A research pipeline that keeps the useful parts.

Inside Field Notes: streaming stage events, collecting evidence, and letting analysis fail without losing the research that came before it.

Anudeep Adiraju · September 2026

01 / System at a glance
  1. 01Request

    Validate credentials before opening the stream

  2. 02T0 · Context

    Website, repository, and form → dossier

  3. 03T1 · Evidence

    Choose targets → harvest posts

  4. 04T2 · Analysis

    Quotable signals → findings → validation

  5. 05Result

    Return the dossier, harvest, and any report

The orchestration path in pipeline.py. Progress and error events travel alongside these stages. A missing dossier is fatal; a later analysis failure can still leave useful earlier results.

The interface needs more than a spinner

Field Notes turns context about a product into a research dossier, collected signals, and an analysis report. Those are different kinds of work with different failure modes. A repository can be understood even when a later research source is unavailable. A collection of posts can remain useful even when the model cannot turn it into a defensible report.

The interesting design decision is to make that structure visible. The backend keeps HTTP framing in main.py, orchestration in pipeline.py, and the actual stages in their own modules. That separation lets the interface follow the process without making the transport layer responsible for the research itself.

Events describe progress; results hold the work

The pipeline is an asynchronous generator. T0 emits events while preprocessing the supplied website, repository, and form. The orchestrator passes those events through and also captures the dossier event for later stages. It does not have to assemble a finished response before the user can see anything.

At the HTTP boundary, each typed event becomes a Server-Sent Events frame. The named event tells a client what kind of update it received; the JSON payload carries the data. This fits a mostly one-way workflow: the browser submits a request and then listens to the run.

Credential checks happen before streaming starts. This matters because, once the response has begun, a late exception cannot be expressed by changing the original HTTP status. The streaming wrapper instead emits an error event. A consumer must handle that event explicitly; a successful HTTP connection does not prove a successful analysis.

event: <event type>
data: <serialized event payload>

Failure belongs to a stage

If T0 produces no dossier, the pipeline emits a fatal error and returns. That is a real dependency: there is no meaningful research target without the initial context. Target selection in T1 has a different policy. If selection raises an exception, the pipeline emits a nonfatal error and uses fallback targets based on the dossier.

Analysis only runs when harvested posts exist and the request has not deliberately stopped at an earlier stage. Before calling analysis, posts are normalized into signals. A post is quotable when its body or one of its comments contains text. Empty material is filtered out rather than treated as evidence.

If analysis or validation fails, the system can preserve T0 and T1 work. The final FieldNote contains the dossier, harvest, and an optional report. “No report” is a state the interface needs to explain, not an excuse to erase the evidence the user already waited for.

Validation is a step, not a guarantee

The analysis path runs findings through a validation step and associates validation results with finding titles before building the report. That provides a place to inspect evidence. It does not turn model output into ground truth. The distinction matters especially when a polished report makes an uncertain conclusion feel settled.

I would keep source material reachable from each finding and expose incomplete or failed stages next to the report. The code’s event model makes that possible, but the architecture alone cannot tell us whether every conclusion is supported. That requires evaluating actual runs.

Where the prototype needs a stronger foundation

Reports are saved as JSON files under out/reports using a normalized slug. This is easy to inspect and useful for a small prototype. A second report with the same slug overwrites the first; concurrent writes and multiple server instances need a more deliberate persistence model.

For a durable service, I would add a run identifier, append-only stage records, atomic report writes, and a replay cursor for interrupted clients. Those are proposed next steps, not features already present. I would also distinguish cancellation from failure so a disconnected browser cannot leave expensive work running accidentally.

The existing pipeline tests replace stages with controlled outputs. They are useful for checking orchestration and event forwarding, but they are not an evaluation of external sources or model quality. The next test layer should deliberately fail each stage and check that the user gets exactly the usable work that survived.