# DECISIONS

<!-- markdownlint-disable MD024 -->

Architectural decisions.

This document records load-bearing choices, rationale, and consequences.
Decisions are append-only by convention. Reversing a decision is a breaking
change. Refinements are recorded as amendments to the existing decision, not as
new decisions.

---

## ARPY-D-0001: The accountable-record stage spine is a closed concern set with derived order

### Date

2026-05-25

### Decision

The Accountable Record stage spine is a closed concern set:

ID, CT, SR, EL, CA, EX, RS, GN, DC, plus CM for commands.

Domains and jurisdictions may specialize a concern through owner prefixes,
domain profiles, and internal substructure. They may not add new concerns to
the spine.

Stage order is derived from inter-stage data dependencies, not chosen as a
policy. The gap-of-ten numbering scheme exists only to leave room for future
AR-owned, contract-version-gated concerns. It is not a domain extension seam and
does not imply reorderability.

### Rationale

An open concern set would reproduce the collapse that Accountable Records exist
to prevent: each domain could define "accountable record" differently, making
cross-domain verification unstable.

A closed concern set lets a verifier say that a judicial record and an energy
record are accountable records in the same sense.

Order follows from data dependency. A stage cannot validly read state produced
by a later stage. Reordering is therefore either a no-op or a correctness bug.

Closed-now is reversible through a future contract-versioned widening. Open-now
is not reversible once domains depend on extension points.

### Consequences

- No stage-extension registry, domain-reserved code range, or reordering API
  will be built.
- `@stage` registration must reject any concern outside the frozen spine.
- Tests must assert that registry order is consistent with actual state-slot
  dependencies.
- Contract docs must state that the concern partition is closed and that stage
  order is derived.
- Widening the partition is allowed only through a deliberate AR contract-version
  change.

### Scope note

Internal subdivision of a concern is specialization, not widening. For example,
an EL hierarchy of categories to element types does not create a new concern.
Adding a sibling concern next to EL would be widening and is version-gated.

---

## ARPY-D-0002: The nine spine concerns form a partition

### Date

2026-05-25

### Decision

ARPY-D-0001's closed spine is accepted as a partition to the following standard:

1. Lifecycle closure is CI-testable.
2. SE distinction ownership is explicit and falsifiable.
3. Every transition-owned distinction must be instrumented by a reconciliation
   artifact.

The current proof is sufficient to keep the referenced decision closed.
The unresolved parts are tracked as open items below;
they refine the proof record and do not reopen the referenced decision.

### Partition properties

#### Property A: lifecycle closure

The pipeline is a DAG over `state.*` slots.

- `s000` is the single root and produces the repository slot.
- Only `s000` reads the raw repository filesystem.
- Every later stage reads only prior slots.
- Every slot has exactly one producer.
- Every produced slot is consumed by a later stage or by external verifiers.

This makes lifecycle exhaustiveness and disjointness testable.

#### Property B: distinction ownership

Each SE distinction must name exactly one owning concern, one owning transition,
or one deliberate exclusion.

| #   | Distinction                        | Owner                           | Kind                | Confidence |
| --- | ---------------------------------- | ------------------------------- | ------------------- | ---------- |
| 1   | identity from graph continuity     | ID / s010                       | concern             | High       |
| 2   | attribution from authority         | CT<->SR / s020<->s030           | transition          | Low        |
| 3   | governance status from correctness | CT / s020; correctness excluded | concern + exclusion | Med-high   |
| 4   | evidence from interpretation       | SR<->EL / s030<->s040           | transition          | Low-med    |
| 5   | dependency from validity           | RS / s070                       | concern             | High       |
| 6   | explanation from proof             | GN<->DC / s080<->s090           | transition          | Low        |
| 7   | record from judgment               | architecture-wide               | exclusion           | High       |

#### Property C: transition instrumentation

Every transition-owned distinction must name a reconciliation artifact that
asserts a parity property over that transition.

A transition-owned distinction without reconciliation is a hole, not an
ownership claim.

### Findings

#### Exclusions are part of the partition

Correctness, truth, and judgment are deliberately excluded.
The verifier does not decide domain meaning, legal correctness,
truth, or final judgment.

This is not an omission.
It is how AR preserves the distinction between record
and judgment.

#### Transition ownership is valid only when instrumented

A distinction may be owned by a concern or by a named transition.
Transition ownership is legitimate only when backed by reconciliation.

Confirmed reconciliation mechanisms include later pipeline boundaries such as
CA<->EX, EX<->RS, RS<->GN, and GN<->DC.

#### Early transition reconciliation remains open

CT<->SR and SR<->EL may not yet have reconciliation artifacts.
These transitions own attribution-from-authority and evidence-from-interpretation.
If no artifacts exist, they must be specified.

#### Row 6 is the thin edge

Explanation-from-proof is the most likely place where future domain pressure
could reveal a missing concern: attestation.

No attestation concern is added now. If ever justified, it would be an
AR-level, contract-version-gated concern in the reserved s080-s090 gap.

#### s040 complexity is provisionally internal hierarchy

EL may have internal hierarchy, such as categories to element types or
declaration to classification. This is acceptable if every subdivision remains a
facet of evidence committed into interpretation.

If any subdivision serves a different distinction,
EL is hiding a concern split and ARPY-D-0002 must be revised.

### Consequences

- Implement Property A as a closure and acyclicity test over the slot DAG.
- Check the distinction-ownership table into the contract repo as a reviewable
  artifact.
- Implement Property C as a test asserting that every transition-owned row names
  an existing reconciliation artifact.
- Add a contract-docs section named "Distinctions Preserved by Exclusion."
- Do not prematurely unify reconciliation artifacts into a shared base type.
  Name the concept now; abstract only after the transition shapes are known.
- Cross-stage producer code that realizes transition-owned distinctions lives in
  `accountable_record.transitions` per ARPY-D-0009.

### Open items

1. Audit CT<->SR and SR<->EL for reconciliation artifacts; specify them if
   absent.
2. Revisit GN<->DC first if domain pushback challenges explanation-from-proof.
3. Confirm the actual s040 structure satisfies the same-distinction diagnostic.

### Amendments

#### Amendment 2026-05-26: `s000` is a conceptual bootstrap concern, not a stage package

Property A above refers to `s000` as "the single root" that "produces the
repository slot" and is the only step that "reads the raw repository
filesystem." This amendment clarifies what `s000` denotes, without changing the
partition or the slot DAG it asserts.

`s000` is a conceptual bootstrap concern. It is not a concrete stage package and
does not appear in the `@stage` registry or in `_STAGE_MODULES`. There is no
`stage_000_*` directory. The registered spine begins at `s010`.

The bootstrap concern is currently realized by run-context and repository-profile
construction, before any registered stage runs:

- `PipelineRunContext.from_target` resolves the target root and establishes the
  repository profile from the manifest via `repository_profile_from_target`.
- The resolved `PipelineRunContext` (target root plus `RepositoryProfile`) is the
  fixed run input every stage shares. It is the concrete form of the "repository
  slot" Property A names; there is no `PipelineState.repository` field. The first
  artifact slot a registered stage produces is `identity`, written by `s010`.

This amendment is descriptive only. It does not add a stage, does not move the
bootstrap concern, and does not alter Property A's claims about lifecycle
closure, single slot production, or acyclic stage dependencies.

Those claims hold in the narrower sense intended here: `s000` names the
bootstrap that resolves the target root and repository profile before registered
stage execution. Later stages may read their concern-owned declared inputs, but
they do not re-establish the repository slot.

If a future change promotes the bootstrap to a concrete registered stage, that is
a spine change governed by ARPY-D-0001 (closed concern set, version-gated
widening) and must be recorded as its own decision.

---

## ARPY-D-0003: The `@stage` decorator makes registration structurally authoritative

### Date recorded

2026-05

### Decision

The `@stage` decorator is the source of stage registration.
A registered stage is not merely annotated by the decorator;
the registration is the executable declaration of the stage's
concern, code, produced slots, consumed slots, and
position in the pipeline.

---

## ARPY-D-0004: Diagnostics are keyed by concern

### Date recorded

2026-05

### Decision

Diagnostics are keyed by concern.
Stage-specific code maps local error conditions into
concern-owned diagnostic identifiers rather than making
typed errors carry stage identity directly.

---

## ARPY-D-0005: The Layer 0 / op / validation separation is consistent

### Date recorded

2026-05

### Decision

Low-level filesystem and parsing behavior belongs in Layer 0.
Operations compose that behavior into stage work.
Validation interprets operation results against
contract expectations.

The layers may collaborate, but they must not collapse into one another.

---

## ARPY-D-0006: Typed errors are concern-agnostic kind markers with per-stage mapping

### Date recorded

2026-05

### Decision

Typed errors identify reusable error kinds.
Per-stage diagnostic mapping decides
how a given error kind is reported within a concern.

This keeps common failure modes reusable without making
shared error classes own stage-specific semantics.

---

## ARPY-D-0007: Verifier does not write to target

### Date recorded

2026-05

### Decision

