WORK-575
ID:WORK-575Status:ready

Route pipeline diagnostics through one reporter, and print them in dev

Split from WORK-573, which bundled two independent changes: making an error-severity diagnostic fail something, and making one visible at all. This item is the second, and it has none of the first's downstream-compatibility argument — which is why WORK-573 itself says to do it regardless of what is decided about exit codes.

WORK-554 measured that a developer running the adapter dev server sees no pipeline diagnostics of any severity — not a muted version, none. That is the more damaging half of its finding, and it is a reporting gap rather than a missing pipeline.

Priority:highComplexity:moderateMilestone:v0.36.0Source:SPEC-135
claude/spec-131-snippet-targeting-q5ra9y View source

Criteria completion

Criteria completion: 0 of 10 (0%) checked; tracking started on Sep 17, no incremental history yet0%25%50%75%100%Sep 17Oct 11

Tracking started Sep 17 — check back for trends.

Branches 5
History 4
  1. 2fd77d8
    Content editedby bjornolofandersson
  2. e0e3908
    Content editedby bjornolofandersson
  3. ee887f0
    Content editedby Claude
    docs(plan): re-source BUG-017, split WORK-573, flip SPEC-132 to implemen
  4. bee157e
    Created (ready)by bjornolofandersson

The data already exists in dev

Worth stating precisely, because the fix is smaller than "wire up dev diagnostics" suggests:

  • packages/sveltekit/src/plugin.ts:171 returns early on if (!isBuild), and that guard wraps the whole content-loading block — loadContent is called once, at :205, inside it.
  • Dev does not use that path. Content comes from virtual:refrakt/content → createRefraktLoader (packages/content/src/refract-loader.ts), which calls loadContent internally and caches the site; the HMR hook calls invalidateSite() and the next request re-loads.
  • So site.pipelineWarnings is fully populated in dev, sitting on the cached site object, and nothing reads it.

The pipeline does the work of producing diagnostics on every content edit and throws them away.

One reporter, not six call sites

WORK-573's AC 5 asks that the other adapters "either get the same treatment or the divergence is recorded with a reason" — six places to keep in sync and six places to drift. SPEC-135 D5 takes the other route.

loadContent is the single function that dev (via createRefraktLoader) and build (via each adapter's plugin) both go through. A reporter option there, defaulting to stderr, serves every adapter and both modes at once, and makes divergence unrepresentable rather than policed by a checklist.

It is also the seam SPEC-135's CLI and MCP surfaces need, so building it here means phases 2 and 3 of that spec are wiring rather than plumbing.

Acceptance Criteria

  • reporter is added to LoadContentFromTreeOptions, and loadContent gains an options-bag overload beside its fourteen-positional form — no fifteenth positional
  • All four callers move onto the options-bag form: sveltekit plugin.ts:205, eleventy data.ts:71, editor server.ts:217, and loader.ts:58
  • The positional loadContent still works and is still exported, so no external consumer breaks
  • The default reporter preserves today's behaviour exactly — formatPipelineSummary to stderr — so no existing consumer changes
  • Every adapter (sveltekit, eleventy, astro, nuxt, next, html) reports through the reporter rather than calling formatPipelineSummary itself
  • A dev session prints its pipeline diagnostics on first content load
  • A dev session prints them again after an HMR reload, reflecting the edited content
  • Dev output is not duplicated when a cached site is reused without re-loading
  • No change to exit codes — that is WORK-573's decision, and this item must not pre-empt it
  • site/ and plan-site/ still build green, with unchanged stderr output

Approach

  1. Add reporter to LoadContentFromTreeOptions and an options-bag overload to loadContent, defaulting to the current stderr behaviour. Nothing observable changes. Not a fifteenth positional — the signature is already at fourteen, and the bag exists one function over precisely because of that. Whether 1.0 keeps this shape is WORK-576.
  2. Move each adapter's process.stderr.write(formatPipelineSummary(…)) onto it, migrating the four callers to the bag as you go.
  3. Have the dev path report after each load — either from createRefraktLoader when it populates its cache, or from the Vite plugin's HMR hook after invalidateSite(). The loader is the better home: it is where both modes converge, and it keeps the sveltekit plugin from being the only adapter with dev diagnostics.

Watch the re-load boundary. The loader caches the site and only re-loads after invalidateSite(). Reporting on cache hit would print the same diagnostics on every navigation; reporting on cache fill prints once per actual content load, which is the behaviour to want.

Blocks

  • WORK-573 — with the reporter seam in place, the exit-code change is one place instead of six

References

  • SPEC-135 — D5 (the reporter goes at loadContent), and the spec this is phase 1 of
  • WORK-554 — the measurement: nothing printed in dev, if (!isBuild) at plugin.ts:171
  • WORK-573 — the item this was split from; keeps the exit-code half
  • packages/content/src/site.ts — loadContent
  • packages/content/src/refract-loader.ts — createRefraktLoader, the dev path
  • packages/content/src/format.ts — formatPipelineSummary
  • packages/sveltekit/src/plugin.ts — the isBuild guard at :171