fix(hackathon-extraction): root-cause fix for invalid-output, found with a production-faithful harness - #37
Merged
Conversation
… and report top-level shape failures
…alidation and harness verification
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This fixes the root cause of the production
llm:invalid-outputfailures for change 3,hackathon-analysis.PRs #33–#36 were driven by local simulations, which fetched HTML from a developer machine and reduced it with a regex. Those simulations could never reproduce production. This PR is based on a production-faithful harness instead. The harness runs the real
srcmodules on Cloudflare (wrangler dev --remote) with the real AI and Browser bindings and Cloudflare egress.Root cause (observed on Cloudflare, bnbchain
tokenized-stocks)403, so production always takes the browser path. The browser'sinnerTextseparates table cells with TABs.Bad control character in string literal.{ "value": null, "snippet": null, "confidence": 0 }instead ofnull.validateExtractionwas all-or-nothing on shape, so one such field rejected the whole response.parsed: false, which made them look like parse failures.Changes
src/adapters/http/html-to-text.ts,src/adapters/browser/rendered-fetcher.ts: a newnormalizePageTextturns tabs and control characters into spaces and collapses runs, while keeping paragraph breaks. It covers the static HTML path and the rendered path.src/adapters/llm/workers-ai-extractor.ts: when a parse fails, raw\t,\rand\ninside JSON string literals are escaped by a string-aware scan, and the parse is retried once. The result is reported asparseFailure: "control-chars"withrecovered: true.src/domain/hackathon/extraction.ts: validation is now per field.{ value: null, ... }object counts asnull, not rejected.wrong-shape.wrong-shape, so garbage still falls back through the majority rule.invalid-shapeis reserved for a non-object top level.parsednow means "JSON parsed". Top-level shape failures logshape: "invalid".scripts/hackathon-harness/andnpm run harness: the harness is now a repo tool. It is kept out of the Worker bundle, vitest and the root typecheck. The design doc now requires fetch, LLM and validation changes to pass the harness before merge.design.md(Worker-egress reality, verification rule).Harness verification (real Cloudflare, before review)
Review
A bounded review ran at medium risk with the reliability lens on all 20 changed paths. It found no severe findings.
Advisory follow-ups, none of which affected the harness runs:
text/plainpath (safe-fetcher.ts) is not normalizedKnown operational constraint
On the Workers Free plan, the Browser Rendering daily quota is small. bnbchain always needs the browser from Cloudflare egress.
Tests
🤖 Generated with Claude Code