The verifier does not mutate the target repository.
It may read, validate, derive reports, and write outputs
only to verifier-owned output locations.

Target mutation belongs to explicit generation or
repair commands, not to verification.

---

## ARPY-D-0008: `write-lock` is a write-intent command, not a verifier alias

### Date recorded

2026-05

### Decision

`write-lock` is a mutating write-intent command.
It is intentionally routed outside the read-through
verifier aliases and may write `data/locks/` artifacts.

`verify-lock` and `run all` remain read-only verification paths.
They validate existing lock artifacts and must not
create missing locks as a side effect.

`write-lock` uses a runner-level explicit stage sequence
so it can run the prerequisite stages while skipping `s070`,
because `s070` verifies a lock that
does not yet exist during lock writing.

### Consequence

The verifier discipline remains intact:
verification stages do not mutate the target repository.
Lock writing is explicit, named, and command-scoped.

---

## ARPY-D-0009: Cross-stage producers live in transition modules

### Date recorded

2026-05

### Decision

Cross-stage producer operations live under `accountable_record.transitions`.

A cross-stage producer is code that creates an artifact for one concern by
translating material produced by another concern.
It is not owned by either stage package.

Transition producer modules use this naming pattern:

```text
<source-concern>_to_<target-concern>_<artifact>.py
```

For example:

```text
generated_to_resolution_lock.py
```

This module translates s080 generated artifact records into the s070
elements-lock payload.

The transition layer is currently scoped to producers only.
Verify-side reconciliations that are consumed by a
single stage remain in that stage until
there is a demonstrated cross-stage consumer.

### Rationale

ARPY-D-0002 established that some accountable distinctions are owned by
transitions rather than by individual concerns.
This decision gives transition-owned producer code
an explicit architectural home.

The write-side element lock builder is not purely s070,
because it builds a resolution lock from generated artifact records.
It is not purely s080, because it emits the s070 lock schema.
It is a transition producer.

Placing this code in `stage_070_resolution` would require dependency inversion
only to avoid importing a later stage.
That would satisfy the import graph while hiding the
real architectural fact that the code spans concerns.

Placing it in `commands` would make the command own cross-stage artifact
semantics.
Commands may orchestrate writes,
but they do not own transition semantics.

### Consequences

- `commands/write_lock.py` orchestrates the write-intent workflow and writes
  the lock file.
- `transitions/generated_to_resolution_lock.py` builds the lock payload.
- `stage_070_resolution` parses, validates, and verifies existing resolution
  locks.
- `stage_080_generated` collects and validates generated artifacts.
- Cross-stage producers use concrete stage models where needed; no protocol is
  required merely to hide a stage-to-stage import.
- Cross-stage producer code that realizes transition-owned distinctions lives in
  `accountable_record.transitions` per ARPY-D-0009.

Import direction is enforced:

```text
commands -> transitions -> stages -> base/pipeline vocabulary
```

The transition layer may import the stage packages it connects.
Stage packages must not import `accountable_record.transitions`.
Pipeline/base vocabulary must not import transitions.
Transitions must not import commands.

### Import-linter enforcement

Two constraints enforce the rule:

1. `accountable_record.transitions` may import stage packages and their base
   modules.
2. Stage packages, pipeline modules, and base vocabulary modules may not import
   `accountable_record.transitions`; transitions may not import commands.

### Scope note

This decision does not move all cross-stage checks into `transitions/`.

Single-stage-consumed reconciliations remain where they are.
If a reconciliation or transition operation
becomes a reusable cross-stage producer or gains
multiple consumers,
that is the trigger to widen the transition layer's charter
with a new decision.

---

## ARPY-D-0010: Generated element artifacts are currently represented by exports

### Date recorded

2026-05-26

### Decision

In the current contract version, the canonical generated element artifact set is
`data/export/elements/`. Stage 080 (GN) treats the exported element artifacts
discovered by stage 060 (EX) as the generated element artifacts it validates and
reconciles against the stage 070 lock. There is no separate `data/generated/`
tree in this contract version, and the verifier neither reads nor writes one.

Concretely, in the current contract version:

- `stage_080_generated` builds its generated registry from the stage 060
  `ExportsArtifact` registry, not from an independent on-disk walk.
- `GENERATED_DIRNAME` resolves to `data/export`, and the element namespace is
  `data/export/elements/`.
- `GENERATED_ELEMENT_FILENAME` (`element.generated.json`) is reserved structural
  vocabulary for future digest-recomputation support. It is not produced or read
  in this contract version.

