Examples are useful until people start treating them as law.

Then they become folklore.

That is not a complaint about YAML. YAML did its job. It gave NodalArc a human-readable artifact that a network engineer could inspect, a generator could emit, a wizard could produce, and git could keep honest. post 006 (One Idea Per File) and post 007 (The YAML Is The Contract) were the right foundation.

But post 010 (Worlds, Not Constellations) changes the load on that foundation. A config shape that works for one Earth LEO run starts to creak when the same session contains MEO, HEO, GEO, multiple LEO constellations, a lunar orbiter, a surface site, and a cislunar relay path.

Loose examples do not scale. The configuration grammar had to become more formal so the system could expand without turning every new orbital regime into a private exception.

One primitive, one grammar

The rule is simple enough to write on a shop wall.

Every primitive has one grammar.

A terminal is a terminal whether it lives in the shipped catalog, a user catalog, an inline experiment, or a file generated by a wizard. A site is a site whether it is on Earth or Luna. A node is a reusable platform until something places it. A constellation is a generated set of space nodes, not a routing policy with wings.

That sounds obvious. Most good rules do, right up to the moment somebody breaks them.

The catalog holds reusable parts: bodies, terminals, orbits, payloads, nodes, sites, site sets, constellations, individual space nodes, space-node sets, and sessions. A session assembles those parts into one run.

Do not blur that line. The moment a constellation starts carrying ground policy, the sky owns the exits. The moment a terminal starts carrying role, a physical device has been confused with how one node mounted it. The moment a session grows a private abbreviated terminal shape, the runtime now has two truths for the same object.

Two truths is one too many.

A reference is not an escape hatch

The grammar defines objects. The loader finds them.

That distinction carries real weight. A field that expects a Terminal may contain a full terminal object or a reference token that resolves to one. Either way, after loading, the runtime sees a terminal that passed the terminal grammar.

Catalog object
terminal:
  id: optical-cislunar-relay
  display_name: Cislunar optical relay terminal
  medium: optical
  signal:
    wavelength_nm: 1550
  bandwidth_mbps:
    transmit: 2500
    receive: 2500
  tracking_capacity: 1
  max_range_km: 450000
  limits:
    azimuth_deg: {min: -180, max: 180}
    elevation_deg: {min: -80, max: 85}
    max_tracking_rate_deg_s: 1.0
  reference: https://example.invalid/source
Reference use
terminals:
  - id: luna_crosslink
    role: crosslink
    terminal: user:terminals/optical/optical-cislunar-relay.yaml
    count: 2
    tags: [relay, cislunar]

The reference is not a second schema. It is not a permission slip to omit range, bandwidth, tracking capacity, or angular limits. It is only a way to avoid copying the object.

That is how the same primitive can be used by a hand-authored file, a catalog entry, a wizard, and a generator without the runtime learning four dialects.

The terminal does not know its job

A terminal primitive is physical truth. It says what the device can do. Medium. Signal. Directional bandwidth. Tracking capacity. Maximum range. Angular limits. Reference data.

It does not say whether the device is access, inter-satellite, crosslink, relay, or backbone. That is deployment truth. It belongs to the mount.

node:
  id: geo-relay-router
  forwarding: routed
  ethernet: []
  payloads: []
  terminals:
    - id: earth_access
      role: access
      terminal: nodalarc:terminals/rf/rf-ka-gateway.yaml
      count: 2
    - id: luna_crosslink
      role: crosslink
      terminal: user:terminals/optical/optical-cislunar-relay.yaml
      count: 2

That separation is not academic. A mount role is a matching key. Physics still decides whether the link can close. Label a terminal crosslink and the endpoint selector can find it. The label does not extend its range by one kilometer. It does not widen its field of regard by one degree. It does not give it another tracker when the count is already consumed.

A site can narrow a mounted terminal's installed capability. It cannot make the terminal better than the primitive says it is.

nodes:
  - id: goldstone-relay
    model: user:nodes/geo-relay-router.yaml
    terminals:
      luna_crosslink:
        installed_count: 1
        capabilities:
          max_range_km: 400000

If the primitive tops out at 450,000 kilometers, an install can limit it to 400,000. It cannot claim 600,000. That would turn a site file into a magic wand, and magic wands make rotten measurements.

Physical truth and deployment truth

Network engineers already know this split in other clothes. An optic has a reach and a speed. A switch port has a role in a design. The optic does not become a core port because somebody put it in the core. The port role changes how the design uses the optic. It does not change the glass.

NodalArc applies the same rule to orbital terminals. The terminal primitive describes the device. The mount and the site installation describe how that device is used in one node and one place.

Closed words where behavior branches

Some fields are open labels for humans. Some fields are closed vocabulary because the engine branches on them.

rf and optical are not vibes. They select different signal models. access, isl, crosslink, and backbone are not prose. They decide which terminal mounts are eligible for a rule. visible_candidates, nearest_n, and explicit_pairs are not comments. They select topology behavior.

If the engine branches on a value, the value belongs to a closed vocabulary. Unknown values fail structural validation. Known but unsupported values fail semantic validation with UnsupportedFeature. Those are different failures, and the difference matters.

Tags are not that.

tags: [earth, geo, relay, cislunar]

Tags are open labels. They organize, group, and select. The engine must not secretly decide that relay means a special link class or that cislunar changes latency treatment. A tag can put a node into a selected set. It cannot change physics.

Selectors make the set logic explicit.

select:
  all:
    - segment: earth_ground
    - tag: gateway

terminal:
  all:
    - role: access
    - medium: rf

all means intersection. any means union. not means complement. A YAML list does not secretly mean OR. A YAML map does not secretly mean AND. The author says what the set operation is, and the resolver checks that it resolves to something real.

The failure belongs before deploy

Permissive parsers make demos easy. They make labs untrustworthy.

If a selector names a tag that matches no nodes, fail. If a link rule asks for RF access terminals and the selected nodes only mount optical crosslinks, fail. If a site installs more terminals than the node model has, fail. If an installed terminal widens the primitive's range, fail. If a runtime-future branch is legal grammar but unsupported today, fail with the right typed error.

Do not run the cluster and hope the operator notices. Do not quietly produce no links and call it a result. Do not let a typo become a clean graph.

The cheapest failure is before deployment. The expensive failure is after the user has collected data from an experiment that never meant what it claimed to mean.

This is why the wizard matters. Not because clicking is nobler than typing. It is not. The wizard matters because it can run the same feasibility and validation work before pods start. A CI generator can do the same thing. A direct YAML upload can do the same thing. Same grammar, same resolver, same refusal to let bad assumptions turn into measurements.

More formal, more room

There is a trap in configuration design. People hear "more formal" and think "less flexible." That is backwards here.

The old loose shape was flexible the way wet cardboard is flexible. You can bend it around anything once. Then it loses its shape.

The stricter grammar is what lets NodalArc expand. A lunar site is still a site. A Mars site can be a site when the runtime is ready. A cislunar optical terminal is still a terminal. A GEO relay is still a placed space node or a member of a space-node set. A routing boundary still rides over a physical link rule instead of pretending the physical rule is routing policy.

The mental model stays intact while the geography grows.

That was the gap after the first YAML posts. We had the right instinct: separate the ideas and keep the session as the artifact. What we learned is that instinct needs teeth. The object grammar is those teeth.

The grammar is not about tidiness.

It is about preserving the name of the thing that changed.

What's next

post 012 (Custody Is Not Connectivity) takes the grammar to the Earth-Luna gap, where a bundle relay discovered that TCP believes the far end exists right now, and that at four hundred thousand kilometers that belief stops being an implementation detail and starts being a lie.