26 KiB
Implementation Plan
Overview
This implementation plan delivers six SQL-level fixes in backend/routes/compliance.js for the cross-vertical duplicate (hostname, metric_id) bug class documented in bugfix.md and design.md. Each fix targets a single endpoint or persistence block:
GET /items—DISTINCT ON (hostname, metric_id)plus a defensivegroupByHostnameSetGET /items/:hostname—DISTINCT ON (metric_id, status)with JS-side sortGET /vcl/statsheavy-hitters and per-team totals —device_teamCTEGET /vcl/statsforecast-burndown —DISTINCT ON (hostname, metric_id)GET /mttr—DISTINCT ON (hostname, metric_id)persistUpload()snapshot block —hostname_statusCTE classifying each hostname viaMIN(status)
The plan follows the bugfix workflow: a single property-based exploration test (Property 1) covering all six affected sites runs on UNFIXED code first to confirm the bug exists, then preservation property tests (Property 2) capture single-vertical and unique-key behaviour as a baseline. Implementation tasks apply each fix in order (3.1–3.6), followed by re-running the exploration test (3.7) and the preservation suite (3.8). The checkpoint runs the full backend test suite to confirm no regressions.
Task Dependency Graph
Tasks are organised into execution waves. Tasks within the same wave may run in parallel; tasks in later waves depend on the completion of earlier waves. Sub-tasks 3.1–3.6 are independent of each other (each touches a distinct query in backend/routes/compliance.js) and form a single parallel wave; sub-tasks 3.7 and 3.8 depend on all six fixes being landed.
{
"waves": [
{
"wave": 1,
"description": "Write the bug-condition exploration property test on UNFIXED code (Property 1, six slices covering /items, /items/:hostname, /vcl/stats heavy-hitters, /vcl/stats forecast-burndown, /mttr, persistUpload snapshot).",
"tasks": ["1"]
},
{
"wave": 2,
"description": "Write the preservation property tests on UNFIXED code (observation-first methodology), recording baseline responses for single-vertical and unique-key fixtures across all five read endpoints and persistUpload(). Property 8.A is written but skipped pending the fix.",
"tasks": ["2"]
},
{
"wave": 3,
"description": "Apply the six SQL-level fixes in backend/routes/compliance.js. Each sub-task targets a distinct query and is independent of the others — sub-tasks 3.1 through 3.6 can be implemented in parallel.",
"tasks": ["3.1", "3.2", "3.3", "3.4", "3.5", "3.6"]
},
{
"wave": 4,
"description": "Re-run the exploration test (3.7) and preservation tests (3.8) against the fixed code. 3.7 confirms all six bug-condition slices now pass; 3.8 confirms preservation properties still pass and unskips Property 8.A to verify the representative-row policy.",
"tasks": ["3.7", "3.8"]
},
{
"wave": 5,
"description": "Checkpoint — run the full backend Jest suite to confirm no regressions in adjacent compliance tests (vcl-compliance-reporting, vcl-aggregated-burndown).",
"tasks": ["4"]
}
]
}
Tasks
-
1. Write bug condition exploration property test
- Property 1: Bug Condition - Cross-Vertical Duplicate
(hostname, metric_id)Distorts Compliance Endpoints - CRITICAL: This test MUST FAIL on unfixed code — failure confirms the bug exists in all six affected sites
- DO NOT attempt to fix the test or the code when it fails
- NOTE: This test encodes the expected behaviour (Property 1–6 from design.md). It will validate the fix when it passes after implementation.
- GOAL: Surface counterexamples that demonstrate cross-vertical duplicate
(hostname, metric_id)rows distort/items,/items/:hostname,/vcl/statsheavy-hitters,/vcl/statsforecast-burndown,/mttr, and thepersistUpload()snapshot block - Scoped PBT Approach: Six concrete failing scenarios (one per affected site) generated by fast-check from a small fixed input space — each scenario seeds two
compliance_itemsrows for the same(hostname, metric_id)acrossvertical IS NULLandvertical = 'NTS_AEO'. Usefast-check(already in use underbackend/__tests__/*.property.test.js). - Place the test at
backend/__tests__/compliance-duplicate-failing-metrics.exploration.property.test.js. Mock../dbwithjest.mockexactly likevcl-compliance-reporting.property.test.jsso route handlers can be invoked against an in-memory fixture, or stand up a transactionalpgtest schema if the repo already supports one — match whichever convention the existing compliance tests use. - Slice 1.A —
/itemsfailing-metrics dedup (Bug Condition isBugCondition: two active rows for(STEAM-INTERSIGHT, 7.1.1), onevertical IS NULL, onevertical = 'NTS_AEO', bothteam = 'STEAM')- Seed
fixtureCrossVerticalDuplicateActivefrom design.md §Test Fixtures (differentseen_count: 3 and 5) - Call
GET /items?team=STEAM&status=active - Assert
response.devices[0].failing_metrics.filter(m => m.metric_id === '7.1.1').length === 1(Property 1 from design.md) - EXPECTED OUTCOME on unfixed code: FAILS —
failing_metricscontains two7.1.1entries becausegroupByHostnamepushes per row
- Seed
- Slice 1.B —
/items/:hostname(metric_id, status)dedup (same fixture)- Call
GET /items/STEAM-INTERSIGHT - Assert
response.metrics.filter(m => m.metric_id === '7.1.1' && m.status === 'active').length === 1(Property 2 from design.md) - Assert the surviving entry's
seen_count === 5(max across duplicates per Property 8) - EXPECTED OUTCOME on unfixed code: FAILS — detail query has no vertical filter and no dedup, returns two
7.1.1/activeentries
- Call
- Slice 1.C —
/vcl/statsheavy-hitters cross-team (Bug Condition: two active rows for the same(hostname, metric_id)whoseteamdiffers across verticals)- Seed
fixtureCrossVerticalTeamMismatchfrom design.md §Test Fixtures (team = 'STEAM'legacy,team = 'ACCESS-ENG'NTS_AEO) - Call
GET /vcl/stats - Assert
SUM(heavy_hitters[*].non_compliant) === stats.non_compliant(Property 3 from design.md) - EXPECTED OUTCOME on unfixed code: FAILS — sum exceeds the global count by 1 because
COUNT(DISTINCT hostname) GROUP BY teamcounts the hostname under both teams
- Seed
- Slice 1.D —
/vcl/statsforecast-burndown blockers (Bug Condition: two active rows for the same(hostname, metric_id)both with non-nullresolution_date, same team)- Seed
fixtureForecastDuplicateResolutionDatefrom design.md §Test Fixtures (team = 'STEAM', bothresolution_date = '2025-09-30') - Call
GET /vcl/stats, locate theSTEAMentry invertical_breakdown - Assert the unclamped
teamNonCompliant - forecastItems.length === blockersANDblockers >= 0(Property 4 from design.md) - EXPECTED OUTCOME on unfixed code: FAILS — unclamped value is
-1, route reports0afterMath.max(blockers, 0), hiding the inconsistency. The unclamped check makes the failure visible
- Seed
- Slice 1.E —
/mttraging buckets (same fixture as Slice 1.A, withseen_count = 5on both rows)- Call
GET /mttr - Assert
SUM(aging[*].total) === COUNT(DISTINCT (hostname, metric_id) WHERE status = 'active')over the seeded items (Property 5 from design.md) - EXPECTED OUTCOME on unfixed code: FAILS —
bucketAgingItems()increments the4–6 cyclesbucket twice, exceeding the distinct count by 1
- Call
- Slice 1.F —
persistUpload()snapshot block (Bug Condition: same(hostname, metric_id, team)withstatus = 'active'in legacy andstatus = 'resolved'in NTS_AEO)- Seed
fixtureCrossVerticalStatusMismatchfrom design.md §Test Fixtures - Run
persistUpload()with a no-op upload, read back thecompliance_snapshotsrow for the current month andSTEAM - Assert
compliant + non_compliant <= total_devices(Property 6 from design.md) - EXPECTED OUTCOME on unfixed code: FAILS — the hostname is counted in both
compliantandnon_compliant, sum exceedstotal_devicesby 1
- Seed
- Run on UNFIXED code and capture all six counterexamples in the test output
- Document the six counterexamples in the test file (a leading comment block listing the failing slice → symptom mapping) so the bug surface is recoverable from the test alone
- Mark task complete when the test is written, run, and all six slice failures are documented
- Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7
- Property 1: Bug Condition - Cross-Vertical Duplicate
-
2. Write preservation property tests (BEFORE implementing fix)
- Property 2: Preservation - Single-Vertical and Unique-Key Inputs Are Byte-For-Byte Unchanged
- IMPORTANT: Follow observation-first methodology — observe behaviour on UNFIXED code, then write property tests that capture it
- Place the test at
backend/__tests__/compliance-duplicate-failing-metrics.preservation.property.test.js. Usefast-checkand the samejest.mock('../db', ...)pattern as the existing property tests - Build the unique-key generator:
fc.array(...)ofcompliance_itemsrows where every(hostname, metric_id)pair is unique across the array. Mixvertical IS NULL,vertical = 'NTS_AEO', and other verticals; mixstatus = 'active'andstatus = 'resolved'; mix teams fromALLOWED_TEAMS - Observation step: For each fixture from design.md §Test Fixtures (
fixtureLegacyOnly,fixtureNTSAEOOnly,fixtureMultiHostnameMixedrestricted to its unique-key subset, plus a hand-built empty-state fixture), run all five read endpoints +persistUpload()on UNFIXED code and capture responses. Persist these as snapshot fixtures alongside the test (e.g., a small JSON file underbackend/__tests__/fixtures/compliance-duplicate-failing-metrics/) so the test compares against the recorded baseline rather than against a moving target - Property 7.A —
/itemsunique-key preservation: For any unique-key fixture, assert the response fromGET /items?team=...&status=...(acrossteam ∈ ALLOWED_TEAMSandstatus ∈ {'active', 'resolved'}) equals the recorded baseline byte-for-byte (deep equality on the JSON response) - Property 7.B —
/items/:hostnameunique-key preservation: For any unique-key fixture, assert the response fromGET /items/:hostnamefor every seeded hostname equals the recorded baseline. Also assert active-then-resolved ordering is preserved (response.metrics[0..k].status === 'active'thenresponse.metrics[k+1..].status === 'resolved', sorted bymetric_idwithin each group), per design.md §Preservation Requirements item 5 - Property 7.C —
/vcl/statsunique-key preservation: Assert response equality acrossstats.compliant,stats.non_compliant, thedonutblock (blocked/in_progress),heavy_hitters(full array), andvertical_breakdown(full array includingblockers). Generate inputs that varyresolution_datedensity to exercise the donut categorisation - Property 7.D —
/mttrunique-key preservation: Assert response equality on the fullagingarray (per-bucket per-team totals) - Property 7.E —
persistUpload()unique-key preservation: For a single-status-per-hostname fixture (every hostname has onlyactiveor onlyresolvedrows, never both, within a team), runpersistUpload()and assert thecompliance_snapshotsrows for the current(snapshot_month, vertical)are identical to the recorded baseline - Property 7.F —
/itemsquery-param validation preservation: Assert that?team=OTHER(not inALLOWED_TEAMS) returns HTTP 400 and?status=invalidreturns HTTP 400, on both unfixed and fixed code. Assert/items/:hostnamefor an unknown hostname returns HTTP 404 - Property 8.A — Representative-row policy on duplicates: For inputs WITH duplicates, assert the surviving entry carries
seen_count = MAX(seen_count),first_seen = MIN(first_seen),last_seen = MAX(last_seen)across the duplicate rows (this is the only preservation property that exercises the duplicate path, since it specifies WHAT the dedup must produce — keep it in the preservation file because it defines the contract the fix must satisfy) - Run the full preservation suite on UNFIXED code
- EXPECTED OUTCOME: Properties 7.A–7.F PASS on unfixed code (they describe baseline behaviour to preserve). Property 8.A is the only one expected to FAIL on unfixed code, since it asserts the post-fix representative-row contract — exclude it from the unfixed-code run or mark it as
test.skipuntil the fix lands, with a comment pointing to task 3.5 - Mark task complete when 7.A–7.F are written, run, and passing on unfixed code, and 8.A is written but skipped pending the fix
- Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7
-
3. Fix for cross-vertical duplicate
(hostname, metric_id)rows distorting compliance endpoints-
3.1 Implement Fix 1:
GET /itemsDISTINCT ON (hostname, metric_id)and defensivegroupByHostnamededup- Edit
backend/routes/compliance.js,router.get('/items', ...)(around line 535) - Rewrite the items query as
SELECT DISTINCT ON (ci.hostname, ci.metric_id) ... ORDER BY ci.hostname, ci.metric_id, ci.seen_count DESC, ci.upload_id DESCkeeping the existingWHERE ci.team = $1 AND ci.status = $2 AND (ci.vertical IS NULL OR ci.vertical = 'NTS_AEO')predicate intact (per design.md Fix 1 step 1 and Preservation Requirements bullet 4) - Edit
groupByHostname()(around line 213) to add a_seenMetricIdsSet per device, pushfailing_metricsonly when themetric_idhas not been seen, aggregateseen_countviaMath.max,first_seenviaMath.min,last_seenviaMath.max, and strip_seenMetricIdsfrom the returned device object so the response shape is unchanged (per design.md Fix 1 step 2) - Bug_Condition: isBugCondition(items) — two or more rows share
(hostname, metric_id)across verticals (design.md §Bug Condition) - Expected_Behavior: Property 1 —
device.failing_metrics.length === new Set(device.failing_metrics.map(m => m.metric_id)).size(design.md §Correctness Properties Property 1) - Preservation: Single-vertical and unique-key inputs unchanged;
(ci.vertical IS NULL OR ci.vertical = 'NTS_AEO')predicate retained;team/statusquery-param validation unchanged (design.md §Preservation Requirements bullets 1, 4, 7) - Requirements: 2.1, 3.1, 3.3, 3.4
- Edit
-
3.2 Implement Fix 2:
GET /items/:hostnameDISTINCT ON (metric_id, status)and JS sort- Edit
backend/routes/compliance.js,router.get('/items/:hostname', ...)(around line 575) - Rewrite the metrics query as
SELECT DISTINCT ON (ci.metric_id, ci.status) ... FROM compliance_items ci ... WHERE ci.hostname = $1 ORDER BY ci.metric_id, ci.status, ci.seen_count DESC, ci.upload_id DESC(per design.md Fix 2 step 1) - Add a JS-side sort on the deduped rows that reproduces the original
ORDER BY ci.status DESC, ci.metric_idordering —metricRows.sort((a, b) => a.status !== b.status ? b.status.localeCompare(a.status) : a.metric_id.localeCompare(b.metric_id))(per design.md Fix 2 step 2) - Leave the existing
metricRows.find(r => r.status === 'active') || metricRows[0]identity lookup unchanged (per design.md Fix 2 step 3) - Bug_Condition: isBugCondition(items) plus duplicate rows for the same
hostnameacross verticals with no vertical filter on the detail query - Expected_Behavior: Property 2 — exactly one entry per
(metric_id, status)pair, surviving entry carriesMAX(seen_count)across duplicates - Preservation: active-before-resolved ordering,
metric_idordering within each status group, identity lookup unchanged - Requirements: 2.2, 2.3, 3.2, 3.3
- Edit
-
3.3 Implement Fix 3:
/vcl/statsheavy-hitters and per-team totals viadevice_teamCTE- Edit
backend/routes/compliance.js,router.get('/vcl/stats', ...)(around line 990, theteamRowsquery) - Replace the heavy-hitters query with a CTE:
WITH device_team AS (SELECT DISTINCT ON (hostname) hostname, COALESCE(team, 'Unknown') AS team, resolution_date FROM compliance_items WHERE status = 'active' ORDER BY hostname, seen_count DESC, upload_id DESC) SELECT team, COUNT(DISTINCT hostname)::int AS non_compliant, MAX(resolution_date) AS compliance_date FROM device_team GROUP BY team ORDER BY COUNT(DISTINCT hostname) DESC(per design.md Fix 3 step 1) - Rewrite the per-team-total query inside the
for (const teamRow of teamRows)loop to use the samedevice_team-style CTE:WITH device_team AS (SELECT DISTINCT ON (hostname) hostname, COALESCE(team, 'Unknown') AS team FROM compliance_items ORDER BY hostname, seen_count DESC, upload_id DESC) SELECT COUNT(*)::int AS total FROM device_team WHERE team = $1(per design.md Fix 3 step 3) - Leave the global
stats.non_compliantquery unchanged — it is already correct (per design.md Fix 3 step 2) - Bug_Condition: a hostname's
teamdiffers across verticals soCOUNT(DISTINCT hostname) GROUP BY teamcounts it under both teams - Expected_Behavior: Property 3 —
SUM(heavy_hitters[*].non_compliant) === stats.non_compliantand each hostname assigned to exactly one team (the team from its representative row) - Preservation: global
stats.compliant/stats.non_compliantunchanged, donut categorisation unchanged - Requirements: 2.5, 3.6
- Edit
-
3.4 Implement Fix 4:
/vcl/statsforecast-burndownDISTINCT ON (hostname, metric_id)- Edit
backend/routes/compliance.js,router.get('/vcl/stats', ...)(around line 1015, theforecastItemsquery) - Rewrite as
SELECT DISTINCT ON (hostname, metric_id) resolution_date FROM compliance_items WHERE status = 'active' AND COALESCE(team, 'Unknown') = $1 AND resolution_date IS NOT NULL ORDER BY hostname, metric_id, seen_count DESC, upload_id DESC(per design.md Fix 4 step 1) - Leave
blockers = teamNonCompliant - forecastItems.lengthand theMath.max(blockers, 0)clamp unchanged — the clamp becomes a no-op in correct operation but stays as belt-and-braces (per design.md Fix 4 steps 2 and 3) - Bug_Condition: duplicate
(hostname, metric_id)rows with non-nullresolution_dateinflateforecastItems.lengthpastteamNonCompliant - Expected_Behavior: Property 4 — unclamped
teamNonCompliant - dedupedForecastCount === blockersandblockers >= 0 - Preservation:
Math.max(blockers, 0)clamp retained as a no-op safeguard, downstream forecast bucketing unchanged - Requirements: 2.6, 3.6
- Edit
-
3.5 Implement Fix 5:
/mttrDISTINCT ON (hostname, metric_id)in SQL- Edit
backend/routes/compliance.js,router.get('/mttr', ...)(around line 824) - Rewrite the query as
SELECT DISTINCT ON (hostname, metric_id) COALESCE(seen_count, 1) AS seen_count, team FROM compliance_items WHERE status = 'active' ORDER BY hostname, metric_id, seen_count DESC, upload_id DESC(per design.md Fix 5 step 1) - Leave
bucketAgingItems()unchanged — its contract is preserved because it is also called from/vcl/stats(per design.md Fix 5 steps 2 and 3) - Bug_Condition: duplicate active rows for the same
(hostname, metric_id)are bucketed twice bybucketAgingItems() - Expected_Behavior: Property 5 —
SUM(aging[*].total) === COUNT(DISTINCT (hostname, metric_id) WHERE status = 'active')and each unique violation bucketed exactly once with a single representativeseen_count - Preservation:
bucketAgingItems()contract unchanged, per-team buckets on single-vertical fixtures unchanged - Requirements: 2.7, 3.7
- Edit
-
3.6 Implement Fix 6:
persistUpload()snapshot viahostname_statusCTE- Edit
backend/routes/compliance.js,persistUpload()(lines 81–192), specifically theverticalStatsquery at line 157 - Rewrite the snapshot query as
WITH hostname_status AS (SELECT team, hostname, MIN(status) AS status FROM compliance_items WHERE team IS NOT NULL GROUP BY team, hostname) SELECT team AS vertical, COUNT(*)::int AS total_devices, COUNT(*) FILTER (WHERE status = 'resolved')::int AS compliant, COUNT(*) FILTER (WHERE status = 'active')::int AS non_compliant FROM hostname_status GROUP BY team(per design.md Fix 6 step 1) - Leave the downstream
INSERT ... ON CONFLICT (snapshot_month, vertical) DO UPDATEblock andcompliance_pctcalculation unchanged (per design.md Fix 6 steps 2 and 3) - Bug_Condition: a hostname has both
activeandresolvedrows for the same team across verticals, so theCASE WHEN status = 'X' THEN hostname ENDpattern lets it appear in bothcompliantandnon_compliant - Expected_Behavior: Property 6 — every snapshot row satisfies
compliant + non_compliant === total_devices(active wins over resolved viaMIN(status)) - Preservation: snapshot rows for single-status-per-hostname fixtures unchanged, error-handling try/catch unchanged
- Requirements: 2.4, 3.5
- Edit
-
3.7 Verify bug condition exploration test now passes
- Property 1: Expected Behavior - Cross-Vertical Duplicate
(hostname, metric_id)Distorts Compliance Endpoints - IMPORTANT: Re-run the SAME test from task 1 — do NOT write a new test
- The test from task 1 encodes the expected behaviour for all six slices (1.A–1.F). When all six slices pass, the bug is fixed across every affected site
- Run
npx jest backend/__tests__/compliance-duplicate-failing-metrics.exploration.property.test.js --runInBand(or the repo's equivalent jest invocation) - EXPECTED OUTCOME: All six slices PASS — Slice 1.A (
/itemsdedup), Slice 1.B (/items/:hostnamededup), Slice 1.C (/vcl/statsheavy-hitters), Slice 1.D (/vcl/statsforecast blockers), Slice 1.E (/mttraging), Slice 1.F (persistUpload()snapshot invariant) - Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7
- Property 1: Expected Behavior - Cross-Vertical Duplicate
-
3.8 Verify preservation tests still pass and unskip Property 8.A
- Property 2: Preservation - Single-Vertical and Unique-Key Inputs Are Byte-For-Byte Unchanged
- IMPORTANT: Re-run the SAME tests from task 2 — do NOT write new tests
- Unskip Property 8.A (the representative-row policy assertion) which was deferred in task 2 because it asserts the post-fix contract
- Run
npx jest backend/__tests__/compliance-duplicate-failing-metrics.preservation.property.test.js --runInBand - EXPECTED OUTCOME: Properties 7.A–7.F continue to PASS (no regressions on unique-key or single-vertical inputs) and Property 8.A now PASSES on the duplicate path (
seen_count = MAX,first_seen = MIN,last_seen = MAXacross duplicates) - Confirm the recorded baseline JSON snapshots match the post-fix output for every unique-key fixture
- Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7
-
-
4. Checkpoint - Run full backend test suite and confirm all tests pass
- Run the full backend test suite from
backend/:npx jest --runInBand(or the repo's standard test command) - Confirm both new property test files pass and that no existing tests under
backend/__tests__/regressed — particularlyvcl-compliance-reporting.property.test.js,vcl-aggregated-burndown.property.test.js, andvcl-compliance-reporting.test.js, all of which exercise overlapping compliance code paths - If any pre-existing test fails, diagnose whether the failure is a genuine regression introduced by the fix or a pre-existing flake. If a regression, return to the relevant sub-task in step 3 and adjust the fix; do not silence the failing test
- Ask the user if any unexpected questions arise about test scope, fixture naming, or whether any preservation snapshot needs to be re-recorded against the fixed code
- Mark complete when the full suite is green
- Run the full backend test suite from
Notes
- Fix sequencing: Tasks 3.1 through 3.6 are independent — each one targets a distinct query and can be implemented and committed in any order. Implementers may parallelise work across these sub-tasks, but tasks 3.7 and 3.8 depend on all six fixes being landed before they can validate.
- Test framework: Both new property tests follow the existing
backend/__tests__/*.property.test.jsconvention —fast-checkfor generators,jest.mock('../db', ...)for mocking thepgpool, and matching helper imports (auditLog,ivantiApi) where required. Seevcl-compliance-reporting.property.test.jsfor the canonical pattern. - Fixture location: Place fixtures at
backend/__tests__/fixtures/compliance-duplicate-failing-metrics/if a directory-based layout is preferred, or inline as factory functions in the test files. Match whichever convention the existing compliance test files use — if they inline factories (asvcl-compliance-reporting.test.jsdoes), follow suit. - Property 8.A skip: Property 8.A in task 2 is intentionally skipped on unfixed code because it asserts the post-fix representative-row contract. Task 3.8 unskips it. This is the only test that exercises the duplicate path inside the preservation file; it lives there because the contract it captures is precisely the one preservation must not violate after the fix lands.
- Adjacent spec coordination: Fix 6 (
persistUpload()snapshot) is conceptually adjacent to but mechanically distinct from thepersistUploadfix incompliance-duplicate-chart-entries. That spec adds aWHERE vertical = $1filter to scope the snapshot to one vertical; this spec adds thehostname_statusCTE so each hostname is classified once within whichever vertical is snapshotted. Both are independently necessary. If both specs land in the same release, ensure the merged query carries both the vertical filter AND thehostname_statusCTE. Math.max(blockers, 0)clamp: Left in place as a belt-and-braces safeguard per design.md Fix 4 step 3. After the fix it becomes a no-op; if a future regression reintroduces inconsistency, Property 4 (Slice 1.D) catches it before the clamp can mask the underlying bug.- Documentation follow-up: Per
.kiro/steering/workflow.md, after the fix lands and is committed tomaster, add a bug report underdocs/bug-reports/on theops/recordsbranch using thecompliance-duplicate-failing-metrics-<YYYY-MM-DD>.mdnaming convention. Each of the six fix sites is a separate## Bug Nentry following the Symptom → Cause → Fix triad.