[object Object]

← back to Cli Printing Press

fix(cli): suppress max_pages_cap_hit warning under --latest-only (#1120)

5f2bde7fa62b2235b83cb2092a069f6b6456beaa · 2026-05-11 16:33:31 -0700 · Trevin Chow

* fix(cli): suppress max_pages_cap_hit warning under --latest-only

sync --latest-only programmatically pins maxPages=1, then emits a
sync_warning per paginated resource when the cap is hit. The flood
masks real sync_anomaly / sync_error events emitted by the same run.

Thread latestOnly through syncResource, syncDependentResources, and
syncDependentResource, and gate both cap-hit emit blocks (flat-path
and dependent-path) with !latestOnly. The break still fires
unconditionally so iteration stops. auto_refresh's syncResource call
passes latestOnly=true since it's a single-page latest-fetch.

graphql_sync.go.tmpl's cap-hit site only emits a human-friendly
stderr message and no JSON sync_warning, so no symmetric edit there.

Closes #928

* fix(cli): derive effectiveLatestOnly so --since override re-enables cap warnings

Greptile flagged a P1: my prior commit threaded the raw --latest-only
flag value into syncResource / syncDependentResources. When a user
combines --latest-only --since 7d, the mutex block at sync.go.tmpl
already declares --latest-only a no-op for the maxPages pin and emits
a stderr warning to that effect. But the raw true value still flowed
into `if !latestOnly`, silently suppressing legitimate cap-hit warnings
on the default 100-page cap — defeating the entire noise-reduction
goal in the exact scenario the issue meant to surface.

Derive `effectiveLatestOnly := latestOnly && since == ""` after the
mutex block and thread that through. Add test assertions covering the
derivation and both callsites.

Files touched

Diff

commit 5f2bde7fa62b2235b83cb2092a069f6b6456beaa
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Mon May 11 16:33:31 2026 -0700

    fix(cli): suppress max_pages_cap_hit warning under --latest-only (#1120)
    
    * fix(cli): suppress max_pages_cap_hit warning under --latest-only
    
    sync --latest-only programmatically pins maxPages=1, then emits a
    sync_warning per paginated resource when the cap is hit. The flood
    masks real sync_anomaly / sync_error events emitted by the same run.
    
    Thread latestOnly through syncResource, syncDependentResources, and
    syncDependentResource, and gate both cap-hit emit blocks (flat-path
    and dependent-path) with !latestOnly. The break still fires
    unconditionally so iteration stops. auto_refresh's syncResource call
    passes latestOnly=true since it's a single-page latest-fetch.
    
    graphql_sync.go.tmpl's cap-hit site only emits a human-friendly
    stderr message and no JSON sync_warning, so no symmetric edit there.
    
    Closes #928
    
    * fix(cli): derive effectiveLatestOnly so --since override re-enables cap warnings
    
    Greptile flagged a P1: my prior commit threaded the raw --latest-only
    flag value into syncResource / syncDependentResources. When a user
    combines --latest-only --since 7d, the mutex block at sync.go.tmpl
    already declares --latest-only a no-op for the maxPages pin and emits
    a stderr warning to that effect. But the raw true value still flowed
    into `if !latestOnly`, silently suppressing legitimate cap-hit warnings
    on the default 100-page cap — defeating the entire noise-reduction
    goal in the exact scenario the issue meant to surface.
    
    Derive `effectiveLatestOnly := latestOnly && since == ""` after the
    mutex block and thread that through. Add test assertions covering the
    derivation and both callsites.
---
 internal/generator/generator_test.go               | 38 +++++++++++++++++-
 internal/generator/templates/auto_refresh.go.tmpl  |  2 +-
 .../generator/templates/channel_workflow.go.tmpl   |  2 +-
 internal/generator/templates/sync.go.tmpl          | 45 ++++++++++++++--------
 .../printing-press-golden/internal/cli/sync.go     | 45 ++++++++++++++--------
 .../sync-walker-golden/internal/cli/sync.go        | 45 ++++++++++++++--------
 .../tier-routing-golden/internal/cli/sync.go       | 27 +++++++++----
 7 files changed, 149 insertions(+), 55 deletions(-)

diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index 82938ebe..87b29c08 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -6472,7 +6472,7 @@ func TestGenerateDependentSyncCompiles(t *testing.T) {
 	// where it gates each dependent.
 	assert.Contains(t, syncContent, "parentFilter := append([]string(nil), resources...)",
 		"sync.go should capture user --resources filter before default expansion")
-	assert.Contains(t, syncContent, "maxPages, parentFilter",
+	assert.Contains(t, syncContent, "effectiveLatestOnly, parentFilter",
 		"sync.go should pass the captured filter into syncDependentResources")
 	assert.Contains(t, syncContent, "parentFilter []string",
 		"syncDependentResources should accept a parent filter")
@@ -6789,6 +6789,42 @@ func TestGeneratedSyncMaxPagesAndStickyCursor(t *testing.T) {
 		`{"event":"sync_warning","resource":"%s","parent":"%s","reason":"max_pages_cap_hit"`,
 		"dependent-resource cap-hit must emit a structured sync_warning with reason max_pages_cap_hit")
 
+	// (b3) Issue #928: --latest-only pins maxPages=1 programmatically, so
+	// the cap is hit on every paginated resource by user intent rather
+	// than anomaly. Both cap-hit emit blocks must sit inside an
+	// `if !latestOnly` guard, otherwise the warning flood masks real
+	// sync_anomaly / sync_error events emitted by the same run.
+	guardBefore := func(marker string) string {
+		idx := strings.Index(syncContent, marker)
+		require.NotEqualf(t, -1, idx, "marker %q must appear in generated sync.go", marker)
+		return syncContent[max(0, idx-400):idx]
+	}
+	assert.Contains(t,
+		guardBefore(`"resource":"%s","reason":"max_pages_cap_hit"`),
+		"if !latestOnly {",
+		"flat-path cap-hit emit must be guarded by `if !latestOnly` (issue #928)")
+	assert.Contains(t,
+		guardBefore(`"resource":"%s","parent":"%s","reason":"max_pages_cap_hit"`),
+		"if !latestOnly {",
+		"dependent-resource cap-hit emit must be guarded by `if !latestOnly` (issue #928)")
+
+	// (b4) The cap-hit guard must consume an effective value derived from
+	// both --latest-only AND --since. When --since is set, --latest-only is
+	// already a no-op for the maxPages pin (block at sync.go.tmpl ~154),
+	// and any cap hit reflects the default 100-page limit — a real anomaly
+	// worth surfacing. Passing the raw --latest-only flag value would
+	// silently suppress legitimate warnings on the --latest-only --since
+	// combined path. Pin the derivation and the callsites that consume it.
+	assert.Contains(t, syncContent,
+		`effectiveLatestOnly := latestOnly && since == ""`,
+		"sync.go must derive effectiveLatestOnly so --since overrides re-enable cap-hit warnings (issue #928 Greptile follow-up)")
+	assert.Contains(t, syncContent,
+		"maxPages, effectiveLatestOnly",
+		"syncResource call must pass effectiveLatestOnly, not the raw --latest-only flag (issue #928 Greptile follow-up)")
+	assert.Contains(t, syncContent,
+		"maxPages, effectiveLatestOnly, parentFilter",
+		"syncDependentResources call must pass effectiveLatestOnly, not the raw --latest-only flag (issue #928 Greptile follow-up)")
+
 	// (c) Sticky-cursor detection on the flat path. The check must compare
 	// against a tracked lastNextCursor and emit the structured warning when
 	// the cursor doesn't advance.
diff --git a/internal/generator/templates/auto_refresh.go.tmpl b/internal/generator/templates/auto_refresh.go.tmpl
index f9a0bb6e..3813ef59 100644
--- a/internal/generator/templates/auto_refresh.go.tmpl
+++ b/internal/generator/templates/auto_refresh.go.tmpl
@@ -189,7 +189,7 @@ func runAutoRefresh(ctx context.Context, flags *rootFlags, db *store.Store, reso
 			return ctx.Err()
 		default:
 		}
-		result := syncResource({{if .HasTierRouting}}syncClientForResource(c, resource){{else}}c{{end}}, db, resource, "", false, 1{{if .Pagination.DateRangeParam}}, ""{{end}}{{if not (isGraphQL .APISpec)}}, nil{{end}})
+		result := syncResource({{if .HasTierRouting}}syncClientForResource(c, resource){{else}}c{{end}}, db, resource, "", false, 1{{if not (isGraphQL .APISpec)}}, true{{end}}{{if .Pagination.DateRangeParam}}, ""{{end}}{{if not (isGraphQL .APISpec)}}, nil{{end}})
 		if result.Err != nil {
 			failures = append(failures, fmt.Sprintf("%s: %v", resource, result.Err))
 		}
diff --git a/internal/generator/templates/channel_workflow.go.tmpl b/internal/generator/templates/channel_workflow.go.tmpl
index c63768cb..1c262a3a 100644
--- a/internal/generator/templates/channel_workflow.go.tmpl
+++ b/internal/generator/templates/channel_workflow.go.tmpl
@@ -288,7 +288,7 @@ and full resync. After archiving, use 'search' for instant full-text search.`,
 			}
 
 			for _, resource := range resources {
-				res := syncResource({{if .HasTierRouting}}syncClientForResource(c, resource){{else}}c{{end}}, s, resource, "", full, 100{{if .Pagination.DateRangeParam}}, ""{{end}}{{if not (isGraphQL .APISpec)}}, nil{{end}})
+				res := syncResource({{if .HasTierRouting}}syncClientForResource(c, resource){{else}}c{{end}}, s, resource, "", full, 100{{if not (isGraphQL .APISpec)}}, false{{end}}{{if .Pagination.DateRangeParam}}, ""{{end}}{{if not (isGraphQL .APISpec)}}, nil{{end}})
 				if res.Err != nil {
 					fmt.Fprintf(cmd.ErrOrStderr(), "  %s: error: %v\n", resource, res.Err)
 					continue
diff --git a/internal/generator/templates/sync.go.tmpl b/internal/generator/templates/sync.go.tmpl
index 26c7be21..68de92fe 100644
--- a/internal/generator/templates/sync.go.tmpl
+++ b/internal/generator/templates/sync.go.tmpl
@@ -167,6 +167,13 @@ Exit codes & warnings:
 					fmt.Fprintln(os.Stderr, "warning: --latest-only ignored because --since is set; --since takes precedence")
 				}
 			}
+			// effectiveLatestOnly drives the max_pages_cap_hit suppression
+			// below. It must reflect whether --latest-only is actually the
+			// cap source — i.e., only when --since is empty. If --since wins
+			// (block above), --latest-only is a no-op for maxPages and any
+			// cap hit reflects the default --max-pages 100 limit, which is
+			// a real anomaly worth surfacing.
+			effectiveLatestOnly := latestOnly && since == ""
 
 			// Resolve --since into an RFC3339 timestamp
 			sinceTS := ""
@@ -198,7 +205,7 @@ Exit codes & warnings:
 				go func() {
 					defer wg.Done()
 					for resource := range work {
-						res := syncResource({{if .HasTierRouting}}syncClientForResource(c, resource){{else}}c{{end}}, db, resource, sinceTS, full, maxPages{{if .Pagination.DateRangeParam}}, syncDates{{end}}, userParams)
+						res := syncResource({{if .HasTierRouting}}syncClientForResource(c, resource){{else}}c{{end}}, db, resource, sinceTS, full, maxPages, effectiveLatestOnly{{if .Pagination.DateRangeParam}}, syncDates{{end}}, userParams)
 						results <- res
 					}
 				}()
@@ -246,7 +253,7 @@ Exit codes & warnings:
 
 {{- if .DependentSyncResources}}
 			// Sync dependent (parent-child) resources sequentially after flat resources.
-			depResults := syncDependentResources(c, db, sinceTS, full, maxPages, parentFilter{{if .Pagination.DateRangeParam}}, syncDates{{end}}, userParams)
+			depResults := syncDependentResources(c, db, sinceTS, full, maxPages, effectiveLatestOnly, parentFilter{{if .Pagination.DateRangeParam}}, syncDates{{end}}, userParams)
 			for _, res := range depResults {
 				if res.Err != nil {
 					if humanFriendly {
@@ -346,7 +353,7 @@ Exit codes & warnings:
 func syncResource(c interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}, db *store.Store, resource, sinceTS string, full bool, maxPages int{{if .Pagination.DateRangeParam}}, dates string{{end}}, userParams *syncUserParams) syncResult {
+}, db *store.Store, resource, sinceTS string, full bool, maxPages int, latestOnly bool{{if .Pagination.DateRangeParam}}, dates string{{end}}, userParams *syncUserParams) syncResult {
 	started := time.Now()
 
 	if !humanFriendly {
@@ -575,12 +582,18 @@ func syncResource(c interface {
 
 		pagesFetched++
 
-		// Enforce page ceiling to prevent runaway syncs on large-catalog APIs
+		// Enforce page ceiling to prevent runaway syncs on large-catalog APIs.
+		// Suppress the cap-hit warning when --latest-only is the cap source:
+		// the template pinned maxPages=1 by user intent, and emitting one
+		// warning per paginated resource would mask real sync_anomaly /
+		// sync_error output in the same stream.
 		if maxPages > 0 && pagesFetched >= maxPages {
-			if humanFriendly {
-				fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items)\n", resource, maxPages, totalCount)
-			} else {
-				fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", resource, maxPages)
+			if !latestOnly {
+				if humanFriendly {
+					fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items)\n", resource, maxPages, totalCount)
+				} else {
+					fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", resource, maxPages)
+				}
 			}
 			break
 		}
@@ -1111,7 +1124,7 @@ func dependentResourceDefs() []dependentResourceDef {
 func syncDependentResources(c {{if .HasTierRouting}}*client.Client{{else}}interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}{{end}}, db *store.Store, sinceTS string, full bool, maxPages int, parentFilter []string{{if .Pagination.DateRangeParam}}, dates string{{end}}, userParams *syncUserParams) []syncResult {
+}{{end}}, db *store.Store, sinceTS string, full bool, maxPages int, latestOnly bool, parentFilter []string{{if .Pagination.DateRangeParam}}, dates string{{end}}, userParams *syncUserParams) []syncResult {
 	allow := make(map[string]bool, len(parentFilter))
 	for _, r := range parentFilter {
 		allow[r] = true
@@ -1121,7 +1134,7 @@ func syncDependentResources(c {{if .HasTierRouting}}*client.Client{{else}}interf
 		if len(allow) > 0 && !allow[dep.ParentTable] && !allow[dep.Name] {
 			continue
 		}
-		res := syncDependentResource({{if .HasTierRouting}}syncClientForResource(c, dep.Name){{else}}c{{end}}, db, dep, sinceTS, full, maxPages{{if .Pagination.DateRangeParam}}, dates{{end}}, userParams)
+		res := syncDependentResource({{if .HasTierRouting}}syncClientForResource(c, dep.Name){{else}}c{{end}}, db, dep, sinceTS, full, maxPages, latestOnly{{if .Pagination.DateRangeParam}}, dates{{end}}, userParams)
 		results = append(results, res)
 	}
 	return results
@@ -1131,7 +1144,7 @@ func syncDependentResources(c {{if .HasTierRouting}}*client.Client{{else}}interf
 func syncDependentResource(c interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}, db *store.Store, dep dependentResourceDef, sinceTS string, full bool, maxPages int{{if .Pagination.DateRangeParam}}, dates string{{end}}, userParams *syncUserParams) syncResult {
+}, db *store.Store, dep dependentResourceDef, sinceTS string, full bool, maxPages int, latestOnly bool{{if .Pagination.DateRangeParam}}, dates string{{end}}, userParams *syncUserParams) syncResult {
 	started := time.Now()
 
 	// Query parent table for the keys to substitute into the child path.
@@ -1290,10 +1303,12 @@ func syncDependentResource(c interface {
 			pagesFetched++
 
 			if maxPages > 0 && pagesFetched >= maxPages {
-				if humanFriendly {
-					fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items) for parent %s\n", dep.Name, maxPages, totalCount, parentID)
-				} else {
-					fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","parent":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", dep.Name, parentID, maxPages)
+				if !latestOnly {
+					if humanFriendly {
+						fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items) for parent %s\n", dep.Name, maxPages, totalCount, parentID)
+					} else {
+						fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","parent":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", dep.Name, parentID, maxPages)
+					}
 				}
 				break
 			}
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/sync.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/sync.go
index 56f7ba5e..041df0f6 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/sync.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/sync.go
@@ -153,6 +153,13 @@ Exit codes & warnings:
 					fmt.Fprintln(os.Stderr, "warning: --latest-only ignored because --since is set; --since takes precedence")
 				}
 			}
+			// effectiveLatestOnly drives the max_pages_cap_hit suppression
+			// below. It must reflect whether --latest-only is actually the
+			// cap source — i.e., only when --since is empty. If --since wins
+			// (block above), --latest-only is a no-op for maxPages and any
+			// cap hit reflects the default --max-pages 100 limit, which is
+			// a real anomaly worth surfacing.
+			effectiveLatestOnly := latestOnly && since == ""
 
 			// Resolve --since into an RFC3339 timestamp
 			sinceTS := ""
@@ -179,7 +186,7 @@ Exit codes & warnings:
 				go func() {
 					defer wg.Done()
 					for resource := range work {
-						res := syncResource(c, db, resource, sinceTS, full, maxPages, userParams)
+						res := syncResource(c, db, resource, sinceTS, full, maxPages, effectiveLatestOnly, userParams)
 						results <- res
 					}
 				}()
@@ -225,7 +232,7 @@ Exit codes & warnings:
 				}
 			}
 			// Sync dependent (parent-child) resources sequentially after flat resources.
-			depResults := syncDependentResources(c, db, sinceTS, full, maxPages, parentFilter, userParams)
+			depResults := syncDependentResources(c, db, sinceTS, full, maxPages, effectiveLatestOnly, parentFilter, userParams)
 			for _, res := range depResults {
 				if res.Err != nil {
 					if humanFriendly {
@@ -321,7 +328,7 @@ Exit codes & warnings:
 func syncResource(c interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}, db *store.Store, resource, sinceTS string, full bool, maxPages int, userParams *syncUserParams) syncResult {
+}, db *store.Store, resource, sinceTS string, full bool, maxPages int, latestOnly bool, userParams *syncUserParams) syncResult {
 	started := time.Now()
 
 	if !humanFriendly {
@@ -543,12 +550,18 @@ func syncResource(c interface {
 
 		pagesFetched++
 
-		// Enforce page ceiling to prevent runaway syncs on large-catalog APIs
+		// Enforce page ceiling to prevent runaway syncs on large-catalog APIs.
+		// Suppress the cap-hit warning when --latest-only is the cap source:
+		// the template pinned maxPages=1 by user intent, and emitting one
+		// warning per paginated resource would mask real sync_anomaly /
+		// sync_error output in the same stream.
 		if maxPages > 0 && pagesFetched >= maxPages {
-			if humanFriendly {
-				fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items)\n", resource, maxPages, totalCount)
-			} else {
-				fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", resource, maxPages)
+			if !latestOnly {
+				if humanFriendly {
+					fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items)\n", resource, maxPages, totalCount)
+				} else {
+					fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", resource, maxPages)
+				}
 			}
 			break
 		}
@@ -1003,7 +1016,7 @@ func dependentResourceDefs() []dependentResourceDef {
 func syncDependentResources(c interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}, db *store.Store, sinceTS string, full bool, maxPages int, parentFilter []string, userParams *syncUserParams) []syncResult {
+}, db *store.Store, sinceTS string, full bool, maxPages int, latestOnly bool, parentFilter []string, userParams *syncUserParams) []syncResult {
 	allow := make(map[string]bool, len(parentFilter))
 	for _, r := range parentFilter {
 		allow[r] = true
@@ -1013,7 +1026,7 @@ func syncDependentResources(c interface {
 		if len(allow) > 0 && !allow[dep.ParentTable] && !allow[dep.Name] {
 			continue
 		}
-		res := syncDependentResource(c, db, dep, sinceTS, full, maxPages, userParams)
+		res := syncDependentResource(c, db, dep, sinceTS, full, maxPages, latestOnly, userParams)
 		results = append(results, res)
 	}
 	return results
@@ -1023,7 +1036,7 @@ func syncDependentResources(c interface {
 func syncDependentResource(c interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}, db *store.Store, dep dependentResourceDef, sinceTS string, full bool, maxPages int, userParams *syncUserParams) syncResult {
+}, db *store.Store, dep dependentResourceDef, sinceTS string, full bool, maxPages int, latestOnly bool, userParams *syncUserParams) syncResult {
 	started := time.Now()
 
 	// Query parent table for the keys to substitute into the child path.
@@ -1177,10 +1190,12 @@ func syncDependentResource(c interface {
 			pagesFetched++
 
 			if maxPages > 0 && pagesFetched >= maxPages {
-				if humanFriendly {
-					fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items) for parent %s\n", dep.Name, maxPages, totalCount, parentID)
-				} else {
-					fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","parent":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", dep.Name, parentID, maxPages)
+				if !latestOnly {
+					if humanFriendly {
+						fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items) for parent %s\n", dep.Name, maxPages, totalCount, parentID)
+					} else {
+						fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","parent":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", dep.Name, parentID, maxPages)
+					}
 				}
 				break
 			}
diff --git a/testdata/golden/expected/generate-sync-walker-api/sync-walker-golden/internal/cli/sync.go b/testdata/golden/expected/generate-sync-walker-api/sync-walker-golden/internal/cli/sync.go
index 782a636e..f0475f0c 100644
--- a/testdata/golden/expected/generate-sync-walker-api/sync-walker-golden/internal/cli/sync.go
+++ b/testdata/golden/expected/generate-sync-walker-api/sync-walker-golden/internal/cli/sync.go
@@ -153,6 +153,13 @@ Exit codes & warnings:
 					fmt.Fprintln(os.Stderr, "warning: --latest-only ignored because --since is set; --since takes precedence")
 				}
 			}
+			// effectiveLatestOnly drives the max_pages_cap_hit suppression
+			// below. It must reflect whether --latest-only is actually the
+			// cap source — i.e., only when --since is empty. If --since wins
+			// (block above), --latest-only is a no-op for maxPages and any
+			// cap hit reflects the default --max-pages 100 limit, which is
+			// a real anomaly worth surfacing.
+			effectiveLatestOnly := latestOnly && since == ""
 
 			// Resolve --since into an RFC3339 timestamp
 			sinceTS := ""
@@ -179,7 +186,7 @@ Exit codes & warnings:
 				go func() {
 					defer wg.Done()
 					for resource := range work {
-						res := syncResource(c, db, resource, sinceTS, full, maxPages, userParams)
+						res := syncResource(c, db, resource, sinceTS, full, maxPages, effectiveLatestOnly, userParams)
 						results <- res
 					}
 				}()
@@ -225,7 +232,7 @@ Exit codes & warnings:
 				}
 			}
 			// Sync dependent (parent-child) resources sequentially after flat resources.
-			depResults := syncDependentResources(c, db, sinceTS, full, maxPages, parentFilter, userParams)
+			depResults := syncDependentResources(c, db, sinceTS, full, maxPages, effectiveLatestOnly, parentFilter, userParams)
 			for _, res := range depResults {
 				if res.Err != nil {
 					if humanFriendly {
@@ -321,7 +328,7 @@ Exit codes & warnings:
 func syncResource(c interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}, db *store.Store, resource, sinceTS string, full bool, maxPages int, userParams *syncUserParams) syncResult {
+}, db *store.Store, resource, sinceTS string, full bool, maxPages int, latestOnly bool, userParams *syncUserParams) syncResult {
 	started := time.Now()
 
 	if !humanFriendly {
@@ -543,12 +550,18 @@ func syncResource(c interface {
 
 		pagesFetched++
 
-		// Enforce page ceiling to prevent runaway syncs on large-catalog APIs
+		// Enforce page ceiling to prevent runaway syncs on large-catalog APIs.
+		// Suppress the cap-hit warning when --latest-only is the cap source:
+		// the template pinned maxPages=1 by user intent, and emitting one
+		// warning per paginated resource would mask real sync_anomaly /
+		// sync_error output in the same stream.
 		if maxPages > 0 && pagesFetched >= maxPages {
-			if humanFriendly {
-				fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items)\n", resource, maxPages, totalCount)
-			} else {
-				fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", resource, maxPages)
+			if !latestOnly {
+				if humanFriendly {
+					fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items)\n", resource, maxPages, totalCount)
+				} else {
+					fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", resource, maxPages)
+				}
 			}
 			break
 		}
@@ -997,7 +1010,7 @@ func dependentResourceDefs() []dependentResourceDef {
 func syncDependentResources(c interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}, db *store.Store, sinceTS string, full bool, maxPages int, parentFilter []string, userParams *syncUserParams) []syncResult {
+}, db *store.Store, sinceTS string, full bool, maxPages int, latestOnly bool, parentFilter []string, userParams *syncUserParams) []syncResult {
 	allow := make(map[string]bool, len(parentFilter))
 	for _, r := range parentFilter {
 		allow[r] = true
@@ -1007,7 +1020,7 @@ func syncDependentResources(c interface {
 		if len(allow) > 0 && !allow[dep.ParentTable] && !allow[dep.Name] {
 			continue
 		}
-		res := syncDependentResource(c, db, dep, sinceTS, full, maxPages, userParams)
+		res := syncDependentResource(c, db, dep, sinceTS, full, maxPages, latestOnly, userParams)
 		results = append(results, res)
 	}
 	return results
@@ -1017,7 +1030,7 @@ func syncDependentResources(c interface {
 func syncDependentResource(c interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}, db *store.Store, dep dependentResourceDef, sinceTS string, full bool, maxPages int, userParams *syncUserParams) syncResult {
+}, db *store.Store, dep dependentResourceDef, sinceTS string, full bool, maxPages int, latestOnly bool, userParams *syncUserParams) syncResult {
 	started := time.Now()
 
 	// Query parent table for the keys to substitute into the child path.
@@ -1171,10 +1184,12 @@ func syncDependentResource(c interface {
 			pagesFetched++
 
 			if maxPages > 0 && pagesFetched >= maxPages {
-				if humanFriendly {
-					fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items) for parent %s\n", dep.Name, maxPages, totalCount, parentID)
-				} else {
-					fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","parent":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", dep.Name, parentID, maxPages)
+				if !latestOnly {
+					if humanFriendly {
+						fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items) for parent %s\n", dep.Name, maxPages, totalCount, parentID)
+					} else {
+						fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","parent":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", dep.Name, parentID, maxPages)
+					}
 				}
 				break
 			}
diff --git a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/cli/sync.go b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/cli/sync.go
index 973de6a2..6d4386f0 100644
--- a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/cli/sync.go
+++ b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/internal/cli/sync.go
@@ -151,6 +151,13 @@ Exit codes & warnings:
 					fmt.Fprintln(os.Stderr, "warning: --latest-only ignored because --since is set; --since takes precedence")
 				}
 			}
+			// effectiveLatestOnly drives the max_pages_cap_hit suppression
+			// below. It must reflect whether --latest-only is actually the
+			// cap source — i.e., only when --since is empty. If --since wins
+			// (block above), --latest-only is a no-op for maxPages and any
+			// cap hit reflects the default --max-pages 100 limit, which is
+			// a real anomaly worth surfacing.
+			effectiveLatestOnly := latestOnly && since == ""
 
 			// Resolve --since into an RFC3339 timestamp
 			sinceTS := ""
@@ -177,7 +184,7 @@ Exit codes & warnings:
 				go func() {
 					defer wg.Done()
 					for resource := range work {
-						res := syncResource(syncClientForResource(c, resource), db, resource, sinceTS, full, maxPages, userParams)
+						res := syncResource(syncClientForResource(c, resource), db, resource, sinceTS, full, maxPages, effectiveLatestOnly, userParams)
 						results <- res
 					}
 				}()
@@ -295,7 +302,7 @@ Exit codes & warnings:
 func syncResource(c interface {
 	Get(string, map[string]string) (json.RawMessage, error)
 	RateLimit() float64
-}, db *store.Store, resource, sinceTS string, full bool, maxPages int, userParams *syncUserParams) syncResult {
+}, db *store.Store, resource, sinceTS string, full bool, maxPages int, latestOnly bool, userParams *syncUserParams) syncResult {
 	started := time.Now()
 
 	if !humanFriendly {
@@ -517,12 +524,18 @@ func syncResource(c interface {
 
 		pagesFetched++
 
-		// Enforce page ceiling to prevent runaway syncs on large-catalog APIs
+		// Enforce page ceiling to prevent runaway syncs on large-catalog APIs.
+		// Suppress the cap-hit warning when --latest-only is the cap source:
+		// the template pinned maxPages=1 by user intent, and emitting one
+		// warning per paginated resource would mask real sync_anomaly /
+		// sync_error output in the same stream.
 		if maxPages > 0 && pagesFetched >= maxPages {
-			if humanFriendly {
-				fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items)\n", resource, maxPages, totalCount)
-			} else {
-				fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", resource, maxPages)
+			if !latestOnly {
+				if humanFriendly {
+					fmt.Fprintf(os.Stderr, "\n  %s: reached --max-pages limit (%d pages, %d items)\n", resource, maxPages, totalCount)
+				} else {
+					fmt.Fprintf(os.Stdout, `{"event":"sync_warning","resource":"%s","reason":"max_pages_cap_hit","message":"reached --max-pages cap of %d; data may be truncated. Re-run with --max-pages 0 (unlimited) or higher to verify."}`+"\n", resource, maxPages)
+				}
 			}
 			break
 		}

← 10cc48a8 fix(cli): infer pagination defaults from plain params; prese  ·  back to Cli Printing Press  ·  fix(cli): exempt CLI-root vendor spec files from PII audit s 6536cfff →