유대선
프로젝트로
·기술 회고·3

What actually moved to Python — and the number I got wrong

An honest capstone on the ADR-020 brain migration: which semantic logic moved to a local Python sidecar, what stayed, and a correction — the audit's headline '~26 sites to convert' was over-counted. The real number is 4 live, ~4-7 worth doing.

The shape of it

Dalkkak's app is ~31k lines of Rust + ~88k of TypeScript. The "AI" that's actually ours — not the BYO Claude that does generation — is a 272-line Python sidecar (apps/ai-brain, sentence-transformers, localhost JSON). It does one thing: turn free text into a semantic decision (rank / classify). That's it. The terminal, git, PTY, UI, MCP wiring — all stay Rust/TS, and should.

What moved, and where it is in the code:

Nothing was deleted or commented out. The structure everywhere is brain first, old keyword path as the safety net — if the sidecar is down or unsure, the app behaves exactly as before. The brain auto-spawns on launch (a bug earlier: it was registered but never actually called — so it never ran; fixed).

The number I got wrong

Earlier I said, with confidence, that an audit had found 26 sites to move to Python, "1순위 11", cross-validated by two independent passes, "신뢰도 최상." I presented 26 as the migration target.

That was wrong, and I'll own it plainly.

Both audit passes asked "is there keyword/regex code here doing something semantic-looking?" and counted yes-answers as "should be ML." But that test over-includes. inferTaskId("worker-3/x.json") is a regex — and it's path parsing, not ML. A secret-redaction gate is keyword matching — and it must stay deterministic, because a model that's 97% right leaks 3% of secrets. Two commandMatch / scoreWorkflowMatch "classifiers" have no caller at all — dead code.

When I went site by site (the catalog: docs/product/adr-020-site-catalog.md), ~12 of the 26 were format/path parsers or security gates that belong in Rust/TS, and several more were dead or structural. The honest count: ~4-7 worth converting, of which 4 are done and live (the two rankers + the two text-classification entry points). Not 26.

Two auditors agreeing didn't make it right — they made the same category error. The real test isn't "does semantic-looking string code exist," it's "would embeddings actually beat this, and is there even a consumer." I should have led with that.

What stays, on purpose

Security/redaction gates, approval gates, explicit enum→enum maps, RFC email threading, file-path/ID parsing, the cosine engine that's already vector-based, and closed-set intent vocabularies (a 14-word recency list). Moving any of these to a model makes them slower and less trustworthy. "Move the AI to Python" never meant "rewrite the app" — it meant move the handful of genuine semantic judgments, behind a clean boundary, and keep the deterministic spine.

The honest ledger