CI Log Diff Open the app

Reference · version 1.0.0-rc.1

CI Log Diff documentation

What goes in, what comes out, and where the limits are. Everything on this page describes the released browser app and CLI.

Input

Two plain-text UTF-8 log files from the same CI job: one you consider known good, one from the failed run. You choose the roles. A file extension is not treated as validation: the worker checks the actual bytes, rejects invalid UTF-8 and NUL bytes, and counts lines and line lengths.

GitHub Actions ::group:: / ::endgroup:: markers are tracked as sections. Unbalanced markers become diagnostics with their line number. ANSI colors, OSC hyperlinks/titles, DCS and other terminal control sequences are removed for display; bidirectional override characters are shown as visible [U+202E]-style tokens. CRLF line endings are accepted. Your files are never modified.

Normalization

The active profile is always visible in the app. It has two parts, both optional:

  • ISO timestamp prefix (on by default): a leading 2026-09-15T10:00:00Z or [2026-09-15T10:00:00.123+02:00] is ignored when comparing.
  • Workspace prefix: an exact path such as /home/runner/work/repo, one per side, replaced with <workspace> only at a path boundary.

Nothing else is normalized. There is no user-supplied regular expression. Changing the profile or either input discards the current result, the selection, the “include log text” choice and any AI permission.

Reading results

A result has one of three statuses:

  • pass — every line was compared and no differences remain after normalization. It does not mean the CI job passed.
  • findings — there are changed blocks or group-marker diagnostics.
  • incomplete — a budget ran out. The result states the last compared line on each side and the reason; lines after that are unknown and are labelled “not compared” in the app.

A changed block is a run of adjacent lines that differ, with no equal line in between. It is added, removed or changed, and carries the 1-based line range on each side. A block is an error candidate when the failed-run side contains an error-like line (for example error, fatal, exception, ##[error], a non-zero exit code) that is not in the known-good side of the same block. “0 failed” and “no errors” do not count. Candidates are leads, not a diagnosis, and a failure that never prints such a word has no candidates — its changes are still listed.

In the app, Original shows the file’s text with terminal controls removed; Normalized shows what was actually compared. The source context around a block can be widened to 40 lines; at most 100 lines per side are rendered at once, with paging for longer blocks.

Export contract

JSON and HTML exports come from the same report object: schema_version 2.1, ruleset ci-log-diff-v2, tool_version, status, coverage, summary, diagnostics, exportSelection (selected and available counts) and changes with id, kind, candidate, anchor and line ranges.

Default exports contain no log text, file names, group labels or workspace paths. With “Include log text” (--include-context in the CLI), each exported block also carries its excerpt: up to 4 lines per side, 500 characters per line, including the group label. This is not secret redaction; review before sharing. Exporting an empty selection keeps the original findings or incomplete status.

The HTML report is a single file with no scripts, images, links or remote resources, and its own restrictive Content Security Policy. See a real example →

Changes from schema 2.0: adjacent differing lines are reported as one block instead of many one-line blocks (ruleset v2); each change has an anchor giving the number of lines before the block on each side; reports carry tool_version; exportSelection.selected counts exported blocks.

Limits

LimitValueWhen it is exceeded
File size8,388,608 bytes (8 MiB) per fileRefused before parsing, with the side named
Lines200,000 per fileRefused, with the side named
Line length65,536 charactersRefused, with the side named
EncodingUTF-8 text, no NUL bytesRefused; no lossy decoding
Alignment work4,000,000 line comparisons; 64-line lookahead, then re-sync on a line that is unique in both logs within 2,000 linesincomplete with the covered range
Result size200 changed blocks or 512 KiBincomplete with the covered range
Deadline5 secondsWorker terminated; no result
Excerpt per block4 lines per side, 500 characters per lineMarked as shortened; line range stays exact
Review window (app only)100 lines per side, 2,000 characters per linePaged; never exported

Browsers do not offer a hard memory cap for a page, so resource use is bounded through these input, work and output budgets plus worker termination. Measured timings depend on the device and are not promised.

CLI

Requires Node.js 24 (24.12 or later). The CLI reads two files inside a root directory you name and can create one new report file there. It makes no network requests and never overwrites a file.

npm ci --ignore-scripts
node apps/cli/main.mjs --root . --before fixtures/good.txt --after fixtures/failed.txt
node apps/cli/main.mjs --root . --before good.log --after failed.log --format html --out report.html
node apps/cli/main.mjs --help
Exit codeMeaning
0Complete comparison, nothing that violates --fail-on
1Complete comparison with policy findings (candidates by default, or changes)
2Invalid arguments or input, refused path, existing output file, or incomplete comparison
130Cancelled (SIGINT)

Paths are relative to --root. Absolute paths, .., symbolic links, FIFOs and other non-regular files are refused. Errors are printed as a JSON code on stderr, never with file contents.

AI agents

There are two ways an assistant can use CI Log Diff today:

  • Local CLI. A coding agent with shell access can run the command above and read the bounded JSON. The default output has no log text, so nothing from the log is fed back as instructions.
  • Optional browser tool. When the browser exposes document.modelContext, the app registers a read-only read_ci_comparison_summary tool. It returns the default export (classifications and line ranges, never text) and only after you tick a visible permission that resets on every new comparison, input change or reset. In browsers without that API nothing is registered and nothing is shown. It has been tested against a mock host only.

There is no remote MCP server, hosted connector or API. Log content is data: nothing in a log can trigger a command, a request or a tool call. Machine-readable copies of this page: overview.md, llms.txt.

Security model

The site is static files. The Content Security Policy allows same-origin scripts, styles and workers only, with connect-src 'none', no unsafe-eval and no inline script execution. The app writes nothing to localStorage, IndexedDB, cookies, the URL or the clipboard. Closing the tab, pressing Reset or changing an input terminates the worker that holds the parsed text. Downloads happen only when you press a download button. Privacy details →