Skip to content

feat(diagnostics): assemble the shareable bundle a report is attached to - #5570

Open
aodhanroche wants to merge 1 commit into
Aodhan/260901/diagnostics-4-healthfrom
Aodhan/260901/diagnostics-5-bundle
Open

aodhanroche wants to merge 1 commit into
Aodhan/260901/diagnostics-4-healthfrom
Aodhan/260901/diagnostics-5-bundle

Conversation

@aodhanroche

@aodhanroche aodhanroche commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Issue

A report is data. A bundle is the thing someone actually attaches to a bug report.

Fix

DiagnosticsBundle stages files in a temp dir, redacts text on the way in, and hands back zip bytes — the caller decides where they land. It's a context manager, so the staging dir goes away even if assembly dies partway.

Inside: manifest.json, a plain-language README.md, report.json, doctor.json, this session's log, the rotated logs newest-first, and the open workflow if it has a saved file.

Worth a look: it uses the caller's Redactor, not its own, so one set of counts covers the whole zip — otherwise the manifest could claim three values hidden while the logs beside it went through a different pass. Logs are tailed against a byte budget so a week-old engine can't produce an unattachable zip, and anything shortened is recorded as a manifest warning, since a log starting mid-session looks identical to a session that started there.

36 tests.

🤖 Generated with Claude Code

A report is data; a bundle is the thing somebody actually attaches to a bug
report. `DiagnosticsBundle` stages files in a temporary directory, redacts every
piece of text on the way in, and hands back zip bytes. It writes nothing into
the workspace -- the caller decides where the bytes land -- and it is a context
manager so the staging directory goes away even when assembly fails partway.

A bundle holds `manifest.json` (what this is, what is in it, and how many values
were hidden), a plain-language `README.md` so the recipient does not have to
guess what the other files are, `report.json`, `doctor.json`, this session's log,
the rotated log files newest-first up to a size budget, and the open workflow
when there is a saved file for it.

Everything copied in goes through the caller's `Redactor`, not a second one of
its own, so one set of redaction counts covers the whole bundle rather than the
report alone -- otherwise a manifest could report three values hidden while the
log files in the same zip had gone through a different pass entirely.

Log files are read as a tail against a byte budget rather than copied whole, so
one engine that has been running for a week cannot produce a bundle too large to
attach. Anything shortened or left out is recorded as a warning in the manifest,
because a truncated log changes what a bundle can prove and a reader has no way
to tell otherwise.

Paths are canonicalized before being read, so a bundle collected through a
symlinked workspace reads the files it means to.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
for path in log_files:
staged_path = f"{LOGS_DIRECTORY_NAME}/{path.name}"

if any(entry.path == staged_path for entry in self._entries):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

AI finding, but I checked and I agree with written below.

Nice piece of work overall — one thing worth a look: the duplicate-name guard here (entry.path == staged_path) is a case-sensitive string comparison, but the entry then gets written to the real filesystem via path.write_text in _write. On a case-insensitive filesystem (default macOS/Windows), two source log files named e.g. Engine.log and engine.log would produce different staged_path strings, sail past this check, and then collide on disk — the second write silently overwrites the first's bytes while the manifest still lists both as separate, correctly-sized entries. Might be worth comparing staged paths case-insensitively (or normalizing to lowercase) to keep the guard matching what the filesystem will actually do.


if remaining <= 0:
warnings.append(
f"Log file '{path.name}' was left out because the bundle already holds "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

AI finding, but I checked and I agree with written below.

Small one: this warning always cites self._max_log_bytes (the full configured budget) rather than how much was actually consumed before hitting remaining <= 0. If an earlier file was skipped for a different reason (e.g. the duplicate-name case above) before the budget was truly exhausted, the message would overstate how much was genuinely used. Might be nice to report self._max_log_bytes - remaining instead, so the manifest gives an accurate picture if someone's chasing down a missing log.

@alex-rumsey-foundry alex-rumsey-foundry left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really solid work on this one — the budget/truncation logic is careful and well tested. Two small correctness nits inline: a case-sensitivity gap in the duplicate-log-name guard, and a slightly misleading 'budget exhausted' warning message. Neither is a big deal, just flagging before merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants