← back to Cli Printing Press
feat(regenmerge): add TEMPLATED-BODY-DRIFT verdict to catch in-place body edits (#468)
abde446ebe376def6f2380d9c400a2132da3e07b · 2026-05-01 15:49:52 -0700 · Trevin Chow
Adds a 6th classification verdict that catches the failure mode the
original plan acknowledged as a known limitation: pub modifies the body
of an existing templated function in-place (calls a hand-written helper,
adds a header-injection loop, etc.) without changing the function's
top-level decl name. Decl-set comparison says TEMPLATED-CLEAN, take-fresh
overwrites the file, the integration is silently destroyed.
Surfaced by sweep dogfood:
- cal-com (#175): pub's client.go do() called requiredHeadersForPath()
inside the templated function, integrating with calcom_versions.go's
per-endpoint API-version map. Take-fresh dropped the call; v2 endpoints
silently degraded.
- pagliacci-pizza (#174): pub's client.go New() chained
surf.Builder().Impersonate().Chrome() for cloud-bypass HTTP. Take-fresh
reverted to the stdlib http.Client. Currently works against pagliacci
but the integration was hand-written for a reason.
Mechanism: per file classified as TEMPLATED-CLEAN by the existing
decl-set check, parse pub and fresh function bodies and collect the
call-target identifier set per function. Any pub function whose body
calls identifiers fresh's same-named function doesn't is flagged as
TEMPLATED-BODY-DRIFT and surfaced for human review instead of auto-
overwritten.
Conservative by design — fires only when both files define a function
with the same canonical name. AddCommand args skip recursion (handled
separately by the lost-registration restoration path).
Tests: 3 regression cases pinning the cal-com shape (helper call dropped),
the pagliacci shape (chained .Method().Method() drop), the
AddCommand-args false-positive guard, and the no-drift baseline.
Reuse cleanup applied: extracted canonicalFuncName(*ast.FuncDecl) helper
shared between extractDecls and bodyCallsByFunc. AddCommand-skip lifted
to a package-level driftSkipSelectors set so future selector skips
don't require touching the AST walker.
Verification:
- go test ./... — 2625 pass (4 new)
- go vet, golangci-lint clean
- Human report renders body_drift per function with the lost call list
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Files touched
M internal/cli/regen_merge.goA internal/pipeline/regenmerge/body_drift.goA internal/pipeline/regenmerge/body_drift_test.goM internal/pipeline/regenmerge/classify.goM internal/pipeline/regenmerge/regenmerge.go
Diff
commit abde446ebe376def6f2380d9c400a2132da3e07b
Author: Trevin Chow <trevin@trevinchow.com>
Date: Fri May 1 15:49:52 2026 -0700
feat(regenmerge): add TEMPLATED-BODY-DRIFT verdict to catch in-place body edits (#468)
Adds a 6th classification verdict that catches the failure mode the
original plan acknowledged as a known limitation: pub modifies the body
of an existing templated function in-place (calls a hand-written helper,
adds a header-injection loop, etc.) without changing the function's
top-level decl name. Decl-set comparison says TEMPLATED-CLEAN, take-fresh
overwrites the file, the integration is silently destroyed.
Surfaced by sweep dogfood:
- cal-com (#175): pub's client.go do() called requiredHeadersForPath()
inside the templated function, integrating with calcom_versions.go's
per-endpoint API-version map. Take-fresh dropped the call; v2 endpoints
silently degraded.
- pagliacci-pizza (#174): pub's client.go New() chained
surf.Builder().Impersonate().Chrome() for cloud-bypass HTTP. Take-fresh
reverted to the stdlib http.Client. Currently works against pagliacci
but the integration was hand-written for a reason.
Mechanism: per file classified as TEMPLATED-CLEAN by the existing
decl-set check, parse pub and fresh function bodies and collect the
call-target identifier set per function. Any pub function whose body
calls identifiers fresh's same-named function doesn't is flagged as
TEMPLATED-BODY-DRIFT and surfaced for human review instead of auto-
overwritten.
Conservative by design — fires only when both files define a function
with the same canonical name. AddCommand args skip recursion (handled
separately by the lost-registration restoration path).
Tests: 3 regression cases pinning the cal-com shape (helper call dropped),
the pagliacci shape (chained .Method().Method() drop), the
AddCommand-args false-positive guard, and the no-drift baseline.
Reuse cleanup applied: extracted canonicalFuncName(*ast.FuncDecl) helper
shared between extractDecls and bodyCallsByFunc. AddCommand-skip lifted
to a package-level driftSkipSelectors set so future selector skips
don't require touching the AST walker.
Verification:
- go test ./... — 2625 pass (4 new)
- go vet, golangci-lint clean
- Human report renders body_drift per function with the lost call list
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
internal/cli/regen_merge.go | 11 +-
internal/pipeline/regenmerge/body_drift.go | 101 +++++++++++++
internal/pipeline/regenmerge/body_drift_test.go | 193 ++++++++++++++++++++++++
internal/pipeline/regenmerge/classify.go | 62 +++++---
internal/pipeline/regenmerge/regenmerge.go | 28 +++-
5 files changed, 369 insertions(+), 26 deletions(-)
diff --git a/internal/cli/regen_merge.go b/internal/cli/regen_merge.go
index 508d2216..9f8666d7 100644
--- a/internal/cli/regen_merge.go
+++ b/internal/cli/regen_merge.go
@@ -113,6 +113,7 @@ func printHumanRegenReport(w io.Writer, report *regenmerge.MergeReport, applied
regenmerge.VerdictPublishedOnlyTemplated,
regenmerge.VerdictNovel,
regenmerge.VerdictTemplatedWithAdditions,
+ regenmerge.VerdictTemplatedBodyDrift,
regenmerge.VerdictNovelCollision,
} {
fmt.Fprintf(w, " %-26s %d\n", v, counts[v])
@@ -122,7 +123,10 @@ func printHumanRegenReport(w io.Writer, report *regenmerge.MergeReport, applied
// Files needing human review.
var needsReview []regenmerge.FileClassification
for _, fc := range report.Files {
- if fc.Verdict == regenmerge.VerdictTemplatedWithAdditions || fc.Verdict == regenmerge.VerdictNovelCollision {
+ switch fc.Verdict {
+ case regenmerge.VerdictTemplatedWithAdditions,
+ regenmerge.VerdictTemplatedBodyDrift,
+ regenmerge.VerdictNovelCollision:
needsReview = append(needsReview, fc)
}
}
@@ -138,6 +142,11 @@ func printHumanRegenReport(w io.Writer, report *regenmerge.MergeReport, applied
fmt.Fprintf(w, " in_fresh_not_published: %v\n", fc.DeclSetDelta.InFreshNotPublished)
}
}
+ if fc.BodyDrift != nil {
+ for fn, calls := range fc.BodyDrift.Functions {
+ fmt.Fprintf(w, " body_drift in %s: %v\n", fn, calls)
+ }
+ }
}
fmt.Fprintln(w)
}
diff --git a/internal/pipeline/regenmerge/body_drift.go b/internal/pipeline/regenmerge/body_drift.go
new file mode 100644
index 00000000..9e0656c9
--- /dev/null
+++ b/internal/pipeline/regenmerge/body_drift.go
@@ -0,0 +1,101 @@
+package regenmerge
+
+import (
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "sort"
+)
+
+// detectBodyDrift parses pub and fresh files, walks each function's body,
+// and returns the per-function set of call-target identifiers pub uses but
+// fresh's same-named function doesn't. Returns nil if there's no drift.
+// Conservative — fires only when both files define a function with the
+// same canonical name.
+func detectBodyDrift(pubPath, freshPath string) *BodyDrift {
+ pubFns := bodyCallsByFunc(pubPath)
+ if pubFns == nil {
+ return nil
+ }
+ freshFns := bodyCallsByFunc(freshPath)
+ if freshFns == nil {
+ return nil
+ }
+
+ driftFns := map[string][]string{}
+ for fn, pubCalls := range pubFns {
+ freshCalls, ok := freshFns[fn]
+ if !ok {
+ // Function exists only in pub. The decl-set check would have
+ // flagged that already (TEMPLATED-WITH-ADDITIONS), so we
+ // shouldn't be here. Skip.
+ continue
+ }
+ var diff []string
+ for call := range pubCalls {
+ if _, present := freshCalls[call]; !present {
+ diff = append(diff, call)
+ }
+ }
+ if len(diff) > 0 {
+ sort.Strings(diff)
+ driftFns[fn] = diff
+ }
+ }
+ if len(driftFns) == 0 {
+ return nil
+ }
+ return &BodyDrift{Functions: driftFns}
+}
+
+// driftSkipSelectors lists method-name selectors whose argument expressions
+// the body-drift walker should NOT recurse into. AddCommand is handled by
+// the lost-registration restoration path — re-flagging its arg calls here
+// would duplicate that signal and trip false positives on every CLI with
+// novel commands.
+var driftSkipSelectors = map[string]struct{}{
+ "AddCommand": {},
+}
+
+// bodyCallsByFunc parses filename and returns a map from canonical function
+// name to the set of call-target identifiers in that function's body. For
+// `foo()` the target is "foo"; for `pkg.Bar()` or `obj.Bar()` it's "Bar"
+// (the selector name — receivers and import aliases differ across files
+// but the function identity is the load-bearing signal). Returns nil if
+// the file fails to parse.
+func bodyCallsByFunc(filename string) map[string]map[string]struct{} {
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, filename, nil, parser.SkipObjectResolution)
+ if err != nil {
+ return nil
+ }
+ out := make(map[string]map[string]struct{}, len(file.Decls))
+ for _, d := range file.Decls {
+ fn, ok := d.(*ast.FuncDecl)
+ if !ok || fn.Body == nil {
+ continue
+ }
+ name := canonicalFuncName(fn)
+ calls := map[string]struct{}{}
+ ast.Inspect(fn.Body, func(n ast.Node) bool {
+ ce, ok := n.(*ast.CallExpr)
+ if !ok {
+ return true
+ }
+ switch fnExpr := ce.Fun.(type) {
+ case *ast.Ident:
+ calls[fnExpr.Name] = struct{}{}
+ case *ast.SelectorExpr:
+ if fnExpr.Sel != nil {
+ calls[fnExpr.Sel.Name] = struct{}{}
+ if _, skip := driftSkipSelectors[fnExpr.Sel.Name]; skip {
+ return false
+ }
+ }
+ }
+ return true
+ })
+ out[name] = calls
+ }
+ return out
+}
diff --git a/internal/pipeline/regenmerge/body_drift_test.go b/internal/pipeline/regenmerge/body_drift_test.go
new file mode 100644
index 00000000..2874e6ff
--- /dev/null
+++ b/internal/pipeline/regenmerge/body_drift_test.go
@@ -0,0 +1,193 @@
+package regenmerge
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// TestBodyDriftCatchesCalComShape pins the cal-com regression that motivated
+// this probe. Pub's client.go calls a hand-written helper
+// (requiredHeadersForPath) inside the templated do() function. Decl-set
+// comparison says TEMPLATED-CLEAN because both files have the same set of
+// top-level decls. Without the body-drift probe, take-fresh-wholesale would
+// silently drop the helper integration and break per-endpoint API
+// versioning at runtime.
+func TestBodyDriftCatchesCalComShape(t *testing.T) {
+ t.Parallel()
+
+ pubCLI := `package client
+
+func do(path string) {
+ authHeader := getAuth()
+ setHeader("Authorization", authHeader)
+ for k, v := range requiredHeadersForPath(path) {
+ setHeader(k, v)
+ }
+}
+
+func getAuth() string { return "" }
+func setHeader(k, v string) {}
+`
+ freshCLI := `package client
+
+func do(path string) {
+ authHeader := getAuth()
+ setHeader("Authorization", authHeader)
+}
+
+func getAuth() string { return "" }
+func setHeader(k, v string) {}
+`
+ pubDir, freshDir := buildSyntheticFixture(t,
+ map[string]string{"internal/client/client.go": pubCLI},
+ map[string]string{"internal/client/client.go": freshCLI})
+
+ report, err := Classify(pubDir, freshDir, Options{Force: true})
+ require.NoError(t, err)
+
+ verdicts := verdictMap(report)
+ assert.Equal(t, VerdictTemplatedBodyDrift, verdicts["internal/client/client.go"],
+ "pub's body calls a helper fresh's body doesn't — must flag drift")
+
+ var fc *FileClassification
+ for i := range report.Files {
+ if report.Files[i].Path == "internal/client/client.go" {
+ fc = &report.Files[i]
+ break
+ }
+ }
+ require.NotNil(t, fc.BodyDrift)
+ assert.Contains(t, fc.BodyDrift.Functions, "do",
+ "do() is the function with drift")
+ assert.Contains(t, fc.BodyDrift.Functions["do"], "requiredHeadersForPath",
+ "requiredHeadersForPath is the dropped call target")
+}
+
+// TestBodyDriftIgnoresAddCommandArgs pins the false-positive guard: the
+// lost-registration mechanism handles pub-only AddCommand calls separately,
+// so body-drift must not re-flag them. Without this filter, every CLI with
+// novel commands would trip body-drift (pub's Execute calls
+// newFooCmd/newBarCmd that fresh's Execute doesn't).
+func TestBodyDriftIgnoresAddCommandArgs(t *testing.T) {
+ t.Parallel()
+
+ pubCLI := `package cli
+
+func Execute() {
+ rootCmd := newRoot()
+ rootCmd.AddCommand(newFooCmd())
+ rootCmd.AddCommand(newBarCmd())
+}
+
+func newRoot() *struct{} { return nil }
+func newFooCmd() *struct{} { return nil }
+func newBarCmd() *struct{} { return nil }
+`
+ freshCLI := `package cli
+
+func Execute() {
+ rootCmd := newRoot()
+}
+
+func newRoot() *struct{} { return nil }
+`
+ pubDir, freshDir := buildSyntheticFixture(t,
+ map[string]string{"internal/cli/root.go": pubCLI},
+ map[string]string{"internal/cli/root.go": freshCLI})
+
+ report, err := Classify(pubDir, freshDir, Options{Force: true})
+ require.NoError(t, err)
+
+ // Decl-set comparison flags this as TEMPLATED-WITH-ADDITIONS because
+ // pub has newFooCmd / newBarCmd that fresh doesn't. The drift probe
+ // shouldn't re-flag the AddCommand args separately. (The decl-set
+ // mismatch is the load-bearing signal here, not body drift.)
+ verdicts := verdictMap(report)
+ got := verdicts["internal/cli/root.go"]
+ assert.NotEqual(t, VerdictTemplatedBodyDrift, got,
+ "AddCommand argument calls must not trigger body drift; lost-registration handles them")
+}
+
+// TestBodyDriftCatchesPagliacciShape pins the second regression that
+// motivated this probe. Pagliacci-pizza's pub client.go used a Chrome-
+// impersonating HTTP client (surf library) inside New() — without it the
+// CLI risks Cloudflare blocks. Decl-set comparison says TEMPLATED-CLEAN;
+// take-fresh-wholesale dropped the integration silently.
+func TestBodyDriftCatchesPagliacciShape(t *testing.T) {
+ t.Parallel()
+
+ pubCLI := `package client
+
+func New() *Client {
+ builder := surf.Builder().Impersonate().Chrome()
+ return &Client{client: builder.Build()}
+}
+
+type Client struct {
+ client interface{}
+}
+`
+ freshCLI := `package client
+
+func New() *Client {
+ return &Client{client: defaultHTTPClient()}
+}
+
+func defaultHTTPClient() interface{} { return nil }
+
+type Client struct {
+ client interface{}
+}
+`
+ pubDir, freshDir := buildSyntheticFixture(t,
+ map[string]string{"internal/client/client.go": pubCLI},
+ map[string]string{"internal/client/client.go": freshCLI})
+
+ report, err := Classify(pubDir, freshDir, Options{Force: true})
+ require.NoError(t, err)
+
+ verdicts := verdictMap(report)
+ assert.Equal(t, VerdictTemplatedBodyDrift, verdicts["internal/client/client.go"],
+ "chained .Impersonate().Chrome().Build() in pub's New() must flag drift")
+
+ var fc *FileClassification
+ for i := range report.Files {
+ if report.Files[i].Path == "internal/client/client.go" {
+ fc = &report.Files[i]
+ break
+ }
+ }
+ require.NotNil(t, fc.BodyDrift)
+ driftCalls := fc.BodyDrift.Functions["New"]
+ for _, want := range []string{"Builder", "Impersonate", "Chrome"} {
+ assert.Contains(t, driftCalls, want,
+ "surf-library call %q must surface in drift report", want)
+ }
+}
+
+// TestBodyDriftClean verifies the no-drift case: same function bodies in
+// both files (modulo comments and whitespace) classify as TEMPLATED-CLEAN.
+func TestBodyDriftClean(t *testing.T) {
+ t.Parallel()
+
+ src := `package cli
+
+func helper(x int) int {
+ return doStuff(x) + 1
+}
+
+func doStuff(x int) int { return x * 2 }
+`
+ pubDir, freshDir := buildSyntheticFixture(t,
+ map[string]string{"internal/cli/helpers.go": src},
+ map[string]string{"internal/cli/helpers.go": src})
+
+ report, err := Classify(pubDir, freshDir, Options{Force: true})
+ require.NoError(t, err)
+
+ verdicts := verdictMap(report)
+ assert.Equal(t, VerdictTemplatedClean, verdicts["internal/cli/helpers.go"],
+ "identical bodies must stay TEMPLATED-CLEAN")
+}
diff --git a/internal/pipeline/regenmerge/classify.go b/internal/pipeline/regenmerge/classify.go
index 7d963a32..3fecc105 100644
--- a/internal/pipeline/regenmerge/classify.go
+++ b/internal/pipeline/regenmerge/classify.go
@@ -62,14 +62,7 @@ func extractDecls(filename string) (declSet, error) {
for _, d := range file.Decls {
switch decl := d.(type) {
case *ast.FuncDecl:
- name := decl.Name.Name
- if decl.Recv != nil && len(decl.Recv.List) > 0 {
- recv := receiverTypeName(decl.Recv.List[0].Type)
- if recv != "" {
- name = "(" + recv + ")." + name
- }
- }
- decls.add(name)
+ decls.add(canonicalFuncName(decl))
case *ast.GenDecl:
for _, spec := range decl.Specs {
switch s := spec.(type) {
@@ -114,6 +107,19 @@ func receiverTypeName(expr ast.Expr) string {
return ""
}
+// canonicalFuncName returns the qualified name used as the dedup key for
+// function/method decls: bare `Name` for top-level funcs, `(*Type).Name`
+// or `(Type).Name` for methods.
+func canonicalFuncName(fn *ast.FuncDecl) string {
+ name := fn.Name.Name
+ if fn.Recv != nil && len(fn.Recv.List) > 0 {
+ if recv := receiverTypeName(fn.Recv.List[0].Type); recv != "" {
+ name = "(" + recv + ")." + name
+ }
+ }
+ return name
+}
+
// hasTemplatedMarker reports whether one of the file's first few lines
// contains the "Generated by CLI Printing Press" marker. Scans the head of
// the file rather than a fixed byte slab so a long line-1 SPDX/license
@@ -219,7 +225,7 @@ func classifyFiles(publishedDir, freshDir string) ([]FileClassification, error)
freshPath := filepath.Join(freshDir, rel)
pubMarker := hasTemplatedMarker(pubPath)
freshMarker := hasTemplatedMarker(freshPath)
- fc = decideBothPresent(rel, pubDecls, freshDecls, pubMarker, freshMarker, freshGlobalDecls)
+ fc = decideBothPresent(rel, pubPath, freshPath, pubDecls, freshDecls, pubMarker, freshMarker, freshGlobalDecls)
}
out = append(out, fc)
}
@@ -236,7 +242,7 @@ func stringSet(s []string) map[string]struct{} {
// decideBothPresent runs the in-both branch of the classification decision
// tree.
-func decideBothPresent(rel string, pub, fresh declSet, pubMarker, freshMarker bool, freshGlobal declSet) FileClassification {
+func decideBothPresent(rel, pubPath, freshPath string, pub, fresh declSet, pubMarker, freshMarker bool, freshGlobal declSet) FileClassification {
fc := FileClassification{Path: rel}
pubExtras := pub.minus(fresh)
@@ -244,21 +250,31 @@ func decideBothPresent(rel string, pub, fresh declSet, pubMarker, freshMarker bo
templated := pubMarker || freshMarker || isStrictSubset(pub, fresh)
if templated {
- if len(pubExtras) == 0 {
- fc.Verdict = VerdictTemplatedClean
- return fc
- }
- // Cross-file decl search: are all "extras" in fresh's global decl
- // set? If so, the generator moved them to a different file —
- // safe to overwrite.
- allMoved := true
- for _, name := range pubExtras {
- if _, ok := freshGlobal[name]; !ok {
- allMoved = false
- break
+ cleanByDeclSet := len(pubExtras) == 0
+ if !cleanByDeclSet {
+ // Cross-file decl search: are all "extras" in fresh's global
+ // decl set? If so, the generator moved them to a different
+ // file — decl-set is clean.
+ allMoved := true
+ for _, name := range pubExtras {
+ if _, ok := freshGlobal[name]; !ok {
+ allMoved = false
+ break
+ }
}
+ cleanByDeclSet = allMoved
}
- if allMoved {
+ if cleanByDeclSet {
+ // Decl-set looks clean. One more check: do pub's function
+ // bodies call identifiers fresh's same-function bodies don't?
+ // Catches in-place body modifications that decl-set comparison
+ // misses (e.g., pub adds a call to a hand-written helper
+ // inside an existing templated function).
+ if drift := detectBodyDrift(pubPath, freshPath); drift != nil {
+ fc.Verdict = VerdictTemplatedBodyDrift
+ fc.BodyDrift = drift
+ return fc
+ }
fc.Verdict = VerdictTemplatedClean
return fc
}
diff --git a/internal/pipeline/regenmerge/regenmerge.go b/internal/pipeline/regenmerge/regenmerge.go
index dd7a0ab9..fa030dbb 100644
--- a/internal/pipeline/regenmerge/regenmerge.go
+++ b/internal/pipeline/regenmerge/regenmerge.go
@@ -23,8 +23,8 @@ const (
VerdictNewTemplateEmission Verdict = "NEW-TEMPLATE-EMISSION"
// VerdictTemplatedClean marks a file present in both trees where
- // published's decl-set is a subset of fresh's. Safe to overwrite with
- // fresh.
+ // published's decl-set is a subset of fresh's AND function bodies
+ // match. Safe to overwrite with fresh.
VerdictTemplatedClean Verdict = "TEMPLATED-CLEAN"
// VerdictTemplatedWithAdditions marks a file present in both trees where
@@ -33,6 +33,14 @@ const (
// overwrite — surface for human review.
VerdictTemplatedWithAdditions Verdict = "TEMPLATED-WITH-ADDITIONS"
+ // VerdictTemplatedBodyDrift marks a file whose decl-set matches
+ // fresh's but whose function bodies call identifiers fresh's same-named
+ // bodies don't. Catches in-place body modifications the decl-set
+ // comparison can't see — e.g., pub's client.go adds a call to a
+ // hand-written helper inside an existing templated function. Preserve
+ // published; surface for human review.
+ VerdictTemplatedBodyDrift Verdict = "TEMPLATED-BODY-DRIFT"
+
// VerdictPublishedOnlyTemplated marks a file present only in published
// that carries the templated marker. Fresh dropped emitting it. Stale
// template emission; user can decide to delete.
@@ -62,6 +70,22 @@ type FileClassification struct {
// NOVEL-COLLISION verdicts. Lists the top-level declaration names that
// differ between published and fresh.
DeclSetDelta *DeclSetDelta `json:"decl_set_delta,omitempty"`
+
+ // BodyDrift is populated for TEMPLATED-BODY-DRIFT verdict. Lists
+ // per-function the call-target identifiers pub references that don't
+ // appear anywhere in fresh's tree.
+ BodyDrift *BodyDrift `json:"body_drift,omitempty"`
+}
+
+// BodyDrift records function-body call-target differences between published
+// and fresh for a templated file with matching decl-sets. Each entry names
+// a function that exists in both files but whose published-side body
+// references identifiers fresh's tree doesn't have — a load-bearing signal
+// that pub hand-edited the body to integrate with custom helpers.
+type BodyDrift struct {
+ // Functions maps the qualified function name (bare or "(*Type).Method")
+ // to the list of pub-only call-target identifiers in its body.
+ Functions map[string][]string `json:"functions,omitempty"`
}
// DeclSetDelta names the declarations that differ between published and fresh
← 08b88654 chore(main): release 3.2.1 (#466)
·
back to Cli Printing Press
·
feat(generator): owner precedence chain — preserve attributi d1a8a467 →