Problem/Motivation
The results store (#3608513: Add results store and trends) already tracks movement of the permission risk check's findings: a risky grant appearing or being revoked shows up in the changes timeline today. But those findings only cover the dangerous classes. For everything else — the ordinary majority of permission-space — the site has no memory at all: "when did the contractor role get that permission?" is answerable only by config-history archaeology on sync-disciplined sites, and by nothing on the rest.
An earlier revision of this issue framed new grants as problems to flag. Review discarded that framing: granting permissions is the CMS working as intended, and a "warning until acknowledged" model would need an acknowledgment workflow while lying about state. What a diagnosis-over-time tool honestly owes is memory, not judgment — an inventory with a timeline.
Steps to reproduce
Grant any non-flagged permission to any role between two check-ups: nothing anywhere records that it happened or when.
Proposed resolution
The check
A new permission_grants check (security category, sibling of permission_risk, sharing PermissionResolver): one OK-severity finding per (role, permission) grant — the full inventory, not only flagged permissions, because the "restrict access"/admin flags are advisory metadata that contrib modules apply inconsistently, and a flagged-only inventory would inherit that unreliability. The store's existing lifecycle machinery provides all semantics for free: a new grant is a "New" event with a date, a revocation is "No longer found", and "when did role X get permission Y" becomes a stored fact.
Deliberate semantics: events, not severity. A new grant stays OK — the news is the timeline entry, not a state judgment. Intended changes need no acknowledgment (a diary is reviewed, not resolved); revocation is the only resolution; this check never gates CI. The existing risky-grant rules in permission_risk keep their severities and their (already working) movement tracking; a risky grant simply appears in both checks, answering two different questions. The first run establishes the baseline as a one-time wave of "New" inventory events — expected and documented, not a bug.
The Inventory section (presentation rule: OK = inventory)
This check makes OK findings load-bearing, which exposes that they currently squat in problem-framed UI. New page-wide rule: OK findings appear in a new "Inventory" report-page section, and nowhere else on the page. The section is the one-stop shop:
- Holdings — current counts per inventory-contributing check ("Permission grants: 1,412 · Configuration overrides: 3"), now-state, labeled "as of the latest check-up".
- Inventory changes — its own movement feed (own pager, element-indexed so
page=N,Mcomposes with the main feed's; the period and area filters apply; severity structurally does not, and the section says so).
"What changed" becomes problems-only. Severity transitions across the OK boundary appear in both feeds with per-feed vocabulary: the main feed says "Got worse (OK → warning)" / "Improved (warning → OK)"; the inventory feed says "Left inventory (became a warning)" / "Entered inventory (improved from a warning)" — one stored event, two readerships, and the holdings count changes are always explained by the inventory feed. Machine formats (json/llm/the store) are untouched — they carry everything.
What this deliberately is not
Not an audit log (no actor attribution, no keystroke trail — who-did-it belongs to audit tooling); not a gate; not a replacement for config-drift's coarse role-config tracking, which remains the right signal for sync-disciplined deploy review.
Recipe: plugin in src/Plugin/SiteDoctor/Check/, kernel test in tests/src/Kernel/Check/ extending CheckKernelTestBase, fixtures in both directions. See CONTRIBUTING.md.
Remaining tasks
- Implement the check + kernel tests, including the two-run "grant appears as a New event" assertion.
- Implement the Inventory section (holdings + movement feed + second pager) and remove OK events from the main feed per the routing above; kernel tests for the routing table.
- Document the first-run baseline wave in the check description and README.
- Escape hatch noted for review: if the full inventory proves heavy on very large sites, scoping becomes a settings knob later — narrowing preserves fingerprints, widening would re-baseline, hence full-first.
User interface changes
New "Inventory" section on /admin/reports/site-doctor (holdings + its own paged changes feed); the main "What changed" feed no longer shows pure OK events.
API changes
One new check plugin. No service or schema changes.
Data model changes
None — uses the existing results store tables. Row-count note: the inventory adds one lifecycle row per current grant (hundreds to low thousands; steady-state writes remain churn-only).
Issue fork site_doctor-3608634
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 #2
amangrover90 commentedComment #5
amangrover90 commented