taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

endpoint-documentation-requirements.rst (57481B)


      1 .. _endpoint-documentation-requirements:
      2 
      3 ==============================================
      4 Endpoint and Method Documentation Requirements
      5 ==============================================
      6 
      7 .. contents:: Table of Contents
      8    :local:
      9    :depth: 2
     10 
     11 Status: proposed convention.  Applies to: every per-endpoint ``.rst`` file
     12 under ``taler-docs/core/`` (HTTP APIs: exchange, merchant, bank-\*,
     13 corebank, challenger, taldir, mailbox, donau, auditor, sync, terminal,
     14 ...) and every per-operation ``.rst`` file under
     15 ``taler-docs/core/wallet-core/`` (wallet-core methods).
     16 
     17 This document is the normative format specification.  Design intent and
     18 rationale, the diagnostic of the current corpus, the migration plan, and
     19 worked examples live in :doc:`DD 105
     20 <../design-documents/105-machine-readable-endpoint-documentation>`.
     21 
     22 The format is language-agnostic.  The shape layer describes wire-level
     23 types (JSON objects exchanged between client and server) via the
     24 historically named ``ts:def`` / ``ts:op`` directives; nothing in this
     25 specification ties the documented semantics to any implementation
     26 language, and generated artifacts may target any language.
     27 
     28 
     29 1. Purpose
     30 ==========
     31 
     32 Each endpoint/method is documented in **exactly one** ``.rst`` file that is
     33 **completely machine-readable**: every semantic claim a client, a doc lint,
     34 or a use-case generator could need is expressed in a directive option, a
     35 field, or a typed block — never only in prose.  Prose remains for human
     36 readers, but it is *rendering*, not *source of truth*.
     37 
     38 The goal is that the following can be **derived** from the doc corpus
     39 without reading prose:
     40 
     41 * per-entity state tables (states, transitions, timers, failure exits),
     42   with every transition naming its trigger and guard,
     43 * per-actor observation projections (who can see which state, through
     44   which endpoint field or notification channel),
     45 * cross-component flows (which call creates an identifier, which calls
     46   carry or consume it, which watched source moves an entity),
     47 * sequence diagrams (happy-path messages from transition edges,
     48   ``alt`` blocks from guards, ``loop`` blocks from timers).
     49 
     50 The docs are **authoritative**: implementations conform to them.  To make
     51 that enforceable, the machine-readable shape layer (``ts:def`` types,
     52 request/response schemas) should be used to generate as much source code
     53 as possible (types, validators, stubs), with drift caught by CI in the
     54 implementation repositories.
     55 
     56 One design philosophy runs through everything below: **derive edges,
     57 declare anchors.**  Anything that is an *edge* (a transition, an arming,
     58 an observation, a carriage of a value) is written once, on the edge
     59 itself — the transition, the method, the effect.  Anything that acts as
     60 a *resolution anchor* (an entity, a timer name, an identifier, a
     61 notification channel) is declared once, minimally, so that the edges can
     62 be checked against it.  If a fact can be derived from edges, it is never
     63 also declared; if a name must be resolvable, it is never left implicit.
     64 
     65 
     66 2. File organization
     67 ====================
     68 
     69 R2.1  One file per method.  No file documents two unrelated methods; no
     70       method is documented in two files.  Two declared exceptions exist:
     71 
     72       * *Variant groups*: one logical method may be documented as
     73         several directive instances in one file when the variants differ
     74         only in a path parameter's value domain (per-record-type delete
     75         endpoints, per-provider webhook entry points) or in long-poll
     76         parameter dialect.  The file is linted as one method with
     77         variants.
     78       * *Proxy stanzas*: wildcard delegation declarations
     79         (``.. http:any::`` with wildcard paths, e.g. reverse-proxy
     80         pass-throughs) are not methods; they carry no request/response
     81         semantics and follow only the naming rules of R2.2.
     82 
     83 R2.2  Naming (required):
     84 
     85       * HTTP endpoints: ``<method>-<path-segments>.rst``, lowercase,
     86         path variables in upper case without ``$``
     87         (``get-withdrawal-operation-WITHDRAWAL_ID.rst``),
     88         placed in the per-API directory (``core/exchange/``,
     89         ``core/merchant/``, ``core/bank-integration/``, ...).
     90       * wallet-core operations: ``<op-name>.rst`` in kebab-case inside
     91         the category directory (``core/wallet-core/balances/``).
     92 
     93 R2.3  Chapter files (``api-*.rst``) keep version history, shared
     94       conventions, and ``.. include::`` of per-method files.  They carry
     95       **no** per-method semantics that the method file itself lacks.
     96 
     97 R2.4  Cross-method information lives in exactly two places outside the
     98       method files: the **entity registry** (§4: entities, lifecycles,
     99       timers, identifiers) and the **per-component notification pages**
    100       (§5: real-time channels).  Method files *reference* these; they
    101       never restate them.
    102 
    103 
    104 3. The method file format
    105 =========================
    106 
    107 A compliant file contains the sections below **in this order**.
    108 Requirement levels: **REQ** (must be present), **COND** (present when
    109 applicable), **OPT**.
    110 
    111 3.1 Method header — REQ
    112 -----------------------
    113 
    114 The header is the method's machine-readable identity card.  Every option
    115 exists because a specific consumer needs it: the lint (vocab checks),
    116 the use-case generator (persona closures, observation bindings), the
    117 code generator (shape extraction), or the exclusion filter (drafts).
    118 
    119 HTTP endpoints keep the existing identity directive, extended with new
    120 options:
    121 
    122 .. code-block:: rst
    123 
    124    .. http:get:: /withdrawal-operation/$WITHDRAWAL_ID
    125       :since: v1
    126       :maturity: stable
    127       :idempotency: readonly
    128       :observes-state: withdrawal-operation.status
    129       :visible-to: wallet-user, bank-customer (capability withdrawal-id)
    130       :wait: long-poll(timeout_ms, old_state)
    131 
    132    One-line summary usable standalone (no markup that breaks extraction).
    133 
    134    Longer prose description...
    135 
    136 wallet-core operations keep ``ts:op``, extended the same way:
    137 
    138 .. code-block:: rst
    139 
    140    .. ts:op:: getBalances
    141       :read-only:
    142       :since: v4
    143       :maturity: stable
    144       :idempotency: readonly
    145       :visible-to: wallet-user
    146       :wait: none
    147 
    148 Options:
    149 
    150 ``:since:`` (REQ)
    151   Protocol/API version introducing the method, exactly ``v<N>``.
    152   Needed so derived artifacts can be sliced by protocol version (an old
    153   wallet talking to a new exchange sees a genuinely different machine).
    154 
    155 ``:deprecated:`` (COND)
    156   Version of deprecation, exactly ``v<N>``; presence implies
    157   ``:maturity: deprecated`` unless ``:maturity:`` says otherwise.
    158 
    159 ``:maturity:`` (REQ, default ``stable``)
    160   One of: ``stable``, ``experimental``, ``draft-no-consumers``,
    161   ``proposed-unimplemented``, ``deprecated``.
    162   Methods marked ``draft-no-consumers`` or ``proposed-unimplemented``
    163   are excluded from use-case derivation (their files exist to document
    164   intent, not behavior).  Examples in the current corpus: wads and
    165   reserve-control endpoints in the exchange API, ``/seed`` in donau,
    166   the apns/account-directory/ebisync APIs.
    167   (The option is named ``:maturity:`` and not ``:status:`` because the
    168   HTTP domain already uses ``:status:`` as a field-name alias for
    169   status codes.)
    170 
    171 ``:idempotency:`` (REQ)
    172   One of: ``readonly``, ``replay-safe``, ``keyed:<field>``, ``unsafe``.
    173   See §3.5 and §7.1.
    174 
    175 ``:observes-state: <entity>.<field>`` (COND)
    176   The response field (dotted path into the response ``ts:def``) that
    177   carries an entity's machine-readable state value
    178   (e.g. ``withdrawal-operation.status``, ``order.order_status``,
    179   ``transaction.state``).  The field's type must be a string union or
    180   an object with an enumerated discriminator — the lint resolves every
    181   union member against the entity's lifecycle (§4).  This is the
    182   **observation binding**: it states that this method's response *is* a
    183   view onto that entity's machine.  Without it, the per-actor state
    184   projections ("who can see which state, where") cannot be computed —
    185   the generator would have to guess the binding from type names or
    186   enum-member spelling, which collides across APIs (``pending``
    187   appears in half a dozen unions).
    188 
    189 ``:visible-to:`` (REQ)
    190   Comma-separated actor surfaces that may legitimately call or observe
    191   through this method (§7.3).  This is the single authorization fact of
    192   the file: the actor value implies the credential mechanism (§7.3
    193   table), so no separate ``:auth:`` field exists.  Where access is
    194   gated by an unguessable identifier rather than credentials, the
    195   capability is named in parentheses:
    196   ``merchant-staff, tax-auditor (capability wtid)``.
    197   Persona projections are computed as closures over this field.
    198 
    199 ``:timer-ref: <entity>.<timer>`` (COND, repeatable)
    200   References a timer **declared at the entity** (§4.3), with a ``:role:``
    201   describing this method's relationship to it:
    202 
    203   * ``arms`` — a successful call starts the clock, typically via a
    204     request field named in ``:field:`` (``POST /private/orders`` arms
    205     ``order.refund-deadline`` via ``refund_delay``).
    206   * ``exposes`` — the response carries the timer's current value, in
    207     the field named by ``:field:``
    208     (``ValidationChallengeInfo.solve_expiration``).
    209   * ``affected-by`` — the method's behavior changes once the timer
    210     fires (``.../challenge/$ID/confirm`` fails with
    211     ``TAN_CHALLENGE_EXPIRED``).
    212 
    213   The timer's semantics are declared once at the entity; the firing
    214   edges live in the lifecycle's transitions.  Method files only declare
    215   their *relationship* to the clock.
    216 
    217 ``:wait:`` (REQ)
    218   ``none``, or ``long-poll(<param1>, <param2>, ...)`` listing this
    219   method's long-poll request parameters.  This acknowledges that the
    220   endpoint can rendezvous with a state change.  It stays at the method
    221   because the parameter dialects are per-endpoint and differ
    222   (``timeout_ms`` alone, ``timeout_ms`` + ``old_state``, the merchant's
    223   ``lp_status``/``lp_not_etag`` algebra).  Real-time *push* channels
    224   are **not** declared here — they live in the component's notification
    225   page (§5).
    226 
    227 3.2 Request — REQ
    228 -----------------
    229 
    230 **Why this section is strict:** the request block is what the code
    231 generator turns into client stubs and validators, and what the lint
    232 cross-checks ``:wait:`` parameters against.  A parameter that exists
    233 only in prose is invisible to both.
    234 
    235 .. code-block:: rst
    236 
    237    **Request:**
    238 
    239    :query timeout_ms:
    240      *Optional.*  Timeout in milliseconds for :ref:`long-polling
    241      <long-polling>`.  Since protocol **v3**.
    242    :header Taler-Purse-Signature:
    243      *Conditionally required.*  Purse signature over the request.
    244    :body:
    245      The request body must be a `WithdrawRequest` object.
    246 
    247    .. ts:def:: WithdrawRequest
    248      ...
    249 
    250 Rules:
    251 
    252 R3.2.1  Every parameter appears exactly once as ``:query:``,
    253         ``:header:``, ``:path:``, or inside the ``ts:def`` body type.
    254         Optionality (``*Optional.*``, ``*Conditionally required.*``)
    255         starts the description; version annotations use ``@since`` /
    256         ``@deprecated`` in the existing style.
    257 
    258 R3.2.2  Every type referenced is defined in a ``ts:def`` block in this
    259         file or in a reachable common file.  No anonymous prose types.
    260 
    261 3.3 Response — REQ
    262 ------------------
    263 
    264 **Why the discipline:** HTTP status codes in this ecosystem do not
    265 follow the naive "2xx = success, else failure" rule (see §3.4).  The
    266 response section declares the *shape* of each outcome; the **Errors**
    267 section declares its *semantics*.  Keeping the two apart is what lets
    268 the generator distinguish a failure edge from a state report.
    269 
    270 .. code-block:: rst
    271 
    272    **Response:**
    273 
    274    :http:statuscode:`200 OK`:
    275      :schema: BankWithdrawalOperationStatus
    276      The withdrawal operation is known to the bank...
    277    :http:statuscode:`404 Not found`:
    278      :error:
    279      The operation was not found.
    280 
    281 Rules:
    282 
    283 R3.3.1  Every documented status code carries ``:schema: <TypeName>``
    284         (a success-shaped body defined in ``ts:def``), ``:error:``
    285         (standard `ErrorDetail` body), or both.  "Both" happens for
    286         non-2xx codes whose body reports state rather than failure —
    287         those additionally get an **Errors** entry with
    288         ``:temporality: progress`` (§3.4).
    289 
    290 3.4 Errors — REQ (explicitly empty if none)
    291 -------------------------------------------
    292 
    293 **Why this section exists:** error codes are overloaded across flows,
    294 and the overloads are not inferable.  A 404 on an unfunded reserve
    295 means "wait, the wire hasn't arrived" (transient); a 404 on an expired
    296 order means "gone forever" (terminal).  A 402 on the order status page
    297 is the *normal unpaid state* (progress); a 402 on purse merge is a
    298 fixable rejection.  Same codes, opposite meanings.  The two fields per
    299 entry — ``:temporality:`` (what kind of outcome this is) and
    300 ``:recovery:`` (what moves the flow forward) — are what make the
    301 failure and timer columns of derived state tables computable instead
    302 of guessed.
    303 
    304 Every non-2xx outcome gets a structured entry — including codes that
    305 are **not failures** (progress signals such as 402-unpaid, 202-pending):
    306 
    307 .. code-block:: rst
    308 
    309    **Errors:**
    310 
    311    :error 404 TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN:
    312      :temporality: transient-state
    313      :recovery: long-poll
    314      The reserve is unknown; the wire transfer may not have arrived.
    315      The wallet should long-poll the reserve status and repeat the
    316      exact same request.
    317    :error 409 TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS:
    318      :temporality: fixable
    319      :recovery: retry-modified
    320      The reserve balance is too low; retry withdrawing less.
    321    :error 451 TALER_EC_EXCHANGE_KYC_REQUIRED:
    322      :temporality: gate
    323      :recovery: start-kyc
    324      The account must pass KYC before this call can succeed.
    325    :error 402 none:
    326      :temporality: progress
    327      :recovery: none
    328      The order is unpaid; the body carries the ``taler_pay_uri`` the
    329      wallet uses to proceed.  This is the normal waiting state of the
    330      flow, not a failure.
    331 
    332 Rules:
    333 
    334 R3.4.1  ``:temporality:`` (REQ per error) — one of ``transient-state``,
    335         ``terminal``, ``fixable``, ``rate-limited``, ``gate``,
    336         ``progress`` (§7.4).  ``progress`` entries are how the
    337         generator knows *not* to derive a failure edge: the code is the
    338         state signal.
    339 
    340 R3.4.2  ``:recovery:`` (REQ per error) — the client action that can
    341         move the flow forward, from §7.5.  For ``progress`` entries this
    342         is usually ``none`` or ``poll-later``.
    343 
    344 R3.4.3  The human-readable condition follows the fields.  Multiple
    345         error codes for one status get one entry each.  Where the error
    346         is caused by a timer firing, the condition references the
    347         entity timer (``:timer-ref:`` in the header makes the method's
    348         relationship explicit).
    349 
    350 3.5 Idempotency — COND (mutating methods)
    351 -----------------------------------------
    352 
    353 **Why this is a first-class field:** retries are the universal response
    354 to network failure, and a request that arrived-but-lost-its-response is
    355 indistinguishable from one that never arrived.  Whether a verbatim
    356 replay is *safe* therefore decides whether the client's failure branch
    357 exists at all — and whether the derived sequence diagram draws a retry
    358 loop or an abort.  This is not guessable: ``POST /withdraw`` is
    359 replay-safe by careful design (repeat the exact request, get the same
    360 response, coins are never lost to a network cut), while
    361 ``POST /private/orders`` without an explicit ``order_id`` creates a
    362 duplicate.  The label also flags design smells: an ``unsafe`` mutating
    363 method in a system built on at-least-once networking deserves review.
    364 
    365 Covered by the ``:idempotency:`` header option (§3.1, vocabulary in
    366 §7.1).  For ``keyed:<field>`` the file additionally documents the key
    367 field's reuse semantics in prose (existing good examples: bank-wire
    368 ``/transfer`` ``request_uid``, cashout ``request_uid``).  The lint
    369 requires: ``POST``/``PATCH``/``DELETE`` without
    370 ``:idempotency: unsafe | replay-safe | keyed:*`` is an error.
    371 
    372 3.6 Lifecycle linkage — COND
    373 ----------------------------
    374 
    375 **Why two bindings and not one:** a method relates to entity machines
    376 (§4) in two fundamentally different ways, and conflating them breaks
    377 different consumers:
    378 
    379 * **Mutators** declare their transitions in **Effects** (§3.7).  The
    380   lint diffs these against the entity's lifecycle: an effect naming an
    381   undeclared transition is a doc bug in one of the two places.  This is
    382   the two-sided check — the lifecycle says "call X drives A→B", the
    383   method file says "I drive A→B on entity E", and the disagreement
    384   surfaces.  One-sided declarations silently rot; that is the lesson
    385   of every doc-versus-code audit on this corpus.
    386 * **Readers** declare the observation binding via ``:observes-state:``
    387   (§3.1).  This is what makes the per-persona "user sees" column of a
    388   state table *generated* rather than authored.
    389 
    390 R3.6.1  Every entity named in either place is registered in the entity
    391         registry with: owner, transitions, terminal states, timers,
    392         identifiers.
    393 
    394 R3.6.2  State values named anywhere in the file resolve to the
    395         entity's lifecycle states (the set inferred from its
    396         transitions).
    397 
    398 3.7 Effects — COND (mutating methods)
    399 -------------------------------------
    400 
    401 **Why this section exists:** request/response documentation describes
    402 what the *caller* gets back — it says nothing about what changed in the
    403 *world*.  ``POST /private/orders`` returns an order id; the state that
    404 matters — "a wallet can now claim this order" — is a side effect
    405 visible through a *different* endpoint by a *different actor*.  A
    406 mutation is not documented until you have said who else can now see
    407 what.  The Effects block is also the generator's source for the
    408 *messages* of a sequence diagram: each effect edge is a signal to
    409 another lifeline.
    410 
    411 .. code-block:: rst
    412 
    413    **Effects:**
    414 
    415    :effect:
    416      :entity: order
    417      :transition: none -> unpaid
    418      :observable-by: :http:post:`/orders/$ORDER_ID/claim`
    419      Creates the order; a wallet can now claim it.
    420 
    421 Field by field:
    422 
    423 ``:entity:``
    424   The lifecycle entity whose machine moves (resolves to §4).
    425 
    426 ``:transition:``
    427   The exact edge performed, in the lifecycle's own notation.  Must
    428   agree with the entity's declared transitions (the two-sided check
    429   of §3.6).
    430 
    431 ``:observable-by:``
    432   The endpoint through which another actor observes the change.  This
    433   is the join that lets a generator draw the *next* message in the
    434   sequence diagram.
    435 
    436 R3.7.1  One ``:effect:`` entry per world-change beyond the response.
    437 
    438 R3.7.2  Real-time signals caused by the change (wallet notifications,
    439         merchant webhooks) are **not** listed here; they follow from the
    440         transition and are documented in the component's notification
    441         page (§5).
    442 
    443 3.8 Identifiers — COND
    444 ----------------------
    445 
    446 **Why this section exists:** methods do not exist in isolation — a
    447 value created by one call shows up in another component's API three
    448 hops later.  This block declares, per *identifier*, the method's role
    449 in that value's life.  Identifiers are the handles and join keys of the
    450 system (``reserve_pub``, ``wtid``, ``order-id``, claim nonces);
    451 ``order-id`` *names* an order entity, but the identifier itself is a
    452 value, not a machine.
    453 
    454 From the union of all method-side tags, the generator derives the
    455 identifier's full life story (created-by / carried-by / read-by /
    456 consumed-by), draws the cross-component value-flow graph, and the lint
    457 gets its two-sided join checks.  The registry (§4.4) holds only the
    458 anchor: the name, the capability property, and prose.
    459 
    460 .. code-block:: rst
    461 
    462    **Identifiers:**
    463 
    464    :identifier reserve_pub:
    465      :role: carries
    466      :registry: :ref:`identifier-reserve-pub`
    467      Wallet-generated reserve key; delivered to the bank here, becomes
    468      the wire subject of the funding transfer.
    469 
    470 The four roles, from the method's perspective:
    471 
    472 * ``creates`` — the value begins to exist here.  ``POST
    473   /private/orders`` creates ``order-id``; the wallet's withdrawal
    474   preparation creates ``reserve_pub``.
    475 * ``carries`` — transports a value created elsewhere: in the URL, body,
    476   response, or wire subject.  The bank-integration selection POST
    477   carries ``reserve_pub`` from wallet to bank; ``POST
    478   /private/transfers`` carries ``wtid`` from the bank statement into
    479   the merchant backend.
    480 * ``reads`` — uses the value as a lookup key.  ``GET
    481   /reserves/$RESERVE_PUB`` reads ``reserve_pub``; ``GET
    482   /transfers/$WTID`` reads ``wtid``.
    483 * ``consumes`` — validity ends here (single-use values): the
    484   challenger's ``/token`` consumes the authorization grant; a
    485   successful ``/orders/$ID/pay`` consumes the claim nonce.
    486 
    487 One method can play several roles on several identifiers: ``POST
    488 /orders/$ID/pay`` *reads* ``order-id``, *consumes* the claim nonce, and
    489 *carries* ``h_contract_terms`` onward to the exchange.
    490 
    491 R3.8.1  ``:role:`` vocabulary is §7.8.  The ``:registry:`` ref must
    492         resolve to the identifier's registry entry (§4.4).
    493 
    494 3.9 Details/Notes — OPT
    495 -----------------------
    496 
    497 Free prose for anything genuinely not expressible above (rationale,
    498 privacy notes, examples).  Must not introduce semantics that contradict
    499 or extend the structured sections; the lint greps for state names and
    500 error codes appearing *only* here.
    501 
    502 
    503 4. Entities and lifecycles
    504 ==========================
    505 
    506 **Why a registry:** state lives *between* methods.  A method file can
    507 honestly describe its own request and response, but the machine the
    508 methods collectively implement — the states, the transitions, the
    509 clocks — is a property of the *entity*, not of any endpoint.  The
    510 registry (``core/entities.rst``, with per-API chapters linking in) is
    511 where that machine is declared once.  Per the design philosophy, the
    512 registry declares anchors (names, owners, terminal states, timer names,
    513 identifier properties) and the edges (transitions) carry their own
    514 facts.
    515 
    516 Each entity has exactly one declaration:
    517 
    518 .. code-block:: rst
    519 
    520    .. entity:: tan-challenge
    521       :owner: corebank
    522 
    523       A TAN challenge issued for multi-factor confirmation of a
    524       corebank operation.
    525 
    526       .. lifecycle::
    527          :terminal: solved, expired, exhausted
    528 
    529          :transition none -> unsolved:
    530            :trigger: call <any MFA-gated call returning
    531              202 + ChallengeResponse>
    532            The challenge is created as part of an MFA-gated operation.
    533          :transition unsolved -> solved:
    534            :trigger: call POST /accounts/$U/challenge/$ID/confirm
    535            Correct TAN submitted.
    536          :transition unsolved -> expired:
    537            :trigger: timer tan-challenge.solve-expiration
    538            The challenge was not solved in time.
    539          :transition unsolved -> exhausted:
    540            :trigger: call POST /accounts/$U/challenge/$ID/confirm
    541            :if: the failed-confirmation counter reaches zero
    542            Too many wrong TANs submitted.
    543 
    544       .. timer:: solve-expiration
    545          The clock by which the challenge must be solved.  (Armed by
    546          challenge creation — derived from the ``arms`` timer-refs of
    547          the creating methods; exposed in
    548          ``ValidationChallengeInfo.solve_expiration`` — derived from
    549          ``exposes`` timer-refs; fires the ``unsolved -> expired``
    550          transition — derived from the lifecycle.)
    551 
    552       .. timer:: retransmission-cooldown
    553          Throttles TAN retransmission; calls while it is active fail
    554          with 429 (derived from the ``affected-by`` timer-ref and the
    555          error entry of the (re)send endpoint).
    556 
    557       .. identifier:: challenge-id
    558          :capability: no (owner-authenticated)
    559          Identifies the challenge on the challenge endpoints.
    560 
    561 4.1 Owner
    562 ---------
    563 
    564 ``:owner:`` (REQ) names the **component holding authoritative state and
    565 executing the transitions** — not merely where the entity is documented
    566 or displayed.  Ownership determines which component's answer is truth;
    567 every other component's view is a projection that may lag (the wallet's
    568 balance is a projection of exchange-owned coin-spend state; the
    569 wallet's order status display is a projection of merchant-owned order
    570 state).  An entity may be *exposed* through several APIs of the owner
    571 (``withdrawal-operation``: bank-integration, corebank, terminal) — name
    572 them in prose under the owner.  Where state is genuinely shared across
    573 components (KYC requirement and legitimization state is projected into
    574 the exchange, the merchant, the bank, and the wallet), the owner is
    575 the component whose answer is authoritative and the projections are
    576 named as such; ownership is about who executes transitions, not about
    577 which trigger kind dominates — a wallet transaction entity is owned by
    578 wallet-core even though its transitions are driven by watches, timers,
    579 and local user actions rather than incoming calls (§6).
    580 
    581 4.2 Lifecycle
    582 -------------
    583 
    584 The lifecycle is the transition list.  **States are inferred**: the
    585 state set is exactly the names appearing as transition sources or
    586 targets (plus the pseudo-state ``none`` for pre-existence, which is
    587 never a real state).  ``:terminal:`` (REQ) is **declared**, not
    588 inferred — it is the absorbing set:
    589 
    590 * it anchors use-case success conditions ("done" = reaching a declared
    591   terminal state);
    592 * it makes the absorbing property checkable: a transition *leaving* a
    593   declared terminal state is a lint error;
    594 * it turns incompleteness into signal: a declared-non-terminal state
    595   with no outgoing transitions is a lint warning (a silently truncated
    596   machine becomes visible).
    597 
    598 ``:final-reactivatable:`` (COND) declares states that anchor success the
    599 same way but from which declared reactivation edges are legal (a paid
    600 order later refunded; a ``done`` wallet transaction reactivated by a
    601 refund query).  Transitions leaving a ``final-reactivatable`` state are
    602 permitted but must be listed explicitly.  See §6 for the wallet
    603 transaction entities that need this.
    604 
    605 Each ``:transition: A -> B:`` has:
    606 
    607 ``:trigger:`` (REQ, repeatable)
    608   What causes the transition (§7.7).  This is the causality layer —
    609   the part of the system that no per-endpoint doc can see locally:
    610 
    611   * ``call <method>`` — an external call to the owner's API
    612     (resolvable to a method file).  Example: the wallet's selection
    613     POST drives ``pending -> selected``.
    614   * ``timer <entity>.<timer>`` — a declared entity timer fires.
    615     Example: ``solve-expiration`` drives ``unsolved -> expired``.
    616   * ``watch <component>.<observable>`` — the owner watches an external
    617     source (the exchange's wirewatch watching the bank's incoming
    618     history; the merchant backend tracking exchange wire arrivals).
    619     This is where **cross-component causality** lives: the transition
    620     names the other component's observable, which is the join the old
    621     prose never made.
    622   * ``external <description>`` — a human or off-API action (bank-UI
    623     2FA confirmation, cash inserted at a terminal, an offline key
    624     ceremony).  Non-API actors enter the machine here, honestly marked
    625     as outside every API.
    626 
    627 ``:if:`` (COND)
    628   The guard: the config/data condition under which this transition
    629   exists at all, referencing observable fields
    630   (``:if: /keys.kyc_enabled and the amount crosses the withdrawal
    631   threshold``).  Guards are how context forks (KYC on/off, thresholds,
    632   feature flags) enter the machine without multiplying machines — one
    633   canonical machine, guarded edges, instead of a combinatorial family
    634   of near-identical diagrams.
    635 
    636 Prose (REQ)
    637   One or two sentences of human explanation.
    638 
    639 4.3 Timers (slim anchors)
    640 -------------------------
    641 
    642 A timer is declared as a **name plus semantics in prose** — nothing
    643 more, because everything structural is derivable from edges:
    644 
    645 * *fires-to*: from the lifecycle transition whose ``:trigger:`` is this
    646   timer;
    647 * *armed-by*: from the method whose ``:timer-ref:`` has role ``arms``;
    648 * *exposed-in*: from the method whose ``:timer-ref:`` has role
    649   ``exposes`` (its ``:field:``);
    650 * *gates*: from the ``429``/``rate-limited`` error entry of the method
    651   whose ``:timer-ref:`` has role ``affected-by``.
    652 
    653 The block survives at all for three reasons: it is the **lint anchor**
    654 every ``:trigger: timer`` / ``:timer-ref:`` resolves against (free
    655 strings across hundreds of files would drift); it is the home of
    656 **config-armed** timers no method arms (``:armed-by:`` is present only
    657 in that case — e.g. ``purse_timeout`` comes from ``GET /keys`` global
    658 fees, not from any call); and it is the **gap marker**: an entity whose
    659 timers are not yet specified records the gap here once (see the
    660 ``withdrawal-operation`` example in DD 105), instead of silence
    661 scattered across N method files.
    662 
    663 4.4 Identifiers (slim anchors)
    664 ------------------------------
    665 
    666 An identifier is declared as **name + ``:capability:`` + prose**:
    667 
    668 .. code-block:: rst
    669 
    670    .. identifier:: reserve_pub
    671       :capability: yes
    672       Reserve public key; possession authorizes reserve-status reads.
    673       Wallet-generated; becomes the wire subject of the funding
    674       transfer.
    675 
    676 ``:capability:`` (REQ: ``yes``/``no``) records whether possession
    677 alone authorizes (unguessable: ``wtid``, ``withdrawal-id``, purse/claim
    678 links) — the property that distinguishes "public by design" (``GET
    679 /keys``) from "public but unguessable" (``/transfers/$WTID`` is usable
    680 by tax auditors *because* of it).  It is a property of the *value*, not
    681 of any method, so it has no other home; it is also what the
    682 ``:visible-to:`` capability annotations resolve against (V3).
    683 
    684 Everything else is derived from the method files' **Identifiers:**
    685 blocks (§3.8): *created-by*, *carried-by*, *read-by*, *consumed-by*.
    686 One exception needs prose: identifiers created **internally** (``wtid``
    687 is minted by the exchange's aggregator process — no method tags
    688 ``creates``) get a created-by sentence here, mirroring config-armed
    689 timers.  The registry also serves as the canonical naming list — the
    690 single place where ``reserve_pub`` is one concept, so that hundreds of
    691 files of tags can be checked for spelling drift.
    692 
    693 
    694 5. Per-component notification pages
    695 ===================================
    696 
    697 **Why these pages exist:** how an actor *learns* that a state changed
    698 is as much a part of the use case as the state change itself — and it
    699 was historically documented in prose scattered across chapters.  The
    700 doctrine of this system is *signals are hints; reads are truth*: a
    701 notification tells you something changed, an authoritative read tells
    702 you what is.  The notification page is where a component declares its
    703 channels, their delivery semantics, and — critically — the read that
    704 backs each signal.
    705 
    706 Every component that offers real-time signals documents them in a
    707 single notification page: ``core/wallet-core/notifications.rst``
    708 (exists today as a flat list of notification types; to be restructured
    709 into the channel form below), a merchant notification page (webhooks,
    710 currently prose in ``api-merchant.rst``), an exchange notification page
    711 (will honestly declare: none — pull + long-poll only), and so on.
    712 
    713 Each channel declaration:
    714 
    715 .. code-block:: rst
    716 
    717    .. notification-channel:: webhook
    718       :component: merchant
    719       :registration: POST /private/webhooks
    720       :delivery: at-least-once
    721       :events: order_created, pay, refund, order_settled,
    722                category_created, category_update, category_delete,
    723                inventory_update, inventory_product_created, ...
    724 
    725       Every event is a hint about an entity transition; the
    726       authoritative read is named per event:
    727       ``pay``/``refund``/``order_settled`` ->
    728       ``GET /private/orders/$ORDER_ID``.
    729 
    730 Rules:
    731 
    732 R5.1  ``:delivery:`` is one of ``lossy-hint`` (no delivery guarantee;
    733       re-read for truth — the wallet notification doctrine),
    734       ``at-least-once`` (duplicates possible, recipient must
    735       deduplicate), ``rendezvous`` (the channel itself blocks until the
    736       state is available).  If the implementation's delivery semantics
    737       are not specified today, write ``unspecified`` — a visible gap,
    738       not silence.
    739 
    740 R5.2  Every event names its **authoritative read** (the endpoint whose
    741       response is the truth).  The lint rejects a channel without a
    742       read reference per event family.
    743 
    744 R5.3  Method files never declare push channels.  The method whose
    745       effect *causes* a signal is linked automatically: effect
    746       transition (§3.7) + channel event mapping (here).
    747 
    748 R5.4  Long-poll is **not** a notification channel; it is a property of
    749       the read endpoint and is declared via ``:wait:`` (§3.1).  A
    750       long-poll response is authoritative state delivered late, not a
    751       hint.
    752 
    753 
    754 6. wallet-core method variant
    755 =============================
    756 
    757 ``ts:op`` files follow the same requirements with this mapping:
    758 
    759 .. list-table::
    760    :header-rows: 1
    761 
    762    * - HTTP concept
    763      - wallet-core op equivalent
    764    * - ``:visible-to:``
    765      - ``wallet-user`` (local message protocol, no credentials)
    766    * - ``:http:statuscode:`` responses
    767      - **Response** ``ts:def`` + **Expected errors**
    768    * - ``:error`` entries
    769      - **Expected errors** entries, same ``:temporality:`` /
    770        ``:recovery:`` fields
    771    * - ``:wait:``
    772      - ``long-poll(...)`` for blocking ops (``waitTransactionState``,
    773        ``waitExchangeReady``, ...), ``none`` otherwise; the wallet's
    774        push channel is documented in ``notifications.rst`` (§5)
    775    * - effects
    776      - transitions on transaction-group entities
    777        (``transaction-withdrawal``, ...) with states from the DD37
    778        machine
    779 
    780 ``TransactionState`` values used in ``:transition:`` fields must match
    781 ``major[:minor][/working]`` notation of DD37 and resolve against
    782 ``core/wallet-core/transactions/get-transaction-by-id.rst``.
    783 
    784 Wallet transaction entities extend the lifecycle model of §4 in four
    785 ways (rationale in DD 105):
    786 
    787 * **Final-reactivatable states.**  DD37's ``done`` is not absorbing
    788   (refund queries, ``rebind-session``, and late KYC requirements can
    789   reactivate a transaction).  Transaction entities declare
    790   ``:final-reactivatable:`` (§4.2) for such states and list the
    791   reactivation edges explicitly.
    792 * **Orthogonal modifiers instead of state explosion.**  The DD37
    793   ``working`` flag and the ``suspended`` / ``suspended-aborting`` /
    794   ``suspended-finalizing`` shadow states are declared as modifiers
    795   (same mechanism as ``:modifiers:`` on other entities), not as
    796   literal states.  A ``:standard-edges: suspend-retry`` declaration on
    797   the lifecycle implies the suspend/resume/retry edge family on every
    798   non-final state, so the inferred state and edge sets include them
    799   without authoring each one.  DD37 state *expressions*
    800   (``pending:*``, ``pending:-``, ``/idle``, comma alternation) are
    801   accepted wherever a state set is matched.
    802 * **Absence as signal.**  The ``deleted`` state exists only in
    803   notifications; its observation is the absence of the record (a
    804   not-found error from ``getTransactionById``).  ``:observes-state:``
    805   bindings may name this explicitly (``transaction.state,
    806   absence-signal: deleted``).
    807 * **Actions are not derived.**  ``txActions`` are computed per state
    808   *and* context; no "possible actions" column is generated from the
    809   lifecycle.
    810 
    811 The registry entries for transaction entities are the canonical
    812 machine-readable form of the DD37 machines; DD37's per-type state
    813 tables are generated from the registry.
    814 
    815 
    816 7. Controlled vocabularies
    817 ==========================
    818 
    819 Controlled vocabularies are where machine-readability lives or dies.
    820 A field whose values come from a closed list can be checked, counted,
    821 and closed over by tooling; a field with free text is prose wearing a
    822 costume.  Every value below is part of the contract: **adding a value
    823 is a specification change** (tooling must learn its meaning), and using
    824 a non-listed value is a lint error (M8.3).  For each value we give the
    825 definition, a real example from the corpus, and what the derivation
    826 tooling does with it.
    827 
    828 7.1 ``:idempotency:`` values
    829 ----------------------------
    830 
    831 The question this field answers is the most frequent operational
    832 question a client faces: *"the network ate my request or its response —
    833 may I send the exact same bytes again?"*  The answer determines whether
    834 the failure branch of a use case exists at all, and what the sequence
    835 diagram draws at every network boundary.
    836 
    837 * ``readonly`` — the call mutates nothing: no entity transition, no
    838   world-change.  (Logging and caching on the server side do not count
    839   as mutation.)  Examples: ``GET /keys``, ``getBalances``.
    840   *Consumers:* the generator expects no **Effects** section; the lint
    841   warns if a ``readonly`` method declares one (V6's inverse).
    842 
    843 * ``replay-safe`` — the server has recorded the request and its
    844   response; a byte-identical repeat returns the same response with no
    845   additional effect.  Examples: ``POST /withdraw`` (repeating the exact
    846   request yields the same blinded signatures — coins are never lost to
    847   a network cut), donau batch-issue (keyed on the recorded request
    848   hash).  *Failure mode prevented:* value loss when the response is
    849   lost after the server committed.  *Consumers:* the generator draws
    850   retry self-loops for free.  Note that replay means *byte-identical*;
    851   a modified request is a new operation, and conflicts there belong to
    852   **Errors** (e.g. ``TALER_EC_EXCHANGE_WITHDRAW_IDEMPOTENT_PLANCHET``).
    853 
    854 * ``keyed:<field>`` — the client supplies an idempotency key; reuse of
    855   the key with the same body suppresses duplicate effects, reuse with a
    856   *different* body is rejected (409).  Examples: bank-wire
    857   ``/transfer`` (``request_uid``), corebank cashouts (``request_uid``,
    858   including the rule that a corrected quote needs a fresh nonce).
    859   *Why it exists:* lets the client retry safely *and* lets the server
    860   detect client bugs.  §3.5 requires prose on the key's reuse
    861   semantics.
    862 
    863 * ``unsafe`` — retries may duplicate effects.  Example:
    864   ``POST /private/orders`` without an explicit ``order_id`` creates a
    865   second order.  Must be justified in prose.  *Consumers:* derivation
    866   tooling surfaces ``unsafe`` mutating methods prominently — in a
    867   system built on at-least-once networking, an unsafe mutation is a
    868   design smell.  *Guidance:* prefer having the client supply the
    869   identifier (``order_id``), which turns ``unsafe`` into
    870   ``keyed:order_id`` by construction.
    871 
    872 7.2 ``:maturity:`` values
    873 -------------------------
    874 
    875 Why this field exists: derivation must know which methods describe
    876 *behavior* and which describe *intent*.  A documented-but-unimplemented
    877 endpoint is not a fact about the system; deriving use cases from it
    878 manufactures fiction.
    879 
    880 * ``stable`` — implemented and conformant; fully derivable.
    881 * ``experimental`` — implemented, but may change without a version
    882   bump.  Derivable, but derived artifacts are marked experimental.
    883   Example: nearly all auditor diagnostic endpoints ("still
    884   experimental" throughout ``api-auditor.rst``).
    885 * ``draft-no-consumers`` — a spec nothing implements or consumes.
    886   Examples: the apns relay, ebisync, the bank account directory (all
    887   self-declared "nothing depends on this API"); sync in practice.
    888   Excluded from derivation; renders with a banner.
    889 * ``proposed-unimplemented`` — a documented proposal, explicitly not
    890   implemented.  Examples: wads, and the reserve-control endpoints
    891   (``/reserves/$PUB/open|close|attest``) in the exchange API.  Excluded
    892   from derivation; kept for design reference.
    893 * ``deprecated`` — still functional, but new derived artifacts must not
    894   use it; the replacement should be named in prose (and
    895   ``:deprecated:`` carries the version).
    896 
    897 7.3 ``:visible-to:`` values (actor ⇒ implied credential)
    898 --------------------------------------------------------
    899 
    900 This field is the file's single authorization fact.  The design
    901 decision: the *actor* is declared and the *mechanism* is implied by the
    902 table below, because declaring both would duplicate one fact in two
    903 drift-prone places.  Persona projections are computed as closures over
    904 this field: a persona is a set of actors; its visible surface is the
    905 set of methods whose ``:visible-to:`` intersects it.
    906 
    907 .. list-table::
    908    :header-rows: 1
    909 
    910    * - Actor
    911      - Implied mechanism
    912    * - ``wallet-user``
    913      - wallet-core local message protocol (no credentials; "visibility"
    914        is possession of the device running the wallet)
    915    * - ``merchant-staff`` or ``merchant-staff:<scope>``
    916      - ``Authorization: Bearer secret-token:...`` with the named
    917        permission scope (``orders-read``, ``transfers-write``, ...)
    918    * - ``aml-officer`` / ``aml-officer:readonly``
    919      - ``Taler-AML-Officer-Signature`` header; the ``:readonly``
    920        variant maps to the officer's read-only access level (the
    921        ``read_only`` flag in ``GET /aml/$OFFICER_PUB``)
    922    * - ``exchange-operator``
    923      - master-key signature (management operations)
    924    * - ``auditor``
    925      - auditor API authentication (see ``api-auditor.rst``)
    926    * - ``tax-auditor``
    927      - none — capability identifiers only (``wtid``); this actor has
    928        *no* credentials anywhere in the system
    929    * - ``bank-customer``
    930      - external bank UI; on APIs, capability ``withdrawal-id``
    931    * - ``bank-admin``
    932      - corebank bearer token (admin scope)
    933    * - ``kyc-provider``
    934      - provider webhook credentials
    935    * - ``any-unauthenticated``
    936      - none — public by design (``GET /keys``).  Do not confuse with
    937        capability-gating: public-by-design means the *content* is
    938        meant for everyone; capability-gated means the *identifier* is
    939        the secret.
    940    * - ``external``
    941      - outside any API (bank statements, PoS displays, offline
    942        ceremonies) — the honest escape hatch for non-API actors
    943 
    944 Capability-gated access is annotated in the field:
    945 ``:visible-to: merchant-staff, tax-auditor (capability wtid)`` means
    946 the listed actors hold the unguessable identifier; the lint resolves
    947 the identifier against the registry (§4.4) and checks its
    948 ``:capability: yes`` (V3).
    949 
    950 7.4 ``:temporality:`` values
    951 ----------------------------
    952 
    953 The classification of an outcome's *kind* — the input that decides
    954 which column of the derived state table (happy / timer / failure) an
    955 edge lands in, and whether a non-2xx is a failure at all.
    956 
    957 * ``transient-state`` — the condition resolves without the client
    958   changing anything; the identical request may succeed later.
    959   Example: 404 ``RESERVE_UNKNOWN`` on an unfunded reserve.
    960   *Derived as:* a wait/retry edge, not a failure exit.
    961 * ``terminal`` — will never succeed; abandon or restart the flow.
    962   Examples: 410 expired purse; 410 already-solved challenge.
    963   *Derived as:* a failure exit to a terminal documentation state.
    964 * ``fixable`` — a *modified* request can succeed.  Examples: 409
    965   insufficient funds (withdraw less); 400 parameter malformed.
    966   *Derived as:* a failure edge with a return loop after modification.
    967 * ``rate-limited`` — retry only after the documented delay.  Examples:
    968   mailbox 429 with the machine-readable ``retry_delay`` field; TAN
    969   retransmission cooldown.  *Derived as:* a retry edge annotated with
    970   the delay's source field.
    971 * ``gate`` — blocked on an action by the user or a third party, outside
    972   this call.  Examples: 451 KYC required; 202 MFA challenge; 402
    973   payment-required at sync.  *Derived as:* a gate edge into the
    974   relevant sub-flow (KYC legitimization, MFA solving).
    975 * ``progress`` — **not an error**: the non-2xx code is the happy-path
    976   state signal.  Examples: 402 unpaid + ``taler_pay_uri`` on the order
    977   status endpoint; 202 pending on deposit tracking; 304 unchanged
    978   during long-poll.  *Derived as:* **no failure edge** — the code *is*
    979   the waiting state's observation.
    980 
    981 Classification guide (ask in order): Is it actually a failure?  →
    982 ``progress``.  Can the caller fix the request?  → ``fixable``.  Does
    983 the condition resolve by itself?  → ``transient-state``.  Does it only
    984 need a delay?  → ``rate-limited``.  Does it need an outside action?
    985 → ``gate``.  Will it never succeed?  → ``terminal``.
    986 
    987 7.5 ``:recovery:`` values
    988 -------------------------
    989 
    990 The client's forward action — the label on the edge that *leaves* this
    991 outcome.  ``:temporality:`` says what kind of outcome it is;
    992 ``:recovery:`` says what to do about it.
    993 
    994 * ``retry-same`` — safe verbatim replay (pairs with
    995   ``:idempotency: replay-safe`` / ``keyed:``; pairing an unsafe method
    996   with ``retry-same`` is a contradiction the lint should flag).
    997 * ``retry-modified`` — change the request per the error's guidance
    998   (lower the amount; add the missing field; set ``max_age`` per the
    999   ``maximum_allowed_age`` hint).
   1000 * ``retry-after`` — wait the documented delay, then ``retry-same``
   1001   (429 + ``retry_delay``).
   1002 * ``long-poll`` — wait on the status endpoint's rendezvous parameters
   1003   (reserve status long-poll).
   1004 * ``poll-later`` — re-read after some time, without rendezvous (the
   1005   sync backup cadence; usual companion of ``progress``).
   1006 * ``refresh-config`` — re-fetch ``/keys`` or ``/config`` first
   1007   (denomination unknown ⇒ outdated keys).
   1008 * ``start-kyc`` — enter the legitimization sub-flow (451 responses
   1009   carry the requirement).
   1010 * ``user-action`` — the human must act outside this API (confirm in the
   1011   bank app; insert cash).
   1012 * ``abort`` — invoke the flow's own abort path (abort the withdrawal;
   1013   delete the purse).
   1014 * ``give-up`` — terminal; no forward action exists.
   1015 * ``none`` — informational; nothing to recover from.
   1016 
   1017 7.6 ``:timer-ref:`` roles
   1018 -------------------------
   1019 
   1020 A method's relationship to an entity clock comes in three kinds, and
   1021 each feeds a different derivation:
   1022 
   1023 * ``arms`` — a successful call starts the clock; ``:field:`` names the
   1024   arming request field (``POST /private/orders`` arms
   1025   ``order.refund-deadline`` via ``refund_delay``).  *Feeds:* the
   1026   "armed when" column of timer tables.  (Config-armed clocks are the
   1027   exception — declared ``:armed-by:`` at the anchor, §4.3.)
   1028 * ``exposes`` — the response carries the clock's current value;
   1029   ``:field:`` names the response field
   1030   (``ValidationChallengeInfo.solve_expiration``).  *Feeds:* the
   1031   observation layer — where an actor reads the time remaining.
   1032 * ``affected-by`` — the method's behavior changes once the clock fires
   1033   (challenge confirm fails with ``TAN_CHALLENGE_EXPIRED``).
   1034   *Feeds:* the join between the timer's firing edge and this method's
   1035   error table.
   1036 
   1037 One method may reference several timers (one ``:timer-ref:`` each); one
   1038 reference carries one role.
   1039 
   1040 7.7 ``:trigger:`` kinds
   1041 -----------------------
   1042 
   1043 A transition without a cause is narrative; the trigger is what makes
   1044 the machine causal — and what makes the sequence diagram drawable,
   1045 since every trigger kind maps to a diagram element (message, timer
   1046 edge, external note).
   1047 
   1048 * ``call <method>`` — an external call to the owner's API.  Resolvable
   1049   to a method file (V13).  The most common kind.  Example: the wallet's
   1050   selection POST drives ``pending -> selected``.
   1051 * ``timer <entity>.<timer>`` — a declared entity timer fires.
   1052   Resolvable to the §4.3 anchor.  Example: ``solve-expiration`` drives
   1053   ``unsolved -> expired``.
   1054 * ``watch <component>.<observable>`` — the owner watches an external
   1055   source.  This is where *cross-component* causality lives: the
   1056   exchange's wirewatch watching ``bank.incoming-history``; the merchant
   1057   backend tracking the exchange's outgoing wires.  Naming form is
   1058   ``component.observable`` so the join stays checkable.
   1059 * ``external <description>`` — a human or off-API action: bank-UI 2FA
   1060   confirmation, cash inserted at a terminal, an offline key ceremony.
   1061   Non-API actors enter the machine here, honestly marked as outside
   1062   every API.  Deliberately not lint-resolvable.
   1063 
   1064 Triggers are repeatable: one transition may have several (``selected ->
   1065 confirmed`` via the confirm endpoint *or* the bank UI).
   1066 
   1067 7.8 Identifier roles
   1068 --------------------
   1069 
   1070 The method↔identifier relationship declared in **Identifiers:**
   1071 (§3.8): ``creates`` (the value begins to exist here), ``carries``
   1072 (transports it onward in URL/body/response/wire subject), ``reads``
   1073 (uses it as a lookup key), ``consumes`` (its validity ends here).
   1074 Definitions and examples are in §3.8.  The roles are always from the
   1075 *method's* perspective: the same ``reserve_pub`` is created by the
   1076 wallet, carried by the bank-integration selection, read by the exchange
   1077 — and the union of those tags is what V21 checks joins with (exactly
   1078 one creator per carried/read value, or an internal-creation note at the
   1079 anchor).
   1080 
   1081 
   1082 8. Machine-readability rules
   1083 =============================
   1084 
   1085 These rules are the contract between authors and tooling.  Each exists
   1086 because a specific derivation or lint check depends on it; breaking one
   1087 does not merely make the docs sloppy — it makes a specific derived
   1088 artifact silently wrong.  Each rule below states the requirement, why
   1089 it exists, a typical violation, and what checks it.
   1090 
   1091 M8.1  One method per file, one identity directive per file (declared
   1092       variant groups excepted, §2).
   1093 
   1094       *Why:* the file is the unit of indexing — filename conventions
   1095       (§2.2), lint scoping, and generated cross-references all assume
   1096       the path↔method mapping is one-to-one.
   1097       *Violation:* two endpoints documented in one file "because they
   1098       are similar" — their semantics can no longer be addressed
   1099       separately, and backfill tools cannot target them.
   1100       *Checked by:* V1 (count identity directives per file).
   1101 
   1102 M8.2  Every element in §3 marked REQ is present; COND is present or
   1103       the absence is obvious from method kind (e.g. GET ⇒ no Effects).
   1104 
   1105       *Why:* the REQ fields are the minimum derivation set — a missing
   1106       REQ field means the generator must guess (the exact failure this
   1107       specification exists to eliminate).  The COND absence rule exists
   1108       so the lint doesn't cry wolf on legitimately absent sections.
   1109       *Violation:* a POST without ``:idempotency:``.
   1110       *Checked by:* the REQ lint rules (V2, V3, V5, V11, ...).
   1111 
   1112 M8.3  All field values come from the §7 vocabularies — free text is a
   1113       lint error.
   1114 
   1115       *Why:* a misspelled value (``rate_limited`` for
   1116       ``rate-limited``) is invisible to a human reviewer but silently
   1117       removes the entry from every derived table.
   1118       *Violation:* ``:temporality: transient`` (missing ``-state``).
   1119       *Checked by:* V2, V3, V5, V10, and the vocabulary checks.
   1120 
   1121 M8.4  Every name that must resolve, resolves: entity names → registry;
   1122       state values → lifecycle transitions (inferred set); ``:schema:``
   1123       types → ``ts:def`` blocks; ``:timer-ref:`` targets → entity timer
   1124       anchors; ``:identifier:`` names → identifier anchors;
   1125       ``:trigger: call`` refs → method files; ``:trigger: timer`` refs
   1126       → entity timers; notification channels → the component's
   1127       notification page.
   1128 
   1129       *Why:* this rule is what turns the corpus from a pile of pages
   1130       into a graph.  Every derived artifact (state tables, persona
   1131       projections, sequence diagrams) is a traversal of these edges; a
   1132       dangling reference is a broken join that no amount of prose
   1133       repairs.
   1134       *Violation:* ``:observes-state: withdrawal-operation.state``
   1135       (the field is ``status``) — the persona projection for the bank
   1136       customer silently loses the withdrawal states.
   1137       *Checked by:* V7, V8, V9, V10, V12, V13, V21.
   1138 
   1139 M8.5  State values and error codes appear in structured positions
   1140       first; prose mentions are decoration.
   1141 
   1142       *Why:* prose is unparseable.  A state or error that exists only
   1143       in a sentence is invisible to every consumer — the machine has a
   1144       state the tooling cannot see.
   1145       *Violation:* "returns 410 when the purse is gone" in the
   1146       description, with no 410 entry in **Errors**.
   1147       *Checked by:* V14 (grep prose for known state names and
   1148       ``TALER_EC_*`` codes absent from the structured fields).
   1149 
   1150 M8.6  Version annotations follow the single style ``@since **vN**`` /
   1151       ``@deprecated **vN**``.
   1152 
   1153       *Why:* derived artifacts must be sliceable by protocol version —
   1154       an old wallet talking to a new exchange sees a genuinely
   1155       different machine, and only uniform field-level versioning makes
   1156       that computable.  The style already exists as a convention; this
   1157       rule makes it enforceable.
   1158       *Violation:* "since v3" (unparseable variant), missing
   1159       annotations on newer fields.
   1160       *Checked by:* V16 (warn-only until backfilled).
   1161 
   1162 M8.7  Draft APIs carry ``:maturity: draft-no-consumers`` and a visible
   1163       banner; derivation tooling skips them.
   1164 
   1165       *Why:* documented intent is not behavior.  Deriving use cases
   1166       from an API nothing implements manufactures fiction with the
   1167       false authority of a generated artifact.
   1168       *Violation:* a use-case table that includes apns device
   1169       registration as if it were a live flow.
   1170       *Checked by:* V17.
   1171 
   1172 M8.8  The ``ts:def`` layer is the codegen source of truth: type
   1173       definitions must be complete enough to generate client/server
   1174       types and validators without consulting prose.
   1175 
   1176       *Why:* the shape layer is what the spec-first model generates
   1177       code from (§1).  A field that exists only in a sentence ("the
   1178       response also contains X when...") produces a generator-visible
   1179       type that disagrees with the documented behavior — the
   1180       documentation-drift disease, now with a compiler.
   1181       *Violation:* optional fields described only in running text;
   1182       union variants mentioned in prose but absent from the
   1183       ``ts:def``.
   1184       *Checked by:* partially by V4 (every outcome needs a schema);
   1185       fully only by codegen dry-runs diffed against implementations.
   1186 
   1187 
   1188 9. Validation rules (lint specification)
   1189 =========================================
   1190 
   1191 A doc lint (to live in ``taler-docs/_exts/``) enforces, as build
   1192 warnings/errors:
   1193 
   1194 .. list-table::
   1195    :header-rows: 1
   1196 
   1197    * - ID
   1198      - Rule
   1199    * - V1
   1200      - Identity directive present exactly once per documented method
   1201        (variant groups: one directive per variant, §2); options parse.
   1202    * - V2
   1203      - ``:since:`` / ``:deprecated:`` match ``v\d+``; ``:maturity:`` in
   1204        vocab.
   1205    * - V3
   1206      - ``:visible-to:`` present; actors and capability annotations in
   1207        vocab; capability identifiers resolve to identifier anchors.
   1208    * - V4
   1209      - Every ``:http:statuscode:`` has ``:schema:`` and/or ``:error:``;
   1210        non-2xx codes with ``:schema:`` have a ``progress`` or ``gate``
   1211        entry in **Errors**.
   1212    * - V5
   1213      - Every non-2xx outcome has an entry with ``:temporality:`` and
   1214        ``:recovery:`` in vocab.
   1215    * - V6
   1216      - Mutating methods have non-``readonly`` ``:idempotency:`` and at
   1217        least one ``:effect:`` (or explicit "no observable effect" note).
   1218    * - V7
   1219      - Entity names (in Effects, ``:observes-state:``,
   1220        ``:timer-ref:``) resolve to entity anchors; identifier names in
   1221        **Identifiers:** blocks resolve to identifier anchors.
   1222    * - V8
   1223      - State values resolve to the named entity's inferred state set
   1224        (including modifier-expanded sets for wallet transaction
   1225        entities, §6).
   1226    * - V9
   1227      - ``:observes-state:`` path exists in the response ``ts:def`` and
   1228        its type is an enumerated union.
   1229    * - V10
   1230      - ``:timer-ref:`` targets exist among the named entity's declared
   1231        timers; roles in vocab; ``:field:`` present when the role is
   1232        ``arms`` or ``exposes``; every declared timer has at least one
   1233        ``arms`` reference or an ``:armed-by:`` in its anchor.
   1234    * - V11
   1235      - ``:wait:`` present; long-poll parameters match the method's
   1236        documented ``:query:`` parameters.
   1237    * - V12
   1238      - Every real-time channel of a component is declared in that
   1239        component's notification page; no push-channel metadata appears
   1240        in method files.
   1241    * - V13
   1242      - Transition triggers resolve (``call`` → method file, ``timer``
   1243        → declared entity timer, ``watch`` → ``component.observable``
   1244        form); **Effects** transitions and lifecycle transitions agree
   1245        (two-sided check); ``:observable-by:`` refs resolve.
   1246    * - V14
   1247      - No ``TALER_EC_*`` or state value appears only in prose.
   1248    * - V15
   1249      - Filename and location follow §2.2.
   1250    * - V16
   1251      - ``@since`` present on every ``ts:def`` field of public types
   1252        (grandfathered: warn-only until backfilled).
   1253    * - V17
   1254      - ``draft-no-consumers`` / ``proposed-unimplemented`` methods
   1255        render a banner and are excluded from derived artifacts.
   1256    * - V18
   1257      - wallet-core ``:transition:`` values match DD37 notation, resolve
   1258        against the ``TransactionMajorState`` / ``TransactionMinorState``
   1259        enums documented in ``get-transaction-by-id.rst``, and edges
   1260        resolve against the transaction entity lifecycles in the
   1261        registry.
   1262    * - V19
   1263      - Lifecycle integrity: every declared terminal state is reachable
   1264        from ``none`` and absorbing (no outgoing transitions); every
   1265        ``final-reactivatable`` state's outgoing transitions are
   1266        explicitly declared reactivation edges; every inferred
   1267        non-terminal state has at least one outgoing transition; every
   1268        inferred state is reachable from ``none``.
   1269    * - V20
   1270      - ``:if:`` guards reference documented config/data fields
   1271        (best-effort resolvability: ``/keys`` fields, request fields,
   1272        documented config).
   1273    * - V21
   1274      - Identifier joins: every identifier with a ``carries`` /
   1275        ``reads`` tag has exactly one ``creates`` across the corpus or
   1276        an internal-creation note in its anchor; every ``creates`` is
   1277        carried or read somewhere.