Skip to content

feat(compliance): lens as default score everywhere, OS-resolved, framework-scoped trend (v0.6.0) - #748

Merged
remyluslosius merged 5 commits into
mainfrom
feat/compliance-lens-default-everywhere
Jul 17, 2026
Merged

feat(compliance): lens as default score everywhere, OS-resolved, framework-scoped trend (v0.6.0)#748
remyluslosius merged 5 commits into
mainfrom
feat/compliance-lens-default-everywhere

Conversation

@remyluslosius

Copy link
Copy Markdown
Contributor

Summary

Reported from three screenshots: the same host shows the all-corpus compliance score everywhere except its detail-page hero tile. For owas-tst02: hero tile could read a framework-scoped %, but the hosts list showed 70.2% (401/571 all rules), Scans Coverage 70%, and the trend 69.8% — the raw all-rules number.

Root cause: the effective-lens resolver (framework.EffectiveTarget) was wired into exactly one production path — GetHostByID (the hero tile). Every other score surface ignored the lens unless the frontend passed an explicit ?framework=.

Fix (backend-only)

With no explicit ?framework=:

  • GET /hosts list compliance_summary scores each host against its own effective target (host_effective_target → org default), resolved per host in one grouped query. Implemented by reusing framework.MatchSQL with a column reference (eff.lens) instead of a bind placeholder, so the family-aware match runs per row. → the list column now matches the hero tile.
  • Scans → Coverage inherits this automatically (it reads the same /hosts endpoint).
  • GET /fleet/score defaults to the org default lens (ComplianceConfig.DefaultFramework). Per-host targets are not aggregated into one fleet number — mixed-fleet cohorts are a later phase; the org default is the single-lens fleet view.

An explicit ?framework= still overrides uniformly. With neither a target nor an org default set, both stay All rules (backward compatible — existing tests unaffected).

The frontend already renders compliance_summary + the fleet score, so the lens-scoped numbers appear with no UI or schema change.

Explicitly out of scope (deferred)

The compliance TREND card stays all-rules: posture_snapshots carries a single all-rules score per host per day with no framework dimension. Making the trend lens-scoped needs new storage (migration + rollup change) — the known "Phase 3c framework-scoped trend". Documented in spec C-06.

Testing

  • gofmt · go build ./... · vet clean · no openapi/generated/frontend drift.
  • specter check 116/116; check --test 0 errors; coverage --strictness annotation 100% — system-compliance-lens 10/10.
  • New API tests (real Postgres): TestAPI_HostsList_DefaultsToPerHostEffectiveTarget (AC-09 — two hosts with different targets each reflect their own; explicit override), TestAPI_FleetScore_DefaultsToOrgLens (AC-10 — org-default applied, explicit override).
  • Regression: existing TestAPI_Fleet*, TestAPI_Hosts*, TestAPI_Compliance*, TestFleetHandlers_NoSQL_NoPoolAccess (AC-13) all green; AC-17 (empty == no-param) holds since both default identically.

🤖 Generated with Claude Code

The effective compliance lens was auto-applied on only ONE surface — the
host-detail hero tile. The hosts-list column, Scans Coverage column, and the
fleet KPI all showed the raw all-corpus score unless the frontend passed an
explicit ?framework=, so one host could read (e.g.) 78% STIG on its detail
page but 70.2% all-rules everywhere else.

Now, with no explicit ?framework=:
- GET /hosts list compliance_summary scores each host against its OWN
  effective target (host_effective_target -> org default), resolved per host
  in one grouped query (reuses framework.MatchSQL with a column reference).
  The Scans Coverage tab inherits this (same endpoint).
- GET /fleet/score defaults to the org default lens
  (ComplianceConfig.DefaultFramework). Per-host targets are not aggregated
  into one fleet number (mixed-fleet cohorts are a later phase).
An explicit ?framework= still overrides uniformly; with neither a target nor
an org default set, both stay All rules (backward compatible).

Backend-only: the frontend already renders compliance_summary + the fleet
score, so the lens-scoped numbers appear with no UI or schema change. The
compliance TREND card stays all-rules (its posture snapshots carry no
framework dimension — a separate storage change, deferred).

Spec: system-compliance-lens v1.4.0 (C-06, AC-09, AC-10). specter check +
check --test clean; spec at 100% annotation coverage. New API tests verify
per-host default, org-default fleet, and explicit-override on both.
…hmark

A per-host compliance score was resolving a family lens with the family-UNION
match (framework.MatchSQL): "stig" matched stig_rhel9 + stig_rhel10 + stig_rhel8
+ stig_ubuntu* all at once. A single host carries mapped rules for several OS
benchmarks, so this graded (e.g.) a RHEL 9 host partly against the RHEL 10 STIG
— the hosts list showed 70% (400/571, family union) while the host-detail hero
tile showed 88% (353/401, stig_rhel9) for the SAME host and lens.