A distinct `data/generated/` tree (canonical machine-readable exports under
`data/export/` separated from rendered or derived generated artifacts under
`data/generated/` ) is reserved for a future contract-versioned change. It is
not part of the current contract and must not be introduced as an implementation
detail.

### Rationale

The directory convention as written distinguishes three buckets:
`data/export/` (canonical machine-readable exported artifacts),
`data/generated/` (rendered or derived generated artifacts), and `data/locks/`
(lock, digest, and provenance assertions). In the current pipeline there is one
generated element artifact kind, and its canonical machine-readable form is the
export. Collapsing generated onto export for this contract version keeps the GN
concern honest: it validates a real artifact set that exists on disk, rather than
asserting parity against an empty or absent `data/generated/` tree.

Recording this prevents two failure modes. First, a future reader seeing
`data/generated/` named in older module docstrings could assume the tree exists
and is authoritative; it does not exist in this contract version. Second, a
contributor could introduce `data/generated/` as a quiet implementation choice,
splitting a concern's artifact surface without a contract-version change. Per
ARPY-D-0001, the generated/export relationship is part of the spine's artifact
contract, so changing it is a deliberate version-gated act, not a refactor.

### Consequences

- `stage_080_generated` continues to consume the stage 060 export registry as the
  generated element artifact set. `GeneratedElementRecord` may remain
  structurally equivalent to `ExportElementRecord` in this contract version.
- No `data/generated/` directory is created, read, or written by the verifier in
  this contract version.
- Domain root repositories must not introduce `data/generated/` as part of their
  first vertical slice unless the AR contract version first introduces that
  split.
- Module docstrings that reference re-walking or owning `data/generated/`
  describe a reserved future tree, not current behavior. They are documentation
  drift to be reconciled with this decision, not evidence of a `data/generated/`
  reader.
- Reserved generated vocabulary (`GENERATED_ELEMENT_FILENAME`,
  `GENERATED_DIRNAME` as a name distinct from the EX export root) is retained for
  a future split and marked as reserved-not-used.
- Introducing a distinct `data/generated/` tree, or otherwise separating the GN
  artifact surface from the EX export surface, requires a new decision and an AR
  contract-versioned change. It is not an implementation-only refactor.

### Scope note

This decision does not split generated from export. It records that, in the
current contract version, they are the same artifact set, and that the split is
reserved for a future contract-versioned change. It does not change the spine
(ARPY-D-0001), the partition (ARPY-D-0002), the verifier-no-write discipline
(ARPY-D-0007), or the transition home for the generated-to-lock producer
(ARPY-D-0009).

