Skip to content

feat(doctor): emit the checks as JSON so a deployment can be monitored - #154

Merged
L4XB merged 2 commits into
mainfrom
feat/doctor-json
Sep 16, 2026
Merged

L4XB merged 2 commits into
mainfrom
feat/doctor-json

Conversation

@L4XB

@L4XB L4XB commented Sep 16, 2026

Copy link
Copy Markdown
Member

Summary

six-community doctor formatted its checks as an aligned text table and nothing
else, so an operator who wants to be told that their deployment stopped being
able to send mail — before a participant fails to receive an invitation — had to
scrape column-aligned text whose width depends on the longest check name.

--json emits one object per check with name, state and detail. The data
was already structured; only the rendering was not.

Closes #126.

Behavior and compatibility

  • six-community doctor --json prints a JSON array on stdout and nothing else,
    on either stream, so it can be piped straight into a parser.
  • The exit code is unchanged in both modes: non-zero when any check failed. A
    monitor reads the body and the status.
  • The text output is untouched. A new test rebuilds the expected table from
    run_doctor() and compares the command's stdout line for line, so the format
    people read cannot drift while the other one is added.
  • The three field names are now an interface. Whatever alerts on state
    breaks the day one of them is renamed, so they are named once in
    DOCTOR_JSON_FIELDS and the test asserts the emitted keys are exactly that
    tuple — a rename fails the suite rather than somebody's monitoring.
  • No migration, no schema change, no new dependency (json is stdlib and is
    imported inside the branch, as elsewhere in this module).

Why an array of objects rather than one object keyed by check name: the checks
have an order an operator reads them in, _provider_checks() returns a variable
number of them, and an array keeps both without making the key set part of the
contract.

Validation

$ cd services/api
$ uv run --frozen --no-sync pytest tests/test_doctor.py tests/test_cli.py -q
10 passed

$ uv run --frozen --no-sync pytest -q
2918 passed, 1 skipped in 283.35s
# the single skip is tests/test_postgres_runtime.py, which wants the CI PostgreSQL service

$ uv run --frozen --no-sync ruff check src scripts tests
All checks passed!

$ uv run --frozen --no-sync mypy src
Success: no issues found in 208 source files

ruff format --check src tests reports one file it would reformat,
tests/test_email_verification.py, and that is unchanged by this branch — it
reports the same on main.

  • Engine checks pass, or the engine is unaffected.
  • API, migration, worker, and web-contract checks pass, or they are unaffected.
  • Web type-check, tests, and production build pass, or the web app is unaffected.
  • Browser-extension contracts and a deployment-bound build pass, or the
    extension is unaffected.
  • macOS Companion boundary check, locked resolution, tests, and release build
    pass on the pinned Xcode toolchains—or the Companion is unaffected.
  • Self-hosting tests and container builds pass, or deployment is unaffected.
  • User-facing behavior has a focused test or the omission is explained.

Review boundaries

  • Security and privacy effects were reviewed, including authentication,
    tenancy, capability URLs, uploads, retention, deletion, browser state, and
    logs as applicable. The JSON carries exactly the detail strings the text
    table already printed; test_no_configured_secret_reaches_the_output now
    runs the command with --json and asserts the same canaries are absent.
  • New network calls and processors are operator-configurable, fail closed,
    and document data egress, cost, retention, and failure behavior—or none
    were added.
  • Dependencies and bundled assets are justified, locked, and
    redistribution-compatible—or none were added.
  • Native-client changes include explicit origin, local-retention/deletion,
    permission, signing, update, and binary-distribution implications.
  • Research-method assumptions, limitations, and provenance remain visible—or
    no research-facing behavior changed.
  • Accessibility and keyboard behavior were reviewed for UI changes—or no UI
    changed.
  • Browser permissions, capture bounds, pairing callbacks, extension storage,
    and generated host access were reviewed—or the extension is unaffected.

Source-release hygiene

  • No secret, private key, production configuration, customer/participant
    data, user upload, database dump, log, private prompt, or non-redistributable
    research content is included.
  • The change belongs in the community stack; payment, subscription,
    commercial-plan, hosted-administration, and marketing-site code remains
    separate.
  • Public behavior and limitations are documented — deploy/community/README.md
    gains the --json invocation, what the three fields mean, and that they are
    a stable interface.
  • CHANGELOG.md is updated for user-visible changes, or the omission is
    explained above.
  • Every commit carries my own matching DCO Signed-off-by trailer.
  • I have read CLA.md and posted its exact acceptance sentence as a
    standalone pull-request comment.
  • I have read and will follow the Code of Conduct.

Visual evidence

Not a UI change. What the new mode prints:

$ six-community doctor --json
[{"name": "database", "state": "ok", "detail": "reachable, 24 tables"}, {"name": "storage", "state": "ok", "detail": "…"}, {"name": "mail", "state": "off", "detail": "registration …"}]
$ echo $?
0

`doctor_cmd` formatted the checks as a column-aligned table and nothing else, so
an operator who wants to know that their deployment stopped being able to send
mail — before a participant fails to receive an invitation — had to scrape text
whose width depends on the longest check name.

`--json` prints one object per check with `name`, `state` and `detail`, as the
whole of standard output and nothing else, so it can be piped straight into a
parser. The exit code is untouched: non-zero when any check failed, in both
modes, because a monitor reads both.

Those three field names are now an interface. Renaming one breaks whatever is
alerting on it, so `DOCTOR_JSON_FIELDS` names them in one place and the test
asserts the emitted keys are exactly that tuple.

The text format is what people read and has not moved: a new test rebuilds the
expected table from `run_doctor()` and compares the command's stdout line for
line. The secret-redaction test now covers the JSON path as well, since that
output gets pasted into issues just as often.

Closes #126

Signed-off-by: L4XB <lukas.buck@e-mail.de>
@L4XB

L4XB commented Sep 16, 2026

Copy link
Copy Markdown
Member Author

I have read and agree to the SixSentences CLA v1.0.

The audit pins a hash per exported file, so a change under services/api has to
rewrite it.

Signed-off-by: L4XB <lukas.buck@e-mail.de>
@L4XB
L4XB merged commit 652edee into main Sep 16, 2026
23 checks passed
@L4XB
L4XB deleted the feat/doctor-json branch September 16, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

doctor: add machine-readable output so it can be monitored

1 participant