Files
open-design/packages
lefarcenandGitHub 34192c6719 fix(diagnostics): keep the previous session's daemon log and cut event tails on line boundaries (#6531)
* fix(diagnostics): keep pre-restart daemon log and line-aligned event tails

Two gaps made incident support bundles useless after a relaunch:

1. Every packaged launch opened each sidecar's latest.log with mode
   "w", destroying the previous session's log — the bundle held only
   the ~70 lines written since the restart while the incident-time
   daemon log was gone. openLog now rotates the prior file aside as
   previous.log (exactly one prior session, rename overwrites), and
   the diagnostics export bundles logs/<app>/previous.log whenever it
   exists, under the same per-file tail cap. The source entry is
   existence-conditional so non-rotating launchers (tools-dev appends)
   do not gain missing-file placeholder noise.

2. The 2MB tail cap on runs/*/events.jsonl cut bytes from mid-file, so
   the first kept line was half a JSON object and jsonl consumers fell
   over on line 1. readMaybeTail now advances the cut to the next line
   boundary (with a guard byte so an already-aligned cut loses
   nothing); a window with no newline keeps the raw cut.

* fix(packaged): keep the prior log when rotation fails instead of truncating it

A non-ENOENT rename failure (Windows share-lock on previous.log, read-only or
exotic filesystem) still fell through to open(path, "w"), destroying the only
copy of the pre-restart log while previous.log stayed unavailable — the exact
data loss this rotation exists to prevent. Truncate only when the prior bytes
are provably elsewhere: moved aside, or never written (first-launch ENOENT).
Otherwise open in append mode, since a merged two-session log is recoverable
and an erased one is not.

* fix(diagnostics): never export a partial record, and never hide an unreadable log

Two holes in the truncation/collection contract:

- A single event larger than the whole tail budget left no complete line to
  align to, and the fallback kept the raw cut — so the export shipped the
  interior of one JSON record and any consumer streaming the bundle through a
  jsonl parser died on line 1, exactly what alignment exists to prevent. Signal
  "no complete line" instead and let the collector record it, so the bundle
  carries a self-describing placeholder rather than half an object.
- The optional-source gate treated EVERY access failure as absence, so a
  previous.log that exists but is unreachable (EACCES on the log directory,
  EIO) vanished with no manifest entry and no warning. Only ENOENT may drop the
  entry now; anything else stays listed and reports the real error.
2026-08-07 07:04:52 +00:00
..