ARPY-D-0011: The contract-resolution lock and the AR element lock are distinct;
neither subsumes the other
Date recorded
2026-05-26
Decision
Two lock artifacts exist in the AR ecosystem. They answer different questions
over different subjects, and neither is derivable from or a superset of the
other.
The contract-resolution lock is owned by se-contract-kit
(se-contract-kit-lock-1). It records which contract artifacts were resolved,
from where, and under which authority: resolved dependencies (owner, name,
version) and resolved artifacts (artifact id, opaque kind, path, source repo,
authority repo, locality). It is an artifact-resolution and provenance lock over
the contract dependency graph. It does not record digests of AR element content
and does not know AR element, package, catalog, or generated semantics.
The AR element lock is owned by accountable-record-py
(ar-element-lock-1, written by the write-lock command per ARPY-D-0008). It
records which AR elements and packages are included, with what digest and
provenance: per-element and per-package digest assertions, inclusion
relationships, versioned identifiers, and the AR resolution policy under which
they were locked. It is an element-digest and provenance lock over verified AR
content. It does not record contract dependency resolution or authority routing.
Neither lock subsumes the other. The contract-resolution lock answers "what
contract artifacts were resolved, from where, under which authority?" The AR
element lock answers "which AR elements and packages are included, with what
digest and provenance?" A repository may carry both: the resolution lock fixes
the contract inputs, and the AR element lock fixes the verified element outputs.
Rationale
The two locks operate at different layers of the boundary established across the
three repositories. se-contract-kit owns generic contract logistics, so its
lock captures resolution provenance for any SE contract repository regardless of
domain. accountable-record-py owns AR pipeline semantics, so its lock captures
element-digest provenance specific to AR content. Conflating them would force one
of two regressions: either the generic kit lock would have to learn AR element
and digest semantics (violating the kit's generic charter, CK-D-001), or the AR
lock would have to absorb contract-dependency resolution that is not AR-specific.
Recording the distinction before any wiring prevents a predictable failure: once
AR-py consumes the kit's resolution layer, a repository will legitimately hold
two lockfiles, and without this entry a future reader could assume one is stale,
redundant, or authoritative over the other, and "consolidate" them. They are not
redundant. They are two locks because there are two distinct things to lock.
Consequences

The contract-resolution lock and the AR element lock remain separate artifacts
with separate schemas (se-contract-kit-lock-1, ar-element-lock-1). Neither
schema is extended to carry the other's subject.
se-contract-kit's lock treats artifact kind as opaque and records no
element digests. AR-specific digest and provenance assertions live only in the
AR element lock.
The AR element lock continues to be written by the write-lock command and
verified by s070 per ARPY-D-0008. This decision does not change how, when, or
where the AR element lock is produced or verified.
A repository carrying both locks is expected, not anomalous. Tooling and
reviewers must not treat the presence of a contract-resolution lock as a
duplicate of, or substitute for, the AR element lock, or vice versa.
Merging, unifying, or making one lock derive from the other is a cross-repository
contract change requiring decisions in both se-contract-kit and
accountable-record-py. It is not an implementation-only refactor.

Scope note
This decision documents a boundary that already exists in the published
artifacts. It does not wire AR-py to the kit's resolution layer, does not change
s060, s070, or any discovery logic, and does not move where either lock is
produced. It records what each lock is and that they are distinct, so that
subsequent wiring decisions start from a stated boundary rather than an implicit
one.

ARPY-D-0012: AR-py may extend the kit's generic verifier frame; AR-specific checks live in AR-py
Date recorded
2026-05-26
Decision
se-contract-kit may own a generic verifier frame: a check-runner, an immutable
check registry, a fixed set of domain-agnostic default checks, and a consumer
extension seam. It must not own AR-specific verifier checks. The kit's checks
must be true of any SE contract repository regardless of domain, and the kit must
treat artifact kind as opaque except for the generic contract kinds it
legitimately recognizes (such as schema, vocabulary, and mapping kinds, which are
checkable without domain knowledge).
accountable-record-py may extend the kit's registry with AR-specific checks.
AR element, catalog, export, generated, and lock checks — every check that
depends on AR pipeline semantics — remain in accountable-record-py and are
appended to the kit's default registry through the kit's extension point. They
are never added to the kit.
This revises the earlier framing that se-contract-kit "should not become a
second verifier." The accurate rule is narrower: the kit may own the generic
verifier frame; it must not own AR-specific checks. A generic frame plus
consumer-supplied domain checks is the intended design, not a boundary violation.
Rationale
se-contract-kit already ships a generic verifier frame — a runner, a registry,
default checks, and an extension seam — and its default checks are domain-agnostic
by construction (existence-and-parse, valid-schema-shape, and mapping closure over
resolved vocabulary), with artifact kind carried as an opaque string rather than
interpreted as AR meaning. This is the good extraction: generic contract-handling
machinery that AR happened to need first, factored so any SE contract repository
can use it. The kit's own decisions already state this (CK-D-001: own generic
machinery, not contract content; CK-D-005: the registry is the extension point).
The boundary that matters is not "frame versus no frame" but "generic versus
AR-specific." A check that asks "do resolved artifacts exist and parse" is generic
and belongs in the kit. A check that asks "does this AR element have a complete
declaration quartet" or "does the catalog reconcile against discovered exports" is
AR-specific and belongs in AR-py. Placing AR-specific checks in the kit would make
the generic substrate depend on AR meaning, breaking its reuse by other SE contract
repositories and collapsing the separation this entry preserves.
Consequences

AR-specific checks (element, catalog, export, generated, lock, and any other
check depending on AR pipeline semantics) are defined in accountable-record-py
and registered by extending the kit's default registry. They are not contributed
to se-contract-kit.
se-contract-kit's default check set remains domain-agnostic. If a proposed kit
check cannot be stated as true of an arbitrary SE contract repository, it does
not belong in the kit.
Artifact kind remains opaque to the kit except for generic contract kinds. AR
kind semantics are interpreted only in accountable-record-py.
The kit's verifier frame and AR-py's AR checks compose through the registry
extension seam. AR-py does not fork, copy, or shadow the kit's default checks; it
appends to them.
Whether and where AR-py actually adopts the kit's frame in place of its current
in-repo validation is a separate wiring decision, not settled here.

Scope note
This decision documents the verifier-frame boundary and revises earlier wording.
It does not wire AR-py to the kit's runner or registry, does not migrate any
existing AR-py validation onto the kit, and does not change s060, s070, or
discovery logic. It records that the kit may own a generic frame and must not own
AR checks, and that AR-py extends rather than forks that frame — so that any later
adoption proceeds from a stated boundary.
