feat(reports): scope the executive report by group + framework (A1)#631
Merged
Conversation
Reports Phase A1 (docs/engineering/reports_design.md §11). POST
/api/v1/reports:generate now accepts an optional scope {group_id?,
framework?}; an empty body keeps the v1.0.0 all-hosts behavior.
- group_id scopes the posture to a group's member hosts via a new
group.Service.ScopeGroup (reuses memberCTE: manual -> group_members,
auto -> hosts.os_family == match_family). An unknown group_id ->
group.ErrNotFound -> 400 reports.invalid_scope; an empty group reads
as zero hosts (distinct from unscoped).
- framework applies the fleet-rollup lens (framework_refs ? key),
counting only rules in that framework.
- The resolved scope (group_id, group name frozen at gen time,
framework) is stored on a new reports.scope JSONB (migration 0041)
and echoed on the wire; scope_label is derived
("Production · CIS", "All hosts · STIG", ...).
- Frontend: a group scope picker beside Generate (select sourced from
GET /api/v1/groups, "All hosts" default), host:write-gated. The
framework-lens UI is deferred until a fleet framework catalog exists
(backend/API/contract already support it).
SDD: api-reports v1.1.0 (C-07, AC-07/08/09), frontend-reports v1.1.0
(C-04, AC-05). New service/DB tests for group + framework scoping and
the label derivation. gofmt/vet/build clean; specter 111 (api-reports
9/9, frontend-reports 5/5, 100% structural); report+group Go suites and
the reports vitest green.
remyluslosius
added a commit
that referenced
this pull request
Jun 21, 2026
Reconciles the docs with the merged reports build-out. - BACKLOG.md: the Reports entry flips Planned -> Phase A shipped (P1->P2), summarizing A1-A4b and the production signing-key op note; remaining work scoped to Phases B-D (OSCAL/CSV, async+scheduling, other kinds). - reports_design.md: a STATUS banner on §11 records Phase A as shipped and the two in-flight plan adjustments (coverage shipped before the structural migration; A3/A4 split backend/frontend). - SESSION_LOG.md: a Phase A handoff entry (per-PR summary, the live verification result, Phases B-D next, and the dev-serve / ephemeral-key gotchas).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First implementation slice of the Reports build-out (
docs/engineering/reports_design.md§11, Phase A1). Makes the executive report scopable instead of all-hosts-only.What
POST /api/v1/reports:generatenow accepts an optional scope{group_id?, framework?}. An empty body keeps the exact v1.0.0 all-hosts behavior.group.Service.ScopeGroup(reusesmemberCTE: manual →group_members, auto →hosts.os_family == match_family).host_countand every rule count reflect only those hosts. Unknown group →group.ErrNotFound→ 400reports.invalid_scope; an empty group reads as zero hosts (distinct from unscoped).framework_refs ? key), counting only rules in that framework.reports.scopeJSONB (migration 0041) and echoed on the wire. scope_label is derived:Production · CIS,All hosts · STIG,Production,All hosts.Frontend
A group scope picker beside Generate (a
<select>sourced fromGET /api/v1/groups, default "All hosts"), host:write-gated.scope_labelalready flows to the Library + detail rows, so the chosen scope shows up automatically.SDD
api-reportsv1.1.0 — C-07 + AC-07 (label derivation, unit), AC-08 (group scope, DB), AC-09 (framework scope, DB).frontend-reportsv1.1.0 — C-04 + AC-05 (scope picker source inspection).Validation
gofmt/go vet/go buildclean.specter check111/111 structural; api-reports 9/9, frontend-reports 5/5, 100%;specter check --test0 errors.Net: no new data collection — reports still derive from
host_rule_state+ groups, so the numbers stay consistent with the Groups page.