Task · SFL-0060

cli: doctor only ever checks one org — add `--all-orgs` for a whole-deployment preflight

Status
To Do
Labels
followup, phase-1, roadmap
Milestone
Feature roadmap — operator ergonomics & platform capabilities
Updated
2026-08-14

Description

What

sf2loki doctor scopes every per-org check to a single org, so a multi-org deployment has no single-command preflight.

run_doctor (src/sf2loki/doctor.py:850) resolves one org and collapses the config to a single-org view before any live check runs:

Everything after that sees exactly one org: auth/permissions/pubsub/entitlement/traceflags (src/sf2loki/doctor.py:891-899), transforms (src/sf2loki/doctor.py:901), and limits (src/sf2loki/doctor.py:909). The docstring states the limitation explicitly (src/sf2loki/doctor.py:856-861). select_org returns a note rendered as a WARN row — “multiple orgs configured […]; this command operates on org ‘’ only” (src/sf2loki/config.py:1455-1461, emitted at src/sf2loki/doctor.py:876-878).

The CLI exposes no way to widen the scope: the doctor subparser has --json and --org NAME only (src/sf2loki/cli.py:80-97), passed through at src/sf2loki/cli.py:200. docs/reference/cli.md:59-62 documents those two flags and nothing else.

CheckResult carries name, status, detail and no org identity (src/sf2loki/doctor.py:90-96); the --json payload is {"checks": [asdict(r) ...], "exit_code": N} (src/sf2loki/doctor.py:820-823). So N invocations emit N payloads whose check names are byte-identical, with nothing to attribute a row to an org.

This is a doctor-only restriction. Multi-org ingestion is first class (OrgConfig src/sf2loki/config.py:1298-1306, Config.resolved_orgs src/sf2loki/config.py:1420, issue #31), and the daemon already probes every org at startup with an order-preserving asyncio.gather (src/sf2loki/app.py:1240-1265). sf2loki --check also validates the whole multi-org config, as does doctor’s own config check, which runs App.build(cfg) on the unscoped config (src/sf2loki/doctor.py:105-120).

Why it matters

A six-org deployment cannot answer “is this deployment ready?” in one command. The operator runs sf2loki doctor --org <name> six times, reads six tables (or six JSON blobs with indistinguishable check names), and derives the overall verdict by hand. CI wiring needs a shell loop plus exit-code aggregation.

The failure mode is silence: an org missing from the hand-written loop gets no preflight at all, and nothing in the output of the runs that did happen indicates coverage was incomplete. A broken integration user, a revoked connected app, or an unreachable Pub/Sub topic in that org first surfaces at runtime — where multi-org semantics are deliberately non-fatal (some-orgs-fail keeps the healthy orgs streaming, src/sf2loki/app.py:1241-1247), so it degrades quietly rather than failing fast.

Six runs also repeat the deployment-wide checks six times, including six source=sf2loki-doctor test writes to Loki (src/sf2loki/doctor.py:358-364), six OTLP probes, six state-store probe objects and six coordinator lease probes — none of which vary by org.

Proposed approach

Add doctor --all-orgs: run the per-org check set once per configured org, run the deployment-wide checks exactly once, and aggregate into one table / one JSON payload with one exit code.

CLI (src/sf2loki/cli.py:86-97)

Check partition — the split is not the one the surface naming suggests:

Row naming and identity

Execution and scope edge cases

Docs


Imported from GitHub issue #144 on 2026-08-14, when this repo migrated from GitHub Issues to Backlog.md. The original issue has been deleted; its verbatim body, labels and comments are preserved in archive/issues-dump.json (jq '.[] | select(.number == 144)' archive/issues-dump.json).

Filed from the 2026-07-30 full-repo audit (11 finder lanes + adversarial verification per finding).

Acceptance Criteria

Definition of Done

References

View the source file on GitHub