[object Object]

← back to Cli Printing Press

docs(plans): template sanitization round 2 + pipeline E2E plan

15f3d250cdcf98b81b660868047b8a04707e600e · 2026-03-24 10:08:52 -0700 · Matt Van Horn

Template fixes (toCamel, flagName, safeTypeName, dedup) applied.
Gauntlet still 4/10 - remaining failures are template logic bugs
(unused vars, duplicate body field declarations, bool type mismatch),
not sanitization. Next round needs command.go.tmpl fixes.

Pipeline E2E plan ready for execution when template score improves.

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

Files touched

Diff

commit 15f3d250cdcf98b81b660868047b8a04707e600e
Author: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Date:   Tue Mar 24 10:08:52 2026 -0700

    docs(plans): template sanitization round 2 + pipeline E2E plan
    
    Template fixes (toCamel, flagName, safeTypeName, dedup) applied.
    Gauntlet still 4/10 - remaining failures are template logic bugs
    (unused vars, duplicate body field declarations, bool type mismatch),
    not sanitization. Next round needs command.go.tmpl fixes.
    
    Pipeline E2E plan ready for execution when template score improves.
    
    Co-Authored-By: GPT-5.4 <noreply@openai.com>
    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
 ...x-template-sanitization-gauntlet-round2-plan.md | 198 +++++++++++++++++++++
 ...24-test-full-pipeline-e2e-with-chaining-plan.md | 163 +++++++++++++++++
 2 files changed, 361 insertions(+)

