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.