Agent Action Capsule · build your own evidence layer
b10 / 11

Reporting: from a verified bundle to a page

Seams ④ Result to ⑤ View. Build: a page drawn only after the bundle verifies. Conform to: draft-mih-zhang-agent-disclosure-bundle-00.

A report is a presentation of an evidence file for one reader. The page is drawn only after the file verifies, by a module the reader's viewer selects from declared manifests, in words that live outside the evidence, so rewording or translating a report never moves an evidence identifier.

The presentation pipeline: evidence file, verify core, frozen context, registry, module, wording pack, shell, packagings A vertical pipeline read top to bottom, with notes on the right. Stage 1: the evidence file, an Evidence Bundle with records, signatures, checkpoint, inclusion proofs and disclosures. Stage 2: the verify core checks each record's identity and signature, graph closure, interval coverage, membership and disclosures; a side branch shows that a failed bundle stops at the refusal, and the banner has three states: passed, failed, unable to verify. Stage 3: the verified context, drawn with a lock, read-only for modules. Stage 4: the registry with two manifests; module A requires composed/v1 and matches, module B forbids composed/v1 and does not; a note says two specific matches are an error, never broken by priority. Stage 5: the module runs canRender, buildModel and render, and never verifies. Stage 6: a wording pack, pinned by its SHA-256 and not sealed, feeds the module from the side. Stage 7: the common shell with a no-network content security policy and the three depth levels L0, L1, L2, which are designed. Stage 8: three formats, offline html, fragment link and embedded, plus a static rendering that runs no script. Stage 9: the English wording pack is replaced by a French one; the page words and wording digest change while the bundle digest and capsule ids are unchanged. 1 · Evidence file evidence-bundle/v2 records · signatures · checkpoint · proofs · disclosures scoped to one audience before anything is drawn 2 · Verify core records · closure · coverage · membership · disclosures failed → the refusal and the checks, nothing else banner: passed | failed | unable to verify 3 · Verified context (frozen) VerifiedBundleContext: one run, read-only a module cannot change the verdict or the resolved disclosures 4 · Registry: match the manifests bundle kind · profiles · extensions required / forbidden A requires composed/v1 ✓ match B forbids composed/v1 ✗ two specific matches = error, never a tie-break 5 · Module A canRender → buildModel → render model: pure, no network, no clock never verifies, never reads the raw file 6 · Wording pack locale "en" wording_sha256 0a04a729… · never sealed 7 · Common shell CSP: connect-src 'none'; scripts pinned by hash L0 what matters L1 why L2 verify no request, no unpinned script; three regions: designed (one today) offline .html fragment #token embedded mount() static runs no script same content, same verify state for one (file, audience) pair 6 · Wording pack locale "fr" new wording_sha256 · page words change ✓ unchanged: bundle digest · every capsule_id · every disclosure digest Presentation words live in the pack. Words a reader relies on as what someone said are bound in a record as commitments, and shown only after the commitment recomputes.

1What it is

Reporting turns one verified evidence file into a page for one reader: verification runs once, its result is frozen, and a registry picks the one presentation module whose declared manifest matches the file.

The module builds a model from verified content only and draws it into a common shell that makes no network request and prints, as an offline file, a fragment link, an embedded view or a static rendering that runs no script; three separate depth levels are designed.

Words split two ways: what someone said, saw or approved is bound in the record as a commitment, while headings and labels live in a hash-pinned wording pack, so the same evidence can be reworded or translated without a single evidence identifier moving.

2Design choices

