Skip to content

Story 10: Maya chooses how her form was extracted#58

Merged
danielnaab merged 28 commits intomainfrom
story-10/variant-picker
Apr 19, 2026
Merged

Story 10: Maya chooses how her form was extracted#58
danielnaab merged 28 commits intomainfrom
story-10/variant-picker

Conversation

@danielnaab
Copy link
Copy Markdown
Member

@danielnaab danielnaab commented Apr 19, 2026

Summary

Trunk of the experiment roadmap. Delivers a user-toggleable variant picker for PDF extraction, plus reusable infrastructure every subsequent experiment story (#59#66) builds on.

Maya uploads a PDF, sees a callout on /new showing which extraction model will run, clicks through to /settings/variants, switches to Haiku, uploads again, and the new spec carries "Extracted by Claude Haiku 4.5" with provenance committed to the project repo.

What shipped

Core infrastructure

  • VariantRegistry / VariantMetadata type aliases over existing StrategyRegistry for cross-task reuse
  • variant-preferences service — per-user, per-task, SQLite-backed, validates variantId against the registry, falls back to default on stale preferences (prevents orphaned projects)
  • Provenance helpers (appendProvenance, readProvenance) — pure functional, non-mutating
  • Task string union + TaskRegistries mapped type; empty stub registries for filling and field-mapping so picker tabs render
  • Public API barrel at src/services/variant-preferences/

User-visible

  • /settings/variants picker — auth-required, redesigned with design-system primitives: per-task section with label + description + "View benchmarks →" link, tile radios for variant selection, accent-bar highlight on anchor-jump
  • User-menu Settings link — picker reachable from the avatar dropdown
  • <VariantCallout> — new design-system component on /new showing the currently-selected extraction model above the upload area with "Change model" / "See benchmarks" affordances
  • <VariantBadge> — compact inline component with per-task verbs (Extracted by / Shaped by / Guided by / Mapped by), rendered on project overview, pending-review, and commit snapshots
  • Contextual experiments sidebar — new sub-navigation under /catalog/experiments/... listing each suite and its variants, matching the design-system catalog pattern

Extraction wiring

  • Project service now takes an ExtractionContext (DI) instead of a singleton extractor — resolves user's preferred variant per call
  • Atomic provenance commitforms/default/provenance.json written in the same git commit as spec.json / form.json / confidence.json / field-mapping.json
  • getProvenance(owner, slug, task, branch?) exposed on ProjectService

Evaluation & catalog

  • 2 new fixtures — USCIS I-9 (128 AcroForm fields) and IRS W-9 (23 AcroForm fields), both with reviewed Opus-generated ground truth
  • Full evaluate compare rerun across expanded fixture set: Opus 99.8% recall, Sonnet 62.1%, Haiku 57.8%
  • Catalog roadmap at /catalog/experiments/roadmap — live index of all follow-up experiment stories (Maya chooses her shaping model #59Maya extracts via structured tool-use #66), their status, and ship targets
  • Catalog pages updated with new metrics and "Selectable in Settings → Variants" links

Follow-up stories filed

Eight user-stories under milestone "Final Project", all blocked on this PR:

Each story's acceptance criteria require a catalog entry with findings, so the benchmark evidence lives alongside the code.

Architecture notes

  • Stale variant preferences (user persisted a variant that was later removed) fall back to the registry default instead of throwing, preventing orphaned projects.
  • Provenance lives in the same commit as the spec it describes. Readers recover the SHA via git log forms/default/provenance.json.
  • Settings picker renders empty-state sections for tasks without variants yet — stories 11-13 just drop entries into their registries to light up their tabs.
  • Design-system additions (VariantBadge, VariantCallout) use --flex-* tokens and cascade layers, no hand-rolled colors.

Test plan

  • bun run check — 759/759 pass, type check clean, biome clean, rebased cleanly on origin/main
  • Unit tests: preferences gateway, service (including stale-preference fallback), provenance helpers, variant-badge component
  • Integration tests: /settings/variants auth redirect, authed render, POST valid/invalid selection, project-service provenance round-trip
  • Fixtures validated via bun run cli evaluate validate
  • Evaluation metrics committed to catalog via bun run cli evaluate compare
  • Manual: upload PDF, verify callout + badge; switch to Haiku, re-upload, verify badge + provenance.json accumulates entries

Files

27 commits, ~2400 lines added. Key additions:

  • src/services/variant-preferences/ (new service)
  • src/entrypoints/app/routes/settings/ (picker route)
  • src/design-system/components/flex-variant-badge/, flex-variant-callout/ (new DS components)
  • catalog/experiments/_roadmap.md, updated variant catalog pages
  • fixtures/i-9/, fixtures/w-9/ with ground truth

Roadmap context

  • Design: notes/story-10-variant-picker/design.md
  • Plan: notes/story-10-variant-picker/plan.md
  • Public roadmap: /catalog/experiments/roadmap

Blocks: #59, #60, #61, #62, #63, #64, #65, #66

@danielnaab danielnaab temporarily deployed to story-10-variant-picker April 19, 2026 04:56 Inactive
@danielnaab danielnaab temporarily deployed to story-10-variant-picker April 19, 2026 05:05 Inactive
@danielnaab danielnaab temporarily deployed to story-10-variant-picker April 19, 2026 11:50 Inactive
@danielnaab danielnaab temporarily deployed to story-10-variant-picker April 19, 2026 11:56 Inactive
@danielnaab danielnaab temporarily deployed to story-10-variant-picker April 19, 2026 12:00 Inactive
@danielnaab danielnaab temporarily deployed to story-10-variant-picker April 19, 2026 12:11 Inactive
@danielnaab danielnaab temporarily deployed to story-10-variant-picker April 19, 2026 12:21 Inactive
@danielnaab danielnaab marked this pull request as ready for review April 19, 2026 12:29
Placeholders until Stories 12 and 13 register actual variants. Mapping
registry is placed under form-documents/ since Story 7 relocated PDF
field-mapping there.
…per project

Replaces the hardcoded extractor passed to createProjectService with an
ExtractionContext that resolves a per-user variant at call time. Each
extraction commits its variantId/modelId/spec version into
forms/default/provenance.json so callers can later surface which model
produced a given spec.

Also mounts the /settings route on the app server so users can change
their variant selection through the picker landed in Task 9.
When the extractor cache is populated (after running evaluate or in
long-running test processes), extraction completes synchronously on cached
PDFs and projects land on an import branch awaiting review rather than
transitioning through extracting/error states. Add the pending-review
state to the list of valid post-creation renders.
…ives

The previous settings page used bare HTML with classes that had no CSS,
so it rendered as unstyled markup. Rebuild it on top of the shared flex
design system — Card / Radio (tile variant) / Button / Alert — and the
page-level l-stack / l-cluster layout primitives so it matches the look
and feel of ProjectOverview and other owner pages.

- Add a breadcrumb + title header modelled on repo-header.
- Wrap each task in a Card with heading, body, and tile-style radios.
- Use Radio in its tile variant for rich per-variant option content.
- Extend Radio's label prop from 'string' to 'Child' so callers can pass
  JSX (bold name + description + catalog link). The change is additive;
  all existing string-label callsites still type-check.
- Scope highlight-on-anchor behavior to an attribute on the task wrapper
  (scroll-margin + accent border) instead of the unstyled
  task-section--highlight class.
- Add src/entrypoints/app/routes/settings/styles.css for page-specific
  typography, header, and highlight rules, and wire it into the bundle.
- Update settings-variants test to match the friendlier empty-state copy.
When browsing under /catalog/experiments/..., the left sidebar now shows
an experiments-scoped navigation: a Back to Catalog link, Overview, any
top-level pages (Roadmap), then one section per suite (PDF Field
Extraction, Shaping Architecture) with each variant listed.

Top-level `_name.md` files (e.g. _roadmap.md) are now reachable at
`/catalog/experiments/<name>` (without the leading underscore). The
legacy URL `/catalog/experiments/_name` still works because the handler
tries both forms.

Factored out titleCaseSlug so suite/variant titles fall back gracefully
when a heading can't be parsed.
@danielnaab danielnaab force-pushed the story-10/variant-picker branch from aaa045a to ee27963 Compare April 19, 2026 12:30
@danielnaab danielnaab temporarily deployed to story-10-variant-picker April 19, 2026 12:30 Inactive
@danielnaab danielnaab temporarily deployed to story-10-variant-picker April 19, 2026 12:38 Inactive
@danielnaab danielnaab merged commit b7a5dea into main Apr 19, 2026
4 checks passed
@danielnaab danielnaab deleted the story-10/variant-picker branch April 19, 2026 12:42
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