feat: the composed widget kind — activities generated from pedagogical primitives - #101
Open
adamstankiewicz wants to merge 7 commits into
Open
adamstankiewicz wants to merge 7 commits into
adamstankiewicz wants to merge 7 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
adamstankiewicz
force-pushed
the
feat/composed-widget-kind
branch
from
August 30, 2026 23:31
a4c0bf2 to
f4fc75e
Compare
adamstankiewicz
force-pushed
the
feat/composed-widget-kind
branch
from
August 30, 2026 23:41
f4fc75e to
5b5fc1b
Compare
adamstankiewicz
force-pushed
the
feat/composed-widget-kind
branch
from
August 30, 2026 23:52
5b5fc1b to
ce50bc0
Compare
adamstankiewicz
force-pushed
the
feat/composed-widget-kind
branch
from
August 31, 2026 00:04
01edddd to
8139b00
Compare
9 tasks
adamstankiewicz
force-pushed
the
feat/composed-widget-kind
branch
from
August 31, 2026 00:17
8139b00 to
a6a0732
Compare
adamstankiewicz
force-pushed
the
feat/composed-widget-kind
branch
from
August 31, 2026 00:18
a6a0732 to
39608df
Compare
Long structured outputs sometimes arrive wrapped in a markdown fence — valid JSON the SDK refuses to parse, observed live on the composed generator. generateStructured now unfences and revalidates against the real schema before burning the cross-vendor retry; validation is never loosened, only the location of the JSON. Both the primary and fallback paths get the salvage, and the negative tests prove fenced-but-invalid stays rejected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tives One registry entry, unbounded activity shapes: the model authors a flat tree of primitives (Sequence with a traversal policy, Reveal, Callout, Text, Group — #100's verdict-free v1 alphabet) and human-written code does everything else. The spec is the tree (hand-maintained union and kind enum updated together per the drift gotcha); compositionProblems gates structure — unique ids, a root, resolving refs, no cycles, two faces per Reveal — with tests proving every rule can reject, because this validator stands between model output and a student's screen. The generator learned three things from live failures, each now a test: models fixate on the components array and drop top-level fields (the envelope is the generator's job; the plan step titles the fallback); models obey the *serialized* schema, so the authoring shape advertises the superset of node fields rather than pretending looseness; and field names drift (body for text, flat policy, front/back shorthand) — one deterministic canonicalization pass maps synonyms and never invents content or repairs references. An unsound tree falls back honestly. assesses is false by construction — nothing in the alphabet measures — and completion wiring is injected renderer-side, never model-authored. The A2UI surface renderer from the demo work graduates to product as the composed kind's renderer. Verified live end to end: buildWidget and /api/widget produce 13–14-component compositions for 3.NF.A.3, and the demo's gated walkthrough completes through Next → Next → Done with the completion counted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sumer Relocated from the conformance PR below, where the retired demo was their last consumer: A2UISurfaceView (the a2learn-catalog renderer the composed kind draws with) and primitives.ts (the catalog URN and the policy vocabulary). Content unchanged — only the PR that introduces them moves, so each PR's diff is what its title says. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
adamstankiewicz
force-pushed
the
feat/composed-widget-kind
branch
from
August 31, 2026 00:39
39608df to
0d347cb
Compare
Adam's verdict on the first compositions was right: a verdict-free reading alphabet composes articles, and articles aren't novel. Check fixes the interaction ceiling without touching the evidence contract — options with instant feedback, wrong answers invite another try, and *nothing leaves the component*: no verdict, no event, assesses stays false. It exists because retrieval beats rereading, not because anyone is measured; the answer that should count as evidence is the Response primitive, still gated on #99, and the schema comment draws that line. Structural rules gate it (2+ options, answer in range, tested); the generator prompt now optimizes for interaction density — a Check every few nodes, Reveal over exposition, Text under ~60 words. Live probe of the full pipeline: a fractions composition came back with 3 Checks, 2 Reveals, 1 Callout, 4 Texts. UI verified via Playwright: miss shows feedback and invites retry, hit settles with icon + word, never color alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two game primitives with real mechanics, implemented entirely renderer-side: Match pairs a fixed left column against a shuffled right column (hits lock with a check, misses shake and clear, live n-of-N progress) — paired-associate retrieval; Hunt asks for every target among near-miss decoys with instant per-tap feedback — discrimination practice, the example/non-example move. Like Check, nothing leaves the component: no verdict, no event, assesses stays false, and the schema comments keep drawing the line to Response (#99). Structural rules gate both (2+ pairs; 3+ items with a target and a decoy; tested), canonicalization covers them, and the generator prompt teaches when each fits. Live probe: one build returned Sequence + 3 Text + 2 Hunt + 2 Check + 1 Match + 1 Reveal + 1 Callout — a genuinely game-shaped activity no fixed kind describes. UI verified via Playwright: match miss does not advance progress, hunt decoys explain themselves, state is icon + word throughout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6 tasks
Inputs split three ways by where the value goes (#100 round 4), and the two local roles ship: Estimate is the commit input — slide, lock it in, then the reveal shows your mark and the actual on the same track, juxtaposed with the gap named, never a verdict badge (committing before seeing is the hypercorrection pedagogy, and nothing is transmitted). Model is the parameter input — an explanation with a knob: the student sets a variable and the authored outcome for that value swaps in. All-numeric options render as a stepped slider, labels as chips, and the invariant-1 line is drawn in the schema: outcomes are authored data per option, never formulas — the renderer selects, it never computes. The third role, values that leave the widget, stays deliberately behind the #99 Response contract. The authoring superset moves from nullable to nullish — with eight more per-type-irrelevant fields, demanding explicit nulls for keys a model naturally omits was the parse failure the probe caught. Structural rules gate both primitives (actual inside range; exactly one outcome per option; tested). Live probe, unprompted: 'Earth's Spheres in Action' came back with a Model knob — Event = Rain falls / Plant grows / Volcano erupts / River flows — plus Check, Hunt, Match, and Reveal. UI verified via Playwright: the reveal juxtaposes without verdict words, and moving the knob swaps outcomes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A composition may now declare finite state (each variable an enumerated
value list with an initial), flip it with Action nodes (set a declared
value, or cycle), gate any component's visibility on it (showWhen), and
display it inside Text ({varName}). Four data-mechanics, one
human-written interpreter — and the model can design widget mechanics
nobody named: counters, staged builds, toggle flows, choose-your-path
machines. Invariant 1 is what makes the tier auditable rather than
bent: because state is finite and declared, the structural gate checks
every reference — undeclared variables, out-of-set values, set+cycle
ops, bad initials all reject, each with a test.
The spelling is deliberate on two axes: it follows A2UI's
dataModel/actions semantics (state rides the surface's dataModel, so
free-form compositions project cleanly — #98 convergence), and state
stays flat and JSON-Patch-friendly because AG-UI's
STATE_SNAPSHOT/STATE_DELTA is the wire form for the day state is
allowed to leave the widget. Local stays the default; sharing state
through the AG-UI channel is #99-contract territory like every other
boundary crossing.
Generator guidance: named primitives first, free-form only for a
mechanic they cannot express. Demo: 'Build three quarters' — a counter
mechanic no primitive names, with a halfway callout that appears at 2/4
and yields to the completion callout at 3/4. Playwright-verified:
interpolation tracks state, visibility flips exactly on the declared
values.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
What & why
The primitives design (#98, #100) folds into the actual product. A new
composedwidget kind — one registry entry — whose spec is a flat tree of pedagogical primitives (Sequencewith a traversal policy,Reveal,Callout,Text,Group: the verdict-free v1 alphabet from #100). The model authors the composition at generation time; human-written renderers draw it; the structural gate (compositionProblems) refuses unsound trees. New activity shapes now exist that no hand-built React kind describes — the generative-substrate direction made real.The
/demo/a2uiexploration page retires; its renderer graduated into the product as the composed kind's component, and/demo/composeddemonstrates the alphabet as a product surface.What a reviewer should scrutinize
Composed.tsx), never model-authored; canonicalization maps field-name synonyms but never invents content or repairs references.assesses: falseby construction — nothing in the alphabet measures. The registry entry documents the derivation plan for when verdict-carrying primitives arrive (a2learn primitives: the exhaustive pedagogical vocabulary, and the build plan #100 phase 3, gated on a2learn interaction contract: a standard event/function vocabulary for registry widgets #99).src/lib/pathway/schema.ts): unique ids, root, resolving refs, no cycles, two faces per Reveal — every rule has a rejection test, because this validator stands between model output and a student's screen.A2UISurfaceViewandprimitives.tsare introduced here, relocated from feat: industry-standard conformance — AG-UI transport + schema-verified A2UI (phase 1) #79 so they arrive with their only consumer.generateStructuredgains fenced-output salvage (shared infra, own commit): unfence + revalidate before the cross-vendor retry; never loosens validation. Negative tests included.body→text, flat policy, front/back shorthand).Deliberately deferred
Check/Response) — blocked on a2learn interaction contract: a standard event/function vocabulary for registry widgets #99 by design.composedover fixed kinds.How it was verified
rm -rf .next && npx tsc --noEmit,pnpm build, targeted lint clean,pnpm mcp:build(shell is prebuild-generated since feat: self-building widget shell with single-source theming #86; nothing to commit)buildWidgetandPOST /api/widget {kind: "composed"}produce 13–14-component compositions for verified 3.NF.A.3; an unsound tree falls back honestly to another kind with a note/demo/composedgated walkthrough completes Next → Next → Done with the completion counted (Playwright)Authorship
Closes nothing yet — implements phase "fold into product" of #100; the tracking issue stays open for the remaining phases.
🤖 Generated with Claude Code