Parent: pp-rewrite
Direct Perl→Python translation fails because models try to preserve syntax-level fidelity at the cost of idiom. Mojolicious-specific mechanisms (helpers, plugins, $c->render, $c->write_chunk, the IOLoop) get translated literally rather than re-expressed in Python idiom.
A semantic spec as intermediate form forces abstraction. Instead of "calls $c->write_chunk in a loop", the spec says "streams server-sent events for log tailing". The Python phase can then choose the right async primitive (e.g. FastAPI's StreamingResponse) rather than mechanically reproducing Mojolicious's API.
Mojolicious is a relatively good source for this. It has clear conventions (Lite vs full app, controllers, helpers, the render pipeline) and both Claude and Gemini know it well.
• Route inventory — every endpoint, HTTP method, path pattern, expected inputs, response shape, content type. Not the Mojolicious routing DSL, but the contract.
• Domain model — what entities exist (servers, groups, deployments, sessions), their attributes, their lifecycle states
• Workflows — operations the user performs end-to-end (e.g. "deploy WAR to group"), expressed as sequences of API calls, validations, and side-effects, independent of how Mojolicious wires them
• Async/streaming behaviour — explicitly flagged: which endpoints stream, which are long-poll, which fan out to multiple backends in parallel. This is where translation goes wrong silently.
• Auth & session model — how sessions are stored, how WebLogic credentials are managed, whether SSO is involved
• Backend API surface — the WebLogic management calls actually used. This is the section to discard and rewrite against modern WebLogic REST.
• Operational invariants — undocumented assumptions baked in over years. The bit that's hardest to extract and most valuable to preserve.
• Perl context (scalar/list) — easy to misread; affects what a function returns
• AUTOLOAD and symbol table tricks — if present, will be silently misinterpreted
• Tied variables — magic behaviour invisible at call site
• Regex magic — Perl regex features that don't have direct Python equivalents (e.g. embedded code, recursive patterns)
• Mojolicious helpers — global-ish methods registered at startup; their definitions may be far from their use sites
• Plugin lifecycle — request-phase hooks affect behaviour without appearing in route handlers
• Async semantics — Mojo::IOLoop and promises map to asyncio but not always cleanly; sync-in-async deadlocks are easy to introduce
• Request lifecycle differences — Mojolicious before/after_dispatch hooks don't have direct FastAPI/Flask analogues; need explicit middleware design
• Streaming responses — chunked encoding, SSE, websockets each need deliberate handling
• Template rendering — if the Perl uses Mojo's templates with embedded Perl, port to Jinja2 with care; the embedded-Perl logic is often where bugs hide
• Holistic coherence — AI is good at file-level translation, mediocre at preserving architectural invariants across the whole codebase. Section-by-section review is essential.
The spec is also a test specification. For each workflow, generate characterisation tests against the existing Perl system before porting. Run the same tests against the Python port. Behavioural equivalence — not code equivalence — is the acceptance criterion.