Task · SKT-0012.04

skforge: fix the mapper and coverage report misreporting what synthkit can model

Description

Three related defects in the forge mapper and its coverage report, found by the SKT-0012 validation run against the EKS lab cluster, 2026-08-27. Grouped because they all distort the same thing: what the report tells an operator synthkit can and cannot model.

1. A mapper gap is reported as a missing construct. The coverage report listed the four k8s-monitoring collector components under the heading “No construct exists (roadmap signal)”. A construct for Alloy self-telemetry does exist and is printed in the prompt own catalogue section a few lines earlier, so the report contradicts itself. This is the most damaging of the three: it converts a fixable name-mapping bug into apparent roadmap work, so nobody fixes it. The report must distinguish “no construct exists” from “a construct exists but nothing maps this name to it”, because those route to completely different fixes.

2. Duplicate addon entries. Two workloads belonging to the same product both matched the same construct, and the skeleton emitted that addon kind twice. It validates and loads (cardinality 9825 on the lab capture), so the consequence is a double declaration and double emission rather than a load failure — which is why it survived to be found by a human reading the YAML rather than by a check.

3. Skipped detections are silent. Five platform products running in the lab cluster were not detected as addons at all. Skipping them is correct — synthkit has no construct for any of them — but nothing in the report says so, so a reader cannot tell “not present in the cluster” from “present and deliberately unmodelled”. A coverage report whose silence is ambiguous is not a coverage report.

Together these mean an operator cannot trust the report to tell them what their forged blueprint will and will not cover.

Acceptance Criteria

Definition of Done

Implementation Notes

DELIVERED 2026-08-27 (lane L11). internal/forge/{mapper.go,report.go} + tests, cmd/skforge/main.go.

Fix 1 (mapper.go:265-291) — resolveAddonKind, a forge-side supplemental table for names capture leaves unmapped but whose construct this build already registers. alloy_health was the only currently-unmapped name with a real construct, matched by the grafana-k8s-monitoring-alloy- prefix covering the chart four Alloy workloads. The rest of the catalogue was checked for other candidates; none applied to this capture.

Fix 2 (mapper.go:150-198) — dedupe by construct KIND rather than by capture detected NAME. Capture own table legitimately maps two detected names to one kind, which is how the duplicate arose. One construct is one declaration, so the dedupe is unconditional.

Fix 3 (mapper.go:299-357, report.go rewritten) — the report now splits addon gaps into two sections rather than one, so it answers AC #1 generally rather than only for the alloy case: “No construct exists (roadmap signal)” versus “Construct exists but is not registered in this build (fix the mapping, not the roadmap)” — build/registry drift is a different bug from a roadmap gap. Every roadmap line now reads “(seen N times, detected — not absent)” so silence can never be misread as not-present. A new detector names the platform products running in the cluster that have no construct at all, aggregated one line per product with the matching workload names as evidence.

The before/after also exposed a second-order defect the validation run had not spotted: the old “addons matched to constructs: 5” was counting the duplicate, so real distinct coverage was 4. CoverageReport now takes the deduplicated skeleton rather than recomputing, so the count cannot drift from the declaration again.

skforge validate on the regenerated skeleton: OK, cardinality 9825 -> 9387 — the duplicate argocd declaration and its double emission removed, alloy_health real telemetry added.

Six new tests, each written and confirmed failing before its fix.

ROOT CAUSE ROUTED ONWARD: the five unmodelled platform products never enter the addon list at all because internal/capture/k8s.go:718-733 has no detection entries for them. The report is now honest about it, but the durable fix is capture-side — tracked as SKT-0012.06.

Final Summary

The coverage report now tells an operator the truth about what synthkit can model.

It distinguishes “no construct exists” from “a construct exists but nothing maps this name to it” — the previous report filed the k8s-monitoring collector components under a roadmap heading while their construct was printed in the prompt own catalogue a few lines earlier, converting a fixable mapping bug into apparent future work so nobody would fix it. Those names now map, and a second section covers build/registry drift, which is a different bug again.

Duplicate addon entries are gone: the dedupe keys on construct KIND rather than capture detected NAME, which is how the duplicate arose since capture legitimately maps two names to one kind.

Skipped detections are no longer silent — each roadmap line reads “detected, not absent”, and platform products running in the cluster with no construct at all are named explicitly rather than buried among generic workload gaps.

The before/after exposed a second-order defect the validation run had missed: the matched-addon count was counting the duplicate, so real distinct coverage was lower than reported. It now derives from the deduplicated skeleton so it cannot drift from the declaration again. Cardinality on the lab capture moved 9825 to 9387 as the duplicate declaration and its double emission went away and the real collector telemetry arrived.

Six new tests, each written and confirmed failing before its fix. Root cause of the silent skips is capture-side and tracked as SKT-0012.06.

View the source file on GitHub