← back to Cli Printing Press
fix(cli): close redfin retro verification gaps (#762)
172c6c299ef48f278faadfa0fbdcef4a39a7c2ec · 2026-05-08 17:36:10 -0700 · Trevin Chow
Files touched
M docs/GLOSSARY.mdM internal/cli/schema.goM internal/cli/schema_test.goM internal/cli/shipcheck.goM internal/cli/shipcheck_test.goM internal/pipeline/seeds.goM skills/printing-press/SKILL.mdA testdata/golden/cases/schema-phase5-marker/artifacts.txtA testdata/golden/cases/schema-phase5-marker/command.txtA testdata/golden/cases/schema-phase5-skip/artifacts.txtA testdata/golden/cases/schema-phase5-skip/command.txtA testdata/golden/expected/schema-phase5-marker/exit.txtA testdata/golden/expected/schema-phase5-marker/stderr.txtA testdata/golden/expected/schema-phase5-marker/stdout.txtA testdata/golden/expected/schema-phase5-skip/exit.txtA testdata/golden/expected/schema-phase5-skip/stderr.txtA testdata/golden/expected/schema-phase5-skip/stdout.txt
Diff
commit 172c6c299ef48f278faadfa0fbdcef4a39a7c2ec
Author: Trevin Chow <trevin@trevinchow.com>
Date: Fri May 8 17:36:10 2026 -0700
fix(cli): close redfin retro verification gaps (#762)
---
docs/GLOSSARY.md | 2 +-
internal/cli/schema.go | 84 ++++++++++++++++++++--
internal/cli/schema_test.go | 52 ++++++++++++++
internal/cli/shipcheck.go | 36 ++++++++--
internal/cli/shipcheck_test.go | 41 ++++++++---
internal/pipeline/seeds.go | 5 +-
skills/printing-press/SKILL.md | 21 +++---
.../cases/schema-phase5-marker/artifacts.txt | 1 +
.../golden/cases/schema-phase5-marker/command.txt | 1 +
.../golden/cases/schema-phase5-skip/artifacts.txt | 1 +
.../golden/cases/schema-phase5-skip/command.txt | 1 +
.../golden/expected/schema-phase5-marker/exit.txt | 1 +
.../expected/schema-phase5-marker/stderr.txt | 0
.../expected/schema-phase5-marker/stdout.txt | 35 +++++++++
.../golden/expected/schema-phase5-skip/exit.txt | 1 +
.../golden/expected/schema-phase5-skip/stderr.txt | 0
.../golden/expected/schema-phase5-skip/stdout.txt | 28 ++++++++
17 files changed, 277 insertions(+), 33 deletions(-)
diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md
index c6f588da..8b481239 100644
--- a/docs/GLOSSARY.md
+++ b/docs/GLOSSARY.md
@@ -38,7 +38,7 @@ Default disambiguation conventions:
| **canonicalargs** | Tiny generator subpackage at `internal/canonicalargs/` exporting `Lookup(name) (string, bool)` for cross-domain positional placeholder names (`since`, `until`, `tag`, `vertical`). Both verify mock-mode dispatch and the SKILL template consult this registry as one step in the lookup chain `spec.Param.Default → canonicalargs → legacy syntheticArgValue switch → "mock-value"`. **Domain-specific names belong in the spec author's `Param.Default`, not here** — anti-pattern: "Never change the machine for one CLI's edge case." |
| **mcp-sync** | Subcommand on the printing-press binary (`printing-press mcp-sync <cli-dir>`) that migrates generated MCP surfaces from the old static novel-feature list to the runtime Cobra-tree mirror. It rewrites generated MCP files, adds the root command export when possible, regenerates `tools-manifest.json`, and refuses hand-edited `internal/mcp/tools.go` unless `--force` is passed. |
| **regen-merge** | Subcommand on the printing-press binary (`printing-press regen-merge <cli-dir> --fresh <fresh-dir>`) that classifies each Go file in a published library CLI by AST decl-set comparison against a fresh-generated tree, applies safe templated overwrites, restores lost AddCommand registrations in `root.go` and resource-parents, and merges `go.mod` while preserving the published monorepo module path. Targets the per-CLI sweep workflow (~5-15 min/CLI vs ~30-90 min manual). Six classification verdicts; stage-and-swap-with-recovery transactional apply; macOS+Linux only. Lives in `internal/pipeline/regenmerge/`. |
-| **shipcheck** | The verification block that gates publishing: dogfood + verify + workflow-verify + verify-skill + scorecard, run together. Dogfood includes `mcp_surface_parity`, so stale static MCP surfaces block shipping. All legs must pass before a printed CLI ships. |
+| **shipcheck** | The verification block that gates publishing: dogfood + verify + workflow-verify + verify-skill + validate-narrative + scorecard, run together. Dogfood includes `mcp_surface_parity`, and validate-narrative checks README/SKILL narrative commands against the built CLI. All legs must pass before a printed CLI ships. |
| **scorecard** / **scoring** | Two-tier quality assessment with a 50/50 weighted composite. Tier 1: infrastructure (16 string-matching dimensions, raw max 160, normalized to 0-50). Tier 2: domain correctness (7 semantic dimensions, raw max 60 when live verify ran, normalized to 0-50). Total /100 with letter grades. Source of truth: `internal/pipeline/scorecard.go` (`tier1Max` / `tier2Max`). Subcommand: `printing-press scorecard`. |
| **machine-owned freshness** | Opt-in freshness contract for store-backed printed CLIs using `cache.enabled`. Covered command paths map to syncable resources; in `--data-source auto` they may run a bounded pre-read refresh before serving local data. `--data-source local` never refreshes, `--data-source live` must not mutate the local store, and env opt-out only disables the freshness hook. This is current-cache freshness, not a guarantee of full historical backfill or API-specific enrichment. |
| **doctor** | Self-diagnostic command shipped inside every printed CLI for end-users to run. Checks environment, auth config, and connectivity at the user's runtime. Unlike dogfood (which validates at generation time), doctor runs post-install. |
diff --git a/internal/cli/schema.go b/internal/cli/schema.go
index 081e4b60..9ed0088e 100644
--- a/internal/cli/schema.go
+++ b/internal/cli/schema.go
@@ -192,18 +192,92 @@ const trafficAnalysisSchemaJSON = `{
}
}`
+const phase5MarkerSchemaJSON = `{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://github.com/mvanhorn/cli-printing-press/schemas/phase5-acceptance.schema.json",
+ "title": "CLI Printing Press phase5-acceptance.json",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["schema_version", "api_name", "run_id", "status", "level", "matrix_size", "tests_passed"],
+ "properties": {
+ "schema_version": {"type": "integer", "const": 1},
+ "api_name": {"type": "string", "minLength": 1},
+ "cli_name": {"type": "string"},
+ "run_id": {"type": "string", "minLength": 1},
+ "status": {"type": "string", "enum": ["pass", "fail"]},
+ "level": {"type": "string", "enum": ["quick", "full"]},
+ "matrix_size": {"type": "integer", "minimum": 1},
+ "tests_total": {"type": "integer", "minimum": 0},
+ "tests_passed": {"type": "integer", "minimum": 1},
+ "tests_skipped": {"type": "integer", "minimum": 0},
+ "tests_failed": {"type": "integer", "minimum": 0},
+ "completed_at": {"type": "string", "format": "date-time"},
+ "summary": {"type": "string"},
+ "auth_context": {"$ref": "#/$defs/auth_context"}
+ },
+ "$defs": {
+ "auth_context": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "type": {"type": "string"},
+ "api_key_available": {"type": "boolean"},
+ "browser_session_available": {"type": "boolean"}
+ }
+ }
+ }
+}`
+
+const phase5SkipSchemaJSON = `{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://github.com/mvanhorn/cli-printing-press/schemas/phase5-skip.schema.json",
+ "title": "CLI Printing Press phase5-skip.json",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["schema_version", "api_name", "run_id", "status", "skip_reason"],
+ "properties": {
+ "schema_version": {"type": "integer", "const": 1},
+ "api_name": {"type": "string", "minLength": 1},
+ "cli_name": {"type": "string"},
+ "run_id": {"type": "string", "minLength": 1},
+ "status": {"type": "string", "const": "skip"},
+ "skip_reason": {"type": "string", "minLength": 1},
+ "auth_context": {"$ref": "#/$defs/auth_context"}
+ },
+ "$defs": {
+ "auth_context": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "type": {"type": "string"},
+ "api_key_available": {"type": "boolean"},
+ "browser_session_available": {"type": "boolean"}
+ }
+ }
+ }
+}`
+
func newSchemaCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "schema",
Short: "Print machine-readable schemas for Printing Press artifacts",
}
- cmd.AddCommand(&cobra.Command{
- Use: "traffic-analysis",
- Short: "Print the traffic-analysis.json schema",
+ cmd.AddCommand(newSchemaPrintCmd("traffic-analysis", "Print the traffic-analysis.json schema", trafficAnalysisSchemaJSON))
+ cmd.AddCommand(newSchemaPrintCmd("phase5-marker", "Print the phase5-acceptance.json schema", phase5MarkerSchemaJSON))
+ cmd.AddCommand(newSchemaPrintCmd("phase5-skip", "Print the phase5-skip.json schema", phase5SkipSchemaJSON))
+ return cmd
+}
+
+func newSchemaPrintCmd(use, short, schema string) *cobra.Command {
+ var asJSON bool
+ cmd := &cobra.Command{
+ Use: use,
+ Short: short,
RunE: func(cmd *cobra.Command, args []string) error {
- fmt.Fprintln(cmd.OutOrStdout(), trafficAnalysisSchemaJSON)
+ fmt.Fprintln(cmd.OutOrStdout(), schema)
return nil
},
- })
+ }
+ cmd.Flags().BoolVar(&asJSON, "json", false, "Emit JSON schema (default; accepted for command symmetry)")
return cmd
}
diff --git a/internal/cli/schema_test.go b/internal/cli/schema_test.go
index d9028d36..117b5e0b 100644
--- a/internal/cli/schema_test.go
+++ b/internal/cli/schema_test.go
@@ -21,3 +21,55 @@ func TestSchemaTrafficAnalysisPrintsJSONSchema(t *testing.T) {
assert.Contains(t, output, `"confidence": {"type": "number"`)
assert.Contains(t, output, `"endpoint_clusters"`)
}
+
+func TestSchemaPhase5MarkerPrintsJSONSchema(t *testing.T) {
+ cmd := newSchemaCmd()
+ cmd.SetArgs([]string{"phase5-marker", "--json"})
+
+ output, err := runWithCapturedStdout(t, cmd.Execute)
+ require.NoError(t, err)
+
+ var schema map[string]any
+ require.NoError(t, json.Unmarshal([]byte(output), &schema))
+ assert.Equal(t, "CLI Printing Press phase5-acceptance.json", schema["title"])
+ for _, field := range []string{
+ "schema_version",
+ "run_id",
+ "api_name",
+ "cli_name",
+ "level",
+ "status",
+ "matrix_size",
+ "tests_total",
+ "tests_passed",
+ "tests_failed",
+ "completed_at",
+ "summary",
+ } {
+ assert.Contains(t, output, `"`+field+`"`)
+ }
+}
+
+func TestSchemaPhase5SkipPrintsJSONSchema(t *testing.T) {
+ cmd := newSchemaCmd()
+ cmd.SetArgs([]string{"phase5-skip"})
+
+ output, err := runWithCapturedStdout(t, cmd.Execute)
+ require.NoError(t, err)
+
+ var schema map[string]any
+ require.NoError(t, json.Unmarshal([]byte(output), &schema))
+ assert.Equal(t, "CLI Printing Press phase5-skip.json", schema["title"])
+ for _, field := range []string{"schema_version", "run_id", "api_name", "cli_name", "status", "skip_reason", "auth_context"} {
+ assert.Contains(t, output, `"`+field+`"`)
+ }
+}
+
+func TestSchemaUnknownNameFails(t *testing.T) {
+ cmd := newSchemaCmd()
+ cmd.SetArgs([]string{"unknown-name"})
+
+ err := cmd.Execute()
+ require.Error(t, err)
+ assert.Contains(t, err.Error(), `unknown command "unknown-name"`)
+}
diff --git a/internal/cli/shipcheck.go b/internal/cli/shipcheck.go
index 21ed5052..db3b5bf0 100644
--- a/internal/cli/shipcheck.go
+++ b/internal/cli/shipcheck.go
@@ -15,7 +15,7 @@ import (
)
// shipcheck is the canonical Phase 4 verification umbrella. It runs each
-// of the five legs as a subprocess of the same printing-press binary,
+// of the six legs as a subprocess of the same printing-press binary,
// aggregates exit codes, and prints a per-leg summary. Legs remain
// callable standalone — this command is purely additive orchestration.
//
@@ -64,11 +64,10 @@ type shipcheckLeg struct {
args func(*shipcheckOpts) []string
}
-// shipcheckLegs enumerates the five legs in canonical execution order.
+// shipcheckLegs enumerates the six legs in canonical execution order.
// Order matters: dogfood writes research.json updates that scorecard
-// later consumes, so dogfood must run before scorecard. workflow-verify
-// and verify-skill have no inter-leg dependencies; their position is
-// driven by the canonical Phase 4 sequence in the /printing-press skill.
+// later consumes, verify builds the CLI binary validate-narrative uses,
+// and scorecard should see all earlier validation failures first.
var shipcheckLegs = []shipcheckLeg{
{
name: "dogfood",
@@ -118,6 +117,18 @@ var shipcheckLegs = []shipcheckLeg{
return a
},
},
+ {
+ name: "validate-narrative",
+ args: func(o *shipcheckOpts) []string {
+ return []string{
+ "validate-narrative",
+ "--strict",
+ "--full-examples",
+ "--research", shipcheckResearchPath(o),
+ "--binary", shipcheckCLIPath(o),
+ }
+ },
+ },
{
name: "scorecard",
args: func(o *shipcheckOpts) []string {
@@ -136,6 +147,18 @@ var shipcheckLegs = []shipcheckLeg{
},
}
+func shipcheckResearchPath(o *shipcheckOpts) string {
+ dir := o.researchDir
+ if dir == "" {
+ dir = o.dir
+ }
+ return filepath.Join(dir, "research.json")
+}
+
+func shipcheckCLIPath(o *shipcheckOpts) string {
+ return filepath.Join(o.dir, filepath.Base(o.dir))
+}
+
// shipcheckLegResult is the per-leg outcome of one umbrella run.
type shipcheckLegResult struct {
Name string
@@ -341,7 +364,7 @@ func newShipcheckCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "shipcheck",
- Short: "Run all five verification legs (dogfood, verify, workflow-verify, verify-skill, scorecard) as one canonical Phase 4 sweep",
+ Short: "Run all six verification legs (dogfood, verify, workflow-verify, verify-skill, validate-narrative, scorecard) as one canonical Phase 4 sweep",
Long: `shipcheck runs every Phase 4 verification leg in sequence and aggregates their
exit codes into a single verdict. It is the canonical local invocation that
matches what the public-library CI runs.
@@ -351,6 +374,7 @@ Legs (in canonical order):
verify — runtime command testing (with --fix to auto-repair common breakage)
workflow-verify — primary workflow end-to-end against the verification manifest
verify-skill — SKILL.md flag/positional/command consistency with the shipped CLI
+ validate-narrative — README/SKILL narrative commands against the built CLI
scorecard — Steinberger quality bar (with --live-check sampled output probes)
In default mode, every leg streams its full output to the terminal as it runs
diff --git a/internal/cli/shipcheck_test.go b/internal/cli/shipcheck_test.go
index dd0c8f72..b00fac44 100644
--- a/internal/cli/shipcheck_test.go
+++ b/internal/cli/shipcheck_test.go
@@ -104,7 +104,7 @@ func runShipcheckCmd(t *testing.T, args ...string) error {
}
// TestShipcheck_AllLegsPass: every leg exits 0, umbrella returns nil.
-// All five legs must be invoked in canonical order with correct argv.
+// All six legs must be invoked in canonical order with correct argv.
func TestShipcheck_AllLegsPass(t *testing.T) {
h := newShipcheckHarness(t)
@@ -117,8 +117,8 @@ func TestShipcheck_AllLegsPass(t *testing.T) {
t.Fatalf("expected %d leg invocations; got %d: %v", len(shipcheckLegs), len(invocations), invocations)
}
- // Confirm canonical order: dogfood, verify, workflow-verify, verify-skill, scorecard.
- wantOrder := []string{"dogfood", "verify", "workflow-verify", "verify-skill", "scorecard"}
+ // Confirm canonical order: dogfood, verify, workflow-verify, verify-skill, validate-narrative, scorecard.
+ wantOrder := []string{"dogfood", "verify", "workflow-verify", "verify-skill", "validate-narrative", "scorecard"}
for i, want := range wantOrder {
// argv[0] is the stub binary path; argv[1] is the leg name.
if len(invocations[i]) < 2 {
@@ -131,7 +131,7 @@ func TestShipcheck_AllLegsPass(t *testing.T) {
}
// TestShipcheck_OneLegFails: verify-skill exits 1, umbrella returns
-// ExitError with code 1; all five legs still ran (no fail-fast).
+// ExitError with code 1; all six legs still ran (no fail-fast).
func TestShipcheck_OneLegFails(t *testing.T) {
h := newShipcheckHarness(t)
t.Setenv("STUB_EXIT_VERIFY_SKILL", "1")
@@ -232,15 +232,34 @@ func TestShipcheck_PassesSpecAndResearchDir(t *testing.T) {
t.Errorf("scorecard argv missing --research-dir: %v", scorecardArgs)
}
- // workflow-verify and verify-skill don't take --spec or --research-dir;
+ // workflow-verify, verify-skill, and validate-narrative don't take --spec or --research-dir;
// confirm they don't get them.
- wfArgs := findInvocation(invocations, "workflow-verify")
- if argvHas(wfArgs, "--spec") {
- t.Errorf("workflow-verify should not receive --spec; got %v", wfArgs)
+ for _, leg := range []string{"workflow-verify", "verify-skill", "validate-narrative"} {
+ args := findInvocation(invocations, leg)
+ if argvHas(args, "--spec") {
+ t.Errorf("%s should not receive --spec; got %v", leg, args)
+ }
+ if argvHas(args, "--research-dir") {
+ t.Errorf("%s should not receive --research-dir; got %v", leg, args)
+ }
}
- vsArgs := findInvocation(invocations, "verify-skill")
- if argvHas(vsArgs, "--spec") {
- t.Errorf("verify-skill should not receive --spec; got %v", vsArgs)
+}
+
+func TestShipcheck_ValidateNarrativeUsesResearchAndBuiltBinary(t *testing.T) {
+ h := newShipcheckHarness(t)
+ researchDir := t.TempDir()
+
+ if err := runShipcheckCmd(t, "--dir", h.dir, "--research-dir", researchDir); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ args := findInvocation(readStubLog(t, h.logFile), "validate-narrative")
+ wantResearch := filepath.Join(researchDir, "research.json")
+ wantBinary := filepath.Join(h.dir, filepath.Base(h.dir))
+ for _, want := range []string{"--strict", "--full-examples", "--research", wantResearch, "--binary", wantBinary} {
+ if !argvHas(args, want) {
+ t.Errorf("validate-narrative argv missing %q: %v", want, args)
+ }
}
}
diff --git a/internal/pipeline/seeds.go b/internal/pipeline/seeds.go
index 869cb57e..905705b3 100644
--- a/internal/pipeline/seeds.go
+++ b/internal/pipeline/seeds.go
@@ -274,7 +274,7 @@ date: {{now}}
# Phase Goal
-Evaluate the generated CLI with one shipcheck block: dogfood, runtime verification, and scorecard evidence.
+Evaluate the generated CLI with one shipcheck block: dogfood, runtime verification, workflow verification, skill verification, narrative validation, and scorecard evidence.
## Context
@@ -299,6 +299,9 @@ Evaluate the generated CLI with one shipcheck block: dogfood, runtime verificati
- printing-press dogfood --dir {{.OutputDir}} --spec <spec>
- printing-press verify --dir {{.OutputDir}} --spec <spec> --fix
+- printing-press workflow-verify --dir {{.OutputDir}}
+- printing-press verify-skill --dir {{.OutputDir}}
+- printing-press validate-narrative --strict --full-examples --research {{.PipelineDir}}/research.json --binary {{.OutputDir}}/<cli-binary>
- printing-press scorecard --dir {{.OutputDir}} --spec <spec>
- Generated CLI binary and help surfaces in {{.OutputDir}}
`,
diff --git a/skills/printing-press/SKILL.md b/skills/printing-press/SKILL.md
index 1f0dc3d1..193f5edf 100644
--- a/skills/printing-press/SKILL.md
+++ b/skills/printing-press/SKILL.md
@@ -1942,7 +1942,7 @@ Phase 1 research brief for auth requirements and manually add env var support to
`config.go` using the pattern: add `APIKey`/`APIKeySource` fields to the Config struct,
and `os.Getenv("<API>_API_KEY")` in the Load function.
-**REQUIRED: Validate narrative `command` strings before saving/publishing examples.**
+**Validate narrative `command` strings before publishing examples.**
The LLM (or human) authoring `research.json` can name commands that don't actually
exist in the generated CLI — `<cli> stats` when the real shape is `<cli> reports stats`,
or a command that was dropped because its endpoint had a complex body. It can also
@@ -1951,12 +1951,14 @@ broken commands ship to the README's Quick Start (`narrative.quickstart`) and th
SKILL's recipes (`narrative.recipes`); users copy-paste them and hit failures on the
very first invocation.
-Build the CLI binary, then run `printing-press validate-narrative` against it. The
-subcommand walks every `narrative.quickstart[].command` and `narrative.recipes[].command`,
-strips the binary name and trailing arguments, and runs `<binary> <words> --help` for
-each. With `--full-examples`, it also runs the complete example under
-`PRINTING_PRESS_VERIFY=1`, appending `--dry-run` when the command advertises it. This
-catches bad flags and argument shapes without making live API calls.
+`printing-press shipcheck` now runs `validate-narrative --strict --full-examples`
+automatically after `verify` builds the CLI binary. The standalone command is still
+useful immediately after editing `research.json`: it walks every
+`narrative.quickstart[].command` and `narrative.recipes[].command`, strips the binary
+name and trailing arguments, and runs `<binary> <words> --help` for each. With
+`--full-examples`, it also runs the complete example under `PRINTING_PRESS_VERIFY=1`,
+appending `--dry-run` when the command advertises it. This catches bad flags and
+argument shapes without making live API calls.
```bash
QUICKSTART_BINARY="$CLI_WORK_DIR/<api>-pp-cli"
@@ -2299,7 +2301,7 @@ Do not rationalize skipping transcendence features because "the CLI already work
## Phase 4: Shipcheck
-Run one combined verification block via the `shipcheck` umbrella, which runs all five legs (dogfood, verify, workflow-verify, verify-skill, scorecard) in canonical order, propagates exit codes, and prints a per-leg verdict summary. The umbrella is the canonical Phase 4 invocation; running the legs individually is supported but not recommended (operators have skipped legs that way and shipped broken CLIs).
+Run one combined verification block via the `shipcheck` umbrella, which runs all six legs (dogfood, verify, workflow-verify, verify-skill, validate-narrative, scorecard) in canonical order, propagates exit codes, and prints a per-leg verdict summary. The umbrella is the canonical Phase 4 invocation; running the legs individually is supported but not recommended (operators have skipped legs that way and shipped broken CLIs).
Before running shipcheck, update the lock heartbeat:
```bash
@@ -2313,7 +2315,7 @@ printing-press shipcheck \
--research-dir "$API_RUN_DIR"
```
-The umbrella defaults to `verify --fix` (auto-repair common failures) and `scorecard --live-check` (sample novel-feature output against real targets). Use `--no-fix` for a read-only pass, `--no-live-check` to skip live sampling, or `--json` for a structured envelope (suppresses per-leg output for clean piping). Pass `--api-key` / `--env-var` through to verify when live testing needs a credential, or `--strict` to make verify-skill treat likely-false-positive findings as failures.
+The umbrella defaults to `verify --fix` (auto-repair common failures), `validate-narrative --strict --full-examples` (README/SKILL narrative command validation), and `scorecard --live-check` (sample novel-feature output against real targets). Use `--no-fix` for a read-only pass, `--no-live-check` to skip live sampling, or `--json` for a structured envelope (suppresses per-leg output for clean piping). Pass `--api-key` / `--env-var` through to verify when live testing needs a credential, or `--strict` to make verify-skill treat likely-false-positive findings as failures.
If a leg fails, re-run that one leg standalone (e.g., `printing-press verify-skill --dir <CLI_WORK_DIR>`) for focused iteration; once it passes, re-run the full `shipcheck` umbrella to confirm no regression in the others.
@@ -2322,6 +2324,7 @@ Interpretation:
- `verify` catches runtime breakage and runs the auto-fix loop for common failures
- `workflow-verify` tests the primary workflow end-to-end using the verification manifest (workflow_verify.yaml). Three verdicts: workflow-pass, workflow-fail, unverified-needs-auth
- `verify-skill` checks that every `--flag` and command path in SKILL.md actually exists in the shipped CLI source. Catches bogus examples invented by the absorb LLM (e.g., `search --max-time` when `--max-time` is a `tonight` flag). Exit 1 = findings to fix; exit 0 = SKILL is honest.
+- `validate-narrative` checks that every README/SKILL narrative command path, flag, and argument shape in research.json resolves against the built CLI under `PRINTING_PRESS_VERIFY=1`
- `scorecard` is the structural quality snapshot, not the source of truth by itself
Fix order (update heartbeat between each fix category to prevent stale lock during long fix loops):
diff --git a/testdata/golden/cases/schema-phase5-marker/artifacts.txt b/testdata/golden/cases/schema-phase5-marker/artifacts.txt
new file mode 100644
index 00000000..a96ba8e2
--- /dev/null
+++ b/testdata/golden/cases/schema-phase5-marker/artifacts.txt
@@ -0,0 +1 @@
+# no extra artifacts
diff --git a/testdata/golden/cases/schema-phase5-marker/command.txt b/testdata/golden/cases/schema-phase5-marker/command.txt
new file mode 100644
index 00000000..0733608c
--- /dev/null
+++ b/testdata/golden/cases/schema-phase5-marker/command.txt
@@ -0,0 +1 @@
+"$BINARY" schema phase5-marker
diff --git a/testdata/golden/cases/schema-phase5-skip/artifacts.txt b/testdata/golden/cases/schema-phase5-skip/artifacts.txt
new file mode 100644
index 00000000..a96ba8e2
--- /dev/null
+++ b/testdata/golden/cases/schema-phase5-skip/artifacts.txt
@@ -0,0 +1 @@
+# no extra artifacts
diff --git a/testdata/golden/cases/schema-phase5-skip/command.txt b/testdata/golden/cases/schema-phase5-skip/command.txt
new file mode 100644
index 00000000..e8ec0164
--- /dev/null
+++ b/testdata/golden/cases/schema-phase5-skip/command.txt
@@ -0,0 +1 @@
+"$BINARY" schema phase5-skip
diff --git a/testdata/golden/expected/schema-phase5-marker/exit.txt b/testdata/golden/expected/schema-phase5-marker/exit.txt
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/testdata/golden/expected/schema-phase5-marker/exit.txt
@@ -0,0 +1 @@
+0
diff --git a/testdata/golden/expected/schema-phase5-marker/stderr.txt b/testdata/golden/expected/schema-phase5-marker/stderr.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/testdata/golden/expected/schema-phase5-marker/stdout.txt b/testdata/golden/expected/schema-phase5-marker/stdout.txt
new file mode 100644
index 00000000..c8a1c61b
--- /dev/null
+++ b/testdata/golden/expected/schema-phase5-marker/stdout.txt
@@ -0,0 +1,35 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://github.com/mvanhorn/cli-printing-press/schemas/phase5-acceptance.schema.json",
+ "title": "CLI Printing Press phase5-acceptance.json",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["schema_version", "api_name", "run_id", "status", "level", "matrix_size", "tests_passed"],
+ "properties": {
+ "schema_version": {"type": "integer", "const": 1},
+ "api_name": {"type": "string", "minLength": 1},
+ "cli_name": {"type": "string"},
+ "run_id": {"type": "string", "minLength": 1},
+ "status": {"type": "string", "enum": ["pass", "fail"]},
+ "level": {"type": "string", "enum": ["quick", "full"]},
+ "matrix_size": {"type": "integer", "minimum": 1},
+ "tests_total": {"type": "integer", "minimum": 0},
+ "tests_passed": {"type": "integer", "minimum": 1},
+ "tests_skipped": {"type": "integer", "minimum": 0},
+ "tests_failed": {"type": "integer", "minimum": 0},
+ "completed_at": {"type": "string", "format": "date-time"},
+ "summary": {"type": "string"},
+ "auth_context": {"$ref": "#/$defs/auth_context"}
+ },
+ "$defs": {
+ "auth_context": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "type": {"type": "string"},
+ "api_key_available": {"type": "boolean"},
+ "browser_session_available": {"type": "boolean"}
+ }
+ }
+ }
+}
diff --git a/testdata/golden/expected/schema-phase5-skip/exit.txt b/testdata/golden/expected/schema-phase5-skip/exit.txt
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/testdata/golden/expected/schema-phase5-skip/exit.txt
@@ -0,0 +1 @@
+0
diff --git a/testdata/golden/expected/schema-phase5-skip/stderr.txt b/testdata/golden/expected/schema-phase5-skip/stderr.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/testdata/golden/expected/schema-phase5-skip/stdout.txt b/testdata/golden/expected/schema-phase5-skip/stdout.txt
new file mode 100644
index 00000000..2b62aec0
--- /dev/null
+++ b/testdata/golden/expected/schema-phase5-skip/stdout.txt
@@ -0,0 +1,28 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://github.com/mvanhorn/cli-printing-press/schemas/phase5-skip.schema.json",
+ "title": "CLI Printing Press phase5-skip.json",
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["schema_version", "api_name", "run_id", "status", "skip_reason"],
+ "properties": {
+ "schema_version": {"type": "integer", "const": 1},
+ "api_name": {"type": "string", "minLength": 1},
+ "cli_name": {"type": "string"},
+ "run_id": {"type": "string", "minLength": 1},
+ "status": {"type": "string", "const": "skip"},
+ "skip_reason": {"type": "string", "minLength": 1},
+ "auth_context": {"$ref": "#/$defs/auth_context"}
+ },
+ "$defs": {
+ "auth_context": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "type": {"type": "string"},
+ "api_key_available": {"type": "boolean"},
+ "browser_session_available": {"type": "boolean"}
+ }
+ }
+ }
+}
← 745f2e75 fix(cli): sync sibling list endpoints (#756)
·
back to Cli Printing Press
·
feat(ci): add verify-skill drift check (#766) 1af0f275 →