diff --git a/docs/plans/2026-03-24-fix-template-sanitization-gauntlet-round2-plan.md b/docs/plans/2026-03-24-fix-template-sanitization-gauntlet-round2-plan.md
new file mode 100644
index 00000000..803f26a2
--- /dev/null
+++ b/docs/plans/2026-03-24-fix-template-sanitization-gauntlet-round2-plan.md
@@ -0,0 +1,198 @@
+---
+title: "Fix Template Sanitization - Gauntlet Round 2 (4/10 to 8/10+)"
+type: fix
+status: completed
+date: 2026-03-24
+origin: docs/plans/dogfood-gauntlet-findings.md
+---
+
+# Fix Template Sanitization - Gauntlet Round 2
+
+## Overview
+
+Round 1 parser fixes (commit 3f096bc) sanitized type names in `mapTypes` and unlocked Jira. But 6 APIs still fail because the `$`, `/`, and other special characters also flow through to:
+- Template helper functions (`toCamel`, `flagName`, `title`) that don't strip special chars
+- `types.go.tmpl` which uses `{{$name}}` directly (Go template variable, not the sanitized name)
+- `command.go.tmpl` which uses param/body names via `{{camel .Name}}`
+
+The fix: harden the template helper functions so any name that passes through `toCamel`, `flagName`, or `goType` is automatically safe for Go code.
+
+## Remaining Failures
+
+| API | Error | Root Cause |
+|-----|-------|------------|
+| Vercel | `$` in `deployments.go` flag names | Param names like `$schema` pass through `camel` -> `$Schema` (invalid Go) |
+| Trello | `/` in `boards.go` type references | Enum values like `modelTypes/card` become Go identifiers via templates |
+| Fly.io | `Version` redeclared in types.go | Two schemas sanitize to same name, causing Go redeclaration |
+| Spotify | Panic in command registration | CLI name `spotify-web-sonallux` triggers Cobra panic (deeper issue in command tree) |
+| Supabase | `"true"` string as bool flag | Param type mapped as `string` but default value `"true"` used with `BoolVar` |
+| Cloudflare | `name is required` | x-api-name extension is nested under `x-api-id` not `x-api-name`, parser didn't find it |
+
+## Acceptance Criteria
+
+- [ ] `toCamel` strips `$`, `.`, `/`, `\` before camelCasing
+- [ ] `flagName` strips `$` prefix
+- [ ] `types.go.tmpl` uses sanitized type names (not raw Go template vars)
+- [ ] Duplicate type names after sanitization are deduplicated (append suffix)
+- [ ] Vercel passes all 7 gates
+- [ ] Trello passes all 7 gates
+- [ ] Fly.io passes all 7 gates
+- [ ] Supabase passes all 7 gates
+- [ ] Spotify passes all 7 gates (or has a clear, documented limitation)
+- [ ] Existing passing APIs still pass (petstore, discord, gmail, telegram, sentry, launchdarkly, jira)
+- [ ] `go test ./...` passes
+
+## Implementation Units
+
+### Unit 1: Harden toCamel to Strip Special Characters
+
+**Files:** `internal/generator/generator.go`
+
+**Root cause:** `toCamel` (line 165) only splits on `_`, `-`, ` `. Characters like `$`, `.`, `/` pass through verbatim.
+
+**Fix:** Add these characters to the split function:
+
+```go
+func toCamel(s string) string {
+    // Strip characters that are invalid in Go identifiers
+    s = strings.TrimLeft(s, "$")
+    parts := strings.FieldsFunc(s, func(r rune) bool {
+        return r == '_' || r == '-' || r == ' ' || r == '.' || r == '/' || r == '\\' || r == '$'
+    })
+    for i, p := range parts {
+        if len(p) > 0 {
+            parts[i] = strings.ToUpper(p[:1]) + p[1:]
+        }
+    }
+    result := strings.Join(parts, "")
+    // Ensure starts with letter
+    if len(result) > 0 && !unicode.IsLetter(rune(result[0])) {
+        result = "V" + result
+    }
+    return result
+}
+```
+
+**Verification:** `toCamel("$schema")` returns `"Schema"`, not `"$Schema"`. `toCamel("modelTypes/card")` returns `"ModelTypesCard"`.
+
+### Unit 2: Harden flagName to Strip $ Prefix
+
+**Files:** `internal/generator/generator.go`
+
+**Fix:** Update `flagName` (line 336):
+
+```go
+func flagName(name string) string {
+    name = strings.TrimLeft(name, "$")
+    name = strings.ReplaceAll(name, "_", "-")
+    name = strings.ReplaceAll(name, "/", "-")
+    name = strings.ReplaceAll(name, ".", "-")
+    return strings.Trim(name, "-")
+}
+```
+
+### Unit 3: Fix types.go.tmpl Type Name Sanitization
+
+**Files:** `internal/generator/templates/types.go.tmpl`, `internal/generator/generator.go`
+
+**Root cause:** The template uses `{{$name}}` which is the raw map key. Even though `mapTypes` sanitizes the key in the parser, the template also needs a `safeTypeName` function for the struct declaration.
+
+**Fix:** Add a `safeTypeName` template function in generator.go:
+
+```go
+"safeTypeName": safeTypeName,
+```
+
+```go
+func safeTypeName(name string) string {
+    // Strip $ prefix, replace dots/slashes with underscores
+    name = strings.TrimLeft(name, "$")
+    name = strings.NewReplacer(".", "_", "/", "_", "\\", "_", "-", "_").Replace(name)
+    var b strings.Builder
+    for _, r := range name {
+        if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
+            b.WriteRune(r)
+        }
+    }
+    result := b.String()
+    if len(result) > 0 && !unicode.IsLetter(rune(result[0])) {
+        result = "T" + result
+    }
+    return result
+}
+```
+
+Update `types.go.tmpl`:
+```
+{{range $name, $typeDef := .Types}}
+type {{safeTypeName $name}} struct {
+{{- range $typeDef.Fields}}
+	{{title .Name}} {{goType .Type}} `json:"{{.Name}}"`
+{{- end}}
+}
+{{end}}
+```
+
+### Unit 4: Deduplicate Type Names After Sanitization
+
+**Files:** `internal/openapi/parser.go`
+
+**Root cause:** Fly.io has schemas that sanitize to the same Go name (e.g., `fly.Version` and `machines.Version` both become `Version`). This causes "redeclared" errors.
+
+**Fix:** In `mapTypes`, after sanitizing the name, check for duplicates:
+
+```go
+usedNames := map[string]int{}
+for _, name := range names {
+    goName := sanitizeTypeName(name)
+    if goName == "" {
+        continue
+    }
+    if count, exists := usedNames[goName]; exists {
+        goName = fmt.Sprintf("%s%d", goName, count+1)
+        usedNames[goName[:len(goName)-1]] = count + 1
+    } else {
+        usedNames[goName] = 1
+    }
+    // ... rest of type mapping uses goName
+}
+```
+
+### Unit 5: Fix Supabase Bool Flag Type Mismatch
+
+**Files:** `internal/openapi/parser.go` or `internal/generator/generator.go`
+
+**Root cause:** A parameter has type "boolean" in the spec but the parser maps it to "string". Then the template generates `cmd.Flags().BoolVar(&flagX, ...)` but `flagX` is declared as `string`.
+
+**Fix:** Check where boolean types flow through `mapSchemaType` in the parser. Ensure OpenAPI `type: boolean` maps to spec type `bool`, not `string`. Also check that `cobraFlagFunc` and `goType` agree - if the param type is `bool`, the flag must be `BoolVar` and the variable must be `bool`.
+
+### Unit 6: Investigate Spotify Panic
+
+**Files:** investigate only
+
+**Root cause:** Spotify's spec has deeply nested sub-resources that cause Cobra command registration to panic. The name is now shorter (`spotify-web-sonallux`) but the command tree construction still crashes.
+
+**Approach:** Generate Spotify, read the panic stack trace, identify which command.go file has the issue. Likely a circular sub-resource reference or duplicate command name.
+
+If the fix is simple (e.g., dedup command names), fix it. If it's a deeper Cobra issue, document as known limitation.
+
+### Unit 7: Re-run Gauntlet and Update Findings
+
+After all fixes, rebuild and re-run all 10 APIs. Update `docs/plans/dogfood-gauntlet-findings.md` with new scores. Add passing CLIs to catalog.
+
+## Scope Boundaries
+
+- Don't change the 50-resource/50-endpoint limits
+- Don't fix the "flat api resource" problem (Sentry/LaunchDarkly) - separate enhancement
+- Don't add new template features (table output, pagination) - just fix sanitization
+- Don't modify test fixtures - the fixes should be backward compatible
+- Every fix must not break existing passing specs
+
+## Sources
+
+- Generator helpers: `internal/generator/generator.go` (toCamel:165, flagName:336, goType:188)
+- Types template: `internal/generator/templates/types.go.tmpl`
+- Command template: `internal/generator/templates/command.go.tmpl`
+- Parser type mapping: `internal/openapi/parser.go:mapTypes` (line 858)
+- Gauntlet findings: `docs/plans/dogfood-gauntlet-findings.md`
+- Round 1 fixes: commit 3f096bc
diff --git a/docs/plans/2026-03-24-test-full-pipeline-e2e-with-chaining-plan.md b/docs/plans/2026-03-24-test-full-pipeline-e2e-with-chaining-plan.md
new file mode 100644
index 00000000..23d06130
--- /dev/null
+++ b/docs/plans/2026-03-24-test-full-pipeline-e2e-with-chaining-plan.md
@@ -0,0 +1,163 @@
+---
+title: "E2E Test - Full Pipeline with Nightnight Chaining on Petstore"
+type: test
+status: active
+date: 2026-03-24
+---
+
+# E2E Test - Full Pipeline with Nightnight Chaining
+
+## Overview
+
+We built the pipeline infrastructure tonight:
+- `printing-press print` creates 6 plan seeds + state.json (verified in E2E test)
+- SKILL.md Workflow 4 documents the autonomous phase loop with CronCreate chaining
+- Review phase seed now includes 3-tier dogfooding
+- Budget gate, heartbeat, error handling, morning report all documented
+
+But we've never actually run the full loop. This plan tests the complete pipeline end-to-end: `printing-press print petstore` -> ce:plan phase 0 -> ce:work phase 0 -> chain to phase 1 -> ... -> morning report.
+
+Petstore is the simplest spec (3 resources, 13 endpoints, no OAuth2) - ideal for validating the pipeline machinery without API complexity.
+
+## Acceptance Criteria
+
+- [ ] `printing-press print petstore` creates pipeline directory with 6 seeds + state.json
+- [ ] Phase 0 (Preflight): ce:plan expands seed, ce:work executes, state.json updated to "completed"
+- [ ] Phase 1 (Scaffold): ce:plan expands seed, ce:work generates CLI, all 7 gates pass
+- [ ] Phase 2 (Enrich): ce:plan researches, ce:work writes overlay.yaml (or skips if no enrichments)
+- [ ] Phase 3 (Regenerate): ce:plan plans merge, ce:work regenerates with overlay
+- [ ] Phase 4 (Review): ce:plan plans review, ce:work runs static checks + dogfood tier 1
+- [ ] Phase 5 (Ship): ce:plan plans ship, ce:work creates git repo + morning report
+- [ ] Budget gate runs between each phase and says CONTINUE (petstore is fast)
+- [ ] CronCreate chains work (or manual chaining if cron not available in test)
+- [ ] Morning report written to `docs/plans/petstore-pipeline/report.md`
+- [ ] state.json shows all 6 phases as "completed"
+- [ ] Generated petstore-cli compiles, runs, doctor works
+
+## Implementation Units
+
+### Unit 1: Initialize Pipeline
+
+```bash
+cd ~/cli-printing-press
+go build -o ./printing-press ./cmd/printing-press
+./printing-press print petstore --output /tmp/petstore-pipeline-test --force
+```
+
+Verify: 6 plan seeds + state.json created.
+
+### Unit 2: Execute Phase 0 (Preflight) Manually
+
+Since we can't use CronCreate chaining in a test, execute each phase manually following Workflow 4:
+
+a. Read the plan seed: `docs/plans/petstore-pipeline/00-preflight-plan.md`
+b. Run: `Skill("compound-engineering:ce:plan", "docs/plans/petstore-pipeline/00-preflight-plan.md")`
+c. Run: `Skill("compound-engineering:ce:work", "docs/plans/petstore-pipeline/00-preflight-plan.md")`
+d. Update state.json: mark preflight as "completed"
+e. Run budget gate - verify CONTINUE
+
+### Unit 3: Execute Phase 1 (Scaffold)
+
+Same pattern:
+a. Read `01-scaffold-plan.md`
+b. ce:plan to expand
+c. ce:work to execute (should run `printing-press generate`)
+d. Verify: CLI generated at output dir, 7 gates pass
+e. Update state.json, run budget gate
+
+### Unit 4: Execute Phase 2 (Enrich)
+
+a. Read `02-enrich-plan.md`
+b. ce:plan to expand (may use WebSearch for docs)
+c. ce:work to execute (should produce overlay.yaml or skip)
+d. Update state.json, run budget gate
+
+### Unit 5: Execute Phase 3 (Regenerate)
+
+a. Read `03-regenerate-plan.md`
+b. ce:plan to expand
+c. ce:work to execute (merge overlay if exists, regenerate)
+d. Verify: CLI still compiles, gates still pass
+e. Update state.json, run budget gate
+
+### Unit 6: Execute Phase 4 (Review + Dogfood)
+
+a. Read `04-review-plan.md`
+b. ce:plan to expand
+c. ce:work to execute:
+   - Static checks (help, names, descriptions)
+   - Dogfood Tier 1 (version, doctor, dry-run)
+   - Write dogfood-results.json and review.md with combined score
+d. Verify: review.md exists with score, dogfood-results.json has test results
+e. Update state.json, run budget gate
+
+### Unit 7: Execute Phase 5 (Ship)
+
+a. Read `05-ship-plan.md`
+b. ce:plan to expand
+c. ce:work to execute:
+   - Git init in output dir
+   - Write morning report
+d. Verify: report.md exists, state.json shows all 6 phases completed
+
+### Unit 8: Validate Final State
+
+```bash
+# Check state.json
+python3 -c "
+import json
+s = json.load(open('docs/plans/petstore-pipeline/state.json'))
+for phase in ['preflight','scaffold','enrich','regenerate','review','ship']:
+    status = s['phases'][phase]['status']
+    print(f'{phase}: {status}')
+    assert status == 'completed', f'{phase} not completed!'
+print('ALL PHASES COMPLETED')
+"
+
+# Check morning report
+cat docs/plans/petstore-pipeline/report.md
+
+# Check review score
+cat docs/plans/petstore-pipeline/review.md
+
+# Check generated CLI
+cd /tmp/petstore-pipeline-test
+./petstore-cli --help
+./petstore-cli doctor
+./petstore-cli version
+```
+
+### Unit 9: Cleanup
+
+Remove test artifacts:
+- `docs/plans/petstore-pipeline/`
+- `/tmp/petstore-pipeline-test/`
+
+## Scope Boundaries
+
+- Use manual phase execution (no CronCreate chaining) - this tests the pipeline logic, not the scheduling
+- Don't fix bugs found in the pipeline seeds - document them for follow-up
+- Don't test with complex APIs (Gmail, Stripe) - Petstore is sufficient for infrastructure validation
+- If a phase fails, document the failure and continue to the next phase manually
+- This is a test plan, not an implementation plan - don't write new code
+
+## Dependencies
+
+- Nightnight chaining plan (completed) - provides Workflow 4/5 in SKILL.md
+- Autonomous dogfood phase (completed) - provides enhanced Review seed
+- Compound Engineering plugin - required for ce:plan and ce:work
+- Parser fixes (completed) - Petstore already passes, so this is stable
+
+## Expected Outcome
+
+If everything works: 6/6 phases complete, morning report shows petstore-cli with quality score, generated CLI runs. This validates the entire pipeline vision: one command creates a plan-per-phase pipeline that can run autonomously.
+
+If something fails: we document exactly which phase broke and why, creating targeted fix plans for the next session.
+
+## Sources
+
+- Pipeline init: `internal/pipeline/pipeline.go`
+- Plan seeds: `internal/pipeline/seeds.go`
+- SKILL.md Workflow 4: `skills/printing-press/SKILL.md` (lines 209-307)
+- State machine: `internal/pipeline/state.go`
+- Review seed with dogfood: commit aae7801

← d8a93e0a fix(generator): harden toCamel, flagName, types template for  ·  back to Cli Printing Press  ·  feat(pipeline): add plan_status field to PhaseState for seed fa2459dd →