# Shape and name detection

This is the detail behind layers 2, 3 and 6 of
[Mapping imported types to standard types](where-packages-come-from.md#mapping-imported-types-to-standard-types),
for fields: how a provider's fields that together make a standard type,
such as an amount and a currency, are read as that type, and which
fields the importer asks you to check. Layers 4 and 5 are under
[The scoped review and unconfirmed operations](the-scoped-review-and-unconfirmed-operations.md).

- **How a provider's shape is decoded at the edge: `wire_parts:`.**
  Stripe's `amount: 1050, currency: "gbp"` decodes to `Money`, and no
  other decoding could do it: `wire:` and `decode:`
  read one member, and `Wire.minor_units` needs one fixed currency. A
  field's `wire_parts:` names, for each part of its standard type, the
  member of the object holding the field it is read from and written to;
  the field has no member of its own on the provider's wire, and is
  written as the contract writes it everywhere else. It is for the
  standard types made of named parts (`Money`'s `amount` and `currency`,
  `InstantRange`'s `start` and `end`, and the others sent as an object of
  members) and a `Point`, by `latitude` and `longitude`, which GeoJSON
  sends as a pair. Every part is spelled, as text, once
  (`package.wire_invalid`), never beside `wire:`, `decode:` or `encode:`.
  Fields may share a part's member, as a charge's amounts share its
  currency, but a field of its own can't take one
  (`package.wire_name_invalid`); written, two values that share a member
  and differ stop the call as one that doesn't encode, naming the member.
  None of a field's members sent is `none` where the field may be; some
  missing is a fault naming each. It follows `wire_name:`'s shape.
- **What the importer maps by shape.** Only what the shapes
  `type.standard_exists` matches name, and only where nothing is guessed
  but the units: a whole number named as `Money`'s amount (`amount`,
  `value`, `cents`, `pence`, `minor`, `amount_minor`) beside a currency
  (`currency`, `currency_code`) of the same optionality is `Money` read
  from both, the currency's field and its own enumeration gone; a
  latitude beside a longitude (`lat`, `lng`, `lon`) of the same
  optionality is a `Point` named `location` (`position` or `point` where
  that is taken). An object that is only an amount in whole numbers and a
  currency, or only a `start` and an `end` that are instants, each
  required and spelled as the contract spells it, is `Money` or
  `InstantRange` wherever it is used, and isn't declared, since nothing
  needs decoding. Any other object of a standard shape is left to
  `type.standard_exists`, whose swap needs a decoding an agent writes.
- **What it leaves to check: `package.standard_candidate`.** A whole
  number named like a time (`created`, `updated`, `deleted`, `expires`,
  `timestamp`, `time`, or ending `_at`, `_time` or `_timestamp`) may be an
  `Instant` sent as seconds or milliseconds; one named like an amount
  (`amount_` or `_amount` besides the shape's names) beside a currency
  may be `Money`; neither is swapped, since only the documentation says
  which. Each is a declinable error on the field, in a provider package's
  public objects, until it says how it is sent or is declined with a
  reason; so is a latitude beside a longitude that isn't mapped. A number
  named like a time that is text (`"2026-10-20"`) isn't flagged: the page
  names whole numbers, and a format says when text is a time.
- **Fakes.** A generated default answer writes a field's parts as their
  members, and a `Money` as the contract sends one, a whole number of
  minor units in the currency its type fixes or `GBP`.
- **The summary.** The status grid counts the fields mapped `by format`
  and `by shape`; "Mapped by shape" lists each shape mapping and how it is
  read, since each is a judgement, where a format's is only counted; each
  guess is under "To check" with its `(agent)` step. `--json` gives every
  mapping as `mapped`, with `at`, `standard`, `by` and the members it is
  read `from`.

The import's own output, for a payment provider's document:

```text
$ jadpo package import openapi tidepay.json
✓ Imported tidepay.json into shared/providers/tidepay/v2024_06_20
    package     Tidepay.v2024_06_20
    read        from its schemas
    resources   charges
    operations  1
    types       3
    standard    4 by shape

Mapped by shape
  Charge.amount  Money, read from `amount` and `currency`
  Charge.fee     Money, sent as the standard type
  Charge.window  InstantRange, sent as the standard type
  Shop.location  Point, read from `lat` and `lng`

To check
  May be `Money` in minor units, its currency in `currency`: check the
  provider's documentation, then make it `Money` read from both with
  `wire_parts:`, or decline `package.standard_candidate` with the reason
  (agent)
    Charge.amount_refunded

  May be an `Instant` sent as whole seconds: check the provider's
  documentation, then make it `Instant { wire: Wire.unix_seconds }`, or
  decline `package.standard_candidate` with the reason (agent)
    Charge.created

✓ imported  1 operation · 3 types · 4 to check

→ jadpo check  checks the package with the app
```

(The provider's two findings, its sandbox host and its failures, are left
out above.)
