[object Object]

← back to Cli Printing Press

fix(cli,skills): manifest display_name preservation + divergence-check exemptions (#390)

401bda54e3059fe89fc55dd2be53541a74a57424 · 2026-04-29 10:55:50 -0700 · Trevin Chow

* fix(cli): mcp-sync no longer propagates stale display_name from .printing-press.json

populateMCPMetadata refused to update m.DisplayName when it was already
set, so once .printing-press.json carried a slug-derived value (e.g.,
"Cal Com" instead of "Cal.com"), every subsequent mcp-sync run would
clobber a hand-fixed manifest.json by reading the stale value back from
.printing-press.json. The upstream chain in mcpsync/sync.go preserves
manifest.json's display_name into parsed.DisplayName before this runs,
so the empty-check is the only thing blocking the chain from working.

Drop the empty-check; always refresh m.DisplayName from
parsed.EffectiveDisplayName(). After this, the workflow is: fix
manifest.json once, then mcp-sync stops reverting it.

TestRefreshCLIManifestFromSpecRefreshesDisplayName pins the contract.

Closes #385

* fix(skills): polish divergence check exempts publish-rewritten paths

The publish step rewrites a CLI's Go module path from <api>-pp-cli to
github.com/mvanhorn/printing-press-library/library/<category>/<api>,
which propagates through go.mod, go.sum, and every .go file's internal
imports. The divergence check's previous exclusion list (build
artifacts, the ledger, the binary) flagged all of these as drift on
every run, drowning out real local edits.

Add go.mod, go.sum to the exclude list. Document that .go file diffs
are expected to show the module-path prefix swap — the agent should
scan for substantive changes beyond the prefix when reading the diff
output.

Don't exclude binary names (<api>-pp-cli, <api>-pp-mcp): those
patterns match both the root-level binary files AND the cmd/ source
directories, so passing them to --exclude silently skips the entire
cmd/ subtree and hides real divergence in main.go. One "Only in local"
line for the built binary is acceptable noise; skipping cmd/ is not.

Closes #387

Files touched

Diff

commit 401bda54e3059fe89fc55dd2be53541a74a57424
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Wed Apr 29 10:55:50 2026 -0700

    fix(cli,skills): manifest display_name preservation + divergence-check exemptions (#390)
    
    * fix(cli): mcp-sync no longer propagates stale display_name from .printing-press.json
    
    populateMCPMetadata refused to update m.DisplayName when it was already
    set, so once .printing-press.json carried a slug-derived value (e.g.,
    "Cal Com" instead of "Cal.com"), every subsequent mcp-sync run would
    clobber a hand-fixed manifest.json by reading the stale value back from
    .printing-press.json. The upstream chain in mcpsync/sync.go preserves
    manifest.json's display_name into parsed.DisplayName before this runs,
    so the empty-check is the only thing blocking the chain from working.
    
    Drop the empty-check; always refresh m.DisplayName from
    parsed.EffectiveDisplayName(). After this, the workflow is: fix
    manifest.json once, then mcp-sync stops reverting it.
    
    TestRefreshCLIManifestFromSpecRefreshesDisplayName pins the contract.
    
    Closes #385
    
    * fix(skills): polish divergence check exempts publish-rewritten paths
    
    The publish step rewrites a CLI's Go module path from <api>-pp-cli to
    github.com/mvanhorn/printing-press-library/library/<category>/<api>,
    which propagates through go.mod, go.sum, and every .go file's internal
    imports. The divergence check's previous exclusion list (build
    artifacts, the ledger, the binary) flagged all of these as drift on
    every run, drowning out real local edits.
    
    Add go.mod, go.sum to the exclude list. Document that .go file diffs
    are expected to show the module-path prefix swap — the agent should
    scan for substantive changes beyond the prefix when reading the diff
    output.
    
    Don't exclude binary names (<api>-pp-cli, <api>-pp-mcp): those
    patterns match both the root-level binary files AND the cmd/ source
    directories, so passing them to --exclude silently skips the entire
    cmd/ subtree and hides real divergence in main.go. One "Only in local"
    line for the built binary is acceptable noise; skipping cmd/ is not.
    
    Closes #387
---
 internal/pipeline/climanifest.go      |  7 ++++---
 internal/pipeline/climanifest_test.go | 25 +++++++++++++++++++++++++
 skills/printing-press-polish/SKILL.md |  9 ++++++++-
 3 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/internal/pipeline/climanifest.go b/internal/pipeline/climanifest.go
