tidaldb/docs/runbooks
jordan c97aaa8e5b fleet remediation: make the workspace gate runnable, then fix what it caught
`cargo test --workspace` could not run at all: dependency resolution failed with
"aws-types@1.3.16 requires rustc 1.91.1" on the 1.91.0 default toolchain, so the
gate the project documents was dead. Making it run exposed a compile break and
two wrong tests that had been invisible for months. Now green end to end:
143 suites, 3155 tests, exit 0.

Toolchain
- rust-toolchain.toml pins the DEV toolchain to 1.91.1. The published MSRV stays
  `rust-version = "1.91"` (the engine builds on 1.91.0); only tidalctl's AWS SDK
  chain needs the patch release, and it now declares that itself.

Consumer crates migrated to the current engine API (clean cutover)
- iknowyou-engine: `AgentPolicy` gained five m10 read/profile-override fields;
  the literal now spreads `..AgentPolicy::default()` as the engine's own doc
  example does, so future fields do not break it again.
- forage-engine: `RetrieveResult` gained p1 `reasons`. The app builds its own
  candidate pool, so it now tags what it knows: PreferenceMatch for the
  preference-vector blend, SemanticMatch (with the seed item) for
  similar-to-saved, ExplorationBudget for pinned discoveries.
- forage-engine: `url_to_item_id` folded into the u32 item universe. The engine
  narrows item IDs to a u32 slot in durable per-user state and rejects anything
  above u32::MAX rather than alias two items forever, so every add_item with a
  64-bit FNV hash failed. 9 of 28 smoke tests were failing on this alone.
- forage-engine: bridge items read the top-2 preference CLUSTERS via
  `query_vectors`, not the single centroid from `preference_vectors().get()`.
  Since m12 that accessor returns only the strongest cluster, so a tech+jazz user
  whose interests split into two clusters looked single-interest and never
  bridged. Falls back to top-2 dimensions when a user has one cluster.

Reconcile tests corrected to the shipped contract
- tidal/tests/m8p3_reconcile_production.rs asserted `3 + 5 == 8` for a windowed
  count after heal. `take_crdt_snapshot` deliberately keys signal contributions
  to ONE canonical contributor (ShardId::SINGLE) because signals are relayed from
  a single writer, so per-node attribution double-counted every replicated event
  on every reconcile. Merge is therefore LWW on (last_update_ns, score) plus
  PN-counter per-node max: nodes converge on the more complete accumulator. The
  old expectation was asserting the bug that fix removed.
- Rewrote to assert convergence, count survival (not 0), and no inflation, and
  added `repeated_reconcile_of_converged_nodes_does_not_creep` - the regression
  guard for the creep itself, which nothing covered.

Pre-commit hook unified
- hooks/pre-commit dropped `-D warnings`: each crate's `[lints]` table is the
  source of truth (`clippy::all`/`unwrap_used` deny, `pedantic` warn), and the
  flag promoted ~58 deliberate pedantic warnings in integration tests to errors,
  making every Rust commit impossible.
- It now lints all five tidal crates instead of path-matching `tidal/`, which
  silently skipped tidal-server, tidal-net, tidal-stress, tidalctl and
  applications/ - the rot above lived in exactly those crates. Ported the
  CODING_GUIDELINES file-length, println, and unsafe-SAFETY checks from the
  divergent untracked copy that this replaces.
- CONTRIBUTING.md now documents the real commands and the toolchain/MSRV split.

Fleet recovery and soak
- scripts/restore-fleet.sh: the fail-closed selective restore, promoted out of an
  ignored tmp/ directory into the repository. Preflights retained storage,
  digest-pinned images, parked state, and aggregate plus per-PV-node scheduler
  headroom before the first scale; writes a durable transcript under
  tmp/restore-logs/ with structured start/error/rollback/complete events.
- k8s manifests park the standalone store, the RF3 cluster, and the soak monitor
  at zero replicas with restore-fleet.sh as the only supported scale-up path.
- soak-eval/soak-watch and the nightly CronJob fail closed on stale or missing
  restart evidence instead of silently skipping the restart-aware half of the gate.
- docs/ops/capacity-planning.md corrects the RAM envelope to the real hot-tier
  formula and separates analytic totals from the measured process envelope.
2026-08-16 12:38:14 -06:00
..
cluster.md fleet remediation: make the workspace gate runnable, then fix what it caught 2026-08-16 12:38:14 -06:00
disaster-recovery.md docs: refresh ops runbooks to the live rc7 / full-placement reality 2026-06-19 19:53:29 -06:00
kubernetes.md docs: refresh ops runbooks to the live rc7 / full-placement reality 2026-06-19 19:53:29 -06:00
on-call.md docs(m12): refresh API, specs, ops, and roadmap to the shipped M12 reality 2026-06-23 21:39:55 -06:00
README.md docs: refresh ops runbooks to the live rc7 / full-placement reality 2026-06-19 19:53:29 -06:00

tidalDB Runbooks — index

Operational documentation for the cluster deployment (ns tidaldb-cluster, single StatefulSet, full-placement RF3, quorum-ack + automatic election). Live state and history live in the orchard9-k3sf repo (cluster-state.yaml, deployments/history/tidaldb.md).

When you need to… Open
Respond to an incident / page — symptom → signal → fix on-call.md
Operate the cluster — API, failover, partition, membership, scale, rolling upgrade cluster.md
Run it on Kubernetes — deploy, digest-pin, probes, scale up/down, troubleshoot pods kubernetes.md
Back up / restore / DR — object-store export, restore, byte-verify, query-proof, R2, rebuild disaster-recovery.md
Recover a single node / standalone engine — corrupt keyspace, WAL, lock, schema ../ops/recovery.md
Size a deployment — single-node tables + the measured Ref-A cluster envelope ../ops/capacity-planning.md
Read the metrics / wire dashboards & alerts ../ops/monitoring.md, ../ops/grafana-dashboard.json, ../ops/prometheus-alerts.yaml
Understand the live perf/topology findings (dev handoff) ../profiling/m12-cluster-deploy-findings.md

Current production facts (2026-06-19)

  • Image: registry.threesix.ai/tidal/server@sha256:171505745b… (m12-writeburst-rc7).
  • Shape: 1 StatefulSet tidaldb, 3 pods = 3 regions = 3 voters, full-placement RF3 (every pod hosts all 3 shard groups; gRPC 9601/9602/9603), HTTPS+mTLS on :9500, metrics :9091.
  • Guarantees live: quorum-ack writes, automatic election/failover, elastic seed-join (learner→voter), inter-node mTLS, per-node Prometheus.
  • Shipped fixes in this image: reseed-loop resolved, seed-join promotion, election-divergence/quarantine, read-SLA (CPU oversubscription), and the write-burst false-partition fix.
  • Known limits: read p99 ≤10 ms to ~1000 rps (ceiling ~11.5k/s, CPU-bound); write knee ~250 rps; soak at 200 rps; 1M corpus needs >16 GiB nodes; 2.5× write-scaling needs ≥5 nodes + partitioned placement.
  • Must-watch signal: tidaldb_cluster_peer_breaker_state (0/1/2) — a live peer stuck at 1 is the write-burst/partition pattern.

Alert rules in ../ops/prometheus-alerts.yaml are accurate but design-reference — not yet loaded by the live Prometheus. Promoting them to a PrometheusRule is the one open observability step (see on-call.md §3).