Skip to content

fix(triage): recover prose-wrapped JSON and send real diffs to triage - #9

Merged
krayong merged 1 commit into
mainfrom
fix/triage-parse-and-diff
Jul 14, 2026
Merged

fix(triage): recover prose-wrapped JSON and send real diffs to triage#9
krayong merged 1 commit into
mainfrom
fix/triage-parse-and-diff

Conversation

@krayong

@krayong krayong commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Why

AI summaries were failing on most scheduled runs with AI unavailable: could not parse claude response: Expecting value: line 1 column 1 (char 0). Both fixes below were reproduced against live claude -p output before being written, and covered test-first.

1. Parser recovers prose-wrapped JSON

claude -p --output-format json is a full agent, not a JSON API — sonnet intermittently wraps the requested JSON in prose. The old parser only stripped a single leading/trailing code fence, so:

  • a prose preamble → Expecting value: line 1 column 1 (char 0)
  • trailing commentary → Extra data

One bad batch of four tripped the whole run's AI-unavailable banner. _parse_response now extracts the JSON object from anywhere in result, and parse failures log the raw stdout so they stay diagnosable.

2. Triage gets a real diff, not site nav

Sitemap sources watch the whole page (content_selector=""), so raw_diff was the first 500 chars of text — pure site-wide nav, identical for every page, no body. Now the full page body is persisted in a new content_text snapshot column (additive migration) and triage receives an old-vs-new diff, so unchanged nav drops out. This also fixes content.py, whose line-based splitlines() diff was meaningless on the whitespace-collapsed one-line text.

Tests

485 pass, lint clean. New: two parser tests for the prose signatures, a sitemap guarantee test that the diff carries the body change and excludes nav, plus updated store/detector doubles.

🦇 Generated by Penny-One at Batcave

Two fixes for the AI triage pipeline, both reproduced against live output.

Parser: claude -p returns a full agent response, and the model sometimes
wraps the requested JSON in prose (a preamble or trailing commentary). The
old parser only stripped a single leading/trailing code fence, so a preamble
raised "Expecting value: line 1 column 1 (char 0)" and trailing text raised
"Extra data". _parse_response now extracts the JSON object from anywhere in
the result, and parse failures log the raw stdout so they stay diagnosable.

Signal: sitemap sources watch the whole page (content_selector=""), so
raw_diff was the first 500 chars of text, which is site-wide nav. Persist
the full page body in a new content_text snapshot column and send triage an
old-vs-new diff, so unchanged nav drops out and only the real change reaches
the model. This also fixes content.py, whose line-based diff was meaningless
on the whitespace-collapsed one-line text.

Co-Authored-By: Penny-One (Batcave) <noreply@anthropic.com>
@krayong
krayong merged commit 663d8bd into main Jul 14, 2026
2 checks passed
@krayong
krayong deleted the fix/triage-parse-and-diff branch July 14, 2026 09:07
krayong added a commit that referenced this pull request Jul 14, 2026
## Why

The `content_text` column added in #9 (diff-based triage) holds the full
page body. `export_seed_sql` does `SELECT *` on snapshots, so that body
flowed into the shipped seed — `seed.sql.gz` went from **1.4 MB → 108
MB**, which the `seed` workflow push hit as GitHub's hard 100 MB file
limit (`GH001`), and which would also bloat the PyPI wheel.

## Fix

Hold `content_text` back from the seed export (`_SEED_EXCLUDED_COLS`).
It's a **local runtime cache** — needed to diff a page against its prior
version, not to detect changes (`content_hash` does that) and not part
of a distributed baseline.

Seeded rows import `content_text` empty and self-heal on the first
change per page: the diff falls back to an excerpt once, then stores the
real body. Change detection, hashes, excerpts, and HTTP validators are
unaffected.

## Why it slipped through #9

Nothing in PR CI builds a real seed — the seed is deliberately off the
CI/release path (the hours-long crawl runs only on schedule/dispatch),
and unit tests use fake stores. The size regression could only surface
when the `seed` workflow actually ran.

## Tests

Added `test_seed_export_excludes_content_text`: a snapshot with a large
body must not appear in the exported SQL, and the round-trip still
preserves hash + excerpt (content_text imports empty). 486 pass, lint
clean.

🦇 Generated by
[Penny-One](https://batman.fandom.com/wiki/Alfred_Pennyworth) at
[Batcave](https://batman.fandom.com/wiki/Batcave)

Co-authored-by: Penny-One (Batcave) <noreply@anthropic.com>
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.

1 participant