Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions docs/a2learn/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# a2learn format changelog

Breaking changes are allowed throughout v0 and are recorded here. Entries
say what changed *and what an implementer must do about it*.

## v0 draft — 2026-08-30

**`manifest.gradeBand` → `manifest.audience` (breaking).** The old field was
a `{min, max}` integer range, which can only express K-12 grade levels: an
undergraduate year or a new-hire cohort has no honest representation in it.
`audience` is a list of scheme-scoped label sets, carrying the naming
authority with the value the way `standards[]` already carries `source`
alongside `code`.

*What an implementer must do:* replace `gradeBand: { min, max }` with an
`audience` entry under scheme `k12-us`, values as strings (`"K"`, `"1"`–
`"12"`). A consumer filtering on grade moves from range comparison to label
matching within a recognized scheme, and MUST treat an unrecognized scheme
as opaque rather than parsing it. No producer outside this repository emits
`gradeBand`, so no migration window is provided.

**`manifest.pedagogy.purposes` is an open vocabulary**, stated normatively:
a consumer ignores a purpose it does not recognize rather than rejecting the
listing. Not breaking for the four existing values.

## v0 draft — 2026-08-21

Initial draft: README (scope: a format riding AG-UI/A2UI/MCP, not a
protocol), `surface.md` (A2UI profile: flat components, `bindTo`
references, `WidgetResult` contract, namespaced `a2learn` extensions),
`manifest.md` (discovery/listing format: verified standards alignment,
pedagogy metadata, content/code tiers, provenance + attestations),
`events.md` (payload shapes with AG-UI and MCP Apps bindings; two-tier
interaction rule), `conformance.md` (Producer / Renderer / Registry / Host
classes; versioning and extension rules; the 1.0 gate), and
`considerations.md` (security, privacy, accessibility).

Reference implementation: this repository. Status tags (Shipped /
Prototype / Planned) mark which sections are exercised by running code.
79 changes: 79 additions & 0 deletions docs/a2learn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# a2learn — draft v0 (experimental)

**a2learn** ("agent-to-learning") is a document format for **agent-generated,
agent-discoverable interactive learning activities**. It defines *data*, not
wire behavior: what a learning activity **is** (its surface), how it is
**described for discovery** (its manifest), and what its **interactions
report** (its events). Transport, rendering, and distribution are borrowed
from specifications that already exist and already have hosts:

