lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

AGENTS.md (27148B)


      1 # Radroots Core Libraries - Agent Specification
      2 
      3 See [CONTRIBUTING.md](CONTRIBUTING.md) for the contributor workflow and
      4 [AGENT_INSTRUCTIONS.md](AGENT_INSTRUCTIONS.md) for extended execution detail.
      5 
      6 This file exists for compatibility with tools that look for AGENTS.md.
      7 
      8 ## 1. Scope and hierarchy
      9 
     10 - This file applies to the full repository.
     11 - Keep this file concise and durable.
     12 - Put detailed procedures, examples, and extended guidance in `AGENT_INSTRUCTIONS.md`.
     13 - If a closer directory-level `AGENTS.md` is added later, it overrides this file for that subtree.
     14 
     15 ## 2. Source of intent
     16 
     17 - Read `contracts/crates/release.v2.toml`,
     18   `contracts/crates/release_v1/radroots_crates_release_v1.toml`, and
     19   `contracts/crates/catalog.v2.toml` before changing a public package, package
     20   identity, dependency, feature, or release control.
     21 - Machine contracts under `contracts/**` are the standalone authority. Human
     22   specifications, decisions, runbooks, and qualification evidence belong
     23   under the parent monorepo's `docs/oss/lib/**` authority and must never become
     24   a standalone build, test, package, or release input.
     25 - The pre-implementation service-event reservation is
     26   `contracts/architecture/decisions/services_hardening_events.v1.json`.
     27   Service-event source, registry, generated, and consumer work must implement
     28   that exact kind, tag, cardinality, query, and supersession contract; it may
     29   not reinterpret the reservation from current prototype wire behavior.
     30 - The pre-implementation local-admin, process-exit, doctor, readiness,
     31   peer-credential, systemd, and bare-Rust host decisions are reserved by
     32   `contracts/architecture/decisions/services_hardening_host.v1.json`.
     33   Service-host and service-owned operator contracts must implement or narrow
     34   that boundary without adding a second transport, exit map, or readiness
     35   authority.
     36 - Source-lock consumer identities include `sdk`, `myc`, and `rhi`.
     37   Only `sdk` is a generated-artifact product identity;
     38   accepting a service consumer marker must not expose an artifact route.
     39   Tera owns its application packages and native/WASM artifact routes. Lib must
     40   not regain an application package or a dependency on the Tera application.
     41 - The canonical service source lock is the bounded, canonical
     42   `radroots.service.source-lock.v2.toml` model. It binds the exact active public
     43   Lib repository and full revision, Lib source-archive and workspace-catalog
     44   digests, the service `Cargo.lock` digest, Rust `1.97.1`, the `service-host`
     45   feature profile, positive config, state, admin, status, and provider contract
     46   versions, and an exact closed Nix-material state. `absent` requires both Nix
     47   files to be absent. `deferred` independently binds an exact mutually
     48   consistent `flake.nix` and `flake.lock` revision and digest without claiming
     49   Nix qualification or active-revision alignment. Keep the model and
     50   diagnostics private to repo tooling, reject noncanonical or extra fields,
     51   and never put credentials, local paths, floating refs, or private repository
     52   identity in it.
     53 - Generate or verify that lock with `cargo xtask service-source-lock --mode
     54   write|check --service-root <absolute-directory> --source-archive
     55   <absolute-bundle>`. The service root supplies the exact
     56   `workspace.metadata.radroots.service_source_lock` Cargo metadata. Every Lib
     57   dependency, the Cargo lock, source archive, and canonical public remote must
     58   agree on the active revision. Deferred Nix inputs must agree with each other
     59   and remain remotely reachable, but need not equal the active revision before
     60   terminal Nix alignment. The command rejects every source-tree change except
     61   the exact generated lock path.
     62 - Generate or verify one immutable service release artifact set with `cargo
     63   xtask service-release-artifacts --mode write|check --service-root
     64   <absolute-directory> --input-root <absolute-directory> --output-root
     65   <absolute-directory> --target <rust-target> --source-date-epoch <seconds>`.
     66   The command consumes the exact fixed input inventory, validates clean and
     67   stable service and Lib source bundles, and emits the canonical binary
     68   archive, OCI/source metadata, CycloneDX SBOM, notices, manifest, unsigned
     69   provenance signing input, and checksums. Signing credentials and signatures
     70   remain external; generated artifacts must contain no protected material.
     71 - The native shared-build qualification contract is
     72   `contracts/architecture/decisions/services_hardening_build_qualification.v2.json`.
     73   It freezes the supported Rust targets, standalone Cargo and xtask commands,
     74   native release evidence, and the fixture agreement among Cargo metadata,
     75   the source lock, and release metadata. Nix package/app/check, development
     76   shell, NixOS-module, and Nix-produced OCI outputs are explicitly deferred
     77   and are not qualified by that contract.
     78 - Current source and tests are implementation evidence. They do not silently
     79   override `radroots.crates.release.v1`.
     80 - Record any evidence-based plan deviation in
     81   `contracts/architecture/deviations.toml` before proceeding. Validate it with
     82   `cargo xtask architecture`; a normative architecture exception also requires
     83   the applicable machine decision under `contracts/architecture/decisions/**`.
     84   Deviation anchors must resolve the Release V1 TOML through a validated
     85   selector: `repositories.<name>`, `repository_policy`, `release_policy`,
     86   `quality_policy.coverage`, or `package.<name>`.
     87 
     88 ## 3. Repository operating model
     89 
     90 - This is a public open-source library workspace; optimize for durable library design, portability, determinism, and explicit contracts.
     91 - Keep release and validation automation forge-agnostic; repo-owned xtask
     92   commands, native Cargo lanes, tags, and contract metadata are canonical,
     93   while committed provider-specific workflow automation is not. Checked-in
     94   Nix surfaces are deferred compatibility inputs, not current qualification
     95   authority.
     96 - Do not add or retain tracked `docs/**`, `.github/**`, or `.act/**` content.
     97   Keep validation forge-agnostic. Any required monorepo orchestration belongs
     98   exclusively to the parent repository's root `.act/**` authority and must not
     99   be copied into this standalone capsule.
    100 - Prefer clean target-state changes over compatibility scaffolding unless compatibility is explicitly required.
    101 - Stay within the requested scope and the smallest coherent file set.
    102 - Do not fold unrelated cleanup, speculative refactors, or roadmap work into the same change.
    103 - Do not create hidden task trackers in markdown checklists, source comments, or stray notes.
    104 - Keep commits and handoff language standalone and open-source-readable; do
    105   not reference non-public repository paths, internal mapping rationale, or
    106   private repository context.
    107 
    108 ## 4. Preflight before edits
    109 
    110 Before editing code:
    111 
    112 - Read this file, `AGENT_INSTRUCTIONS.md`, and `README`.
    113 - When preserving deferred Nix behavior, read `flake.nix` and the relevant
    114   implementation files under `build/nix/`, but do not install, invoke, or
    115   require Nix as part of current qualification.
    116 - Run `cargo extbuild doctor` before the first governed build, test, check,
    117   generation, package, artifact, or release-preflight command, then route the
    118   command through `cargo extbuild run --`.
    119 - Discover commands from checked-in repo surfaces; do not invent ad hoc workflows.
    120 - Read the current implementation and nearby tests before designing a change.
    121 - Inspect `git status --short` before broad edits or refactors.
    122 - Fail early when the task is blocked by missing prerequisites, contaminated scope, or unresolved public contract questions.
    123 
    124 ## 5. Canonical command surface
    125 
    126 - `cargo extbuild run -- cargo check --workspace --all-targets --locked`
    127 - `cargo extbuild run -- cargo test --workspace --all-targets --locked`
    128 - `cargo extbuild run -- cargo xtask contract validate`
    129 - `cargo extbuild run -- cargo xtask release preflight`
    130 - `cargo extbuild run -- cargo xtask architecture` for controlled deviation records and local spec
    131   anchors
    132 - Public API baselines live in `contracts/api_baselines/**`. Regenerate one
    133   with `cargo-public-api` `0.52.0` and rustdoc JSON from
    134   `nightly-2026-07-16`, writing the reviewed output back to that directory.
    135 - targeted `cargo check -p <crate>` and `cargo test -p <crate>` through
    136   `cargo extbuild run --`
    137 - `cargo xtask dto-roots --write` after changing configured DTO exports and
    138   `cargo xtask dto-roots --check` for exact generated-root freshness
    139 - targeted `cargo xtask contract ...`, `cargo xtask coverage ...`, `cargo xtask release ...`, or `cargo xtask hygiene ...` only when narrowing a repo-owned workflow
    140 - `cargo xtask hygiene prototype-contracts` for the governed report-only
    141   service-prototype census; use `--strict` only when the cleanup sequence has
    142   made every non-allowlisted finding release-blocking
    143 - if Beads is active, read `.beads/PRIME.md`
    144 
    145 ## 6. Rust engineering rules
    146 
    147 - Use Rust `1.97.1`, edition `2024`, resolver `3`, and workspace dependency
    148   versions from the root `Cargo.toml` after the release-v1 workspace cutover.
    149 - Preserve intended `no_std` portability; gate `std`, wasm, and runtime-specific behavior explicitly.
    150 - Keep core logic functional and composable: prefer pure transformations, explicit state, and narrow side-effect boundaries.
    151 - Prefer enums, newtypes, and typed domain models over stringly APIs, boolean mode switches, or loosely typed maps.
    152 - Avoid hidden panics in library code; reserve `unwrap` and `expect` for tests, build tooling, or proven internal invariants.
    153 - Prefer typed public error surfaces; do not expose opaque convenience errors as stable library contracts.
    154 - Avoid `unsafe` unless it is strictly necessary and documented by invariants close to the code.
    155 - Borrow first, clone late, and allocate intentionally.
    156 - Keep `lib.rs` thin as a module manifest and public re-export surface.
    157 - Treat generated bindings and generated type artifacts as generated; do not hand-edit them.
    158 - Add or update deterministic tests for new behavior, invariants, parsing, conversions, feature gates, and cross-target behavior where relevant.
    159 
    160 ## 7. Architecture, contract, and release discipline
    161 
    162 - `contracts/` and `tools/xtask` are authoritative for core-library contracts, conformance, coverage, hygiene, and release-candidate governance.
    163 - `contracts/crates/catalog.v2.toml` is the package-catalog authority. Preserve
    164   imported packages as `provenance_kind = "imported"` with their immutable
    165   repository, revision, path, and tree digest. New repository-native packages
    166   must be active, unpublished `provenance_kind = "native"` entries and must
    167   store only `introduction_tree_sha256`; never embed a self-referential
    168   introducing commit OID.
    169 - Before validating a new native catalog entry, stage the complete package path
    170   and run `cargo xtask catalog check` or `cargo xtask catalog write`. The
    171   pre-commit digest is derived from stage-zero index records, not the mutable
    172   worktree. After the introducing commit, the same command derives the first
    173   adding commit from repository history and verifies its immutable tree. Do
    174   not rewrite that digest for later source changes.
    175 - Behavior changes that affect public surfaces must update the relevant contract metadata, conformance vectors, export rules, or validation flows in the same change.
    176 - Preserve deferred flake expressions as unqualified compatibility inputs;
    177   do not use their evaluation or outputs as evidence until an accepted
    178   contract explicitly reactivates them.
    179 - This repository owns packages 1-17 in `radroots.crates.release.v1`, from
    180   `radroots_core` through `radroots_geonames`. `radroots_sdk` and `radroots`
    181   remain owned by the standalone SDK repository.
    182 - Public packages have no dependency on private Radroots packages. Every
    183   Radroots dependency edge points downward in the approved graph.
    184 - Domain and protocol packages do not own storage, live networking, host UI,
    185   executors, schedulers, or process-global behavior.
    186 - Generic SPIs do not expose concrete SQLx, Tokio, Reqwest, Nostr SDK,
    187   keyring, or operating-system implementation types.
    188 - Preview, code-generation, fixture, binding-generator, coverage, xtask, and
    189   implementation-assembly packages remain private and absent from published
    190   feature closures.
    191 - During the migration, every package remains non-publishable until its
    192   package-realistic release gates pass and publication is explicitly
    193   authorized. `contracts/releases/publish_policy.toml` is the machine
    194   authority; validation metadata does not authorize upload.
    195 
    196 ## 8. Service hardening boundaries
    197 
    198 - Service hardening is clean-slate: do not add or preserve prototype
    199   configuration readers, environment-file configuration, prototype state
    200   importers, JSON/JSONL mutable state, fallback path searches, compatibility
    201   aliases, deprecated modules/APIs/re-exports, dual wire encodings, or old/new
    202   feature switches. Update affected consumers directly.
    203 - `radroots_service_host` owns reusable host mechanics only, and
    204   `radroots_service_sqlite` owns reusable SQLite mechanics only. Neither crate
    205   may contain Myc or RHI domain configuration, tables, policy, or business
    206   rules, and neither may become a broad lifecycle framework.
    207 - Each service instance has one live SQLite database. Keep its pool private to
    208   the owning store, keep live mutable state daemon-owned, and route live-state
    209   mutations from local tools through the typed, permissioned Unix-socket
    210   local-admin boundary.
    211 - `radroots_service_sqlite::ServiceSqliteHost` is the sole public owner of the
    212   private SQLx pool. Service code may execute typed SQLx queries only through
    213   the sealed `&mut ServiceSqliteTransaction` executor passed to
    214   `ServiceSqliteHost::transaction`; do not expose or reconstruct raw pools,
    215   pooled connections, SQLx transactions, commit/rollback handles, or inner
    216   accessors. Do not attach or detach secondary SQLite databases through the
    217   transaction executor. Writable host construction must finish governed
    218   migrations before returning, while read-only inspection must require current
    219   migration and schema state. Every host owner must explicitly await
    220   `ServiceSqliteHost::close`: close permanently stops admission, drains admitted
    221   work, applies the fixed unblocked `TRUNCATE` WAL checkpoint for writable
    222   hosts only, closes its private checkpoint connection, and explicitly releases
    223   writer or inspection authority. A cancelled close retains authority and must
    224   be resumed through the host-owned connect/checkpoint/connection-close driver;
    225   Drop is not an asynchronous close or completion proof. Do not add public
    226   checkpoint knobs, background close tasks, or Drop-based async cleanup.
    227 - `ServiceBackupManifest` is the sole v1 backup-manifest model. Preserve its
    228   exact 1,024-byte compact canonical JSON, raw canonical-byte SHA-256, typed
    229   service/instance/source-generation/schema/time binding, singleton
    230   `state.sqlite` inventory, exact `ok` integrity projection, and mandatory
    231   protected-material exclusion. Parsing is structural only and must not perform
    232   filesystem or SQLite work. Online capture belongs only to the writable
    233   `ServiceSqliteHost`: admit one capture at a time, use SQLite's incremental
    234   online-backup API, create a caller-selected new owner-only staging directory,
    235   return the manifest in memory, and retain host authority until success or
    236   exact-artifact cancellation cleanup completes. Do not expose raw backup
    237   handles, capture credentials, invent a manifest filename, read an ambient
    238   clock, or fold untrusted verification or restore behavior into capture.
    239 - Untrusted backup verification is the synchronous, task-free
    240   `verify_backup_bundle` boundary. Require an independently protected manifest
    241   digest, exact `ServiceDatabaseIdentity`, and caller-supplied positive member
    242   limit; retain the verified directory and member descriptors in the sealed
    243   non-cloneable proof. Do not expose paths or raw handles, treat pathname-only
    244   verification as restore authority, create an internal task/deadline, mutate
    245   the bundle, or introduce restore markers, staging, replacement, or recovery
    246   into verification. Later restore work must consume the retained member and
    247   reverify its staged copy.
    248 - Restore recovery markers are private `radroots_service_sqlite` mechanics.
    249   Preserve the fixed sibling names, exact 2,048-byte canonical v1 JSON,
    250   domain-separated self-checksum, typed database and backup intent, and the
    251   only legal durable sequence `prepared -> live_retained ->
    252   replacement_installed`. Marker creation and advancement require retained
    253   writer authority, descriptor-relative owner-only files, exact inode
    254   revalidation, file and parent synchronization, and create-new scratch plus
    255   atomic replacement. Reads never repair or remove evidence. Do not expose
    256   marker types or paths, truncate markers in place, accept caller-selected
    257   names, or move, copy, open, or delete a database in the marker checkpoint;
    258   restore staging, replacement, and open-time recovery remain separate steps.
    259 - Offline restore staging consumes a sealed `VerifiedServiceBackup`, acquires
    260   exclusive writer authority after every governed host has closed, and creates
    261   only the fixed adjacent `state.restore-staged.sqlite` file. It must copy from
    262   the retained source descriptor, reverify exact metadata, migration prefix,
    263   schema catalog, integrity, foreign keys, length, and digest through retained
    264   descriptors, and keep authority plus exact cleanup ownership across caller
    265   cancellation. The returned sealed capability owns the staged inode until
    266   finalization or an identity-checked drop cleanup attempt; failed cleanup must
    267   remain evidence that later admission rejects. Staging must not create a
    268   marker, rename live state, retain an old live database, or install a
    269   replacement; those are later finalization and recovery boundaries.
    270 - Atomic restore finalization consumes only a sealed `StagedServiceRestore`.
    271   Staging must bind the exact live inode, length, and digest that finalization
    272   will retain. The owned blocking worker creates and synchronizes `prepared`
    273   before disarming stage cleanup, then uses descriptor-relative no-replace
    274   renames and parent synchronization for live-to-backup and staged-to-live,
    275   advancing the marker only after each durable rename. Cancellation observed
    276   before the worker atomically claims commit ownership may cleanly stop;
    277   caller loss after that handoff is an unknown immediate outcome, including
    278   the interval before `prepared` is durable. Once `prepared` is durable, stage
    279   cleanup must remain disarmed after every later error so the marker never
    280   loses a bound artifact.
    281   Successful finalization returns no host and leaves the old live database and
    282   `replacement_installed` marker for the next writable open to recover. Other
    283   open modes reject that evidence as `Recovery`. Finalization itself must not
    284   roll back, delete recovery evidence, reopen SQLite, or expose paths,
    285   descriptors, marker controls, or rename controls.
    286 - Interrupted restore recovery is private and automatic only for
    287   read-write-existing open under exclusive `WriterAuthority`, before any
    288   SQLite connection or await point. Initialize, initialized-open, and
    289   read-only inspection must reject every fixed stage, backup, marker, or
    290   marker-scratch artifact without mutation. Recovery must bind the marker to
    291   the requested database identity, hash and revalidate exact owner-only
    292   single-link artifacts, reject sidecars, and let exact topology decide the
    293   sole action: roll back `prepared` while old live is still installed, then
    294   roll forward once old live is durably retained. Persist every inferred phase
    295   before the next destructive step; retire exact backup before marker; and
    296   admit marker scratch only as a canonical topology-consistent one-edge
    297   successor whose exact bound inode is removed and durably reproduced through
    298   the governed marker-advance path without overwriting the valid marker.
    299   Repeated recovery may
    300   finish already-absent stage or backup cleanup, but every other missing,
    301   replaced, linked, malformed, mismatched, or ambiguous artifact remains
    302   `Recovery` evidence. Do not expose recovery controls, add a background task
    303   or hidden timeout, repair without writer authority, or fold Step 070
    304   integrity/status APIs and the later process failpoint harness into recovery.
    305 - Explicit active integrity inspection belongs only to
    306   `ServiceSqliteHost::inspect_integrity`. It admits at most one check per host,
    307   uses one governed read snapshot, accepts an injected positive wall-clock
    308   timestamp, and returns only the closed SQLite/foreign-key outcomes plus at
    309   most two stable diagnostic codes in canonical order. Preserve authority
    310   precedence after every await. Do not expose raw SQLite diagnostics, paths,
    311   SQL, pool handles, or dependency errors; persist or cache the result; read an
    312   ambient clock; create a timer/task; or weaken the strict restore/backup
    313   integrity verifier. Callers own the monotonic deadline by cancelling the
    314   future. The host-owned integrity driver must retain a cancelled in-flight
    315   connection and its explicit close future until the SQLx worker terminates;
    316   retry and host close resume that cleanup before proceeding. A retry must
    317   inject a new timestamp.
    318 - State-filesystem capacity inspection is an explicit, synchronous,
    319   host-independent doctor and admission input. Callers must supply a positive
    320   `MinimumFreeBytes`; there is no default threshold. The platform adapter
    321   measures unprivileged available bytes through a retained owner-owned state
    322   directory descriptor that is not group/other writable, and the immutable
    323   result classifies exact equality as
    324   ready and anything below the policy as low disk. Measurement failure is a
    325   typed unavailable result, never fabricated low-disk evidence. Consumers may
    326   cache a successful snapshot and project low disk to the stable
    327   `database_low_disk` reason, but passive readiness handlers must never invoke
    328   the adapter. Keep inspection advisory: do not add a reservation, host/pool or
    329   SQLite dependency, ambient timer, background sampler, service default, or
    330   status persistence to this crate.
    331 - Durability failpoints are private, instance-scoped test mechanics only. Keep
    332   a closed before/after inventory across initialization, transaction commit,
    333   online backup, restore-marker persistence, restore rename/synchronization,
    334   and explicit close. One armed controller may fail one selected edge once;
    335   ordinary controllers have zero behavior. Never export failpoint types, use
    336   process-global failpoint state, select a point from environment or service
    337   configuration, or add a Cargo feature that alters production behavior.
    338   Process-level crash and signal qualification must remain in private Cargo
    339   test binaries. Pass only one bounded temporary root over stdin, require a
    340   fixed stdout token from an occurrence-aware failpoint barrier before
    341   `SIGKILL`, and retain a parent kill-on-drop watchdog. Cover writer-lock death
    342   and the exact pre-marker, prepared, marker-scratch, installed-replacement,
    343   and terminal-marker restore topologies under a permissive child umask.
    344   Require Linux x86_64 execution for OS-level qualification; macOS aarch64 on
    345   the current machine is developer evidence only. No other platform or
    346   architecture is an active qualification gate. Do not ship a helper binary,
    347   add production signal/process behavior, poll filesystem state for crash
    348   timing, or claim abrupt power-loss durability from process-death tests.
    349 - Runtime-management flows consume a sealed `RuntimeContext` for every service
    350   instance. They must not reconstruct service paths from raw identifiers,
    351   ambient selectors, or manager-owned roots, and registries must not persist
    352   duplicate config, state, logs, run, secrets, or binary paths. Manager-owned
    353   install and process-tracking artifacts remain separate; uninstall and
    354   cleanup must never recursively delete canonical service state or secrets.
    355   The manager has no credential read/write authority, executable artifact
    356   names are validated single path components, and ordinary manager errors and
    357   `Debug` output must not expose filesystem paths, file contents, or raw
    358   dependency-owned causes.
    359 - Runtime-path consumers must use the sole typed
    360   `services/<service>/<instance>` model through `RuntimeContext`. The generic
    361   app/service/worker/shared namespace, public raw root containers, path
    362   overrides, ambient process-environment selectors, bootstrap helpers, and
    363   duplicate service-instance path constructors are removed breaking surfaces;
    364   do not restore them or add compatibility aliases.
    365 - Runtime-distribution and runtime-management service metadata is the sealed
    366   exact Myc/RHI v1 inventory. Both services support multiple validated
    367   instances, one TOML config, explicit initialization with existing-only run,
    368   detailed HTTP/1.1-over-Unix local administration, cached
    369   `/livez`/`readyz`/`metrics`, and only Linux x86_64/aarch64 Tier-1 eligibility
    370   in `target` posture. This metadata does not authorize service registration,
    371   PID/config/log probing, lifecycle actions, artifact names, channels, archive
    372   resolution, or a `qualified` support claim.
    373 - Library code must not initialize a tracing subscriber, parse a process CLI,
    374   read service configuration from environment variables, install signal
    375   handlers, create a Tokio runtime, call `process::exit`, or spawn arbitrary
    376   signer executables.
    377 - Inject time, entropy, transport, providers, and failpoints. Bound queues,
    378   pools, retries, requests, responses, and collections; redact sensitive data
    379   from logs, status, metrics, fixtures, errors, and ordinary `Debug` output.
    380 - Preserve public Nostr interoperability while removing Radroots-owned
    381   prototype behavior; clean-slate rules never authorize protocol drift.
    382 
    383 ## 9. Irreversible actions
    384 
    385 Do not publish crates, create release tags, change crates.io ownership, merge
    386 or rename repositories, merge pull requests, rotate credentials, or mutate
    387 trusted-publisher configuration without explicit authorization.
    388 
    389 ## 10. Commit and deviation directives
    390 
    391 - Format commits as `<scope>: <imperative summary>`.
    392 - Use lowercase scopes that match the crate or subsystem being changed.
    393 - Leave a blank line after the summary when writing a multi-line commit.
    394 - Use `- ` bullets for notable changes, validations, or compatibility notes when a body is needed.
    395 - Split unrelated changes into separate commits.
    396 - If repository evidence proves a planned step obsolete or unsafe, record the
    397   evidence, affected specification anchor, disposition, and validation in
    398   `contracts/architecture/deviations.toml`. A normative architecture change
    399   also requires an approved machine decision under
    400   `contracts/architecture/decisions/**`. Never silently skip or reorder work.
    401 
    402 ## 11. Definition of done
    403 
    404 - The requested change is implemented.
    405 - Affected code, tests, docs, and contract surfaces are updated together.
    406 - Relevant canonical validation ran, or a concrete blocker is reported.
    407 - The handoff states what changed, what validations ran, and any follow-up risks or assumptions.