[object Object]

← back to Cli Printing Press

fix(score): preserve spec extension, remove hardcoded repo path

98977f2371823c03ac3193a4aa6ebf9830c1de40 · 2026-03-27 21:50:14 -0700 · Trevin Chow

- copySpecToOutput now preserves the original file extension (.json,
  .yaml, .yml) instead of always writing spec.json
- Skill no longer hardcodes ~/cli-printing-press — runs from the
  current repo directory
- Spec resolution in skill checks for spec.json, spec.yaml, spec.yml
- Added tests for YAML/YML extension preservation and empty specFlag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 98977f2371823c03ac3193a4aa6ebf9830c1de40
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Fri Mar 27 21:50:14 2026 -0700

    fix(score): preserve spec extension, remove hardcoded repo path
    
    - copySpecToOutput now preserves the original file extension (.json,
      .yaml, .yml) instead of always writing spec.json
    - Skill no longer hardcodes ~/cli-printing-press — runs from the
      current repo directory
    - Spec resolution in skill checks for spec.json, spec.yaml, spec.yml
    - Added tests for YAML/YML extension preservation and empty specFlag
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
 internal/pipeline/fullrun.go         | 10 ++++---
 internal/pipeline/fullrun_test.go    | 51 ++++++++++++++++++++++++++++--------
 skills/printing-press-score/SKILL.md | 12 ++++-----
 3 files changed, 53 insertions(+), 20 deletions(-)

diff --git a/internal/pipeline/fullrun.go b/internal/pipeline/fullrun.go
index 77204e96..09b2ec0c 100644
--- a/internal/pipeline/fullrun.go
+++ b/internal/pipeline/fullrun.go
@@ -422,8 +422,8 @@ func PrintComparisonTable(results []*FullRunResult) string {
 	return b.String()
 }
 
