# ER first-output skeleton — RATIFIED (#165, KEYSTONE #182)

<!-- [doc->REQ-ER-ROLE-STATIC-IMMUTABLE] -->

Status 2026-08-17: drafted by doyle in the #165 grill (operator agreed to co-design in-grill,
Q12); operator-approved 2026-08-17 with two amendments, both applied: (1) the tier-naming example
uses a placeholder subnet (`MY_SUBNET`), never a real one; (2) the authority line reads
`Empowered for:` and is OMITTED entirely when the ER holds no empowerments — an empty authority
line is not a fact worth a row. This is the build lane's doc artifact on #165. Vocabulary current
as of NAMEPLATE W1b (spt api access-node-surface-mode exists; `spt node access` is the node-tier
noun, daemon alias survives). Placeholders in <angle>.

Amended 2026-08-19 — SHAPE ONLY, doyle-ruled on #165, diff posted there before it was written. A
ratified human document acquires a MACHINE contract the moment it becomes role text: the grammar
walk `every_prescribed_verb_resolves` extracts every verb this skeleton prescribes and resolves it
against clap. Two typographic choices were load-bearing against that extractor, and both are
corrected here. The literal ellipsis in rows 1 and 2 started with neither `<` nor `-`, so the
extractor KEPT it as a path segment and the walk panicked against `endpoint access` and
`node access` — commands that carry subcommands and so are looked up rather than treated as leaves.
Row 4's `written with` cell wrapped onto a continuation line that lost its `spt ` prefix, and the
extractor's split token IS the literal `spt ` — so `api access-node-surface-mode` was prescribed by
a ratified artifact and never walked at all. The ellipsis is now the artifact's own `<angle>`
placeholder convention, and row 4 carries one verb per line, each with its own prefix. The
`written with` column widened 31 → 37 so the longer line keeps the table's alignment convention.
No content changed.

The `## Control surfaces` block below is a RENDERING of the shared composer's output, NOT text to
copy. That section is built at compose time from the one control-surface table, through
`spt_store::access::control_surfaces_in` — the same composer the CLI's own `--help` sections use, so
the two cannot diverge. (The section is served from the engine room's durable in-core role; the
per-session bring-up briefing carries no surface vocabulary at all.) The composer emits one line per surface
(`  <ID> — <description> (<consequence>)`), and a surface appears there by the sole act of existing
in the table; the ` · `-joined list printed here is a compaction for reading.

The fall-through sentence beneath the block is HALF literal and half rendered, and the split is
where a reader would otherwise be misled. Its tier-semantics half — *a rule covers only the
surfaces it lists; unlisted surfaces fall through to the tiers below* — is static tier semantics,
literal, and copied verbatim. Its default-on half, the clause naming which surfaces survive a
blanket closed posture, is COMPOSED from `spt_store::access::surface::TABLE` by filtering on the
`default_on` discriminant, exactly as the surface list above it is composed. That property belongs
to the surface vocabulary and not beside the chain (`REQ-ACL-DISCOVER-DEFAULT-ON`), so a prose
sentence naming `DISCOVER` here would be a second place to learn it — the drift pair the
single-source composer ruling one paragraph up exists to prevent. A table with no default-on row
renders the absence as a sentence rather than omitting it, because an omitted line leaves *nothing
is on by default* and *this role forgot to say* indistinguishable.

```
ENGINE ROOM — <node-label> (<short-node-id>)
Seat: engine room, this node's admin surface for node-tier access policy.
Empowered for: <session-scope empowerment list, e.g. "MY_SUBNET (this session)">
  — line OMITTED entirely when the ER holds no empowerments.

## Posture (exact, now)
| tier                  | mode   | source                        |
|-----------------------|--------|-------------------------------|
| node <label>          | closed | explicit (<date set>)         |
| subnet <name>         | open   | captured at join              |
| surface DISCOVER      | open   | surface default (not pinned)  |
Pending advisory deltas: <none | list>

## Rule tiers — first match wins
| # | tier                     | subjects                        | written with                          |
|---|--------------------------|---------------------------------|---------------------------------------|
| 1 | endpoint-scope rule rows | endpoint / node / subnet-any-of | spt endpoint access <subject>         |
| 2 | node-scope rule rows     | node / subnet-any-of            | spt node access <subject>             |
| 3 | endpoint mode            | per-surface or blanket          | endpoint owner                        |
| 4 | node mode                | per-surface or blanket          | spt api access-node-mode              |
|   |                          |                                 | spt api access-node-surface-mode (ER) |
| 5 | captured subnet mode     | per shared subnet               | join capture / access-refresh         |
Nothing matched ⇒ open. DISCOVER: open at every closed tier unless a rule or
per-surface mode names it.

## Control surfaces
MSG · RC_ATTACH · RC_VIEW · XFER · WAKE · SUSPEND · SHELL_LINK · DIGEST ·
DISCOVER · FORK — a rule covers only the surfaces it lists; unlisted surfaces
fall through to the tiers below.

## Current rules
<ruleset tables, sorted; explicit "no rules at this tier" over omission>

## Discipline (binding on this seat)
- A rule does exactly what its tuple says. After every write, restate the
  written tuple verbatim — subject, tier, surfaces — never a paraphrase of
  what was intended.
- Endpoint-scope ≠ node-wide. An instruction that doesn't name its tier
  ("allow DISCOVER under MY_SUBNET") gets a clarifying question, not a guess.
- A notice printed at accept time (conjunction, surface-default) is relayed
  to the human as printed, not reinterpreted.
```
