← Context/ prompts
Run /speckit-specify — Response Reviewer Structured-Output Extension (Packs-and-Bundles Feature 1)
Feature 1 of the packs-and-bundles series: the structured-output surface every pack will land on. Zero packs, zero bundles implemented.
Why narrow scope
/speckit-specify produces one feature spec per invocation. The
packs-and-bundles pattern from Packs and Bundles Pattern decomposes
into four shippable features; this is feature 1. Going broad (specifying
the framework + the first pack + the first bundle in one feature) would
conflate surface-correctness with pack-correctness with orchestration-
correctness. Narrow gives us a real first feature: the surface every
pack lands on, demonstrable in isolation with mock pack responses.
The three subsequent features get their own /speckit-specify calls —
listed under "Future features" below.
Prerequisite reading
/speckit-specify should read these before drafting:
.specify/memory/constitution.md— the ratified principlescontext-v/blueprints/Packs-and-Bundles-Pattern.md— the pattern this feature operationalizes (the foundation layer)context-v/explorations/Entity-Profile-Augmentation-Workflow.md— the exploration that produced the blueprint (open questions resolved inline are load-bearing)context-v/specs/Response-Reviewer-and-Response-Store.md— the shipped spec for the surface being extended (status: Shipped)context-v/plans/Impose-Theme-Modes-System.md— the three-mode theme contract the new theme tokens must respect (status: Shipped)services/response-store/src/store.ts— the currentResponseRecordtype that gets extendedapps/response-reviewer/— the shipped remote whose renderer gets extended (locate the candidate card render path)packages/shared-ui/— likely home for the new confidence-pill componentpackages/theme/theme.css— where the three confidence color tokens land
How to invoke
/speckit-specify Read context-v/prompts/Run-Speckit-Specify-Response-Reviewer-Structured-Output.md and follow its "Feature description" section. Scope is intentionally narrow — schema extension + renderer extension + confidence-pill component + theme tokens. No pack implementations. No bundle runtime. No dedup capability. Those land in features 2-4.Feature description
What we're building
The surface every pack will land on. Four pieces:
ResponseRecordschema extension inservices/response-store/src/store.ts. Today's shape (response_id, run_id, prompt_id, row_id, record_set_id, output_column, model, request_body, response_text, edited_text, flag, accepted, created_at, reviewed_at, edited_at) gains six new fields:outcome: 'found' | 'not_found' | 'error' | 'skipped' | 'pending'structured: Candidate | null— sibling payload, present iffoutcome === 'found'for a pack responsearchival_markdown: string | null— structured rendered down to human-readable markdown; nullablepack_id: string | null— set iff this response was a pack firebundle_id: string | null— set iff fired from a bundlepass: 1 | 2 | null— set iff bundle is two-pass
With
Candidatedefined per the blueprint:TYPESCRIPTtype Candidate = { url: string; display_name: string; confidence: number; // 0-100 snippet?: string; source_metadata?: Record<string, unknown>; };Backfill discipline in
store.ts'sload()— the existing load-time backfill pattern (already used foredited_text/edited_at) extends to coerce older records:outcome→'found'ifresponse_textis non-empty, else'pending'structured,archival_markdown,pack_id,bundle_id,passall →null
No data migration script. The load-time backfill is the migration (same approach the shipped codebase already uses).
Response Reviewer renderer extension in
apps/response-reviewer/(the shipped remote). The renderer today showsresponse_textas free-form prose with edit/triage ergonomics. The extension:If
structured === null: render exactly as today (backward compatible, no visual change for non-pack responses).If
structured !== null: render a candidate card above the prose. The card displays:The confidence pill (new component, see #4) immediately before the URL
The URL (clickable, opens in new tab)
The
display_namenext to the URLThe
snippet(collapsible by default — read it without leaving the card)A small source badge (the
pack_id, until a per-pack render config exists; this is a v1 affordance, not the final shape from the blueprint)
The prose (
response_text) renders below the card as today. The triage controls (good/partial/wrong/needs-rerun/needs-human) stay where they are.
The confidence pill component at
packages/shared-ui/confidence-pill.svelte(or similar — the plan phase picks the exact path;packages/shared-ui/is the natural home). The component:Accepts
confidence: number(0-100) as its sole required propRenders the numeric value inside a small pill
Color band by range: 0-39 = low (red family), 40-69 = med (amber family), 70-100 = high (green family)
Colors come from new theme tokens (see #5) — never hardcoded
Optional
tooltip?: stringprop for the hover reasoning text
Theme tokens in
packages/theme/theme.css. Three new named tokens, each defined in the light, dark, and vibrant mode blocks per the three-mode contract from Impose Theme Modes System:--color-confidence-low— red family--color-confidence-med— amber family--color-confidence-high— green family
The exact hex values are a design call the spec doesn't pin —
/speckit-plancan propose values; the user signs off. The discipline is: defined in all three modes, semantically named, no raw hex in the pill component.Outcome-driven renders for non-
foundoutcomes. The renderer handles each outcome value distinctly:found: candidate card + prose (above)not_found: informational thin row ("source ran, zero candidates"), no triage controls (this is not a triage state)error: thin row with the error message + a retry button stub (retry implementation is out of scope; button can be disabled with a tooltip "retry coming in a later feature")skipped: thin row showing the carried-forward URL markedgood(this is the dedup pre-population case from the blueprint; the stored response shows what would have fired)pending: spinner row, no controls
Constraints (per constitution + blueprint)
Svelte 5 with
$staterunes throughoutBackward compatible — every existing
ResponseRecordinservices/response-store/data/responses.jsonmust continue to render exactly as today after the migration. No visual diff for non-pack responses.No new external dependencies — the pill is a small Svelte component, no charting libs, no design-system imports
Theme tokens only for color — never hardcoded hex in the pill or card components
Three-mode contract — every new token defined in all three mode blocks; the pill verified in all three modes
Mock-driven tests — feature ships with a small fixture of pack-shaped responses (mock LinkedIn, mock Candid) so the renderer is exercised without any actual pack existing
No pack implementations — repeatedly: this feature ends where the first pack begins
What's explicitly out of scope
linkedin-packor any other pack (feature 2)The bundle runtime — roster, orchestration, carry-forward (feature 3)
The
profiles.dedup.scancapability (feature 3)Two-pass orchestration + entity-typed bundles (feature 4)
The retry button's actual retry implementation (later)
Per-pack render-config plumbing (the blueprint's "render config" on each pack — comes online with the first real pack; for now the source badge displays the raw
pack_id)The
source_metadataper-source display (the blueprint mentions per-pack format hints; those land per-pack in feature 2+)The promote-to-canonical write-back of
profiles.<source>clusters — that's part of the existing promote handler; extension scope is noted but lives in a later feature
What "done" looks like
Manual smoke test:
Existing responses render exactly as today in Response Reviewer — the schema migration is invisible to the user.
A test fixture with three mock pack responses (one
found, onenot_found, oneerror) lands inresponses.json; opening Response Reviewer shows:The
foundresponse: candidate card with confidence pill (green for 87, amber for 55, red for 22 in the same fixture) above the prose; clickable URL; collapsible snippetThe
not_foundresponse: thin row with the informational message, no triage controlsThe
errorresponse: thin row with error text and a disabled retry button
Toggling theme mode (light / dark / vibrant) re-tints the pill correctly in all three — colors visibly different per mode but semantically consistent (low always feels "red," etc.).
A
pendingmock response shows the spinner row.A
skippedmock response shows the carried-forward URL markedgood.
If all five steps work, the surface is real and the next feature
(linkedin-pack) has a foundation to land on.
Implementation order suggestions (for /speckit-plan later)
Schema extension in
store.ts(types + backfill inload())Theme tokens in
theme.css(all three modes, all three tokens)Confidence-pill component in
packages/shared-ui/Renderer extension in
apps/response-reviewer/— candidate card, then non-foundoutcomesTest fixture (five mock responses covering all five outcomes)
Manual smoke walkthrough across the three modes
Future features (each its own /speckit-specify later)
Feature 2 —
linkedin-packend-to-end. First concrete pack. Own MCP server, own federated microfrontend (thin — most rendering is Response Reviewer), search-then-confirm scraper, extraction-schemarender-config exports. Fires directly from a prompt template (no bundle layer yet) so pack-correctness isn't conflated with orchestration-correctness.
Feature 3 —
profile-builder.commonbundle (LinkedIn + X). First concrete bundle. Bundle runtime — pack roster, single-pass orchestration (no two-pass yet), chat verb registration, theprofiles.dedup.scanpre-flight hook. Addsx-packsince two packs is the minimum to prove orchestration without burying it.Feature 4 — Two-pass orchestration + first entity-typed bundle. Adds carry-forward between passes, the human checkpoint, and
profile-builder.philanthropic-org(or another entity-typed bundle) exercising the default-4-to-6 source-selection discipline against the 18-source Tier-2 philanthropy list.
This ordering is a recommendation, not a contract — adjust as reality contacts the code.
After this feature spec lands
Review
.specify/memory/specs/NNN-*/spec.md(the file/speckit-specifycreates)./speckit-clarify— there are likely 3-5 ambiguities worth surfacing (the exact hex values, whether the source badge stays raw-pack_id in v1, the not_found row's affordance shape, etc.)./speckit-planto draft the implementation plan./speckit-checklistto generate quality gates./speckit-tasksto break the plan into actionable tasks./speckit-analyzeto cross-check consistency.Review gate.
/speckit-implement— execute.
Related
.specify/memory/constitution.md— the ratified principlescontext-v/blueprints/Packs-and-Bundles-Pattern.md— the patterncontext-v/explorations/Entity-Profile-Augmentation-Workflow.md— the exploration with the resolved open questionscontext-v/specs/Response-Reviewer-and-Response-Store.md— the shipped spec being extendedcontext-v/plans/Impose-Theme-Modes-System.md— the three-mode contract the new tokens respectcontext-v/blueprints/Spec-Kit-and-Context-V-Coexistence.md— workflow framework