| Layer | Provided by | a2learn's relationship |
| --- | --- | --- |
| Surface description | [A2UI](https://a2ui.org) | a2learn-surface is a *profile*: valid A2UI plus namespaced learning extensions |
| Agent ↔ UI transport | [AG-UI](https://github.com/ag-ui-protocol/ag-ui) | a2learn payloads ride AG-UI events unchanged |
| Distribution into chat hosts | [MCP Apps](https://modelcontextprotocol.io) | activities are invokable MCP tools; surfaces render in MCP Apps iframes |

This is a deliberate scope choice: **a2learn is a format, not a protocol** —
and we say *format*, not *specification*, on purpose: it earns the second
word when a second independent implementation exists. New protocols require
every host to implement wire behavior; formats spread by emit-and-validate. The one part with protocol flavor — the event vocabulary —
is specified as payload shapes with *bindings* to AG-UI and MCP, never as a
new wire protocol.

## The documents

1. [`surface.md`](./surface.md) — the activity itself: an A2UI-conformant
surface carrying learning semantics (state contract, gating, language).
2. [`manifest.md`](./manifest.md) — the listing: standards-verified,
pedagogy-aware discovery metadata an agent can query at runtime. This is
the registry/marketplace's format.
3. [`events.md`](./events.md) — what interactions report: result and action
payloads, with two transport bindings.
4. [`conformance.md`](./conformance.md) — conformance classes (Producer,
Renderer, Registry, Host), versioning and extension rules, and the gate
for 1.0.
5. [`considerations.md`](./considerations.md) — security, privacy
(education records, minors), and accessibility considerations.
6. [`CHANGELOG.md`](./CHANGELOG.md) — every breaking change, with what an
implementer must do about it.

## Conventions

The key words **MUST**, **MUST NOT**, **SHOULD**, **SHOULD NOT**, and
**MAY** are to be interpreted per RFC 2119; text without them is
informative. Conformance is always claimed against a class in
[`conformance.md`](./conformance.md), never against "a2learn" in general.

## Design principles

- **Data, never code.** An activity is renderable data resolved against a
host's component catalog. Model output never ships executable logic; the
trust boundary is the catalog. (Marketplace corollary: activity *instances*
are open data; new component *kinds* are code and travel a separate,
curated channel.)
- **Generation-shaped.** Structured output reliably fails on nested unions
and wide flat objects. The format's wire shapes may be rich; *authoring*
representations stay flat, with mapping at the boundary. References are
plain strings (`bindTo`), never expression languages.
- **Standards-anchored, verifiably.** Manifest alignment claims name their
source and verification status. Unverified claims are representable but
distinguishable — an index can rank on it.
- **Degrade gracefully.** A plain A2UI renderer that ignores every a2learn
extension still renders a usable surface. Extensions add semantics; they
must not be load-bearing for basic rendering.

## Status

Draft v0, reference-implementation-led: this repository is the reference
implementation, and the format documents what ships (or is explicitly marked
**Planned**). Breaking changes are allowed and changelogged throughout 0.x.
The format formalizes reality, never aspiration — see the format-strategy
lesson: implementation first (Jupyter, glTF, Markdown), standards-first is how
formats fragment (QTI).

Each section is tagged with an implementation status:

- **Shipped** — on `main`, exercised by the app.
- **Prototype** — working in the composition prototype (`/demo/ag-ui-compose`
lineage), not yet in the core product path.
- **Planned** — designed, not yet built; most likely to change.
99 changes: 99 additions & 0 deletions docs/a2learn/conformance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# a2learn conformance — draft v0

The key words **MUST**, **MUST NOT**, **SHOULD**, **SHOULD NOT**, and
**MAY** are to be interpreted as described in
[RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). Statements without
these keywords are informative.

Nothing conforms to "a2learn" in general; implementations conform to one or
more **conformance classes**. This is what makes conformance claimable and
eventually testable.

## Classes

### Producer

Emits surfaces and/or manifests — a generation pipeline, an authoring tool,
an exporter.

- MUST emit surfaces whose base shape is valid A2UI (flat component list,
id references) and whose learning semantics live only in the namespaced
`a2learn` member.
- MUST NOT emit executable code in any surface or manifest.
- MUST ensure referential integrity before publishing: every `children`,
`bindTo`, and gate path names an id that exists.
- MUST NOT mark a standards claim `verified: true` unless the code resolved
against the named source.
- SHOULD keep authoring-side generation shapes flat (see README design
principles); the wire shape is what this format governs.

### Renderer

Consumes surfaces and presents them to a learner.

- MUST resolve `component` names only against a catalog under its control;
unknown names MUST degrade to a visible notice, never code execution or a
crash.
- MUST validate props against the catalog entry before rendering.
- MUST ignore unknown members of the `a2learn` extension (render the
surface as if they were absent) and MUST NOT fail on them.
- MUST apply surface updates by id-merge; a renderer MAY reject an update
whose `version` it does not support.
- SHOULD render partial component lists progressively during streaming.
- MUST implement the local/event interaction tiers per
[`events.md`](./events.md): local-tier interaction MUST NOT be forwarded
to a model by default.

### Registry

Indexes manifests and answers discovery queries.

- MUST NOT present unverified standards claims as verified, in query
results or listings.
- MUST NOT serve `tier: "code"` listings through a content-tier channel;
code-tier distribution requires the curated path (signed publishers,
review).
- MUST preserve manifest fields it does not understand (round-tripping),
and MUST record — not grant — reviewer attestations.
- SHOULD rank or filter on `verified`, `a11y`, and `reviewed` so consuming
agents can express trust requirements.
- MUST NOT include learner data in manifests or query responses; a registry
indexes content, never people.

### Host

Runs the event bindings between an activity and an agent (a native app, an
MCP Apps host, a first-party embedded client).

- MUST deliver event-tier payloads to the agent side as structured data,
never by interpolating them into prompt text as instructions.
- MUST label learner-authored text entering a model context as untrusted
content.
- MUST own an explicit policy map from payload type to conversation
behavior (silent context · visible turn · tool call) and SHOULD apply
rate limits so a chatty surface cannot storm a model.
- MAY extend the binding with richer channels (identity, telemetry); the
payload shapes MUST remain as specified.

## Versioning

Surfaces and manifests carry an integer `version` / `a2learn` field.
During v0, breaking changes are allowed and are recorded in
[`CHANGELOG.md`](./CHANGELOG.md); implementations SHOULD pin to the version
they were built against and MUST NOT silently reinterpret a document with a
higher version.

## Extension rule

Third parties MAY add fields under their own namespace prefix (`x-` or a
reverse-DNS key). Producers and registries MUST preserve such fields;
renderers MUST ignore the ones they do not understand. The `a2learn`
namespace itself is reserved for this formatification.

## Path to 1.0

v1.0 is gated on: at least **two independent implementations** of the
Renderer class, machine-readable schemas plus a conformance fixture suite,
and a window of **two months without breaking changes**. The format
formalizes what implementations ship — a requirement no implementation
exercises is a candidate for removal before 1.0, not a reason to build.
65 changes: 65 additions & 0 deletions docs/a2learn/considerations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# a2learn — security, privacy, and accessibility considerations (draft v0)

Informative unless marked with RFC 2119 keywords. This format targets
learners who are often children; these sections are not boilerplate.

## Security considerations

**The catalog is the trust boundary.** Activities are data resolved against
host-controlled catalogs; nothing in a conforming pipeline lets a surface
introduce executable code (see Producer/Renderer requirements in
[`conformance.md`](./conformance.md)). Any implementation change that lets
model- or publisher-authored content reach the page by another route is a
vulnerability, not a feature request.

**Two-tier distribution exists because the tiers have different threat
models.** Content-tier listings are inert data; the worst malicious content
can do is be pedagogically bad or offensive — a moderation problem.
Code-tier listings are a software supply chain aimed at classrooms —
extension-store rules apply (signed publishers, review before listing,
revocation). Registries MUST keep the tiers distinguishable end to end.

**Prompt injection has two doors here.** (1) Manifest text (titles,
summaries, catalog descriptions) is injected into composer/agent prompts by
consuming systems — registries SHOULD bound length and character repertoire,
and consumers SHOULD treat manifest text as content, not instructions.
(2) Learner-authored text travels in event payloads and surface context —
hosts MUST label it untrusted when it enters a model context.

**Embedded rendering** inherits the MCP Apps sandbox model (iframes, opaque
origins, host-mediated messaging). Hosts SHOULD NOT weaken that sandbox for
content-tier activities.

## Privacy considerations

**A `WidgetResult` stream is an education record.** Aggregated results and
data-model snapshots constitute student performance data; deployments in
US K-12 contexts inherit FERPA/COPPA-shaped obligations, and equivalents
elsewhere. The format's division of responsibility:

- **Manifests and surfaces MUST NOT contain learner data.** Content and
people never travel in the same artifact; registries index only content.
- **Event payloads SHOULD be data-minimal.** `detail` payloads SHOULD NOT
carry direct identifiers; identity binding is the host's job, at the
transport layer, under its own compliance regime.
- **Persistence is the deployer's obligation**, not the format's: hosts
that store results own retention, deletion, and access rights. The
format's contribution is separability — learner-keyed state is
structurally distinct from content, so deletion can cascade cleanly.

**Generated content in front of minors.** `provenance.generated` exists so
consuming systems can require review gates for AI-generated activities.
Hosts serving minors SHOULD run a content-safety pass on generated
surfaces before first exposure; the reviewer-attestation mechanism lets a
trust layer make that check portable.

## Accessibility considerations

Interaction accessibility (keyboard operability, focus, ARIA state) is a
property of catalog components; structural accessibility (heading order,
landmarks, reduced motion, live-region announcements for injected content)
is a property of surfaces. Manifest `a11y` fields are **declarations** —
useful for filtering, honest only if attested. A trust layer MAY certify
them; a registry MUST NOT convert declarations into certifications on its
own authority. Language access is first-class: `lang` is required in
manifests so agents can filter honestly rather than assume English.
70 changes: 70 additions & 0 deletions docs/a2learn/events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# a2learn-events — draft v0

What interactions report back to the agent side. These are **payload
shapes with transport bindings**, not a wire protocol: a2learn defines the
JSON; AG-UI and MCP define how it moves. This is the smallest of the three
documents on purpose — most interaction never leaves the client.

## The two-tier interaction rule *(normative, Prototype)*

- **Local tier** — clicks, drags, reveals, in-game moves resolve entirely in
the renderer at zero latency and zero tokens. They are never events.
- **Event tier** — pedagogy-changing moments reach the agent: a result was
produced, help was requested, an author steered. Only these payloads
exist in this format.

A conforming host MUST NOT forward local-tier interaction to a model by
default; chatty surfaces would otherwise be a cost and privacy hazard.

## Payloads *(Planned; the debate-ai and writing-workshop widgets ship
working precedents of the loop today)*

```json
{ "type": "widget_result",
"surfaceId": "…", "componentId": "quiz-1",
"result": { "status": "complete", "correct": false, "score": null, "attempts": 1 } }

{ "type": "action",
"surfaceId": "…", "componentId": "help-button",
"name": "help_requested", "context": { "step": 3 } }

{ "type": "author_edit",
"surfaceId": "…", "componentId": "intro-text",
"op": "set_text", "payload": { "text": "…" } }
```

`widget_result` carries the same `WidgetResult` contract the surface's data
model uses ([`surface.md`](./surface.md)) — one shape from interaction to
data model to event to report. A `widget_result` with `correct: false` is
the canonical remediation trigger: the agent answers on the same stream
with a `surfaceUpdate` adding a gated remediation section.

## Binding 1 — AG-UI *(Prototype)*

Native hosts run activities over an AG-UI event stream. Downstream, surfaces
and progress ride standard events (`RUN_STARTED`, `CUSTOM` carrying
surface messages, `STATE_DELTA` for data-model patches, `RUN_FINISHED`).
Upstream, a2learn payloads post to the host's run endpoint and appear to
the agent as structured input — never as prompt text.

## Binding 2 — MCP Apps *(partially Shipped)*

Chat-embedded activities run in MCP Apps iframes and report through the
`ui/*` channel:

- `ui/update-model-context` — silent context: results the agent should know
without a visible turn *(Shipped for three widgets)*.
- UI-initiated messages — events that should *trigger* an agent turn
(help requests).
- Host-proxied tool calls — consent-gated in third-party hosts.

The host owns the policy map from payload type → conversation behavior
(silent context · visible turn · tool call). A first-party host may add
richer channels (identity, first-party telemetry); the payloads stay
identical.

## Untrusted-content rule *(normative)*

Event payloads and surface context can contain learner-authored text. Hosts
MUST label it as untrusted content when it enters a model context — it is
data about the learner, never instructions to the agent.
Loading
Loading