React-idiom refinement pass for audiobook-curator components - #331
Conversation
Reformat the 14 remaining single-line JSON config blobs in the curator MCP tool routes into authored multi-line object literals, preserving value and key order so serialized tool metadata stays byte-identical. (audit_library.tsx's config lands with the component wave.)
- Replace lowercase render helpers and receipt.operation mega-switches with per-receipt components: FileCard/EditionCard, four shelves, three rankings, three evidence trails, four mutation receipts, four integrity reports; ChapterOutline now takes normalized chapters. - Normalize receipt duck-typing at the boundary in view-models.ts; cards take clean typed props. - Type receipts as JSON-compatible aliases and drop every 'as unknown as JsonValue' cast; parsed external JSON is JsonObject. - Extract shared CandidateGroupCallout and headline helpers to end the audit/analysis markup and MCP/CLI headline duplication. - Express file lists and per-row errors as structured blocks (FileList) instead of joined template-literal prose. - Author remaining single-line JSON config blobs as object literals.
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 070da044de
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| asRecord(parsed); | ||
| return Object.freeze(parsed); |
There was a problem hiding this comment.
Return the normalized Audiolocate record
When Audiolocate emits valid JSON whose root is null, a scalar, or an array, the result of asRecord(parsed) is discarded and the non-object value is returned despite the JsonObject contract. verifyAudibleSample then dereferences .found (crashing for null) or can publish a receipt with a wrongly shaped fingerprint; return and freeze the normalized record instead, as the previous implementation did.
AGENTS.md reference: AGENTS.md:L10-L13
Useful? React with 👍 / 👎.
| {receipt.errors.map((row, index) => ( | ||
| <Callout key={`${row.path}:${String(index)}`} tone="error">{`${row.path}: ${row.error}`}</Callout> | ||
| ))} |
There was a problem hiding this comment.
Cap per-file error callouts before rendering
For inventories containing thousands of unprobeable files, this now creates one document node per error with no cap, while createInventory accepts as many as 4,096 media files and the structured receipt itself already consumes roughly three JSON-budget nodes per error. Around 2,500 ordinary short errors, the receipt plus these new callouts exceeds the runtime's 10,000-node document limit and the public inventory route fails instead of returning its retained-error report; render a bounded subset and add a remaining-count marker as is already done for cards.
AGENTS.md reference: AGENTS.md:L10-L16
Useful? React with 👍 / 👎.
Summary
Follow-up to #315: the component library worked but read like vanilla JS wearing React. This pass makes it idiomatic without changing a byte of receipt/schema/JSON output.
Per finding (from the coordinator review of main):
fileCard/editionCard,inspectionShelf/inventoryShelf/auditShelf/selectionShelf/remaining, plus the same pattern incandidate-ranking,evidence-trail,mutation-receipt,integrity-report) is now a capitalized component rendered as JSX.LibraryShelf,AudiobookCard,CandidateRanking,EvidenceTrail,MutationReceipt, andIntegrityReportswitch wrappers are deleted; routes render their receipt-specific component directly (InspectionShelf,InventoryShelf,AuditShelf,SelectionShelf,FileCard/EditionCard,SearchRanking/IdentifyRanking/SelectionRanking,AcousticTrail/IdentifyTrail/WhisperTrail, four mutation + four integrity components). No polymorphic call site remained anywhere.ChapterOutlinekeeps its genuinely shared rendering but now takes normalizedchaptersprops via per-receipt mappers.FileListprimitive; per-row error/warning callouts replacejoin('; ')blobs; selection alternates and candidate-group files render as list blocks.Calloutstays a string-children atomic block on purpose: the runtime document model (Agent.Context/Agent.Error) is block-based with string content, so structure lives in structured-prop components that lower to sibling blocks — not in ReactNode children that would escape the callout block.DataListremains the markdown-lowering primitive.src/components/view-models.tsowns all receipt duck-typing (fileCardModel,editionCardModel); cards take clean typed props (title/author/narrator/duration/format). Receipts untouched behaviorally.CandidateGroupCalloutreplaces the copy-pasted duplicate/multipart markup inAuditShelfandLibraryAnalysis.src/components/headlines.tsderives the five headlines that were duplicated between MCP tools and CLI twins (inventory,select,audible-search,audit,convert).Agent.Result value={...}is direct — all fiveas unknown as JsonValuecasts are gone with zero runtime cost; parsed external JSON (Audible/audiolocate payloads) is typedJsonObject. The 15 single-line JSONconfigblobs are authored object literals, serialization-order-preserving (verified byte-identical).Parity and pins
--jsonoutput, and parity tests unchanged and green.streaming.test.tsasserts the new bullet-list block); no pin weakened or removed.Gates (all local, post-rebase onto 3368b5b)
examples:check(all examples) ✅check(validate, build, typecheck, unit+parity, route-unit) ✅typecheck✅,lint✅examples-reale2e in real Chrome at 1440×900: 5/5 ✅Examples-only change; no changeset needed.