Chosen: verify once, in the core, before any module is chosen; build one frozen VerifiedBundleContext that every module reads.
Rejected: each view resolving the disclosures again from the raw file, where a view can read a payload the verifier classified as mismatched. (presentation contract v0 §2, §3.1; agent-action-capsule#202)
Chosen: modules are selected by declarative manifests; one module wins over another only through forbids; two specific modules that both match are an error, checked at registration and at resolution.
Rejected: an if/else chain over payload types in the viewer, where adding a page shape means editing the viewer and precedence is never written down; and breaking ties by priority or registration order. (contract §4.3–§4.5 and Appendix A; #207)
Chosen: evidentiary words (a request, an approval sentence, displayed terms) are bound by one of three mechanisms: a digest with an out-of-band opening, a sha256-jcs-nonce256 commitment, or a separately sealed text record.
Rejected: clear-text labels, statuses and reasons inside a sealed payload: changing a heading would change the evidence's identifier, and producer prose would look like a checked result. The cited counter-example is the report/v1 row model. (contract §7.1, §7.2)
Chosen: presentation words in a wording pack (aac.wording-pack/v0), one locale per pack, referenced by wording_sha256 over its exact bytes; the builder refuses a pack that does not hash to it, and a renderer falls back to the core's own labels.
Rejected: wording compiled into code, or carried in the manifest: a manifest carries no title, label or description, and an unknown member is an error. (contract §4.1, §7.3, §7.4; presentation builder v0 §2.1)
Chosen: a bundle that fails gets the refusal and its check list and nothing else; a viewer that cannot compute SHA-256 says “Unable to verify in this viewer”, never “failed”.
Rejected: a softened summary over a failed bundle, and drawing a missing browser capability as a failed check. (contract §8 I1; #226)
Chosen: a per-page Content-Security-Policy: no connections, and every script and inserted stylesheet listed by its SHA-256, so a module script the builder did not pin does not run.
Rejected: trusting a module because of its name or where it came from. The pin shows which code ran; it does not make the code correct. (contract §5.1, §9.2; #203, #214)
Chosen: a module declares its own presentation_api and runtime_min; a viewer that cannot honour them refuses the module and names it on the page.
Rejected: silently rendering a fallback in its place. (contract §3.2, I8)

3What it shows / what it does not

Shows

  • Whether the file verified, before anything else is drawn; on failure, which checks failed.
  • Which module drew the page, and that its script is the one the builder pinned (CSP hash).
  • Every extension in the file, each with its integrity and whether this viewer interpreted it; an unknown one reads “meaning not interpreted”, never hidden.
  • Evidentiary words only after their commitment recomputes; without an opening, that the text is committed and not opened.
  • The same content and verify state in every packaging supported for one file and one audience.

Does not show

  • That the records are true, or that an answer was right: that is judgment, kept on its own step (b9).
  • That a module's layout is a fair reading of the evidence: the pin shows which code ran, not that it is correct.
  • Anything from the static page by itself: it runs no script and says it is not self-verifying.
  • Anything from a picture or a text summary of the same model: those are presentation, never something a reader can check.
  • Who holds a signing key in the real world.

4Example

Two example manifests from the spec repository. Both match the same profile; one forbids the composition extension and the other requires it, so no bundle matches both. Neither carries a word of display text.

{
  "spec_version": "aac.presentation-manifest/v0",
  "id": "org.example.unilateral/v0",
  "presentation_api": "aac.presentation-api/v0",
  "runtime_min": "0.1.0",
  "trust_class": "trusted-executable",
  "requires": {
    "bundle_kind": "evidence-bundle/v2",
    "profiles": ["spec_version:org.example.exchange/v0"]
  },
  "forbids": { "extensions": ["composed/v1"] },
  "audiences": ["*"],
  "formats": ["html", "fragment", "embedded"],
  "fallback": false,
  "priority": 1,
  "executable": {
    "carrier": "module-slot",
    "script_sha256": "1f0e3dad99908345f7439f8ffabdffc41f0e3dad99908345f7439f8ffabdffc4"
  }
}

Source: agent-action-capsule:schemas/examples/presentation-manifest-v0/example-unilateral.json @ 2ef8aeb49e2334e24c404bd62d3fdb161770873c; its pair example-composition-aware.json has the same requires plus "extensions": {"required": ["composed/v1"]}.

NameTypeMeaning
idstringThe module id, <dotted name>/v<major>. The aac. prefix is reserved for modules the spec repository ships; anyone else uses their own name.
presentation_apistringThe module-to-runtime interface it is written against. A viewer that does not implement it refuses the module out loud.
runtime_minMAJOR.MINOR.PATCHThe lowest runtime version it needs; versioned independently of the id and of the interface.
trust_classenumtrusted-executable (a pinned script) or declarative (fields and a wording pack, no code).
requiresobjectBundle kind (equality), profile tokens <key>:<value>, and extensions that must be engaged.
forbidsobjectProfiles or extensions that must be absent: the only way one specific module excludes another.
audiences / formatsarraysWho the page is for ("*" for all) and which packagings the module supports.
fallback / prioritybool / intpriority only ranks a specific module above a fallback; it never settles a tie between two specific modules.
executableobjectWhere the script lives (module-slot) and its script_sha256; the page's CSP lists that digest.

The words come from a separate pack whose exact bytes hash to the wording_sha256 a declarative manifest names (0a04a729bf29caecda23cae6b5faa320f8aabc495458e99180150059e63c3730 for this one):

{ "wording_pack_version": "aac.wording-pack/v0",
  "id": "org.example.rules-table.wording/v0",
  "locale": "en",
  "entries": {
    "module.title": "Rules table",
    "page.title": "Rules comparison",
    "heading.l0": "What the comparison found",
    "state.unknown": "Not established: no finding either way",
    … } }

Source: agent-action-capsule:schemas/examples/presentation-manifest-v0/example-wording-pack.json and example-declarative-rules.json @ 2ef8aeb49e2334e24c404bd62d3fdb161770873c (entries trimmed).

The test of an open presentation layer

An outside developer, with only the open pieces, writes a module such as acme.monthly-risk-report/v1: a manifest under their own name, a script pinned by its hash, a wording pack. The builder puts it in a page that verifies offline, and nobody has to add a case to the viewer for it. Today that path exists for a pinned script: capsule-viewer's builder tests build pages from org.example.* module-slot modules, and refuse one whose interface or runtime the page cannot honour. The declarative path (no code at all) is designed, not built.

5Status

PieceStatusEvidence
Verify coreliveagent-action-capsule ts/src/bundle.ts, ts/src/verify.ts @ 2ef8aeb49e2334e24c404bd62d3fdb161770873c; released in @action-state-group/agent-action-capsule 0.3.0 on npm (tag ts/v0.3.0, 40f1567fee15f677debdfb56c4c39ba884880dce).
Frozen verified contextlive#202; ts/test/verified-bundle-context.test.ts; in ts 0.3.0.
“Unable to verify” distinct from failedlive#226; UNABLE_TO_VERIFY_LINES in ts/src/evidence-graph-view.ts; in ts 0.3.0.
Extension rows: integrity + interpretationlive#205; in ts 0.3.0. The Go, Python and Rust verifiers render no page and still report every extension uninterpreted (contract Appendix C.6).
Registry, ambiguity error, refusalliveTypeScript #207 (ts/src/presentation-registry.ts, ts 0.3.0); Go #217 (go/presentation/, tag go/v0.10.0 626074f631eb20454aef0b3a0f67320ddaf66119); both run the shared vectors/presentation-resolution/.
Generic composition viewlive#208 (ts/src/composed-view.ts); in ts 0.3.0.
Shell: slots, base CSS, per-page CSPliveBuilt in #203 and #214; moved to capsule-viewer (Apache-2.0 AND BSD-3-Clause) in capsule-viewer#33 and removed from the spec repository in #302; released as @action-state-group/capsule-viewer 0.1.0 on npm (tag ts/v0.1.0, 5c9042578de2eccccf4a93b6d820945ce1208e2e).
Builder: three formats + static rendering, availability, wording-pack hash checkliveBuilt in #209 and #216; now capsule-viewer ts/src/presentation-builder.ts (availablePackagings, checkWordingPack) in capsule-viewer 0.1.0; fragment codec and mount() stay in the spec repository (ts/src/presentation-fragment.ts, ts/src/presentation-mount.ts).
Contract, manifest schema, exampleslive (design spec)spec/presentation-contract-v0.md (#206, revision 1 #215), schemas/presentation-manifest-v0.json and its checker @ 2ef8aeb. A design specification in the repository, not an Internet-Draft.
Module servicesbuilt: 3 of 8details, badge, disclosure are built; section, citation, format.*, countersigner and wording are designed, not built (contract Appendix C.9).
Separate L0 / L1 / L2 regionsdesignedThe reference runtime hands a module one element for all three today (contract Appendix C.10).
Declarative renderer (no-code modules)designedNot built: no declarative module can be rendered by the reference runtime today (contract Appendix C.9). The example manifests and pack exist as schema fixtures.
An outside developer's moduleplannedThe pinned-script path is exercised by capsule-viewer ts/test/presentation-builder.test.ts @ 204e07a87cae17886b87f1324a5040a4aec6b8bf with org.example.* modules. No module from outside the repositories' authors was checked.

6Specs

7What you need before the next step