← back to Cli Printing Press
fix(cli): scorecard human output shows N/A for opt-out dimensions (#406)
0954bcafcc0142cd0517aa2faf48c2e0ba4ce687 · 2026-04-29 20:55:14 -0700 · Trevin Chow
* fix(cli): scorecard human output shows N/A for opt-out dimensions
The scorecard's human-readable print was missing six dimensions
entirely (mcp_token_efficiency, mcp_remote_transport, mcp_tool_design,
mcp_surface_strategy, cache_freshness, live_api_verification, plus
agent_workflow). Three of those mark themselves "didn't measure" via
sc.UnscoredDimensions when the spec hasn't opted into the feature
they score (no MCP intents, no cache, etc.); the scorer's composite
denominator already excludes them, but the missing per-dimension
print line meant the user only saw "0/10" via downstream consumers
that read scorecard.json directly. Reads as a defect when in fact
the dimension was never scored.
Add the missing dimensions to renderHumanScorecard. The optional ones
go through the existing renderScore (now `render`) closure so they
display as N/A when sc.IsDimensionUnscored returns true. The agent
workflow dimension is required, so it just renders %d/10.
Extract the print into a top-level renderHumanScorecard(io.Writer,
*Scorecard) function so it's directly testable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(cli): drop stale renderOptionalScore reference in doc comment
The renderHumanScorecard doc comment named "renderOptionalScore" — a
function that doesn't exist; the closure inside the function is just
called "render". One-line fix from the /simplify pass.
Larger structural cleanups the simplify reviewers surfaced — dimension-
name string constants and a table-driven render shared with
writeScorecardMD — touch existing scorecard.go callers and warrant
their own PRs rather than being squeezed into this one.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(cli): scorecard prints MCPQuality + extract dimension-name constants
Two review follow-ups:
1. Add the missing MCP Quality print line. The scorecard scores
mcp_quality (separate from mcp_description_quality), includes it in
the tier-1 raw sum, and surfaces it in writeScorecardMD's table —
but the human print path skipped it entirely. Users running
`printing-press scorecard` saw a Total that didn't account for one
of the dimensions feeding it. Now prints `MCP Quality %d/10`
alongside the others.
2. Extract dimension-name string constants in
internal/pipeline/scorecard.go (DimMCPDescriptionQuality,
DimMCPTokenEfficiency, ...). The dimension keys are referenced at
~10 sites — recordOptionalScore calls, scorecardTierMax calls,
UnscoredDimensions appends, and writeScorecardMD's data tables —
plus the new render() call sites in cli/scorecard.go. A typo at
any one site silently flips the dimension's classification because
IsDimensionUnscored does a string match against UnscoredDimensions.
Constants make every typo a compile error.
The constants only cover dimensions that can land in
UnscoredDimensions (the silent-bug class). Always-scored dimensions
remain string literals; they don't go through the unscored-set
machinery and the typo risk doesn't apply.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
M internal/cli/scorecard.goA internal/cli/scorecard_test.goM internal/pipeline/scorecard.go
Diff
commit 0954bcafcc0142cd0517aa2faf48c2e0ba4ce687
Author: Trevin Chow <trevin@trevinchow.com>
Date: Wed Apr 29 20:55:14 2026 -0700
fix(cli): scorecard human output shows N/A for opt-out dimensions (#406)
* fix(cli): scorecard human output shows N/A for opt-out dimensions
The scorecard's human-readable print was missing six dimensions
entirely (mcp_token_efficiency, mcp_remote_transport, mcp_tool_design,
mcp_surface_strategy, cache_freshness, live_api_verification, plus
agent_workflow). Three of those mark themselves "didn't measure" via
sc.UnscoredDimensions when the spec hasn't opted into the feature
they score (no MCP intents, no cache, etc.); the scorer's composite
denominator already excludes them, but the missing per-dimension
print line meant the user only saw "0/10" via downstream consumers
that read scorecard.json directly. Reads as a defect when in fact
the dimension was never scored.
Add the missing dimensions to renderHumanScorecard. The optional ones
go through the existing renderScore (now `render`) closure so they
display as N/A when sc.IsDimensionUnscored returns true. The agent
workflow dimension is required, so it just renders %d/10.
Extract the print into a top-level renderHumanScorecard(io.Writer,
*Scorecard) function so it's directly testable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(cli): drop stale renderOptionalScore reference in doc comment
The renderHumanScorecard doc comment named "renderOptionalScore" — a
function that doesn't exist; the closure inside the function is just
called "render". One-line fix from the /simplify pass.
Larger structural cleanups the simplify reviewers surfaced — dimension-
name string constants and a table-driven render shared with
writeScorecardMD — touch existing scorecard.go callers and warrant
their own PRs rather than being squeezed into this one.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(cli): scorecard prints MCPQuality + extract dimension-name constants
Two review follow-ups:
1. Add the missing MCP Quality print line. The scorecard scores
mcp_quality (separate from mcp_description_quality), includes it in
the tier-1 raw sum, and surfaces it in writeScorecardMD's table —
but the human print path skipped it entirely. Users running
`printing-press scorecard` saw a Total that didn't account for one
of the dimensions feeding it. Now prints `MCP Quality %d/10`
alongside the others.
2. Extract dimension-name string constants in
internal/pipeline/scorecard.go (DimMCPDescriptionQuality,
DimMCPTokenEfficiency, ...). The dimension keys are referenced at
~10 sites — recordOptionalScore calls, scorecardTierMax calls,
UnscoredDimensions appends, and writeScorecardMD's data tables —
plus the new render() call sites in cli/scorecard.go. A typo at
any one site silently flips the dimension's classification because
IsDimensionUnscored does a string match against UnscoredDimensions.
Constants make every typo a compile error.
The constants only cover dimensions that can land in
UnscoredDimensions (the silent-bug class). Always-scored dimensions
remain string literals; they don't go through the unscored-set
machinery and the typo risk doesn't apply.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
internal/cli/scorecard.go | 84 ++++++++++++++++++-------------
internal/cli/scorecard_test.go | 110 +++++++++++++++++++++++++++++++++++++++++
internal/pipeline/scorecard.go | 63 ++++++++++++++---------
3 files changed, 202 insertions(+), 55 deletions(-)
diff --git a/internal/cli/scorecard.go b/internal/cli/scorecard.go
index 29444c40..4d0d99cd 100644
--- a/internal/cli/scorecard.go
+++ b/internal/cli/scorecard.go
@@ -3,6 +3,7 @@ package cli
import (
"encoding/json"
"fmt"
+ "io"
"os"
"strings"
"time"
@@ -74,39 +75,7 @@ func newScorecardCmd() *cobra.Command {
}
// Human-readable output
- s := sc.Steinberger
- renderScore := func(name string, score, max int) string {
- if sc.IsDimensionUnscored(name) {
- return "N/A"
- }
- return fmt.Sprintf("%d/%d", score, max)
- }
-
- fmt.Printf("Quality Scorecard: %s\n\n", sc.APIName)
- fmt.Printf(" Output Modes %d/10\n", s.OutputModes)
- fmt.Printf(" Auth %d/10\n", s.Auth)
- fmt.Printf(" Error Handling %d/10\n", s.ErrorHandling)
- fmt.Printf(" Terminal UX %d/10\n", s.TerminalUX)
- fmt.Printf(" README %d/10\n", s.README)
- fmt.Printf(" Doctor %d/10\n", s.Doctor)
- fmt.Printf(" Agent Native %d/10\n", s.AgentNative)
- fmt.Printf(" MCP Desc Quality %s\n", renderScore("mcp_description_quality", s.MCPDescriptionQuality, 10))
- fmt.Printf(" Local Cache %d/10\n", s.LocalCache)
- fmt.Printf(" Breadth %d/10\n", s.Breadth)
- fmt.Printf(" Vision %d/10\n", s.Vision)
- fmt.Printf(" Workflows %d/10\n", s.Workflows)
- fmt.Printf(" Insight %d/10\n", s.Insight)
- fmt.Printf("\n Domain Correctness\n")
- fmt.Printf(" Path Validity %s\n", renderScore("path_validity", s.PathValidity, 10))
- fmt.Printf(" Auth Protocol %s\n", renderScore("auth_protocol", s.AuthProtocol, 10))
- fmt.Printf(" Data Pipeline Integrity %d/10\n", s.DataPipelineIntegrity)
- fmt.Printf(" Sync Correctness %d/10\n", s.SyncCorrectness)
- fmt.Printf(" Type Fidelity %d/5\n", s.TypeFidelity)
- fmt.Printf(" Dead Code %d/5\n", s.DeadCode)
- fmt.Printf("\n Total: %d/100 - Grade %s\n", s.Total, sc.OverallGrade)
- if len(sc.UnscoredDimensions) > 0 {
- fmt.Printf(" Note: omitted from denominator: %s\n", strings.Join(sc.UnscoredDimensions, ", "))
- }
+ renderHumanScorecard(os.Stdout, sc)
if live != nil {
fmt.Printf("\nLive Check (behavioral sample)\n")
@@ -161,3 +130,52 @@ func newScorecardCmd() *cobra.Command {
return cmd
}
+
+// renderHumanScorecard writes the human-readable scorecard table to w.
+// Optional dimensions (those that may land in sc.UnscoredDimensions
+// when the spec hasn't opted into the feature being measured) render
+// as "N/A" so the scorer's "didn't measure" signal isn't flattened
+// into a 0/10 that reads as a defect.
+func renderHumanScorecard(w io.Writer, sc *pipeline.Scorecard) {
+ s := sc.Steinberger
+ render := func(name string, score, max int) string {
+ if sc.IsDimensionUnscored(name) {
+ return "N/A"
+ }
+ return fmt.Sprintf("%d/%d", score, max)
+ }
+
+ fmt.Fprintf(w, "Quality Scorecard: %s\n\n", sc.APIName)
+ fmt.Fprintf(w, " Output Modes %d/10\n", s.OutputModes)
+ fmt.Fprintf(w, " Auth %d/10\n", s.Auth)
+ fmt.Fprintf(w, " Error Handling %d/10\n", s.ErrorHandling)
+ fmt.Fprintf(w, " Terminal UX %d/10\n", s.TerminalUX)
+ fmt.Fprintf(w, " README %d/10\n", s.README)
+ fmt.Fprintf(w, " Doctor %d/10\n", s.Doctor)
+ fmt.Fprintf(w, " Agent Native %d/10\n", s.AgentNative)
+ fmt.Fprintf(w, " MCP Quality %d/10\n", s.MCPQuality)
+ fmt.Fprintf(w, " MCP Desc Quality %s\n", render(pipeline.DimMCPDescriptionQuality, s.MCPDescriptionQuality, 10))
+ fmt.Fprintf(w, " MCP Token Efficiency %s\n", render(pipeline.DimMCPTokenEfficiency, s.MCPTokenEff, 10))
+ fmt.Fprintf(w, " MCP Remote Transport %s\n", render(pipeline.DimMCPRemoteTransport, s.MCPRemoteTransport, 10))
+ fmt.Fprintf(w, " MCP Tool Design %s\n", render(pipeline.DimMCPToolDesign, s.MCPToolDesign, 10))
+ fmt.Fprintf(w, " MCP Surface Strategy %s\n", render(pipeline.DimMCPSurfaceStrategy, s.MCPSurfaceStrategy, 10))
+ fmt.Fprintf(w, " Local Cache %d/10\n", s.LocalCache)
+ fmt.Fprintf(w, " Cache Freshness %s\n", render(pipeline.DimCacheFreshness, s.CacheFreshness, 10))
+ fmt.Fprintf(w, " Breadth %d/10\n", s.Breadth)
+ fmt.Fprintf(w, " Vision %d/10\n", s.Vision)
+ fmt.Fprintf(w, " Workflows %d/10\n", s.Workflows)
+ fmt.Fprintf(w, " Insight %d/10\n", s.Insight)
+ fmt.Fprintf(w, " Agent Workflow %d/10\n", s.AgentWorkflow)
+ fmt.Fprintf(w, "\n Domain Correctness\n")
+ fmt.Fprintf(w, " Path Validity %s\n", render(pipeline.DimPathValidity, s.PathValidity, 10))
+ fmt.Fprintf(w, " Auth Protocol %s\n", render(pipeline.DimAuthProtocol, s.AuthProtocol, 10))
+ fmt.Fprintf(w, " Data Pipeline Integrity %d/10\n", s.DataPipelineIntegrity)
+ fmt.Fprintf(w, " Sync Correctness %d/10\n", s.SyncCorrectness)
+ fmt.Fprintf(w, " Live API Verification %s\n", render(pipeline.DimLiveAPIVerification, s.LiveAPIVerification, 10))
+ fmt.Fprintf(w, " Type Fidelity %d/5\n", s.TypeFidelity)
+ fmt.Fprintf(w, " Dead Code %d/5\n", s.DeadCode)
+ fmt.Fprintf(w, "\n Total: %d/100 - Grade %s\n", s.Total, sc.OverallGrade)
+ if len(sc.UnscoredDimensions) > 0 {
+ fmt.Fprintf(w, " Note: omitted from denominator: %s\n", strings.Join(sc.UnscoredDimensions, ", "))
+ }
+}
diff --git a/internal/cli/scorecard_test.go b/internal/cli/scorecard_test.go
new file mode 100644
index 00000000..d685897b
--- /dev/null
+++ b/internal/cli/scorecard_test.go
@@ -0,0 +1,110 @@
+package cli
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+
+ "github.com/mvanhorn/cli-printing-press/v2/internal/pipeline"
+ "github.com/stretchr/testify/assert"
+)
+
+// TestRenderHumanScorecardShowsNAForUnscoredDimensions — the scorer
+// reports "didn't measure this" via the UnscoredDimensions slice.
+// Without N/A rendering the user sees "MCP Tool Design 0/10" alongside
+// real defects and reads it as a real defect, when in fact the spec
+// hasn't opted into the feature being scored.
+func TestRenderHumanScorecardShowsNAForUnscoredDimensions(t *testing.T) {
+ t.Parallel()
+ sc := &pipeline.Scorecard{
+ APIName: "test-api",
+ OverallGrade: "B",
+ Steinberger: pipeline.SteinerScore{
+ OutputModes: 10,
+ Auth: 10,
+ ErrorHandling: 10,
+ TerminalUX: 9,
+ README: 8,
+ Doctor: 10,
+ AgentNative: 10,
+ MCPDescriptionQuality: 10,
+ LocalCache: 10,
+ Breadth: 7,
+ Vision: 8,
+ Workflows: 8,
+ Insight: 6,
+ AgentWorkflow: 9,
+ Total: 85,
+ },
+ UnscoredDimensions: []string{
+ pipeline.DimMCPTokenEfficiency,
+ pipeline.DimMCPRemoteTransport,
+ pipeline.DimMCPToolDesign,
+ pipeline.DimMCPSurfaceStrategy,
+ pipeline.DimCacheFreshness,
+ pipeline.DimAuthProtocol,
+ pipeline.DimLiveAPIVerification,
+ },
+ }
+
+ var buf bytes.Buffer
+ renderHumanScorecard(&buf, sc)
+ got := buf.String()
+
+ for _, dim := range []string{
+ "MCP Token Efficiency N/A",
+ "MCP Remote Transport N/A",
+ "MCP Tool Design N/A",
+ "MCP Surface Strategy N/A",
+ "Cache Freshness N/A",
+ "Auth Protocol N/A",
+ "Live API Verification N/A",
+ } {
+ assert.Contains(t, got, dim, "expected %q in human output", dim)
+ }
+
+ assert.NotContains(t, got, "MCP Tool Design 0/10",
+ "unscored dimensions must not render as 0/10")
+ assert.NotContains(t, got, "Cache Freshness 0/10",
+ "unscored cache_freshness must not render as 0/10")
+
+ // Note line lists every unscored dimension so the user knows the
+ // composite denominator is reduced.
+ for _, dim := range sc.UnscoredDimensions {
+ assert.Contains(t, got, dim,
+ "unscored-dimensions note must list %q", dim)
+ }
+ assert.Contains(t, got, "omitted from denominator")
+}
+
+// TestRenderHumanScorecardShowsScoreForScoredDimensions — when a
+// dimension IS scored (even at 0/10), the score renders as-is. A
+// genuine 0/10 — e.g., cache_freshness on a CLI that has a local
+// store but hasn't declared the freshness contract — is a real
+// finding the user should see, not be masked.
+func TestRenderHumanScorecardShowsScoreForScoredDimensions(t *testing.T) {
+ t.Parallel()
+ sc := &pipeline.Scorecard{
+ APIName: "test-api",
+ Steinberger: pipeline.SteinerScore{
+ OutputModes: 10,
+ LocalCache: 10,
+ CacheFreshness: 0, // scored, but at 0 — a real finding
+ },
+ // CacheFreshness is NOT in UnscoredDimensions: the scorer
+ // found a local store and assessed the freshness contract
+ // (it's just absent).
+ UnscoredDimensions: nil,
+ }
+
+ var buf bytes.Buffer
+ renderHumanScorecard(&buf, sc)
+ got := buf.String()
+
+ assert.Contains(t, got, "Cache Freshness 0/10",
+ "scored cache_freshness=0 must render as 0/10, not N/A")
+ assert.NotContains(t, got, "Cache Freshness N/A",
+ "scored dimension at 0 is not the same as unscored")
+ assert.False(t, strings.Contains(got, "omitted from denominator"),
+ "with no unscored dimensions, the composite-note line must not appear")
+}
diff --git a/internal/pipeline/scorecard.go b/internal/pipeline/scorecard.go
index 32c2427f..c4ac428d 100644
--- a/internal/pipeline/scorecard.go
+++ b/internal/pipeline/scorecard.go
@@ -76,6 +76,25 @@ type SteinerScore struct {
CalibrationNote string `json:"calibration_note,omitempty"`
}
+// Dimension identifiers used by recordOptionalScore, scorecardTierMax,
+// IsDimensionUnscored, and renderers (renderHumanScorecard,
+// writeScorecardMD). Any dimension that can land in
+// Scorecard.UnscoredDimensions has a constant here so a typo at any
+// call site fails the compile rather than silently returning false
+// from IsDimensionUnscored. The string values match the JSON struct
+// tags above and must stay in lockstep.
+const (
+ DimMCPDescriptionQuality = "mcp_description_quality"
+ DimMCPTokenEfficiency = "mcp_token_efficiency"
+ DimMCPRemoteTransport = "mcp_remote_transport"
+ DimMCPToolDesign = "mcp_tool_design"
+ DimMCPSurfaceStrategy = "mcp_surface_strategy"
+ DimCacheFreshness = "cache_freshness"
+ DimPathValidity = "path_validity"
+ DimAuthProtocol = "auth_protocol"
+ DimLiveAPIVerification = "live_api_verification"
+)
+
// CompScore compares our score against a competitor on a single dimension.
type CompScore struct {
Name string `json:"name"`
@@ -120,18 +139,18 @@ func scoreInfrastructureDimensions(sc *Scorecard, outputDir string) {
sc.Steinberger.AgentNative = scoreAgentNative(outputDir)
sc.Steinberger.MCPQuality = scoreMCPQuality(outputDir)
mcpDescScore, mcpDescScored := scoreMCPDescriptionQuality(outputDir)
- recordOptionalScore(sc, &sc.Steinberger.MCPDescriptionQuality, "mcp_description_quality", mcpDescScore, mcpDescScored)
+ recordOptionalScore(sc, &sc.Steinberger.MCPDescriptionQuality, DimMCPDescriptionQuality, mcpDescScore, mcpDescScored)
mcpTokenScore, mcpTokenScored := scoreMCPTokenEfficiency(outputDir)
- recordOptionalScore(sc, &sc.Steinberger.MCPTokenEff, "mcp_token_efficiency", mcpTokenScore, mcpTokenScored)
+ recordOptionalScore(sc, &sc.Steinberger.MCPTokenEff, DimMCPTokenEfficiency, mcpTokenScore, mcpTokenScored)
remoteScore, remoteScored := scoreMCPRemoteTransport(outputDir)
- recordOptionalScore(sc, &sc.Steinberger.MCPRemoteTransport, "mcp_remote_transport", remoteScore, remoteScored)
+ recordOptionalScore(sc, &sc.Steinberger.MCPRemoteTransport, DimMCPRemoteTransport, remoteScore, remoteScored)
toolDesignScore, toolDesignScored := scoreMCPToolDesign(outputDir)
- recordOptionalScore(sc, &sc.Steinberger.MCPToolDesign, "mcp_tool_design", toolDesignScore, toolDesignScored)
+ recordOptionalScore(sc, &sc.Steinberger.MCPToolDesign, DimMCPToolDesign, toolDesignScore, toolDesignScored)
strategyScore, strategyScored := scoreMCPSurfaceStrategy(outputDir)
- recordOptionalScore(sc, &sc.Steinberger.MCPSurfaceStrategy, "mcp_surface_strategy", strategyScore, strategyScored)
+ recordOptionalScore(sc, &sc.Steinberger.MCPSurfaceStrategy, DimMCPSurfaceStrategy, strategyScore, strategyScored)
sc.Steinberger.LocalCache = scoreLocalCache(outputDir)
cacheFreshnessScore, cacheFreshnessScored := scoreCacheFreshness(outputDir)
- recordOptionalScore(sc, &sc.Steinberger.CacheFreshness, "cache_freshness", cacheFreshnessScore, cacheFreshnessScored)
+ recordOptionalScore(sc, &sc.Steinberger.CacheFreshness, DimCacheFreshness, cacheFreshnessScore, cacheFreshnessScored)
sc.Steinberger.Breadth = scoreBreadth(outputDir)
sc.Steinberger.Vision = scoreVision(outputDir)
sc.Steinberger.Workflows = scoreWorkflows(outputDir)
@@ -150,7 +169,7 @@ func recordOptionalScore(sc *Scorecard, target *int, dimension string, score int
func scoreSpecDimensions(sc *Scorecard, outputDir, specPath string) error {
if specPath == "" {
// No spec: mark spec-dependent dimensions as unscored.
- sc.UnscoredDimensions = append(sc.UnscoredDimensions, "path_validity", "auth_protocol")
+ sc.UnscoredDimensions = append(sc.UnscoredDimensions, DimPathValidity, DimAuthProtocol)
return nil
}
@@ -163,19 +182,19 @@ func scoreSpecDimensions(sc *Scorecard, outputDir, specPath string) error {
// Hand-built commands intentionally go beyond the spec; path-validity
// is not applicable. Mark unscored so the tier-2 denominator excludes
// it rather than awarding a 10-point cushion the CLI didn't earn.
- sc.UnscoredDimensions = append(sc.UnscoredDimensions, "path_validity")
+ sc.UnscoredDimensions = append(sc.UnscoredDimensions, DimPathValidity)
} else {
pathValidity := evaluatePathValidity(outputDir, spec)
sc.Steinberger.PathValidity = pathValidity.score
if !pathValidity.scored {
- sc.UnscoredDimensions = append(sc.UnscoredDimensions, "path_validity")
+ sc.UnscoredDimensions = append(sc.UnscoredDimensions, DimPathValidity)
}
}
authProtocol := evaluateAuthProtocol(outputDir, spec)
sc.Steinberger.AuthProtocol = authProtocol.score
if !authProtocol.scored {
- sc.UnscoredDimensions = append(sc.UnscoredDimensions, "auth_protocol")
+ sc.UnscoredDimensions = append(sc.UnscoredDimensions, DimAuthProtocol)
}
return nil
}
@@ -195,7 +214,7 @@ func scoreDomainDimensions(sc *Scorecard, outputDir string, verifyReport *Verify
if liveScore, scored := scoreLiveAPIVerification(verifyReport); scored {
sc.Steinberger.LiveAPIVerification = liveScore
} else {
- sc.UnscoredDimensions = append(sc.UnscoredDimensions, "live_api_verification")
+ sc.UnscoredDimensions = append(sc.UnscoredDimensions, DimLiveAPIVerification)
}
}
@@ -893,7 +912,7 @@ func ApplyLiveCheckToScorecard(sc *Scorecard, live *LiveCheckResult) {
return
}
sc.Steinberger.LiveAPIVerification = score
- sc.UnscoredDimensions = removeUnscoredDimension(sc.UnscoredDimensions, "live_api_verification")
+ sc.UnscoredDimensions = removeUnscoredDimension(sc.UnscoredDimensions, DimLiveAPIVerification)
recomputeScorecardTotals(sc)
applyScorecardCalibration(sc)
sc.OverallGrade = computeGrade(sc.Steinberger.Percentage)
@@ -954,7 +973,7 @@ func recomputeScorecardTotals(sc *Scorecard) {
sc.Steinberger.AgentWorkflow,
)
- tier1Max := scorecardTierMax(sc, 200, "mcp_description_quality", "mcp_token_efficiency", "cache_freshness", "mcp_remote_transport", "mcp_tool_design", "mcp_surface_strategy")
+ tier1Max := scorecardTierMax(sc, 200, DimMCPDescriptionQuality, DimMCPTokenEfficiency, DimCacheFreshness, DimMCPRemoteTransport, DimMCPToolDesign, DimMCPSurfaceStrategy)
tier1Normalized := 0
if tier1Max > 0 {
tier1Normalized = (tier1Raw * 50) / tier1Max
@@ -970,7 +989,7 @@ func recomputeScorecardTotals(sc *Scorecard) {
sc.Steinberger.LiveAPIVerification,
)
- tier2Max := scorecardTierMax(sc, 60, "live_api_verification", "path_validity", "auth_protocol")
+ tier2Max := scorecardTierMax(sc, 60, DimLiveAPIVerification, DimPathValidity, DimAuthProtocol)
tier2Normalized := 0
if tier2Max > 0 {
tier2Normalized = (tier2Raw * 50) / tier2Max
@@ -2386,15 +2405,15 @@ func buildGapReport(s SteinerScore, unscored []string) []string {
{"doctor", s.Doctor},
{"agent_native", s.AgentNative},
{"mcp_quality", s.MCPQuality},
- {"mcp_description_quality", s.MCPDescriptionQuality},
- {"mcp_token_efficiency", s.MCPTokenEff},
+ {DimMCPDescriptionQuality, s.MCPDescriptionQuality},
+ {DimMCPTokenEfficiency, s.MCPTokenEff},
{"local_cache", s.LocalCache},
{"breadth", s.Breadth},
{"vision", s.Vision},
{"workflows", s.Workflows},
{"insight", s.Insight},
- {"path_validity", s.PathValidity},
- {"auth_protocol", s.AuthProtocol},
+ {DimPathValidity, s.PathValidity},
+ {DimAuthProtocol, s.AuthProtocol},
{"data_pipeline_integrity", s.DataPipelineIntegrity},
{"sync_correctness", s.SyncCorrectness},
{"type_fidelity", s.TypeFidelity},
@@ -2517,15 +2536,15 @@ func writeScorecardMD(sc *Scorecard, pipelineDir string) error {
{"Doctor", "doctor", s.Doctor},
{"Agent Native", "agent_native", s.AgentNative},
{"MCP Quality", "mcp_quality", s.MCPQuality},
- {"MCP Description Quality", "mcp_description_quality", s.MCPDescriptionQuality},
- {"MCP Token Efficiency", "mcp_token_efficiency", s.MCPTokenEff},
+ {"MCP Description Quality", DimMCPDescriptionQuality, s.MCPDescriptionQuality},
+ {"MCP Token Efficiency", DimMCPTokenEfficiency, s.MCPTokenEff},
{"Local Cache", "local_cache", s.LocalCache},
{"Breadth", "breadth", s.Breadth},
{"Vision", "vision", s.Vision},
{"Workflows", "workflows", s.Workflows},
{"Insight", "insight", s.Insight},
- {"Path Validity", "path_validity", s.PathValidity},
- {"Auth Protocol", "auth_protocol", s.AuthProtocol},
+ {"Path Validity", DimPathValidity, s.PathValidity},
+ {"Auth Protocol", DimAuthProtocol, s.AuthProtocol},
{"Data Pipeline Integrity", "data_pipeline_integrity", s.DataPipelineIntegrity},
{"Sync Correctness", "sync_correctness", s.SyncCorrectness},
}
← 6586c0a2 feat(cli): per-resource base_url override in spec schema (#4
·
back to Cli Printing Press
·
fix(cli): dogfood agent-context discovery reads stdout only fe1e6d77 →