Problem/Motivation
Site Doctor's positioning is "site diagnosis over time" — but as of 1.0.0-alpha1, every run is point-in-time: checks report what is wrong now and the result is discarded. Nothing can answer when a finding first appeared, whether it was resolved, or which way the trend is going — the questions that matter most on a mature site (drift arriving in deployment waves vs. accumulating gradually, warning counts rising vs. burning down).
Findings already carry deterministic fingerprints (introduced in alpha1 for exactly this purpose): a hash of the finding's identity — check, finding type, subject — never its state. The same real-world problem produces the same fingerprint run after run. This issue adds the persistence layer that turns those fingerprints into longitudinal memory.
Steps to reproduce
Run site-doctor:check twice with a config change (or any fix) in between; nothing reports what appeared, persisted, or was resolved between the two runs, and there is no admin UI showing check results at all.
Proposed resolution
Lifecycle findings model
Findings are stored as one row per distinct fingerprint with first_seen / last_seen / resolved markers — not repeated per run. A finding that persists unchanged for a year costs one row and zero writes; the tables grow with churn (the signal), not with time × findings. A severity change closes the row and opens a new one, preserving worsened/improved history. Resolution is detected by set-difference against the current run's fingerprints, computed only for checks that completed in that run — a check that did not run (disabled, crashed, filtered) never fabricates a resolution.
Storage
Four tables via the schema API (dropped on uninstall), all access through two services — ResultsStore (write) and TrendRepository (read):
site_doctor_run— one row per run (window, trigger, complete/partial status).site_doctor_run_check— per-check outcome per run, so "ran clean" is distinguishable from "did not run".site_doctor_run_count— per-run counts by check / finding type / severity; small enough to keep long-term, this feeds aggregate trends beyond the finding purge horizon.site_doctor_finding— the lifecycle rows.
Scheduled collection
Cron runs all enabled cheap checks on a configurable interval (default daily) and publishes to the store. CLI runs persist too (opt out with --no-store). The run structure accommodates future queued/chunked expensive checks without schema change.
Dashboard
New report at /admin/reports/site-doctor behind a new restrict-access-flagged view site doctor reports permission: current status by category, run-to-run churn (appeared / persisted / resolved), and trend views. Every trend view states its retention horizon. The dashboard only reads stored results — it never triggers checks — and is render-cached, invalidated when a run publishes.
Retention and knobs
All in site_doctor.settings with config schema: active findings kept indefinitely; resolved finding detail purged after 90 days (configurable); run summaries kept 365 days (configurable); disabled_checks (reported in output as disabled, never silently); persist_info_findings (default true — noise-averse sites keep Info checks running without storing lifecycle rows).
TrendRepository is the single projection source for all current and future surfaces. CLI trend output, Tool API exposure, and a thin read-only JSON endpoint for external multi-site dashboards are planned for later milestones and deliberately out of scope here.
Recipe: this lands as phased MRs against this issue; services in src/, kernel tests in tests/src/Kernel/, lifecycle transitions covered by a fixture matrix in both directions. See CONTRIBUTING.md.
Remaining tasks
- MR A: schema +
ResultsStorewrite path + lifecycle-transition kernel test matrix (appear / persist / resolve / severity change / reopen / partial run / crashed check / disabled check). - MR B: cron scheduling +
disabled_checks+persist_info_findings+ settings schema +--no-store. - MR C:
TrendRepository+ dashboard + permission + cache tag. - MR D: retention purge (chunked deletes on cron) + horizon printing + documentation.
User interface changes
New administrative report at /admin/reports/site-doctor (status, churn, and trend views). No changes to existing output surfaces beyond the CLI gaining --no-store.
API changes
New ResultsStore and TrendRepository services; new permission view site doctor reports; new settings keys (disabled_checks, persist_info_findings, collection.interval, retention.*). The result SCHEMA_VERSION may bump during alpha per the documented stability policy; fingerprint identity rules do not change.
Data model changes
Four new database tables (site_doctor_run, site_doctor_run_check, site_doctor_run_count, site_doctor_finding), created by the module and removed on uninstall. The module continues to write only to its own reporting tables.
Issue fork site_doctor-3608513
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #10
amangrover90 commentedComment #12
amangrover90 commented