← back to Cli Printing Press
docs(cli): refresh solution docs (#1146)
44e7efe43b1cc9fee8fe30843f3cd06c926301ea · 2026-05-11 21:13:12 -0700 · Trevin Chow
* docs(cli): refresh solution docs
* docs(cli): clarify dry-run cache note
Files touched
M docs/solutions/best-practices/steinberger-scorecard-scoring-architecture-2026-03-27.mdM docs/solutions/design-patterns/auth-envvar-rich-model-2026-05-05.mdM docs/solutions/design-patterns/dry-run-default-for-mutator-probes-in-test-harnesses-2026-05-05.mdM docs/solutions/design-patterns/dual-key-identity-fields-2026-05-06.mdM docs/solutions/design-patterns/http-client-cache-invalidate-on-mutation-2026-05-05.mdM docs/solutions/logic-errors/auth-none-dead-code-in-config-client-2026-05-08.mdM docs/solutions/logic-errors/store-columns-sourced-from-request-params-instead-of-response-2026-05-08.mdM docs/solutions/security-issues/mcp-sql-search-readonly-bypass-2026-05-08.md
Diff
commit 44e7efe43b1cc9fee8fe30843f3cd06c926301ea
Author: Trevin Chow <trevin@trevinchow.com>
Date: Mon May 11 21:13:12 2026 -0700
docs(cli): refresh solution docs (#1146)
* docs(cli): refresh solution docs
* docs(cli): clarify dry-run cache note
---
...er-scorecard-scoring-architecture-2026-03-27.md | 33 ++++++++++++++--------
.../auth-envvar-rich-model-2026-05-05.md | 10 +++----
...-mutator-probes-in-test-harnesses-2026-05-05.md | 2 +-
.../dual-key-identity-fields-2026-05-06.md | 2 +-
...ient-cache-invalidate-on-mutation-2026-05-05.md | 8 +++---
...h-none-dead-code-in-config-client-2026-05-08.md | 12 +++++---
...equest-params-instead-of-response-2026-05-08.md | 2 +-
.../mcp-sql-search-readonly-bypass-2026-05-08.md | 2 +-
8 files changed, 42 insertions(+), 29 deletions(-)
diff --git a/docs/solutions/best-practices/steinberger-scorecard-scoring-architecture-2026-03-27.md b/docs/solutions/best-practices/steinberger-scorecard-scoring-architecture-2026-03-27.md
index 018bf4d3..11f2350c 100644
--- a/docs/solutions/best-practices/steinberger-scorecard-scoring-architecture-2026-03-27.md
+++ b/docs/solutions/best-practices/steinberger-scorecard-scoring-architecture-2026-03-27.md
@@ -8,7 +8,7 @@ problem_type: best_practice
component: tooling
symptoms:
- "Developers modify scoring dimensions without understanding tier weighting, causing score drift"
- - "New dimensions added without updating tier sum constants (120 for Tier 1, 50 for Tier 2)"
+ - "New dimensions added without updating tier sum constants (200 for Tier 1, 60 for Tier 2)"
- "Pattern strings changed without verifying they match actual generated code output"
- "File discovery helpers misused, causing double-counting or missed files"
root_cause: inadequate_documentation
@@ -29,13 +29,13 @@ tags:
## Problem
-The Steinberger scorecard in `internal/pipeline/scorecard.go` is an 18-dimension scoring system with non-obvious conventions. Developers modifying scoring functions introduce bugs when they don't understand the system's implicit rules around units, ordering, file scope, and pattern matching limitations.
+The Steinberger scorecard in `internal/pipeline/scorecard.go` is a two-tier scoring system with non-obvious conventions. Developers modifying scoring functions introduce bugs when they don't understand the system's implicit rules around units, ordering, file scope, optional dimensions, and pattern matching limitations.
## Scoring Architecture
### Dimension structure
-**Tier 1 -- Infrastructure (12 dimensions, 0-10 each, 120 raw max):**
+**Tier 1 -- Infrastructure (20 dimensions, 0-10 each, 200 raw base max):**
| Dimension | Max | Primary files checked |
|-----------|-----|----------------------|
@@ -46,13 +46,21 @@ The Steinberger scorecard in `internal/pipeline/scorecard.go` is an 18-dimension
| README | 10 | `README.md` |
| Doctor | 10 | `doctor.go` |
| AgentNative | 10 | `root.go`, `helpers.go`, command files |
+| MCPQuality | 10 | MCP server files and tool registration |
+| MCPDescriptionQuality | 10 | `tools-manifest.json` and MCP tool descriptions; optional |
+| MCPTokenEff | 10 | MCP surface sizing and token budget; optional |
+| MCPRemoteTransport | 10 | MCP transport support; optional |
+| MCPToolDesign | 10 | MCP tool grouping and intent design; optional |
+| MCPSurfaceStrategy | 10 | Large-surface MCP strategy; optional |
| LocalCache | 10 | `client/client.go`, `store/store.go` |
+| CacheFreshness | 10 | cache invalidation and freshness strategy; optional |
| Breadth | 10 | All CLI files (excludes `infraAllFiles`) |
| Vision | 10 | `store/store.go`, `root.go`, vision-related files |
| Workflows | 10 | All CLI files (excludes `infraCoreFiles`) |
| Insight | 10 | All CLI files (excludes `infraCoreFiles`) |
+| AgentWorkflow | 10 | async jobs, profiles, delivery, feedback readiness |
-**Tier 2 -- Domain Correctness (6 dimensions, varying max, 50 raw max):**
+**Tier 2 -- Domain Correctness (7 dimensions, varying max, 60 raw base max):**
| Dimension | Max | Primary files checked |
|-----------|-----|----------------------|
@@ -62,16 +70,19 @@ The Steinberger scorecard in `internal/pipeline/scorecard.go` is an 18-dimension
| SyncCorrectness | 10 | All CLI files |
| TypeFidelity | 5 | Command files |
| DeadCode | 5 | `root.go`, `helpers.go`, other CLI files |
+| LiveAPIVerification | 10 | live verify report; optional |
### Total formula
```
-tier1Normalized = (tier1Raw * 50) / 120 // scale 0-120 → 0-50
-tier2Normalized = (tier2Raw * 50) / 50 // scale 0-50 → 0-50
+tier1Max = scorecardTierMax(sc, 200, optionalTier1Dims...)
+tier2Max = scorecardTierMax(sc, 60, optionalTier2Dims...)
+tier1Normalized = (tier1Raw * 50) / tier1Max
+tier2Normalized = (tier2Raw * 50) / tier2Max
Total = tier1Normalized + tier2Normalized // 0-100
```
-Each tier contributes exactly 50 points max. If you add a Tier 1 dimension, update the `120` constant. If you add a Tier 2 dimension, update the `50` constant.
+Each tier contributes exactly 50 points max. If you add a Tier 1 dimension, update the `200` base constant in `recomputeScorecardTotals`. If you add a Tier 2 dimension, update the `60` base constant. If the new dimension can be unscored, add a `Dim*` constant and include it in the matching `scorecardTierMax` call.
### Unscored dimensions
@@ -88,9 +99,7 @@ type Scorecard struct {
Render unscored dimensions as `N/A`, omit them from gap reports, and remove their max points from the denominator before normalizing the tier:
```go
-tier2Max := 50
-if sc.IsDimensionUnscored("path_validity") { tier2Max -= 10 }
-if sc.IsDimensionUnscored("auth_protocol") { tier2Max -= 10 }
+tier2Max := scorecardTierMax(sc, 60, DimLiveAPIVerification, DimPathValidity, DimAuthProtocol)
tier2Normalized := (tier2Raw * 50) / tier2Max
```
@@ -211,7 +220,7 @@ Six prefixes appear in both workflow and insight lists: `stale`, `conflicts`, `s
10. **For AuthProtocol, score runtime emission after using the spec to identify the contract.** OpenAPI `securitySchemes` can model one composed header protocol as multiple same-prefix `apiKey` headers. Expand only signing-style companions, preserve explicit OR alternatives, and verify each required header is assigned in the generated client.
-11. **Update tier constants when adding dimensions.** Tier 1 constant is `120` (12 x 10). Tier 2 constant is `50`. Both live in the `RunScorecard` function. If dimensions can become unscored, adjust the runtime denominator too.
+11. **Update tier constants when adding dimensions.** Tier 1 base is `200`; Tier 2 base is `60`. Both live in `recomputeScorecardTotals`. If dimensions can become unscored, add a dimension constant and thread it through `scorecardTierMax` so the runtime denominator changes with `UnscoredDimensions`.
12. **Test every scoring function independently.** Each `score*()` function should have fixture-based tests covering: high score, low score, dimension-specific edge cases, and unscored/unknown states for evidence-dependent dimensions.
@@ -223,4 +232,4 @@ For detailed examples of bugs caused by violating these rules, see `docs/solutio
- `docs/solutions/logic-errors/scorer-dogfood-composed-header-auth-and-example-continuations-2026-05-05.md` -- composed header-auth scoring and shell-style example-tokenizer bug fixes
- `docs/plans/2026-03-25-feat-visionary-research-phase-plan.md` -- defines the Steinberger vision scoring and workflow/insight semantics
- `docs/plans/2026-03-25-fix-scorecard-too-easy-real-quality-plan.md` -- predecessor plan that redesigned scoring from presence-only to quality-aware
-- `skills/printing-press/references/scorecard-patterns.md` -- **STALE**: documents only 9 of 18 dimensions, wrong total range, pre-broadening file assumptions. Needs full rewrite.
+- `skills/printing-press/references/scorecard-patterns.md` -- **STALE**: documents the older scorecard shape, wrong total range, and pre-broadening file assumptions. Needs full rewrite.
diff --git a/docs/solutions/design-patterns/auth-envvar-rich-model-2026-05-05.md b/docs/solutions/design-patterns/auth-envvar-rich-model-2026-05-05.md
index 0097ab79..3ae5acae 100644
--- a/docs/solutions/design-patterns/auth-envvar-rich-model-2026-05-05.md
+++ b/docs/solutions/design-patterns/auth-envvar-rich-model-2026-05-05.md
@@ -4,7 +4,7 @@ date: 2026-05-05
category: design-patterns
module: cli-printing-press-generator
problem_type: design_pattern
-component: auth
+component: authentication
severity: medium
applies_when:
- "One legacy list is driving multiple auth surfaces with different filtering rules"
@@ -39,10 +39,10 @@ Normalize before merging generated artifacts. `NormalizeEnvVarSpecs` must run at
## Code Patterns
-- `internal/spec/spec.go:467` defines `AuthEnvVar`; `internal/spec/spec.go:484` defines `CanonicalEnvVar()` and is the selector template for human-prose surfaces.
-- `internal/openapi/parser.go:583` shows `applyAuthVarsRichOverride`, the conservative parser gate: malformed rich overrides warn and fall back to generated defaults.
-- `internal/authdoctor/classify.go:100` shows the mixed-version safety pattern: prefer rich manifest specs, warn on disagreement, and fall back to legacy env vars.
-- `internal/pipeline/climanifest.go:336` shows pre-merge normalization and name dedup across global and tier auth blocks.
+- `internal/spec/spec.go:567` defines `AuthEnvVar`; `internal/spec/spec.go:631` defines `CanonicalEnvVar()` and is the selector template for human-prose surfaces.
+- `internal/openapi/parser.go:805` shows `applyAuthVarsRichOverride`, the conservative parser gate: malformed rich overrides warn and fall back to generated defaults.
+- `internal/authdoctor/classify.go:70` shows the mixed-version safety pattern: prefer rich manifest specs, warn on disagreement, and fall back to legacy env vars.
+- `internal/pipeline/climanifest.go:466` shows pre-merge normalization and name dedup across global and tier auth blocks.
## Related
diff --git a/docs/solutions/design-patterns/dry-run-default-for-mutator-probes-in-test-harnesses-2026-05-05.md b/docs/solutions/design-patterns/dry-run-default-for-mutator-probes-in-test-harnesses-2026-05-05.md
index 9c443f1f..1deae360 100644
--- a/docs/solutions/design-patterns/dry-run-default-for-mutator-probes-in-test-harnesses-2026-05-05.md
+++ b/docs/solutions/design-patterns/dry-run-default-for-mutator-probes-in-test-harnesses-2026-05-05.md
@@ -1,7 +1,7 @@
---
title: Default mutator probes to dry-run; carve real-API rejection into a separate test kind
date: 2026-05-05
-category: docs/solutions/design-patterns
+category: design-patterns
module: live_dogfood
problem_type: design_pattern
component: testing_framework
diff --git a/docs/solutions/design-patterns/dual-key-identity-fields-2026-05-06.md b/docs/solutions/design-patterns/dual-key-identity-fields-2026-05-06.md
index 3e3df894..a8f4f228 100644
--- a/docs/solutions/design-patterns/dual-key-identity-fields-2026-05-06.md
+++ b/docs/solutions/design-patterns/dual-key-identity-fields-2026-05-06.md
@@ -175,5 +175,5 @@ author: "Trevin Chow" # display-shaped
- `docs/solutions/conventions/soft-validation-in-reusable-library-packages-2026-05-06.md` — the empty-value behavior for `OwnerName` (soft warn + slug fallback) so the generator stays reusable from tests / mcp-sync / regen-merge without forcing every caller to set the field
- `docs/solutions/conventions/preserve-original-authorship-in-multi-author-retrofits-2026-05-06.md` — when `OwnerName` flows through a sweep over published content (vs. a fresh print), the resolver must NOT trust the operator's git config; lessons from the public-library retrofit
- `internal/generator/plan_generate.go` — `resolveOwnerForNew` / `resolveOwnerNameForNew` split
-- `internal/spec/spec.go:103` — `OwnerName` field declaration with comment cross-referencing the slug field
+- `internal/spec/spec.go:109` — `OwnerName` field declaration with comment cross-referencing the slug field; the adjacent `Printer` / `PrinterName` pair applies the same slug-vs-display split to printer attribution
- `AGENTS.md` — Naming and Disambiguation section pins the lesson for future contributors
diff --git a/docs/solutions/design-patterns/http-client-cache-invalidate-on-mutation-2026-05-05.md b/docs/solutions/design-patterns/http-client-cache-invalidate-on-mutation-2026-05-05.md
index 40554e03..14da58c2 100644
--- a/docs/solutions/design-patterns/http-client-cache-invalidate-on-mutation-2026-05-05.md
+++ b/docs/solutions/design-patterns/http-client-cache-invalidate-on-mutation-2026-05-05.md
@@ -46,13 +46,13 @@ The MCP-side `NoCache=true` workaround (auto memory [claude]) (session history)
**If your generated client ships a response cache, invalidate the entire cache directory at the success branch of any non-GET request.** The two-pronged approach:
1. **Constructor default for MCP-shaped consumers** (already in template via PR #521 (session history)): `c.NoCache = true` for clients driven by agents.
-2. **Cache invalidation on mutation for ALL clients**: in `do()`, after a successful response, if the request was non-GET, drop the entire cache.
+2. **Cache invalidation on mutation for ALL clients**: in `do()`, after a successful response, if the request was non-GET and not a dry run, drop the entire cache.
```go
// In do(), success branch:
if resp.StatusCode < 400 {
c.limiter.OnSuccess()
- if method != http.MethodGet {
+ if method != http.MethodGet && !c.DryRun {
c.invalidateCache()
}
return json.RawMessage(respBody), resp.StatusCode, nil
@@ -73,7 +73,7 @@ Constraints on the call site:
- Only fire on `resp.StatusCode < 400` — 4xx/5xx didn't mutate state.
- Only fire when `method != http.MethodGet`.
-- Skip on dry-run paths and inside the retry loop (only the final success).
+- Skip on dry-run paths (`c.DryRun`) and inside the retry loop (only the final success).
- Best-effort: ignore the `RemoveAll` error so a cache-clear failure does not fail the surrounding successful mutation.
### Discarded alternative — selective invalidation by resource family
@@ -116,7 +116,7 @@ if resp.StatusCode < 400 {
```go
if resp.StatusCode < 400 {
c.limiter.OnSuccess()
- if method != http.MethodGet {
+ if method != http.MethodGet && !c.DryRun {
c.invalidateCache()
}
return json.RawMessage(respBody), resp.StatusCode, nil
diff --git a/docs/solutions/logic-errors/auth-none-dead-code-in-config-client-2026-05-08.md b/docs/solutions/logic-errors/auth-none-dead-code-in-config-client-2026-05-08.md
index b3cfdfd2..1e6f4aea 100644
--- a/docs/solutions/logic-errors/auth-none-dead-code-in-config-client-2026-05-08.md
+++ b/docs/solutions/logic-errors/auth-none-dead-code-in-config-client-2026-05-08.md
@@ -1,4 +1,6 @@
---
+title: "No-auth specs emitted dead token scaffolding in config.go and client.go"
+category: logic-errors
module: internal/generator
date: "2026-05-08"
problem_type: logic_error
@@ -21,9 +23,11 @@ tags:
- test-coverage
---
+# No-auth specs emitted dead token scaffolding in config.go and client.go
+
## Problem
-The Printing Press generator gates `auth.go` emission, the `root.go` `HasAuthCommand` registration, and the scorecard's `scoreAuth` exemption on `Generator.shouldEmitAuth()` (`internal/generator/generator.go:1808`). Two additional artifacts were never wired through the same gate: `config.go.tmpl` and `client.go.tmpl`.
+The Printing Press generator gates `auth.go` emission, the `root.go` `HasAuthCommand` registration, and the scorecard's `scoreAuth` exemption on `Generator.shouldEmitAuth()` (`internal/generator/generator.go:1898`). Two additional artifacts were never wired through the same gate: `config.go.tmpl` and `client.go.tmpl`.
For any spec where `shouldEmitAuth()` returns false — `auth.type: "none"` AND no `AuthorizationURL` AND no `graphql_persisted_query` traffic-analysis hint — the generator emitted:
@@ -43,7 +47,7 @@ func (g *Generator) shouldEmitAuth() bool {
}
```
-The predicate's doc comment enumerates the call sites that "must agree". Before PR #704, only three of the five did.
+The predicate's doc comment names the central auth-emission gate. Before PR #704, `auth.go`, `root.go`, and `scoreAuth` agreed on it, while `config.go.tmpl` and `client.go.tmpl` still emitted token scaffolding through separate template data.
## Symptoms
@@ -112,7 +116,7 @@ case "config.go.tmpl":
## Why This Works
-`Generator.shouldEmitAuth()` is the canonical predicate. Plumbing its return value into both template-data structs makes the templates gate on the exact same condition that gates `auth.go`. All five call sites enumerated in the predicate's doc comment now agree by construction.
+`Generator.shouldEmitAuth()` is the canonical predicate. Plumbing its return value into both template-data structs makes the templates gate on the exact same condition that gates `auth.go`, `root.go`, and `scoreAuth`.
When `shouldEmitAuth()` is false, no token field, no token method, no refresh function, and no associated import reach the emitted CLI. When it is true, the full scaffolding emits as before. No caller in either branch is left without its callee.
@@ -152,7 +156,7 @@ runGoCommand(t, outputDir, "mod", "tidy")
runGoCommand(t, outputDir, "build", "./...")
```
-Use the existing `runGoCommand` helper (`internal/generator/generator_test.go:981`); it auto-skips under `-short` so the unit lane stays fast and full CI catches the regression.
+Use the existing `runGoCommand` helper (`internal/generator/generator_test.go:1244`); it auto-skips under `-short` so the unit lane stays fast and full CI catches the regression.
## Related
diff --git a/docs/solutions/logic-errors/store-columns-sourced-from-request-params-instead-of-response-2026-05-08.md b/docs/solutions/logic-errors/store-columns-sourced-from-request-params-instead-of-response-2026-05-08.md
index 563f7e8a..7c0ab4c6 100644
--- a/docs/solutions/logic-errors/store-columns-sourced-from-request-params-instead-of-response-2026-05-08.md
+++ b/docs/solutions/logic-errors/store-columns-sourced-from-request-params-instead-of-response-2026-05-08.md
@@ -131,7 +131,7 @@ s.Types["Issue"] = spec.TypeDef{Fields: []spec.TypeField{
**Pin the response-vs-request distinction with a regression test on every schema-builder change.** The fix added `TestBuildSchema_ColumnsFromResponseSchema` (request params don't leak), `TestBuildSchema_ParamResponseNameOverlap` (when names collide, the response field's *type* drives the column — fixture uses param=string, response=integer; asserts INTEGER), and `TestBuildSchema_NoResponseTypeFallback` (table-driven over both unregistered-type-name and empty-Item cases; both yield only base columns). Future changes to `collectResponseFields` should keep these green.
-**Golden coverage gap to close.** The golden suite has no fixture for `internal/store/store.go` (the generated SQLite DDL). A regression that reverts column sourcing would pass `scripts/golden.sh verify` silently. Adding a store.go golden — with a spec that has both request params and a distinct response shape — would catch this class of bug mechanically.
+**Golden coverage gap to close.** The golden suite now includes generated `internal/store/store.go` output for the sync-walker fixture, but it still does not pin this specific response-vs-request distinction with a spec that has both request params and distinct response fields. A regression that reverts column sourcing could still pass `scripts/golden.sh verify` silently unless a golden fixture asserts the typed SQLite DDL shape for that case.
**Bug shape is wider than this instance.** The same wrong-source pattern produced a related defect documented in `docs/solutions/logic-errors/mcp-handler-conflates-path-and-query-positional-params-2026-05-05.md` (MCP handler treating URL path placeholders as positional query args). When adding new generator surfaces that read from endpoints, explicitly identify which field of `spec.Endpoint` is the authoritative source and add a comment naming the invariant, as the rewritten `collectResponseFields` now does.
diff --git a/docs/solutions/security-issues/mcp-sql-search-readonly-bypass-2026-05-08.md b/docs/solutions/security-issues/mcp-sql-search-readonly-bypass-2026-05-08.md
index 2046352a..9022d794 100644
--- a/docs/solutions/security-issues/mcp-sql-search-readonly-bypass-2026-05-08.md
+++ b/docs/solutions/security-issues/mcp-sql-search-readonly-bypass-2026-05-08.md
@@ -1,7 +1,7 @@
---
title: Generated MCP sql tool opened read-write SQLite under a false readOnlyHint
date: "2026-05-08"
-category: docs/solutions/security-issues/
+category: security-issues
module: internal/generator/templates
problem_type: security_issue
component: tooling
← 8990fc24 feat(skills): offer browser-sniff backend install at preflig
·
back to Cli Printing Press
·
docs(cli): require /printing-press-publish skill for CLI pub 403341c7 →