index b91da34f..7682439a 100644
--- a/internal/pipeline/climanifest.go
+++ b/internal/pipeline/climanifest.go
@@ -245,9 +245,10 @@ func populateMCPMetadata(m *CLIManifest, parsed *spec.APISpec) {
 	m.AuthEnvVars = parsed.Auth.EnvVars
 	m.AuthKeyURL = parsed.Auth.KeyURL
 	m.AuthOptional = parsed.Auth.Optional
-	if m.DisplayName == "" {
-		m.DisplayName = parsed.EffectiveDisplayName()
-	}
+	// Always refresh from parsed; an empty-check here would let a
+	// stale .printing-press.json overwrite the spec on the next
+	// mcp-sync cycle.
+	m.DisplayName = parsed.EffectiveDisplayName()
 }
 
 // GenerateManifestParams holds the information available at generate time
diff --git a/internal/pipeline/climanifest_test.go b/internal/pipeline/climanifest_test.go
index c1f7229e..3a3aa053 100644
--- a/internal/pipeline/climanifest_test.go
+++ b/internal/pipeline/climanifest_test.go
@@ -808,6 +808,31 @@ func TestWriteMCPBManifestPreservesExistingDescription(t *testing.T) {
 	})
 }
 
+func TestRefreshCLIManifestFromSpecRefreshesDisplayName(t *testing.T) {
+	// RefreshCLIManifestFromSpec must overwrite an existing
+	// DisplayName, not preserve it — otherwise stale slug-derived
+	// values survive across mcp-sync cycles.
+	dir := t.TempDir()
+	writeManifest(t, dir, CLIManifest{
+		APIName:     "cal-com",
+		DisplayName: "Cal Com", // stale slug-derived value
+		MCPBinary:   "cal-com-pp-mcp",
+		MCPReady:    "full",
+	})
+	parsed := &spec.APISpec{
+		Name:        "cal-com",
+		DisplayName: "Cal.com", // authoritative value from upstream preservation
+	}
+
+	require.NoError(t, RefreshCLIManifestFromSpec(dir, parsed))
+
+	data, err := os.ReadFile(filepath.Join(dir, CLIManifestFilename))
+	require.NoError(t, err)
+	var got CLIManifest
+	require.NoError(t, json.Unmarshal(data, &got))
+	assert.Equal(t, "Cal.com", got.DisplayName)
+}
+
 func writeManifest(t *testing.T, dir string, m CLIManifest) {
 	t.Helper()
 	data, err := json.Marshal(m)
diff --git a/skills/printing-press-polish/SKILL.md b/skills/printing-press-polish/SKILL.md
index 9870c948..a751c2b1 100644
--- a/skills/printing-press-polish/SKILL.md
+++ b/skills/printing-press-polish/SKILL.md
@@ -124,7 +124,14 @@ The internal copy at `$CLI_DIR` can drift from the public library (`mvanhorn/pri
 
 1. **Locate the public library clone.** Honor `$PRINTING_PRESS_LIBRARY_PUBLIC` if set; otherwise scan the user's filesystem however fits this platform. Validate every candidate by checking the git remote points at `mvanhorn/printing-press-library` — other directories may share the name (forks, accidental name collisions). If multiple valid clones exist, prefer the most recently modified; ask the user to disambiguate only if still unclear.
 2. **Locate this CLI inside the clone.** `find <clone>/library -type d -name "<api>-pp-cli"` or equivalent.
-3. **Run `diff -r <public-cli-dir> $CLI_DIR`** (excluding build artifacts, the `.printing-press-tools-polish.json` ledger, and the binary).
+3. **Run `diff -r <public-cli-dir> $CLI_DIR`** with these exclusions, all of which are expected to diverge after publish:
+   - `.printing-press-tools-polish.json` (local ledger, not published)
+   - `go.mod` and `go.sum` — publish rewrites the module path from `<api>-pp-cli` to `github.com/mvanhorn/printing-press-library/library/<category>/<api>`
+   - All `.go` files where the only difference is the rewritten import path (the publish step propagates the new module path through every internal import). When inspecting `.go` diffs, scan for substantive changes — anything beyond the module-path prefix swap is real divergence.
+
+   Concretely: `diff -r --exclude=go.mod --exclude=go.sum --exclude=.printing-press-tools-polish.json <public-cli-dir> $CLI_DIR`.
+
+   Don't pass `--exclude='<api>-pp-cli'` or `--exclude='<api>-pp-mcp'` — those names match both the root-level binary files **and** the `cmd/<api>-pp-cli/` and `cmd/<api>-pp-mcp/` source directories. Excluding by binary name silently skips the entire `cmd/` subtree, hiding real divergence in `main.go`. The "Only in $CLI_DIR: <api>-pp-cli" line for the built binary is one row of expected output, not noise worth filtering at the cost of completeness.
 4. **Surface the result** before continuing.
 
 Outcomes:

← 0320269d feat(cli,skills): polish ledger per-item enforcement + AGENT  ·  back to Cli Printing Press  ·  feat(cli): mcp-sync reads display_name from public library r 64f67981 →