-// copySpecToOutput copies the source spec file into <outputDir>/spec.json
-// so that standalone scoring can find it without the original spec path.
+// copySpecToOutput copies the source spec file into <outputDir>/spec.<ext>
+// preserving the original extension so YAML specs stay YAML and JSON stays JSON.
 // Only copies when specFlag is "--spec" (local file). Errors are non-fatal.
 func copySpecToOutput(specFlag, specURL, outputDir string) error {
 	if specFlag != "--spec" {
@@ -433,7 +433,11 @@ func copySpecToOutput(specFlag, specURL, outputDir string) error {
 	if err != nil {
 		return fmt.Errorf("reading spec %s: %w", specURL, err)
 	}
-	dst := filepath.Join(outputDir, "spec.json")
+	ext := filepath.Ext(specURL)
+	if ext == "" {
+		ext = ".json"
+	}
+	dst := filepath.Join(outputDir, "spec"+ext)
 	if err := os.WriteFile(dst, data, 0o644); err != nil {
 		return fmt.Errorf("writing %s: %w", dst, err)
 	}
diff --git a/internal/pipeline/fullrun_test.go b/internal/pipeline/fullrun_test.go
index 5a8608db..7cd1d007 100644
--- a/internal/pipeline/fullrun_test.go
+++ b/internal/pipeline/fullrun_test.go
@@ -5,6 +5,7 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
+	"strings"
 	"testing"
 	"time"
 
@@ -67,18 +68,38 @@ func TestCopySpecToOutput(t *testing.T) {
 		name      string
 		specFlag  string
 		setup     func(t *testing.T, dir string) string // returns specURL
-		wantCopy  bool
+		wantFile  string                                 // expected output filename
 		wantError bool
 	}{
 		{
-			name:     "copies spec when flag is --spec",
+			name:     "copies json spec preserving extension",
 			specFlag: "--spec",
 			setup: func(t *testing.T, dir string) string {
 				specPath := filepath.Join(dir, "input-spec.json")
 				require.NoError(t, os.WriteFile(specPath, []byte(`{"openapi":"3.0.0"}`), 0o644))
 				return specPath
 			},
-			wantCopy: true,
+			wantFile: "spec.json",
+		},
+		{
+			name:     "copies yaml spec preserving extension",
+			specFlag: "--spec",
+			setup: func(t *testing.T, dir string) string {
+				specPath := filepath.Join(dir, "openapi.yaml")
+				require.NoError(t, os.WriteFile(specPath, []byte("openapi: 3.0.0\n"), 0o644))
+				return specPath
+			},
+			wantFile: "spec.yaml",
+		},
+		{
+			name:     "copies yml spec preserving extension",
+			specFlag: "--spec",
+			setup: func(t *testing.T, dir string) string {
+				specPath := filepath.Join(dir, "api.yml")
+				require.NoError(t, os.WriteFile(specPath, []byte("openapi: 3.0.0\n"), 0o644))
+				return specPath
+			},
+			wantFile: "spec.yml",
 		},
 		{
 			name:     "skips when flag is --docs",
@@ -86,7 +107,13 @@ func TestCopySpecToOutput(t *testing.T) {
 			setup: func(t *testing.T, dir string) string {
 				return "https://developers.notion.com/reference"
 			},
-			wantCopy: false,
+		},
+		{
+			name:     "skips when flag is empty",
+			specFlag: "",
+			setup: func(t *testing.T, dir string) string {
+				return ""
+			},
 		},
 		{
 			name:     "returns error when spec file missing",
@@ -94,7 +121,6 @@ func TestCopySpecToOutput(t *testing.T) {
 			setup: func(t *testing.T, dir string) string {
 				return filepath.Join(dir, "nonexistent.json")
 			},
-			wantCopy:  false,
 			wantError: true,
 		},
 	}
@@ -114,15 +140,18 @@ func TestCopySpecToOutput(t *testing.T) {
 				assert.NoError(t, err)
 			}
 
-			dst := filepath.Join(outputDir, "spec.json")
-			if tt.wantCopy {
+			if tt.wantFile != "" {
+				dst := filepath.Join(outputDir, tt.wantFile)
 				data, readErr := os.ReadFile(dst)
-				require.NoError(t, readErr, "spec.json should exist in output dir")
+				require.NoError(t, readErr, "%s should exist in output dir", tt.wantFile)
 				expected, _ := os.ReadFile(specURL)
-				assert.Equal(t, expected, data, "spec.json content should match source")
+				assert.Equal(t, expected, data, "content should match source")
 			} else if !tt.wantError {
-				_, readErr := os.ReadFile(dst)
-				assert.True(t, os.IsNotExist(readErr), "spec.json should not exist")
+				// Verify no spec file was created
+				entries, _ := os.ReadDir(outputDir)
+				for _, e := range entries {
+					assert.False(t, strings.HasPrefix(e.Name(), "spec."), "no spec file should exist, found %s", e.Name())
+				}
 			}
 		})
 	}
diff --git a/skills/printing-press-score/SKILL.md b/skills/printing-press-score/SKILL.md
index 43121550..012ee41d 100644
--- a/skills/printing-press-score/SKILL.md
+++ b/skills/printing-press-score/SKILL.md
@@ -26,7 +26,7 @@ Score generated CLIs against the Steinberger bar. Supports rescoring, scoring by
 ## Prerequisites
 
 - Go 1.21+ installed
-- The printing-press repo at ~/cli-printing-press
+- Running from inside the cli-printing-press repo (or a worktree of it)
 
 ## Step 1: Parse Arguments
 
@@ -77,7 +77,7 @@ If nothing resolves, report the error: "Could not find CLI '<name>'. Provide a p
 
 For each resolved CLI directory, find the OpenAPI spec:
 
-1. Check `<cli-dir>/spec.json` — if it exists, use it
+1. Check for a spec file in `<cli-dir>/` — look for `spec.json`, `spec.yaml`, or `spec.yml` (the extension is preserved from the original spec during generation)
 2. If not found, scan `docs/plans/*-pipeline/state.json` files for one matching this CLI's directory. Read its `spec_path` field. If that file exists on disk, use it.
 3. If no spec found, **proceed without `--spec`**. Note to the user: "No spec found — Tier 2 (domain correctness) scores will be 0. Provide a spec path to get full scoring."
 
@@ -86,7 +86,7 @@ For each resolved CLI directory, find the OpenAPI spec:
 Before running the scorecard, build the printing-press binary:
 
 ```bash
-cd ~/cli-printing-press && go build -o ./printing-press ./cmd/printing-press
+go build -o ./printing-press ./cmd/printing-press
 ```
 
 If the build fails, report the error and stop.
@@ -98,7 +98,7 @@ If the build fails, report the error and stop.
 Run the scorecard command:
 
 ```bash
-cd ~/cli-printing-press && ./printing-press scorecard --dir <resolved-path> --json
+./printing-press scorecard --dir <resolved-path> --json
 ```
 
 If a spec was found, add `--spec <spec-path>`.
@@ -141,10 +141,10 @@ Run **both** scorecard commands in **parallel** using two simultaneous Bash tool
 
 ```bash
 # Call 1:
-cd ~/cli-printing-press && ./printing-press scorecard --dir <path1> --spec <spec1> --json
+./printing-press scorecard --dir <path1> --spec <spec1> --json
 
 # Call 2:
-cd ~/cli-printing-press && ./printing-press scorecard --dir <path2> --spec <spec2> --json
+./printing-press scorecard --dir <path2> --spec <spec2> --json
 ```
 
 Parse both JSON outputs.

← 64e5ea58 refactor(scorecard): extract infra maps and add workflow/ins  ·  back to Cli Printing Press  ·  docs(scorecard): document intentional workflow/insight prefi 5ed49878 →