Specification · v1 Working Draft
anvil·md

Specification  ·  Section 6 of 14

The stamp

The answer half: the <stamp> tag, its payload per block kind, and why it renders as the frozen block rather than a second bubble.

6.1 Shape

<stamp block="…" kind="…" [payload attributes…]>
  [optional child elements]
  A sentence a human would have typed.
</stamp>
  • Attributes are the truth. Parse those.
  • The body is the courtesy.
  • One tag per block; several answered blocks means several tags, in touch order.
  • The client writes it. The agent never does.

Named <stamp> and not <input>: <input> collides with a real HTML element, which a renderer or a model could plausibly confuse.

6.2 Per block

<!-- @choice select=one -->
<stamp block="project-kind" kind="choice" value="product" label="Product UI">
It is a product UI.
</stamp>

<!-- @choice / @gallery select=many -->
<stamp block="mood" kind="gallery" values="ed,br" labels="Editorial,Brutal">
I picked Editorial and Brutal.
</stamp>

<!-- @input -- note the masked secret -->
<stamp block="company" kind="input">
  <field name="legal">Acme Ltd</field>
  <field name="token">••••••</field>
I filled in the company details.
</stamp>

<!-- @upload -- files are ALSO attached to this turn as real parts -->
<stamp block="assets" kind="upload" count="2">
  <file name="brandbook.pdf" mime="application/pdf" size="2451920" pages="48" ref="blob_7f3ac91"/>
  <file name="logo.svg" mime="image/svg+xml" size="14204" ref="blob_c02de5"/>
I uploaded the brand book and the logo.
</stamp>

<!-- @link -->
<stamp block="refs" kind="link" count="1">
  <url href="https://example.com" fetched="yes" title="Example" desc="…"/>
These are the ones.
</stamp>

<!-- @scale -->
<stamp block="tone" kind="scale" steps="5">
  <dial name="formal" value="2" norm="0.25" poles="Formal|Playful"/>
</stamp>

<!-- @order -->
<stamp block="priorities" kind="order" values="craft,speed,cost">
Craft, then speed, then cost.
</stamp>

<!-- skipped / expired -->
<stamp block="refs" kind="link" skipped="yes">Skipped that one.</stamp>
<stamp block="mood" kind="gallery" expired="yes">That one timed out.</stamp>

Every tag may also carry at= and, where more than one human can act, by=. Always include label/labels alongside value/values — whoever summarises this conversation later will not remember what br meant.

Use one serializer for all of this. One escaping policy, one place to fix it when a label contains a quote. Every block hand-rolling its own tag is a future escaping bug.

6.3 It renders as the stamp, not as a bubble

The tag is a real user turn in the model’s context. In the UI it renders as the stamped block, and nowhere else. A duplicate “you chose Editorial” bubble is noise, and worse, it separates the answer from the question it answered.

6.4 Free text still works

The composer never goes away. A human who ignores a block and types prose has answered it. Design every block so that “never answered” is survivable — most of them will not be. That is what @void is for.