py2go
End-to-end Python → Go migration. Discover the source, lock the design, scaffold the Go module, translate file-by-file with TDD, validate behavioral parity against real data, cut over, then sweep dead code.
The skill is opinionated. It enforces idiomatic Go output over Python-shaped Go, rejects dead toolchains (Grumpy/py2go transpilers), defaults to sqlc over GORM, defaults to pgx over lib/pq, defaults to Gin for HTTP, prefers stdlib slog over zap/zerolog, and treats real-data parity validation as non-optional.
When to load which reference
| Task |
Open |
| Decide which Python pattern maps to which Go idiom |
references/translation-rules.md |
| Pick the right Go stack per project type |
(playbooks - extend as needed) |
| Configure strangler-fig gateway / traffic shadow |
(extend as needed) |
| Drive from an existing OpenAPI/proto spec |
(extend as needed) |
The 7 phases
discover → design → scaffold → translate → validate → cutover → cleanup
- Discover - two-pass: 7 discovery prompts individually → synthesize to
notes/
- Design - emit
CLAUDE.md (translation rules) + MIGRATION.md (ordered file map + checkboxes)
- Scaffold -
go mod init, layout, lint (golangci-lint), CI, Makefile, smoke target
- Translate - per-file loop: Python source → Go test first → Go impl →
go build && vet && test -race && lint → commit
- Validate - golden-file parity on real production data + integration tests + dead-code sweep
- Cutover - hard cutover (default) or strangler-fig (
--strangler)
- Cleanup - orphan sweep, dependency audit, retro
Project-type playbooks (auto-detected in discover)
| Type |
Detected from |
Go stack |
| CLI |
Click/Typer imports, entry_points console_scripts |
Cobra + Viper + lipgloss |
| TUI |
Textual / Rich.live / prompt_toolkit |
Bubble Tea + Bubbles + Lipgloss |
| HTTP |
FastAPI/Flask/Django/Starlette |
Gin (default) / chi / Echo / Fiber |
| Pipeline |
pandas/polars/Airflow/Prefect/Dagster |
streaming []T + channels; qframe; STOP if NumPy/SciPy heavy |
| Worker |
Celery/RQ/Dramatiq/Arq/Taskiq |
asynq (Redis) or river (Postgres) |
| Library |
__init__.py exports |
pkg/ layout with forced public-API decision |
Hard guardrails (skill enforces)
- No 1:1 syntax port - Go function signatures must not mirror Python verbatim across >50% of lines.
- TDD-or-bust under
--strict-tdd - Go test mtime must precede Go impl mtime per commit.
- No
lib/pq - use pgx/v5. lib/pq is in maintenance mode.
- No
golang/mock - use go.uber.org/mock (Google archived original).
- No GORM by default -
sqlc is default; GORM requires explicit --orm=gorm flag.
- No
panic for business errors - return errors.
- No blind
internal/ - design phase must produce an explicit public-API decision.
- No synthetic-only validation - validate phase refuses to mark complete without a real-data fixture path.
- NumPy/SciPy refusal - if discovery detects them load-bearing, STOP with gRPC-wrap recommendation instead.
Cross-refs into the rest of the skill ecosystem
- gostack - Sam Berthe's Go libraries (lo, oops, do, mo, slog, hot, ro). See
references/translation-rules.md for where each library is the right answer.
vd:cook - once a plan + MIGRATION.md is in place, drive execution phase-by-phase
vd:debug - for the on-call story oops/slog enables in the migrated service
vd:ship - for the final cutover commit + PR
Versions snapshot (verified 2026-05-23)
Locked defaults - change requires explicit flag.
| Concern |
Pinned default |
Cite |
| HTTP framework |
gin v1.x |
Go Survey 2025: 48% adoption |
| Postgres driver |
pgx/v5 |
lib/pq in maintenance |
| DB access |
sqlc |
2× faster than GORM on 15k-row reads |
| Migrations |
golang-migrate |
Multi-DB, CI-friendly |
| Config |
viper |
Cobra ecosystem alignment |
| Logger |
log/slog (stdlib) |
Survey 2025 default for new code |
| Validation |
go-playground/validator |
Default with Gin |
| Mocking |
go.uber.org/mock |
Google archived golang/mock |
| OpenAPI codegen |
oapi-codegen |
Supports Gin/chi/Fiber |
| Queue |
asynq (Redis) or river (Postgres) |
Celery-shaped or PG-native |
| Cache |
ristretto or gostack@hot |
See gostack/hot |
| Auth |
golang-jwt/v5 + argon2 |
OWASP 2025 recommendation |
| Observability |
OpenTelemetry + Prometheus |
Industry standard |
| Build/release |
goreleaser + ko |
Container without Dockerfile |
Adding a new project-type playbook
- Detect signal in discover phase (imports, file conventions)
- Drop
references/playbook-<type>.md with the canonical Go stack for that shape
- Add a row to the "Project-type playbooks" table above
- Update translation rules where the playbook diverges from defaults
1---2name: py2go3description: Migrate Python projects to idiomatic Go end-to-end. Branches into 6 project-type playbooks (CLI, TUI, HTTP backend, data pipeline, async worker, library) with the right stack defaults (Gin, pgx, sqlc, slog, etc.) and pinned library versions. Default strategy: LLM module-by-module rewrite with golden-file parity tests; --strangler for live-traffic gradual cutover; --spec-first for OpenAPI/proto-driven regeneration. Use when porting a Python codebase to Go, when scaffolding a Go rewrite of a Python service, or when generating CLAUDE.md/MIGRATION.md for an AI-driven migration.4license: MIT5---67# py2go89End-to-end Python → Go migration. Discover the source, lock the design, scaffold the Go module, translate file-by-file with TDD, validate behavioral parity against real data, cut over, then sweep dead code.1011The skill is opinionated. It enforces idiomatic Go output over Python-shaped Go, rejects dead toolchains (Grumpy/py2go transpilers), defaults to `sqlc` over GORM, defaults to `pgx` over lib/pq, defaults to `Gin` for HTTP, prefers stdlib `slog` over zap/zerolog, and treats real-data parity validation as non-optional.1213## When to load which reference1415| Task | Open |16|---|---|17| Decide which Python pattern maps to which Go idiom | [references/translation-rules.md](references/translation-rules.md) |18| Pick the right Go stack per project type | (playbooks - extend as needed) |19| Configure strangler-fig gateway / traffic shadow | (extend as needed) |20| Drive from an existing OpenAPI/proto spec | (extend as needed) |2122## The 7 phases2324```25discover → design → scaffold → translate → validate → cutover → cleanup26```27281. **Discover** - two-pass: 7 discovery prompts individually → synthesize to `notes/`292. **Design** - emit `CLAUDE.md` (translation rules) + `MIGRATION.md` (ordered file map + checkboxes)303. **Scaffold** - `go mod init`, layout, lint (`golangci-lint`), CI, Makefile, smoke target314. **Translate** - per-file loop: Python source → Go test first → Go impl → `go build && vet && test -race && lint` → commit325. **Validate** - golden-file parity on real production data + integration tests + dead-code sweep336. **Cutover** - hard cutover (default) or strangler-fig (`--strangler`)347. **Cleanup** - orphan sweep, dependency audit, retro3536## Project-type playbooks (auto-detected in discover)3738| Type | Detected from | Go stack |39|---|---|---|40| CLI | Click/Typer imports, `entry_points` console_scripts | Cobra + Viper + lipgloss |41| TUI | Textual / Rich.live / prompt_toolkit | Bubble Tea + Bubbles + Lipgloss |42| HTTP | FastAPI/Flask/Django/Starlette | **Gin** (default) / chi / Echo / Fiber |43| Pipeline | pandas/polars/Airflow/Prefect/Dagster | streaming `[]T` + channels; qframe; **STOP if NumPy/SciPy heavy** |44| Worker | Celery/RQ/Dramatiq/Arq/Taskiq | asynq (Redis) or river (Postgres) |45| Library | `__init__.py` exports | `pkg/` layout with forced public-API decision |4647## Hard guardrails (skill enforces)48491. **No 1:1 syntax port** - Go function signatures must not mirror Python verbatim across >50% of lines.502. **TDD-or-bust under `--strict-tdd`** - Go test mtime must precede Go impl mtime per commit.513. **No `lib/pq`** - use `pgx/v5`. lib/pq is in maintenance mode.524. **No `golang/mock`** - use `go.uber.org/mock` (Google archived original).535. **No GORM by default** - `sqlc` is default; GORM requires explicit `--orm=gorm` flag.546. **No `panic` for business errors** - return errors.557. **No blind `internal/`** - design phase must produce an explicit public-API decision.568. **No synthetic-only validation** - validate phase refuses to mark complete without a real-data fixture path.579. **NumPy/SciPy refusal** - if discovery detects them load-bearing, STOP with gRPC-wrap recommendation instead.5859## Cross-refs into the rest of the skill ecosystem6061- **[gostack](../gostack/SKILL.md)** - Sam Berthe's Go libraries (lo, oops, do, mo, slog, hot, ro). See `references/translation-rules.md` for where each library is the right answer.62- `vd:cook` - once a plan + MIGRATION.md is in place, drive execution phase-by-phase63- `vd:debug` - for the on-call story `oops`/`slog` enables in the migrated service64- `vd:ship` - for the final cutover commit + PR6566## Versions snapshot (verified 2026-05-23)6768Locked defaults - change requires explicit flag.6970| Concern | Pinned default | Cite |71|---|---|---|72| HTTP framework | gin v1.x | Go Survey 2025: 48% adoption |73| Postgres driver | pgx/v5 | lib/pq in maintenance |74| DB access | sqlc | 2× faster than GORM on 15k-row reads |75| Migrations | golang-migrate | Multi-DB, CI-friendly |76| Config | viper | Cobra ecosystem alignment |77| Logger | log/slog (stdlib) | Survey 2025 default for new code |78| Validation | go-playground/validator | Default with Gin |79| Mocking | go.uber.org/mock | Google archived golang/mock |80| OpenAPI codegen | oapi-codegen | Supports Gin/chi/Fiber |81| Queue | asynq (Redis) or river (Postgres) | Celery-shaped or PG-native |82| Cache | ristretto or `gostack@hot` | See [gostack/hot](../gostack/references/hot.md) |83| Auth | golang-jwt/v5 + argon2 | OWASP 2025 recommendation |84| Observability | OpenTelemetry + Prometheus | Industry standard |85| Build/release | goreleaser + ko | Container without Dockerfile |8687## Adding a new project-type playbook88891. Detect signal in discover phase (imports, file conventions)902. Drop `references/playbook-<type>.md` with the canonical Go stack for that shape913. Add a row to the "Project-type playbooks" table above924. Update translation rules where the playbook diverges from defaults