Skip to content

React-idiom refinement pass for audiobook-curator components - #331

Merged
ScriptedAlchemy merged 2 commits into
mainfrom
wave/audiobook-react-idiom
Sep 2, 2026
Merged

React-idiom refinement pass for audiobook-curator components#331
ScriptedAlchemy merged 2 commits into
mainfrom
wave/audiobook-react-idiom

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

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):

  1. Helper functions → real components. Every lowercase render helper invoked as a function call (fileCard/editionCard, inspectionShelf/inventoryShelf/auditShelf/selectionShelf/remaining, plus the same pattern in candidate-ranking, evidence-trail, mutation-receipt, integrity-report) is now a capitalized component rendered as JSX.
  2. Union mega-switches killed. LibraryShelf, AudiobookCard, CandidateRanking, EvidenceTrail, MutationReceipt, and IntegrityReport switch 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. ChapterOutline keeps its genuinely shared rendering but now takes normalized chapters props via per-receipt mappers.
  3. Structure over string-assembly. New FileList primitive; per-row error/warning callouts replace join('; ') blobs; selection alternates and candidate-group files render as list blocks. Callout stays 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. DataList remains the markdown-lowering primitive.
  4. Normalize at the boundary. src/components/view-models.ts owns all receipt duck-typing (fileCardModel, editionCardModel); cards take clean typed props (title/author/narrator/duration/format). Receipts untouched behaviorally.
  5. Dedupe. Shared CandidateGroupCallout replaces the copy-pasted duplicate/multipart markup in AuditShelf and LibraryAnalysis. src/components/headlines.ts derives the five headlines that were duplicated between MCP tools and CLI twins (inventory, select, audible-search, audit, convert).
  6. Honest typing + authored configs. Receipt types are now JSON-compatible type aliases (interfaces lack implicit index signatures), so every Agent.Result value={...} is direct — all five as unknown as JsonValue casts are gone with zero runtime cost; parsed external JSON (Audible/audiolocate payloads) is typed JsonObject. The 15 single-line JSON config blobs are authored object literals, serialization-order-preserving (verified byte-identical).

Parity and pins

  • Receipts, schemas, --json output, and parity tests unchanged and green.
  • Rendered-text deltas are deliberate structure improvements: candidate-group files and unmeasurable-file lists moved from joined sentences into bullet-list blocks; region/probe errors render one callout per row; selection alternates render as a list. One route-unit pin added (streaming.test.ts asserts the new bullet-list block); no pin weakened or removed.
  • README component tour updated to the new component inventory.

Gates (all local, post-rebase onto 3368b5b)

  • examples:check (all examples) ✅
  • example check (validate, build, typecheck, unit+parity, route-unit) ✅
  • root typecheck ✅, lint
  • workbench examples-real e2e in real Chrome at 1440×900: 5/5 ✅

Examples-only change; no changeset needed.

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.
@ScriptedAlchemy
ScriptedAlchemy merged commit bb20b2f into main Sep 2, 2026
4 checks passed
@changeset-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 070da04

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T23:15:00.173406Z 070da04 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ScriptedAlchemy
ScriptedAlchemy deleted the wave/audiobook-react-idiom branch September 2, 2026 23:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +152 to +153
asRecord(parsed);
return Object.freeze(parsed);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed on main (merge 3127bfc).

Comment on lines +56 to 58
{receipt.errors.map((row, index) => (
<Callout key={`${row.path}:${String(index)}`} tone="error">{`${row.path}: ${row.error}`}</Callout>
))}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed on main (merge 3127bfc).

ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
* fix(audiobook-curator): normalize Audiolocate results (#331 r3919535592)

* fix(audiobook-curator): cap inventory error callouts (#331 r3919535600)
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