# lang-migration Skill — Real-World Case Study
> **This repository is a live demonstration of the [lang-migration skill](https://github.com/suifei/lang-migration-skill).**
>
> It contains both the original Python source project and its complete Go port, produced end-to-end by an AI agent following the skill's six-phase pipeline. Everything in this document is derived from real migration artifacts — no numbers are estimated or fabricated.
>
> 中文报告 / Chinese version: [README_zh.md](./README_zh.md)
---
## Projects in This Repository
| Directory | Language | Description |
|-----------|----------|-------------|
| [`genericagent/`](./genericagent/) | Python 3.11 | Source — [GenericAgent](https://github.com/lsdefine/GenericAgent), a minimal self-evolving autonomous agent framework |
| [`go-GenericAgent/`](./go-GenericAgent/) | Go 1.25 | Target — 1:1 Go port produced by the lang-migration skill |
| [`migration_workspace/`](./migration_workspace/) | YAML | All persistent migration state: inventory, ecosystem map, IPO registry, gap report |
---
## What Is the lang-migration Skill?
The [lang-migration skill](https://github.com/suifei/lang-migration-skill) is an AI-executable workflow for porting an entire open-source project from one programming language to another. It is not a prompt template — it is a **structured, multi-session, self-auditing pipeline** enforced through five YAML state files and six sequential phases.
```
P0 Bootstrap → [PGR-0] → P1 Asset Scan → [PGR-1] → P2 Ecosystem Map → [PGR-2]
→ P3 IPO Analysis → [PGR-3] → P4 Translation → [PGR-4] → P5 Verification → [PGR-5]
```
**Core design principles:**
| Principle | What it prevents |
|-----------|-----------------|
| No file is useless — every file gets a migration strategy | AI silently skipping inconvenient files |
| Structural equivalence first, behavioral only when forced | AI drifting to "inspired by" rewrites instead of 1:1 ports |
| No mock, ever | Tests that pass by lying |
| Block, don't skip — stop and ask the human on ambiguous decisions | Silent wrong assumptions accumulating into structural debt |
| State persists in YAML across sessions | Losing context between sessions, re-doing work, inconsistency |
| Phase Gate Review (PGR) before every phase transition | AI marking phases DONE without completing the work |
The PGR mechanism is the skill's most distinctive feature: **a phase is not complete when the AI says it is complete; it is only complete when an autonomous audit loop produces zero findings and sets `passed_at` in the YAML.**
---
## Migration At a Glance
All numbers below come directly from [`migration_workspace/migration-state.yaml`](./migration_workspace/migration-state.yaml) and [`migration_workspace/asset-inventory.yaml`](./migration_workspace/asset-inventory.yaml).
### Scale
| Metric | Value |
|--------|-------|
| Source files scanned | 150 |
| Files fully translated | 147 (98%) |
| Files skipped (GAP_ACCEPTED) | 3 |
| Ecosystem symbols mapped | 58 |
| Functions documented (P3 IPO) | 142 |
| Functions translated (P4) | 142 / 142 |
| Verification checks run (P5) | 898 |
| Verification failures | 0 |
| `go build ./...` | ✅ clean |
| `go vet ./...` | ✅ clean |
| Test packages passing | 37 / 37 |
| Overall test coverage | 60.1% |
| Critical bugs found in P5 | 2 (goroutine leak + `init()` panic) |
### Asset Strategy Breakdown (P1)
| Strategy | Count | Meaning |
|----------|-------|---------|
| `translate` | 52 | Full semantic rewrite in Go |
| `adapt` | 15 | Structure changes required (e.g. merged files, path drift) |
| `direct_use` | 57 | Assets copied as-is (config, HTML, images, SOPs) |
| `reference_only` | 26 | Read for context, no output generated |
### Ecosystem Mapping (P2)
58 Python ecosystem symbols mapped across three categories:
| Equivalence type | Count | Example |
|-----------------|-------|---------|
| `structural` | 38 | `json` → `encoding/json`, `re` → `regexp` |
| `behavioral` | 10 | `asyncio` → goroutines + channels |
| `partial` | 7 | `PIL.Image` → `github.com/disintegration/imaging` |
| `none` (GAP_ACCEPTED) | 1 | `streamlit` (no Go equivalent; others solved via ecosystem substitution or CGO bridging) |
> **Note**: `ultralytics/YOLO` is implemented via `github.com/yalue/onnxruntime_go` (CGO); `rapidocr-onnxruntime` is replaced by Tesseract. Neither is GAP_ACCEPTED — see Limitations section.
New Go dependencies introduced: 15 packages (see [`go-GenericAgent/go.mod`](./go-GenericAgent/go.mod)).
> **On CGO**: `internal/memory/uidetect` calls the ONNX Runtime shared library via `github.com/yalue/onnxruntime_go`, which requires CGO. The package uses dual build-tag design: default build (`go build ./...`) compiles to stubs returning `ErrCGORequired`; building with `-tags cgo` links `onnxruntime.dll`/`.so` for full YOLO inference. Tests are run as `go test -tags cgo -count=1 ./...`.
---
## The Migration Process — Session by Session
The migration ran over **7 active sessions**, all tracked in YAML state.
| Session | Phase | Key Work |
|---------|-------|----------|
| 1 | P0–P2 | Bootstrap, full asset scan (150 files), complete ecosystem mapping (58 entries) |
| 2 | P3–P4 partial | IPO analysis for 55 core functions; translation of core engine (`loop.go`, `agent.go`, `llm/` — split into 6 files) |
| 3 | P4 continued | All frontends (Telegram, Discord, DingTalk, WeCom, QQ, Feishu, TUI, ACP bridge), memory subsystems (adbui, procmem, ljqctrl), reflect subsystem, TMWebDriver, simphtml |
| 4 | P4 gap-fill | `ga_cli`, session compression, vision API, entry-point scripts, config templates |
| 5 | P5 coverage | Integration tests; goroutine leak fix (`streamCh` never closed); overall coverage 60.1% |
| 6 | P6 audit | End-to-end audit; desktop pet (Win32 `UpdateLayeredWindow`, per-pixel alpha, CJK bubble text); final asset count 147 |
| 7 | P5 supplement | `ga_cli` 48.7%→66.4%; `qq` 52.0%→65.9%; `dingtalk` 44.3%→50.4%; `tmwebdriver` 43.7%→48.4% |
---
## What the Skill Got Right
### 1. The YAML state files actually worked
Every session started by reading `migration-state.yaml`. No context was re-established manually. The `current_task` block told the AI exactly where to resume, including blocked items requiring human input. Across **7 active sessions** with the same agent (GitHub Copilot), the migration never had to restart.
### 2. PGR caught real bugs
The Phase Gate Review mechanism caught two categories of real problems:
**Path drift** — The AI translated files to structurally-better locations than the inventory predicted. Example:
- `frontends/btw_cmd.py` → inventoried as `internal/frontends/btw/btw.go`, actually at `internal/frontends/chatcommon/btw.go` (merged with related files)
- `llmcore.py` → inventoried as a single file, actually split into 6 files (`client.go`, `mykey.go`, `session.go`, `sse.go`, `types.go`, `utils.go`) for Go idiom
**Anti-cheating mechanism activated** — The gap report (`migration_workspace/gap-report.yaml`) caught that `migration-state.yaml` claimed 150 assets DONE while asset-inventory.yaml still had 66 entries marked `TODO`. The PGR forced a full re-audit. This is the "batch fabrication" pattern the skill explicitly guards against.
### 3. The blocking protocol preserved correctness on ambiguous decisions
Two ecosystem decisions required human approval before proceeding:
- **`streamlit` → no Go equivalent**: Decided to replace with conductor (HTTP + SSE) rather than embed Python. Required explicit `GAP_ACCEPTED` sign-off.
- **`ultralytics` (YOLO) → implemented via ONNX Runtime + CGO**: Initial assessment flagged a behavior gap; human decision approved the ONNX export path (PyTorch model → ONNX → `onnxruntime_go` + CGO), preserving full detection capability under `-tags cgo` builds. `confirmed_by: human`.
Both decisions are logged in [`migration_workspace/ecosystem-map.yaml`](./migration_workspace/ecosystem-map.yaml) with `confirmed_by: human`.
### 4. The "no mock" policy found two real production bugs
**Bug 1 — Goroutine leak** (P5 integration tests):
```go
// internal/agent/agent.go — BEFORE fix
go func() {
// ... LLM streaming loop
// streamCh was NEVER closed → for-select in caller blocked forever after loop ended
}()
```
Fix: `defer close(streamCh)`. A mock test would have closed the channel itself and never exposed this.
**Bug 2 — Package `init()` panic** (P5 unit tests):
`continue_cmd.go` contained a PCRE lookahead `(?=^===...)` in a package-level `regexp.MustCompile`. Go's RE2 engine doesn't support lookaheads — the program panicked at startup, failing all 13 `chatcommon` tests. Fix: replaced with a line-by-line scanner (behavioral equivalence). This bug passed P4 compilation but was caught by P5 test execution — validating that PGR requires tests to actually run.
### 5. Structural equivalence produced idiomatic Go
The IPO analysis (P3) documented algorithm steps explicitly, which meant the Go translation preserved loop structure and control flow. The result is Go code that reads naturally — not "Python in Go syntax."
Example architectural decisions from IPO analysis:
- `MixinSession` multi-backend failover with `springSec=300` spring-back to primary
- History lives in `backend.history`, not in `messages` slice (mirrors Python design)
- `ToolClient` (text-protocol) vs `NativeToolClient` (native function calling) preserved as separate types
---
## What the Skill Didn't Cover — Honest Limitations
### ML/CV: Ecosystem substitution, not gaps
The three Python ML/CV modules are handled differently — not all are missing:
| Python | Go approach | Result |
|--------|-------------|--------|
| `ultralytics` (YOLO) | `internal/memory/uidetect` — ONNX Runtime via `github.com/yalue/onnxruntime_go` (CGO) | Full YOLO inference with `-tags cgo` build; stubs otherwise |
| `rapidocr-onnxruntime` | `internal/memory/ocr` — Tesseract (`tesseract` binary + `chi_sim+eng`) | Functional OCR, different backend, no CGO needed |
| `streamlit` | `internal/frontends/conductor` — HTTP + SSE | Replaced by custom web frontend; covers core interactive use cases |
True `GAP_ACCEPTED` items (no implementation):
| Python | Reason | Impact |
|--------|--------|--------|
| `pywebview` | Python-only embedded browser | Launch wrapper skipped; low impact |
| `PyQt5` / `tkinter` (Qt frontend) | Python-only GUI | Qt frontend skipped; desktop pet reimplemented via Win32 API |
| WeChat personal bot (`iLink`) | Proprietary Python API, no Go SDK | WeChat frontend deferred |
### MINOR_DEVIATION: Two behavioral differences from source
| Feature | Python behavior | Go behavior | Reason |
|---------|----------------|------------|--------|
| Telegram streaming | Live-edit bot message as chunks arrive | Collect full response, then send in ≤4000-char segments | `telebot.v3` has no clean streaming-edit API |
| `parsePairs` log splitting | PCRE lookahead regex | Line-by-line scanner | Go RE2 engine does not support lookaheads |
Both deviations are documented in [`go-GenericAgent/MIGRATION_NOTES.md`](./go-GenericAgent/MIGRATION_NOTES.md) with justification. Neither affects correctness.
### The skill requires sustained human attention
The migration required approximately **7 active sessions** of human supervision. While the YAML state files handled continuity, the human had to:
- Approve `GAP_ACCEPTED` decisions (2 critical ones)
- Unblock path-drift discrepancies discovered during PGR
- Review retrospective entries after bug fixes
This is not a "one prompt, done" tool. It is a structured collaboration protocol.
---
## Translated Module Summary
**Frontends** (`internal/frontends/`): conductor (HTTP+SSE), telegram, feishu, discord, dingtalk, wecom, qq, tui (bubbletea), chatcommon (4 files merged), acp, desktoppet (Win32 reimplementation), wechat (GAP_ACCEPTED placeholder)
**Memory subsystems** (`internal/memory/`): keychain, adbui, procmem (Windows), ljqctrl (Windows), sessions, vision, skillsearch, autonomous, ocr (Tesseract), uidetect (ONNX Runtime + CGO)
**Reflect subsystem** (`internal/reflect/`): autonomous, scheduler, goalmode, teamworker
**Core engine**: loop (`agent_loop.py`), agent (`agentmain.py`), llm (6 files from `llmcore.py`), bbs, simphtml, tmwebdriver, configure, coderun, ga_cli, plugins
---
## Evidence: The Go Agent Runs
The following screenshot shows `go-GenericAgent` running the Conductor frontend (`go-GenericAgent conductor --host 0.0.0.0 --port 8900`), processing real requests including subagent dispatch and tool execution:

**What the screenshot shows:**
- **Left panel (Conductor / subagent view)**: Three completed task threads — web browsing (`msn.cn/zh-cn/gaming/ai` page summary extracted), arithmetic (`1+999=1000`), and live news query via Google Search
- **Right panel (Conversation)**: The user chat, showing the Go agent autonomously dispatching subagents, calling `code_run` to launch Chrome with a URL, and receiving structured results
The core agent loop (`internal/loop/loop.go`), LLM client (`internal/llm/`), tool dispatch, and SSE streaming are all Go code translated from the Python source.
---
## Quantitative Skill Evaluation
| Dimension | Score | Evidence |
|-----------|-------|----------|
| File coverage | 98% (147/150) | `migration-state.yaml` → `assets_done: 147` |
| Semantic fidelity | High | 142/142 functions translated; IPO docs match source algorithm |
| Build quality | Pass | `go build ./...` clean, `go vet ./...` clean |
| Test coverage | 37 packages | Real-data integration tests, no mocks |
| Behavioral equivalence | ~95% | 2 known MINOR_DEVIATIONs, both documented |
| Feature parity | ~95% | YOLO→ONNX Runtime+CGO; OCR→Tesseract; desktop pet→Win32; only 3 true GAP_ACCEPTED items |
| Anti-regression quality | Strong | PGR caught fabricated inventory state; no mock policy caught goroutine leak |
| Session continuity | Excellent | 7 sessions, zero re-establishment overhead due to YAML state |
**Overall assessment**: For a project of this scope (150 files, 12 frontends, 10 memory subsystems, Win32 GUI, 6 chat platforms, streaming LLM client), the skill produced a complete, buildable, testable Go codebase in **7 sessions**. The structural equivalence principle was respected. The anti-cheating mechanisms activated on real problems and resolved them. The functional gaps are honest and documented.
The migration would have been significantly harder without the skill's YAML state persistence and PGR audit loop.
---
## Repository Structure
```
.
├── genericagent/ # Python source (read-only reference)
├── go-GenericAgent/ # Go port output
│ ├── cmd/ # Entry points: go-GenericAgent, ga, hub, ga_cli
│ ├── internal/ # All packages
│ │ ├── agent/ # agentmain.py → agent.go
│ │ ├── loop/ # agent_loop.py → loop.go
│ │ ├── llm/ # llmcore.py → client.go, session.go, sse.go …
│ │ ├── frontends/ # 12 frontends (conductor, telegram, discord, …)
│ │ ├── memory/ # 10 memory subsystems
│ │ ├── reflect/ # autonomous, scheduler, goalmode, teamworker
│ │ ├── bbs/ # agent_bbs.py → bbs.go
│ │ ├── simphtml/ # simphtml.py → simphtml.go
│ │ └── tmwebdriver/ # TMWebDriver.py → tmwebdriver.go
│ ├── assets/ # 147 direct-use assets (images, HTML, SOPs)
│ ├── go.mod
│ └── MIGRATION_NOTES.md # Deviations and GAP_ACCEPTED rationale
├── migration_workspace/ # All persistent YAML migration state
│ ├── migration-state.yaml # Phase tracker, session entry point
│ ├── asset-inventory.yaml # 150 files × strategy × status
│ ├── ecosystem-map.yaml # 58 Python symbols → Go equivalents
│ ├── ipo-registry.yaml # 142 functions × IPO documentation
│ └── gap-report.yaml # P6 audit findings (STALE — all resolved)
└── README.md # This file
```
---
## Links
- **lang-migration skill**: https://github.com/suifei/lang-migration-skill
- **GenericAgent (Python source)**: https://github.com/lsdefine/GenericAgent
- **go-GenericAgent (Go port)**: https://github.com/suifei/go-GenericAgent *(publishing)*
---
## License
The migration skill and this case study are MIT licensed.
GenericAgent is copyright its original authors; see [`genericagent/LICENSE`](./genericagent/LICENSE).