Fix: the three per-host SCORE surfaces now resolve a family to the host's OWN
OS-specific key via new framework.OSResolvedMatchSQL(famRef, osFamily, osVersion):
a RHEL 9 host's "stig" -> stig_rhel9 only. An OS-neutral family (nist_800_53,
pci_dss_4, srg) and an explicit specific key resolve to the bare key.

- loadHostListComplianceByIDs (hosts list + Scans Coverage): joins hosts, OS-resolves per row.
- loadHostComplianceSummary (GetHostByID compliance_summary): OS-resolves.
- fleetrollup.FleetComplianceScore: joins hosts, each measured against its own OS benchmark then aggregated (a mixed-OS fleet's "stig" still spans rhel9+rhel10, each via its own key).

The family-union MatchSQL is retained for the family-scoped rule LISTS (fleet
top-failing-rules, recent-changes), where "any host's STIG rule" is the intent.

Result: the list column, host-detail tile, and fleet score all agree for a
host (owas-el9-ks01 reads 88% everywhere, not 70% on the list / 88% on detail).

Spec system-compliance-lens: C-06 amended (OS-resolved scores), C-02 clarified
(family union scopes to rule-lists), AC-03/AC-09/AC-10 updated to assert the
wrong-OS variant is excluded. Backend-only; no schema change. specter clean,
100% coverage; framework + fleetrollup + server suites green.
…lows the lens

The compliance trend card read all-rules posture snapshots (one score per
host per day, no framework dimension), so it showed 68% all-rules while the
host-detail tile / list / coverage showed 88% STIG for the same host once a
lens was the default.

Store the snapshot per lens instead. Migration 0053 adds a `framework` column
(PK now host_id, snapshot_date, framework). The daily Rollup writes the
all-rules series (framework='') PLUS one row per framework FAMILY the host has
rules in, each OS-RESOLVED via the same rule as the score surfaces: a RHEL 9
host's "stig" row scores stig_rhel9 only (a stig_rhel10 rule it carries is
excluded), OS-neutral families (nist_800_53, pci_dss_4, srg) resolve via the
bare key. A rule counts at most once per family.

The trend endpoints resolve the effective lens server-side (host: effective
target; fleet: org default) and read that series — so the trend line now
agrees with the tile. Storing every family means switching the org default
just selects a different pre-computed series with no recompute. History is
start-fresh: pre-0053 rows are the all-rules series, per-family accrues forward.

HostTrend/FleetTrend gain a lens param (a specific key normalizes to its
family, stig_rhel9 -> stig). No API schema change (lens is resolved
server-side, not a query param).

Specs: system-posture-snapshots v1.1.0 (C-01 amended, C-04, AC-03),
api-compliance-trend v1.1.0 (C-03, AC-05); compliance-lens C-06 updated (trend
now in scope). specter clean, 100% coverage; posture + trend-handler suites green.
@github-actions github-actions Bot added size/XL and removed size/L labels Jul 17, 2026
The Groups page compliance numbers were the only score surface still fully
UNLENSED — both the per-group AvgCompliancePct and the groups-page fleet
AvgCompliancePct counted raw passing/(pass+fail) over all rules, ignoring the
org default lens and per-host/group targets. So a Groups page showed all-rules
while the hosts list / tile / trend showed STIG.

Now both are lens-scoped and OS-resolved (framework.OSResolvedMatchSQL):
- Per-group AVG: each member scored against its EFFECTIVE target
  (host_effective_target, else the org default), resolved to that host's OS key
  (a RHEL 9 member's stig -> stig_rhel9), so a group's AVG matches the hosts
  list / host-detail tile. Connectivity counts (hosts/online/down) stay OS-agnostic.
- Groups-page fleet AVG: the org default lens, each host OS-resolved — the same
  rule as GET /fleet/score, so the two fleet KPIs agree.
Empty target + empty org default = all rules (backward compatible; NULLIF
guards the empty-string case).

The handler loads the org default (h.sysCfg.LoadCompliance) and threads it into
group.List / group.Summary. Backend-only, no schema/API change.

Spec system-compliance-lens AC-11 (groups lens-scoped + OS-resolved). specter
clean, 100% coverage; group + server-group suites green.
Compliance-lens Phase 3 completion: the framework lens is the default score on
every surface (list, Coverage, fleet, Groups, host trend), per-host scores
resolve to the host's OS-specific benchmark (a RHEL 9 host's STIG = stig_rhel9,
not the family union), and framework-scoped posture snapshots (migration 0053)
make the compliance trend follow the lens.
@remyluslosius remyluslosius changed the title feat(compliance): default the framework lens across the score surfaces feat(compliance): lens as default score everywhere, OS-resolved, framework-scoped trend (v0.6.0) Jul 17, 2026
@remyluslosius
remyluslosius merged commit 1375e45 into main Jul 17, 2026
27 checks passed
@remyluslosius
remyluslosius deleted the feat/compliance-lens-default-everywhere branch July 17, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant