6.3 KiB
Refinement Phase 9 — Observability, ops hardening, docs honesty & scale-later
Mission: make the running platform diagnosable and honest, and record the explicitly-deferred scale work so nobody mistakes it for missing MVP scope. This is the "finish before launch, then keep for later" bucket.
Track: backend (observability/docs) + explicit deferrals · Depends on: nothing hard (start anytime; finish the observability items before launch) · Unlocks: production diagnosability Before you start, read ../../phases/_shared/agent-operating-rules.md.
1. Context
This phase is the server audit's post-phase-7 (observability & ops hardening) + post-phase-8 (scale & later). Full evidence & fixes: ../server/post-phase-backend-plan.md § post-phase-7/8.
2. Required reading
- ../server/post-phase-backend-plan.md § post-phase-7 (7.1–7.6) and § post-phase-8 (8.1–8.5).
- ../server/runtime-services.md § 4 (Prometheus), § 17 (Elasticsearch — deliberately later).
3. Scope
Observability & ops hardening (post-phase-7 — finish before launch)
- 9.1 — add tracing + consolidate the two metric stacks. OTel is metrics-only (no
WithTracing/OTLP); two overlapping Prometheus stacks run. AddWithTracing(AspNetCore + EF) exporting OTLP; pick one metrics stack; wire trace-id intoApiResult.requestIdfor support correlation. - 9.2 — broaden health checks + split readiness/liveness. The single check is app-DB only; add log-DB,
object-storage (write probe), Redis (when Phase 7 lands); expose
/healthz/livevs/healthz/ready; remove the deadcurrentUrlline. - 9.3 — revisit prod log levels + notification channels. Deployed envs write Warning+ only (every Information-level audit trail dropped); raise to Information+ with retention (or a file/OTLP sink); ensure no PII is logged (the SMS OTP log disappears with Phase 8 5.1); delete the dead Elasticsearch sink block + package or revive it deliberately.
- 9.4 — audit-log growth & archival.
audit_logsis append-only with no retention (and Phase 6 §6.3 grows it); add a retention/archival policy as a Phase 7 job; define legal retention for money/verification rows first. - 9.5 — decide
TicketMessage.Bodyencryption + the gRPC plugin's fate. Ticket bodies are the refund/dispute paper trail (users type phone numbers, addresses, clinical detail) and are plaintext with no documented decision — recommend encrypting via the existing converter pattern. The gRPC plugin duplicates only the OTP/token flow, forces the HTTP/2 posture, and enables reflection unconditionally — remove it or give it a dedicated HTTP/2 endpoint + disable reflection outside Development. - 9.6 — keep the docs honest. Prune the stale mocks-registry duplicate rows, rename the
IJobSchedulerrow to "recurring jobs", mark delivered/answered REQs, noteIPaymentCaptureSimulator's removal. (RootCLAUDE.mdrule 7 — stale instructions are worse than none.)
Scale & later (post-phase-8 — explicitly NOT MVP; record, don't build)
- 9.7 — Elasticsearch read backend + outbox feeder.
SqlNurseSearchis real and correct;Search:Backendfails fast on any non-sqlvalue. BuildElasticNurseSearch+ the outbox/CDC feeder only when SQL search shows strain. Not now. - 9.8 — analytics pipeline.
IAnalyticsSinkwritesops.SystemEventsfire-and-forget; pipe to a warehouse/stream when product needs it. - 9.9 — holiday-calendar feed. The table is manually maintained; a yearly ops-checklist item is an acceptable alternative to a lunar-Hijri drift feed.
- 9.10 — push/SMS notification channels.
InAppNotificationDispatcherdrops non-InApp channels; add fan-out (SMS via Phase 8 5.1's sender, FCM push) when the UX demands. - 9.11 — deferred product tables (
organizations,organization_nurses,fraud_flags,recurring_booking_schedules,bnpl_settlement_entries, availability slots, customer national-ID KYC, geo bulk import) — all verified absent and all pure additive migrations when product pulls them. Leave documented.
4. Mocks & seams
None new. 9.7/9.10 are the make-it-real steps for the INurseSearch (Elastic backend) and
INotificationDispatcher (SMS/push channels) rows — deferred by design.
5. Critical rules
- No PII in logs — verify after 9.3 (the OTP log must be gone; clinical text and IBANs are never logged).
- Deferrals are documented, not silent — 9.7–9.11 stay explicitly out of MVP with a written "when to pull it" trigger, so a future reader doesn't mistake them for gaps.
- Don't build Elasticsearch/analytics/push "because" — each has a concrete trigger (search strain, product need, UX demand). Until then, SQL search / in-app notifications are the real, correct MVP.
6. Definition of Done
- Tracing exports OTLP; one metrics stack;
requestIdcarries the trace id. - Health checks cover the real dependencies with
/healthz/livevs/healthz/ready. - Prod logs Information+ with retention and no PII; the dead ES sink is resolved.
- Audit retention policy exists; ticket-body encryption + gRPC decisions are made and documented.
- The mocks-registry / REQ tracker / architecture maps are honest and current.
- 9.7–9.11 are recorded as deferred with explicit pull-triggers.
dotnet build/dotnet testgreen.
7. How to test
- A request produces a trace with a
requestIdthat matches theApiResult.requestIdin the response. /healthz/readyfails when a dependency (object storage / Redis) is down;/healthz/livestays up.- Grep the logs after an OTP + a booking + a payout → no phone number, OTP, IBAN, or clinical text appears.
8. Hand off & document
- Update
server/CLAUDE.md(observability wiring, ticket encryption, gRPC decision), the mocks-registry, and the REQ tracker. Update ../server/runtime-services.md for any new observability service. Save a memory note capturing the encryption/gRPC decisions and the deferral triggers.