← back to Cli Printing Press
feat(cli): expose PRINTING_PRESS_DOGFOOD env signal for long-running commands (#1493)
96fca0815d80417d8a3b046ba20e822aa7a792d6 · 2026-05-15 22:51:11 -0700 · Trevin Chow
* feat(cli): expose PRINTING_PRESS_DOGFOOD env signal for long-running commands
The live-dogfood matrix applies a flat 30s per-command timeout. Long-running
novel commands (full sync loops, content crawlers, bulk archive walks) trip
the timeout on their real happy path and the matrix verdict flips to FAIL
even when the command itself is healthy.
This adds a dogfood env signal that mirrors the existing PRINTING_PRESS_VERIFY
pattern: the runner sets PRINTING_PRESS_DOGFOOD=1 on every subprocess, and
cliutil.IsDogfoodEnv() is emitted into every printed CLI so commands can
short-circuit to a bounded code path (paginate once, smaller --limit) and
fit inside the timeout.
Unlike IsVerifyEnv, this does not mean "don't hit the network" -- dogfood
is a real-API matrix. The helper docs and the new AGENTS.md / SKILL.md
sections call out the distinction so authors don't substitute mocked data
for real calls.
Includes a test that asserts the runner-side const matches the template-side
emitted helper so a future rename on either side doesn't silently break
every IsDogfoodEnv() short-circuit.
Closes #1232
* test(cli): unset PRINTING_PRESS_DOGFOOD before env-propagation assertion
Without the unset, a CI runner that already has PRINTING_PRESS_DOGFOOD=1
in its environment would pass the assertion via inheritance through
applyDefaultSubprocessEnv -> os.Environ() regardless of whether the
runner's own append line is present. t.Setenv with empty string forces
the test to prove the append line is what gets the var into the
subprocess.
---------
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Files touched
M AGENTS.mdM internal/generator/templates/cliutil_verifyenv.go.tmplM internal/pipeline/live_dogfood.goM internal/pipeline/live_dogfood_test.goM skills/printing-press/SKILL.mdM testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cliutil/verifyenv.go
Diff
commit 96fca0815d80417d8a3b046ba20e822aa7a792d6
Author: Trevin Chow <trevin@trevinchow.com>
Date: Fri May 15 22:51:11 2026 -0700
feat(cli): expose PRINTING_PRESS_DOGFOOD env signal for long-running commands (#1493)
* feat(cli): expose PRINTING_PRESS_DOGFOOD env signal for long-running commands
The live-dogfood matrix applies a flat 30s per-command timeout. Long-running
novel commands (full sync loops, content crawlers, bulk archive walks) trip
the timeout on their real happy path and the matrix verdict flips to FAIL
even when the command itself is healthy.
This adds a dogfood env signal that mirrors the existing PRINTING_PRESS_VERIFY
pattern: the runner sets PRINTING_PRESS_DOGFOOD=1 on every subprocess, and
cliutil.IsDogfoodEnv() is emitted into every printed CLI so commands can
short-circuit to a bounded code path (paginate once, smaller --limit) and
fit inside the timeout.
Unlike IsVerifyEnv, this does not mean "don't hit the network" -- dogfood
is a real-API matrix. The helper docs and the new AGENTS.md / SKILL.md
sections call out the distinction so authors don't substitute mocked data
for real calls.
Includes a test that asserts the runner-side const matches the template-side
emitted helper so a future rename on either side doesn't silently break
every IsDogfoodEnv() short-circuit.
Closes #1232
* test(cli): unset PRINTING_PRESS_DOGFOOD before env-propagation assertion
Without the unset, a CI runner that already has PRINTING_PRESS_DOGFOOD=1
in its environment would pass the assertion via inheritance through
applyDefaultSubprocessEnv -> os.Environ() regardless of whether the
runner's own append line is present. t.Setenv with empty string forces
the test to prove the append line is what gets the var into the
subprocess.
---------
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
---
AGENTS.md | 3 ++
.../generator/templates/cliutil_verifyenv.go.tmpl | 25 ++++++++++++
internal/pipeline/live_dogfood.go | 8 ++++
internal/pipeline/live_dogfood_test.go | 45 ++++++++++++++++++++++
skills/printing-press/SKILL.md | 7 ++++
.../internal/cliutil/verifyenv.go | 25 ++++++++++++
6 files changed, 113 insertions(+)
diff --git a/AGENTS.md b/AGENTS.md
index d013aaf3..11b5f39f 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -41,6 +41,9 @@ Hand-written novel commands that perform visible actions (open browser tabs, sen
1. Print by default; require explicit opt-in (`--launch`, `--send`, `--play`, etc.) to actually act.
2. Short-circuit when `cliutil.IsVerifyEnv()` is true. The verifier sets `PRINTING_PRESS_VERIFY=1` in every mock-mode subprocess; this env-var check is the floor that catches any side-effect command the verifier's heuristic classifier misses.
+### Long-running commands under live-dogfood
+Hand-written novel commands whose happy path is an expensive network operation (full sync loops, content crawlers, bulk archive walks) MUST curtail work when `cliutil.IsDogfoodEnv()` returns true. The `printing-press dogfood --live` runner sets `PRINTING_PRESS_DOGFOOD=1` in every subprocess and applies a flat 30s per-command timeout; without a short-circuit, the happy-path test trips the timeout and the matrix verdict flips to FAIL even when the command itself is healthy. Unlike `IsVerifyEnv`, this does NOT mean "don't hit the network" — dogfood is a real-API matrix. Use it to bound work (paginate once, fetch a bounded sample, honor a smaller `--limit` default), never to substitute mock data for real calls.
+
### Generator-reserved namespaces
`internal/cliutil/` and `internal/mcp/cobratree/` are generator-owned packages emitted into every printed CLI. Do not hand-author code in them and do not name agent-authored helpers that collide with their exports — regen will overwrite the work. Novel-feature code goes in command packages and may import from `cliutil`.
diff --git a/internal/generator/templates/cliutil_verifyenv.go.tmpl b/internal/generator/templates/cliutil_verifyenv.go.tmpl
index 2be49a9a..b05f0d20 100644
--- a/internal/generator/templates/cliutil_verifyenv.go.tmpl
+++ b/internal/generator/templates/cliutil_verifyenv.go.tmpl
@@ -12,6 +12,13 @@ import "os"
// spamming the user's environment during verify runs.
const VerifyEnvVar = "PRINTING_PRESS_VERIFY"
+// DogfoodEnvVar is the env var the printing-press live-dogfood runner
+// sets in every subprocess. Distinct from VerifyEnvVar because dogfood
+// is a real-network matrix: commands may still perform actual API
+// calls, just curtailed (paginate-once, bounded crawl, etc.) so the
+// runner's flat 30s per-command timeout doesn't trip.
+const DogfoodEnvVar = "PRINTING_PRESS_DOGFOOD"
+
// IsVerifyEnv reports whether the current process is running under the
// printing-press verifier in mock mode. Generated commands with side
// effects pair this check with print-by-default + explicit opt-in
@@ -28,3 +35,21 @@ const VerifyEnvVar = "PRINTING_PRESS_VERIFY"
func IsVerifyEnv() bool {
return os.Getenv(VerifyEnvVar) == "1"
}
+
+// IsDogfoodEnv reports whether the current process is running under
+// the printing-press live-dogfood matrix. Long-running commands (full
+// sync loops, content crawlers, bulk archive walks) should use this
+// to curtail work so the flat 30s per-command timeout doesn't kill an
+// otherwise healthy happy_path test. Typical pattern: paginate once,
+// fetch a bounded sample, or honor a smaller --limit default.
+//
+// if cliutil.IsDogfoodEnv() {
+// return crawl(ctx, opts.WithMaxPages(1))
+// }
+//
+// Unlike IsVerifyEnv this does NOT mean "don't hit the network" —
+// dogfood is a real-API matrix. Use this only to bound work, never to
+// substitute mock data for real calls.
+func IsDogfoodEnv() bool {
+ return os.Getenv(DogfoodEnvVar) == "1"
+}
diff --git a/internal/pipeline/live_dogfood.go b/internal/pipeline/live_dogfood.go
index 713b795c..2acd0e0e 100644
--- a/internal/pipeline/live_dogfood.go
+++ b/internal/pipeline/live_dogfood.go
@@ -43,6 +43,13 @@ const reasonNoLiveSignal = "no live happy/json pass; credential-unavailable skip
const reasonUnavailableRunnerCredentials = "unavailable for runner credentials"
const reasonFileFixtureRequired = "file fixture required"
+// dogfoodEnvVar is the env signal every live-dogfood subprocess
+// inherits. Generated commands with a long-running happy path detect
+// this via cliutil.IsDogfoodEnv() and curtail work (paginate once,
+// honor a smaller --limit) so the matrix's per-command timeout
+// doesn't kill an otherwise healthy run.
+const dogfoodEnvVar = "PRINTING_PRESS_DOGFOOD"
+
type LiveDogfoodOptions struct {
CLIDir string
BinaryName string
@@ -859,6 +866,7 @@ func runLiveDogfoodProcess(binaryPath, cliDir string, args []string, timeout tim
cmd := exec.CommandContext(ctx, binaryPath, args...)
cmd.Dir = cliDir
applyDefaultSubprocessEnv(cmd)
+ cmd.Env = append(cmd.Env, dogfoodEnvVar+"=1")
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
cmd.Stdout = &limitedWriter{w: stdout, remaining: MaxOutputBytes}
diff --git a/internal/pipeline/live_dogfood_test.go b/internal/pipeline/live_dogfood_test.go
index 8bb1808b..3172985c 100644
--- a/internal/pipeline/live_dogfood_test.go
+++ b/internal/pipeline/live_dogfood_test.go
@@ -4,11 +4,13 @@ import (
"encoding/json"
"os"
"path/filepath"
+ "regexp"
"runtime"
"strings"
"testing"
"time"
+ "github.com/mvanhorn/cli-printing-press/v4/internal/generator"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -104,6 +106,49 @@ func TestRunLiveDogfoodDoesNotWriteAcceptanceMarkerOnFail(t *testing.T) {
assert.True(t, os.IsNotExist(statErr), "failed live dogfood must not write an acceptance marker")
}
+// TestDogfoodEnvVarMatchesEmittedTemplate guards against the runner-side
+// const and the emitted-CLI helper drifting apart. They live in
+// separate Go modules so a shared import is impossible; this test reads
+// the template as text and asserts the literal matches dogfoodEnvVar.
+// Without it, a typo on either side would silently break every
+// IsDogfoodEnv() short-circuit in printed CLIs.
+func TestDogfoodEnvVarMatchesEmittedTemplate(t *testing.T) {
+ content, err := generator.TemplateFS.ReadFile("templates/cliutil_verifyenv.go.tmpl")
+ require.NoError(t, err)
+
+ re := regexp.MustCompile(`const\s+DogfoodEnvVar\s*=\s*"([^"]+)"`)
+ match := re.FindStringSubmatch(string(content))
+ require.Len(t, match, 2, "DogfoodEnvVar const not found in cliutil_verifyenv.go.tmpl")
+ assert.Equal(t, dogfoodEnvVar, match[1], "runner-side dogfoodEnvVar must match template-side DogfoodEnvVar literal")
+}
+
+// TestRunLiveDogfoodProcessSetsDogfoodEnvVar asserts the live-dogfood
+// subprocess inherits PRINTING_PRESS_DOGFOOD=1 so long-running commands
+// can short-circuit via cliutil.IsDogfoodEnv() to fit inside the
+// matrix's per-command timeout.
+func TestRunLiveDogfoodProcessSetsDogfoodEnvVar(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("test uses a shell script as the fake binary; skip on Windows")
+ }
+
+ // Unset before the call so a CI runner that happens to have
+ // PRINTING_PRESS_DOGFOOD pre-set in its environment can't make the
+ // assertion pass via inheritance — the test must prove the runner's
+ // own append line is what gets the var into the subprocess.
+ t.Setenv("PRINTING_PRESS_DOGFOOD", "")
+
+ dir := t.TempDir()
+ binPath := filepath.Join(dir, "echo-env")
+ script := "#!/bin/sh\nprintf '%s' \"${PRINTING_PRESS_DOGFOOD:-}\"\n"
+ if err := os.WriteFile(binPath, []byte(script), 0o700); err != nil {
+ t.Fatalf("write fixture: %v", err)
+ }
+
+ run := runLiveDogfoodProcess(binPath, dir, nil, 5*time.Second)
+ require.NoError(t, run.err, "fixture: %s", run.stderr)
+ assert.Equal(t, "1", run.stdout, "live-dogfood subprocess should see PRINTING_PRESS_DOGFOOD=1")
+}
+
func TestRunLiveDogfoodErrorPathAcceptsExpectedNonZeroExit(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("test uses a shell script as the fake binary; skip on Windows")
diff --git a/skills/printing-press/SKILL.md b/skills/printing-press/SKILL.md
index 089bcc02..b0911a79 100644
--- a/skills/printing-press/SKILL.md
+++ b/skills/printing-press/SKILL.md
@@ -2339,6 +2339,13 @@ After building each command in Priority 1 and Priority 2, verify these 10 princi
}
```
This is defense-in-depth: the verifier also runs a heuristic side-effect classifier, but it can miss commands whose `--help` text and source don't match the heuristics. The env-var check is the floor.
+ - **Long-running commands curtail work under live-dogfood.** Any hand-written command whose happy path is an expensive network operation (full sync loops, content crawlers, bulk archive walks) MUST check `cliutil.IsDogfoodEnv()` and curtail work to fit inside the matrix's flat 30s per-command timeout. `printing-press dogfood --live` sets `PRINTING_PRESS_DOGFOOD=1` in every subprocess. Pattern:
+ ```go
+ if cliutil.IsDogfoodEnv() {
+ return crawl(ctx, opts.WithMaxPages(1))
+ }
+ ```
+ Distinct from `IsVerifyEnv`: dogfood is a real-API matrix, so curtail work (paginate once, smaller `--limit`), never substitute mock data for real calls.
10. **Per-source rate limiting**: any hand-written client in a sibling internal package (`internal/source/<name>/`, `internal/recipes/`, `internal/phgraphql/`, etc. — anything not generator-emitted) that makes outbound HTTP calls MUST use `cliutil.AdaptiveLimiter` and surface `*cliutil.RateLimitError` when 429 retries are exhausted. Empty-on-throttle is indistinguishable from "no data exists" and silently corrupts downstream queries. Read [references/per-source-rate-limiting.md](references/per-source-rate-limiting.md) when authoring a sibling client. Enforced at generation time by dogfood's `source_client_check`.
#### Verify-friendly RunE template
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cliutil/verifyenv.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cliutil/verifyenv.go
index fc409acc..f18bb240 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cliutil/verifyenv.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cliutil/verifyenv.go
@@ -12,6 +12,13 @@ import "os"
// spamming the user's environment during verify runs.
const VerifyEnvVar = "PRINTING_PRESS_VERIFY"
+// DogfoodEnvVar is the env var the printing-press live-dogfood runner
+// sets in every subprocess. Distinct from VerifyEnvVar because dogfood
+// is a real-network matrix: commands may still perform actual API
+// calls, just curtailed (paginate-once, bounded crawl, etc.) so the
+// runner's flat 30s per-command timeout doesn't trip.
+const DogfoodEnvVar = "PRINTING_PRESS_DOGFOOD"
+
// IsVerifyEnv reports whether the current process is running under the
// printing-press verifier in mock mode. Generated commands with side
// effects pair this check with print-by-default + explicit opt-in
@@ -28,3 +35,21 @@ const VerifyEnvVar = "PRINTING_PRESS_VERIFY"
func IsVerifyEnv() bool {
return os.Getenv(VerifyEnvVar) == "1"
}
+
+// IsDogfoodEnv reports whether the current process is running under
+// the printing-press live-dogfood matrix. Long-running commands (full
+// sync loops, content crawlers, bulk archive walks) should use this
+// to curtail work so the flat 30s per-command timeout doesn't kill an
+// otherwise healthy happy_path test. Typical pattern: paginate once,
+// fetch a bounded sample, or honor a smaller --limit default.
+//
+// if cliutil.IsDogfoodEnv() {
+// return crawl(ctx, opts.WithMaxPages(1))
+// }
+//
+// Unlike IsVerifyEnv this does NOT mean "don't hit the network" —
+// dogfood is a real-API matrix. Use this only to bound work, never to
+// substitute mock data for real calls.
+func IsDogfoodEnv() bool {
+ return os.Getenv(DogfoodEnvVar) == "1"
+}
← 6fc98d75 chore(main): release 4.8.0 (#1491)
·
back to Cli Printing Press
·
fix(cli): always emit boolean body fields, no zero-guard (#1 9d3050b5 →