Skip to content

The verb registry gains a `seamTier` carrier field — IR-002 step 2, blocked on step 1's ratification

Date: 2026-09-12 Status: closed — ratified 2026-09-13 Supersedes: none Superseded-by: none — current

irina/SYNTHESIS.md §1.1 found that FR-003’s seam-class ceilings have no carrier anywhere in this repo: the verb envelope (spec/verbs/_common.schema.json Envelope) carries {verb, target_repo, idempotency_key, dry_run, actor, triggered_by} and nothing else, the registry row (registry.yaml: slug, url, default_branch, contract_sha, role, profile, fleets_allowed) carries no class axis, and host/src/ registry.ts’s PetrovaTool interface carries only mutating. A ceiling that is ruled in prose and attaches to nothing is not enforced by anything — the same shape mary-wiki F-097 found in a check that could never fail.

docs/decisions/2026-09-12-seam-class-taxonomy.md (IR-002 step 1, amended 2026-09-12 to reconcile against mary-wiki PR #653) names the taxonomy this field carries (AT0–AT3) and rules the assignment mechanism: a verb’s tier is set by a named, dated decision document, and the verb’s own schema PR cites that decision by id rather than proposing a tier inline. That doc’s first sign-off box is what this act depends on. This doc is IR-002 step 2 — the schema act itself — drafted now, per FR-017’s direction (justification authored before the act), but it may not be applied until step 1’s first box is ticked, because applying a carrier for a taxonomy that isn’t yet ratified would let the schema act itself stand in for the ratification it’s supposed to consume.

One canonical home, not two. host/src/registry.ts’s own header already documents three prior forks of this exact file, each caused by a second copy of tool metadata drifting from the first. PetrovaTool gains two optional fields, following the same shape as the existing mutating flag rather than duplicating anything into spec/verbs/<verb>.schema.json as a second, separately-maintained copy:

export interface PetrovaTool {
name: string;
description: string;
schema: Record<string, unknown>;
handler: ToolHandler;
mutating?: boolean;
/** AT0-AT3, per docs/decisions/2026-09-12-seam-class-taxonomy.md. Required
* when mutating is true; absent only for read tools (implicitly AT0). */
seamTier?: "AT0" | "AT1" | "AT2" | "AT3";
/** Path to the decision doc that assigned this tool's seamTier. Required
* whenever seamTier is set. A tier with no citation is not a grant. */
authorizingDecision?: string;
}

spec/verbs/_common.schema.json gains a matching $defs.SeamTier (enum ["AT0","AT1","AT2","AT3"]) and $defs.AuthorizingDecision (a string pattern requiring a docs/decisions/*.md path), for schemas that need to reference the type — but the registry.ts entries remain the source of truth an enforcing test reads, exactly as mutating is today. A verb schema file may echo its tier for human readability; it never sets it independently.

Enforcement, extending the existing test rather than adding a parallel one. host/tests/registry.test.ts already has "flags every mutating tool, including the one with no act. prefix" (:46). A new case in the same file asserts, for every tool where mutating === true: seamTier is set, authorizingDecision is set, and authorizingDecision resolves to a file that exists in docs/decisions/ at the checked-out ref. This is a narrowing — it cannot fail a build today because no tool carries either field yet — so it does not engage FR-017’s widening test on its own; populating the four tools the seam- class doc names as examples (open_decision, update_milestone, registry_edit/phase transitions, sweeps_trigger) is a separate, subsequent act once step 1 is ratified, not bundled into this one.

What this act explicitly does not do. It does not populate any tool’s seamTier — every entry in TOOLS keeps seamTier and authorizingDecision absent until a decision names it. It does not implement min-of-both composition with registry profile — that remains docs/decisions/2026-09-12-seam-class-taxonomy.md’s second, separately-gated sign-off box, itself withheld pending a named falsification. It does not touch registry.yaml or .petrova/ contract.yaml — the taxonomy doc’s Context surveyed both as candidate carriers and the seam-class doc’s Decision section places the tier on the verb, not the repo, so neither file needs a new column for this.

  • Carry seamTier in spec/verbs/<verb>.schema.json only, per the seam- class doc’s literal wording (“spec/verbs/<verb>.schema.json gains a required seam_tier field”) — rejected: that file already documents itself as “the contract the CLI and Fleet MCP host implementations compile against,” separate from registry.ts’s TOOLS array, and registry.ts’s own header names three past forks caused by exactly this shape (two lists of the same metadata, kept in step by hand). Mirroring mutating’s existing home avoids manufacturing a fourth fork.
  • Per-invocation credential minting keyed to (verb, target_repo, scope), per the earlier cross-system synthesis stream that reviewed this proposal — not rejected, deferred: that is a materially larger build (a minting authority, a verification step at call time) and the seam-class doc’s ratified assignment mechanism is static, per-verb, set-at-schema-time. A credential-scoped carrier is a candidate replacement for this field once min-of-both and the wider autonomy-ladder ruling (mary-wiki F-072) are further along, not a reason to withhold the simpler static carrier now.
  • Apply this act immediately, ahead of step 1’s ratification — rejected: the fields would exist with nothing yet permitted to set them meaningfully, and a later session finding seamTier fields already wired could read that as evidence the taxonomy was already load- bearing, which is exactly the “narrow ruling reads as complete” shape mary-wiki F-100 names. Drafted now, applied after step 1 signs.
  • Fold this into step 1’s document as one combined act — rejected: the seam-class doc’s own Consequences section says steps 2-4 are “unblocked once the taxonomy/assignment box below is ticked,” naming them as separate acts; bundling would make the taxonomy ruling and the code change one thing to countersign instead of two, weakening what the human is actually attesting to at each step.

For code: none yet. This document specifies the PetrovaTool field addition, the _common.schema.json $defs addition, and the registry.test.ts extension; none is implemented pending sign-off, and implementation is blocked a second time behind 2026-09-12-seam-class-taxonomy.md’s first countersign box independent of this doc’s own.

For docs: irina/tickets/IR-002-seam-class-sequence.xml step 2 is this document; steps 3 (validator: does a schema PR’s cited decision actually assign the tier it claims) and 4 (population: assigning seamTier to the four named verbs) remain separately gated and are not scoped here.

For in-flight phases: none.

For invariants: none changed by this doc alone. Once applied, it gives FR-003’s ceiling a field to attach to for the first time in this repo, but attaches no ceiling to any verb — that is steps 3 and 4’s work, each its own decision.

  • docs/decisions/2026-09-12-seam-class-taxonomy.md (IR-002 step 1 — this act’s precondition; specifically its Assignment authority section and first sign-off box)
  • mary-wiki PR #653, docs/decisions/2026-09-12-autonomy-tier-taxonomy-drafted-f-072.md (the reconciling AT0-AT3 taxonomy this field carries)
  • irina/SYNTHESIS.md §1.1 (source of the “no carrier” finding)
  • irina/tickets/IR-002-seam-class-sequence.xml (this doc is step 2 of 4)
  • host/src/registry.ts (three prior forks of this exact file, cited as the reason this doc keeps one canonical home rather than a second list)
  • host/tests/registry.test.ts:46 (the existing mutating enforcement test this doc extends rather than parallels)
  • Subagent: claude-sonnet-5 (session_01ASqvWd4BQWQfLQEqSgjEDz)
  • Human: alex@devarno.com (2026-09-13) — ratifies the seamTier/authorizingDecision field shape and its home in host/src/registry.ts. Precondition met: step 1’s first box ticked 2026-09-13.