Skip to content

05 / 05 · 2025.10 — Present

◯◯ Co., Ltd. (SES)

Working in Japan as an SES engineer on legacy-system modernisation: automatic COBOL functional specs with ANTLR4 + RAG + MCP, a people-and-organisation sync tool for a major electronics maker's departmental system, and — as the engineer in charge — a rule-plus-generative-AI CMD → C# conversion platform.

Role
Developer (Python)
Industry
Contract development · legacy modernisation (SES)
Location
Japan
Tenure
1 yr 1 mo

Key results

  • 1,084

    External-command mappings used by the CMD → C# converter (plus 33 built-in commands and 27 AST node types)

  • ~30 sections

    Organisation size covered by the people-and-organisation sync tool (4 departments)

  • 4

    Retrieval tools exposed to the model over MCP to look up the business meaning of COBOL variables on demand

Projects here

PROJECTS · 3

2026.04 – Present · Engineer in charge (architecture design, core implementation, review)

Legacy batch (CMD/JCL) → C# automatic conversion platform

An automatic conversion platform that migrates long-running Windows batch scripts (CMD, Shift-JIS) to C# programs on an in-house batch framework. Rewriting by hand is costly and hard to prove equivalent, so I built a two-stage system — rule-based conversion plus generative-AI completion — guided by one principle: leave a TODO rather than silently change business logic.

  • Two-stage architecture: stage one converts most of each script deterministically (preprocess → Lark parse → AST → rule engine → C# generation); stage two calls an LLM only for CALL :label subroutines the rules cannot express, keeping AI scope and risk minimal
  • CMD grammar modelling: designed and kept extending a ~490-line Lark (LALR) grammar that copes with cmd.exe quirks — redirects anywhere, the ambiguity of < > & |, %VAR:old=new% substitution, many IF/ELSE bracket styles, labels containing Japanese
  • Input-normalisation layer (8 passes): unifies mixed line endings, glued closing brackets, multi-line ELSE, ECHO metacharacters and full-width spaces without changing semantics, so the grammar stays small
  • AST and rule engine: 27 custom AST node types; 33 built-in commands plus a 1,084-entry external-command mapping table (job-process and job-flow call modes); variable expansion handled separately for string, argument-line and integer-expression contexts
  • LLM subroutine conversion with semantic-preservation checks: LabelMapper aligns the .cs TODO, the .cs.ast call node and the .cmd source; structural and business-semantic validators (ERRORLEVEL, cancel branches, error jumps and status flags must survive); on failure the reason is fed back for up to 7 retries, otherwise the TODO is kept for a human
  • Cost and stability: JSON cache keyed by SHA-256 of the normalised label block, exponential back-off on 429/5xx, automatic fallback from Chat Completions to the Responses API, temperature=0 for reproducibility
  • Quality loop: ~465 test cases over the grammar and transpiler layers; batch runs emit .cs.ERR files that are classified into four causes (CMD source issue / grammar gap / code defect / environment), with a shared list of common errors
  • Python
  • Lark (LALR)
  • regex
  • Azure OpenAI
  • C# / .NET
  • pytest
  • Git

System architecture

Two-stage conversion architecture
Legacy CMD scriptsShift-JIS batch files Stage 1 · deterministic transpile pipeline Preprocess8 normalisation passes LALR parseLark ~490-line grammar AST transform27 node types C# generationclass / Main / using Rule engine + variable expansion33 built-in commands + 1,084 mappings Stage-1 output.cs (with TODOs) · .cs.ast · .utf8.cmd Stage 2 · AI completion of CALL :LABEL Three-way alignmentTODO·AST·CMD Cache lookupSHA-256 hash Azure OpenAItemperature 0 Two-layer validationStructure & logic Feedback-driven retryUp to 7 times, otherwise keep the TODO Final output.label.ai.cs (original .cs is never overwritten)

2026.01 – 2026.03(3 mos) · Development, testing, release and hand-over of a Python sync tool (dispatched)

Departmental project-management system — people & organisation auto-sync tool

A software-engineering division of a major electronics maker built its own project-management and status-reporting platform on Redmine (Angular + Go + Redmine). People and organisation data live in an internal staff directory with no usable API, so I built the sync tool: it scrapes members and the org tree with Selenium and syncs them into Redmine accounts, department/section organisations and permission groups (4 departments, ~30 sections).

  • Data-source migration: when the old staff directory was retired, moved the crawler to the new site — new page structure and Microsoft SSO login — while keeping the legacy role markers so downstream sync logic needed no change
  • Selenium (headless Edge) logs in automatically, walks the org tree and looks up each person's unit and role; results are pushed one-way from the intranet PC to an Azure server via paramiko SFTP
  • Sync batch: diffs old and new data, generates and runs SQL for account creation, deactivation, re-hire unlock and department/section/role sync; updates the reporting permission groups through the Redmine REST API, leaving allow-listed members untouched
  • Three kinds of SendGrid alert e-mails; on an unexpected organisation change the job aborts and the SQL runs only after the owner confirms it (two-phase), so bad data never lands in production directly
  • Centralised department mappings scattered in code into a database config table; handled the April reorganisation per the customer's policy and designed/ran test cases covering screens, the application flow and permission groups
  • Diagnosed and fixed production issues: lock mis-detection from duplicate people, missing accounts for re-hires, page time-outs under network jitter, Edge driver mismatch
  • Released to two production environments (intranet PC + Azure VM): backup, deploy, dry-run with DB writes disabled to verify the SQL, then go live; wrote the operations runbook (deployment, alert guide, troubleshooting, org-change SOP) and handed over
  • Ran Fortify / FOSSology security and licence scans over the whole project, fixed high-severity findings such as JSON and SQL injection, and compiled the OSS licence list
  • Python
  • Selenium
  • paramiko
  • MySQL / MariaDB
  • Redmine REST API
  • SendGrid
  • Bash
  • PowerShell
  • Azure VM

System architecture

① Customer intranet · WINDOWS operations PC ② AZURE VM · production server Task Scheduler → executor.ps1 Excel macro → staff allow-list find_users_emails_and_departments Automates Microsoft SSO login Expand org tree · collect e-mails → new_users / new_organization.csv generate_all_users_data Look up each e-mail: unit, role, name → all_users_data.csv transport_csv paramiko SFTP upload into a timestamped directory remove_old_dir Clean up expired CSVs and logs Each step is its own subprocess; a failure stops later steps Staff directory Staff directory (old directory system retired) Org tree Employee search SFTP cron → executor.sh Find the latest data directory of the day SFTP landing directory Dedicated account · batchgroup rights manage_user_batch (11 steps) 1 csv_validator: headers, e-mail format, dangerous characters 2 Org diff → update_organization.sql Unexpected org change → exit code 2 → abort and alert 3 People diff → update_users.sql New members enabled · leavers deactivated · re-hires unlocked 4 Permission-group diff → curl script Reporting groups · department-head group · allow-list group kept 5 Run SQL / scripts → clean old directories Two-phase execution EXEC_UPDATE_USER_SQL_FLAG controls whether to write to the DB directly On org change, generate SQL first; after the owner confirms, run executor_after_confirmed.sh MySQL (Redmine DB) users · email_addresses Department / section · roles · config Redmine REST API Group member add / remove SendGrid 3 kinds of alert e-mails System owner Release manually after SQL is confirmed
Customer intranet · WINDOWS PC AZURE VM · DOCKER COMPOSE Browser users Department members · managers · approvers Apache (Bitnami) HTTPS reverse proxy /home /dashboard_* /project_mgmt → Angular /dashapi → Go API rest → Redmine Angular 18 SPA nginx container · 13 pages Dashboard / reports / charts Applications · approvals · guards Go API (Gin) 40+ REST endpoints · OpenAPI Worker pool fetches issues concurrently Transactions + row locks · approval flow Redmine Issue / Tracker / custom fields Projects · members · groups · permissions Injected custom JS / CSS REST API API Key SendGrid External e-mail API Approval notifications Extension-table reads / writes MySQL (shared with Redmine) Redmine native tables: users · email_addresses · groups_users · issues … Extension tables: departments · sections · divisions · user_* (dept / section × role) application_requests · application_operation_logs · config user_queries (saved searches) · lock_table (concurrency control) Alert e-mails Run generated SQL manage_user_batch (Python · cron) ★ mine ① CSV validation → ② org diff → ③ generate user add / deactivate SQL ④ sync department / section & roles → ⑤ sync reporting permission groups Unexpected org change → exit code 2, abort + e-mail alert Run phase two only after the owner confirms (executor_after_confirmed) SFTP landing directory Dedicated sftpuser · batchgroup cron picks the latest dated folder Staff directory Microsoft SSO login · org tree Simulated login + traversal cross_page_batch ★ mine Selenium + Edge Headless Automates MS account login Expand department / section nodes and collect e-mails Filter by mailing-list allow-list Retry · explicit waits · logging Allow-list comes from an Excel macro tool CSV outputs new_users.csv new_organization.csv transport_csv paramiko SFTP upload Create remote directory by timestamp SFTP

2025.10 – 2025.12(3 mos) · Design, implementation and validation (led individually)

Automatic COBOL functional-spec generation with ANTLR4 + RAG + MCP

The customer's core batch work still runs on COBOL written decades ago: no usable design documents, abbreviated variables such as TR1-HINSHU whose business meaning is scattered across DB design manuals and glossaries, and fewer and fewer people who know COBOL. The goal: feed in COBOL source and get a Japanese functional specification (Markdown) that business staff can read. Design principle — deterministic work (syntax, layout, call graph) is done by programs; the model only gives meaning and writes it up.

  • Model validation and change of course: benchmarked Gemma-3 4B on COBOL comprehension; LoRA fine-tuning on Colab could not finish because of memory limits. Since the business dictionary changes per customer, moved to RAG, which needs no retraining
  • Knowledge base: unified DB design manuals, glossaries and table/file/job-flow definitions into {key, value}, split semantically by file → record → field → group; embedded only the key (multilingual-e5-small) into ChromaDB, stripping OCCURS subscripts before lookup and building aliases and hierarchy paths
  • COBOL parsing: generated a Python lexer/parser/visitor from Cobol85.g4; preprocessing for Shift_JIS→UTF-8, line-number removal and dialect directives such as $SET; turned the parse tree into a compact AST for the LLM; a variable extractor handling REDEFINES / OCCURS / COMP / level-88
  • Call-hierarchy analysis: built a call graph from PERFORM, dropped EXT paragraphs that only serve as exits in RTN/EXT pairs, and used a topological sort to fix a bottom-up analysis order
  • MCP integration: exposed vector search through FastMCP as four tools — lookup_field, lookup_record, batch_lookup_fields, get_subroutine_summary — so the model fetches only the variables it uses instead of hundreds of definitions
  • Generation and prompt tuning: per subroutine, extract input/output/work variables and callees, look definitions up via MCP, pass lower-level summaries up, and have Gemma-3 write Markdown; prompt constraints drop empty items and require business language instead of literal MOVE paraphrases
  • Python
  • ANTLR4
  • ChromaDB
  • SentenceTransformers
  • MCP (FastMCP)
  • Gemma-3 4B
  • Hugging Face Transformers
  • Google Colab
  • WSL

System architecture

Knowledge base + MCP + spec-generation pipeline
① Offline · knowledge-base build (once) Sources DB design manuals · glossary table / file / job-flow defs Semantic split file → record → field → group Unify as {key, value} embed only the key (field ID) Embedding multilingual-e5-small JA / ZH / EN search ChromaDB aliases · no OCCURS index hierarchy paths kept MCP server (FastMCP) lookup_field · lookup_record batch_lookup_fields · get_subroutine_summary vector search exposed as tools ② Online · spec generation (once per program) COBOL source Unisys dialect Preprocess Shift_JIS → UTF-8 line numbers · dialect ANTLR4 parse Cobol85.g4 → AST Call graph + topo sort PERFORM · skip EXT bottom-up Gemma-3 4B per-subroutine generation Functional spec .md (Japanese) fetch only needed definitions Deterministic work (syntax, layout, call graph) is done by programs; the model only gives meaning and writes it up

System data flow

Abstract sketch · no internal details
  1. Legacy CMD scripts

    Shift-JIS · municipal systems

  2. Normalise + Lark parse

    8 passes · ~490-line grammar

    ▲ My part

  3. Rule engine

    33 built-in + 1,084 external

    ▲ My part

  4. C# generation

    .cs · TODO where unsupported

  5. LLM completion + 2-layer check

    CALL :label · up to 7 retries

    ▲ My part

  6. Output

    .label.ai.cs · failures stay TODO

What I learned here

To work in Japan for the long term I moved here in July 2024, studied Japanese at a language school and passed JLPT N2, then joined Japanese development sites as an SES engineer after leaving the school in November 2025.

All three engagements are about making legacy systems easier to understand and migrate. The COBOL project showed me that RAG quality depends on chunk granularity and retrieval-key design; in the sync tool, with an isolated network and no API, one-way push, two-phase execution and alerts protect production data; the CMD → C# converter fixed a principle for me — leave deterministic work to programs, let generative AI handle only the smallest units rules cannot express, validate every output, and keep a TODO rather than silently change business logic.

TECH STACK

  • Python
  • Lark
  • ANTLR4
  • Azure OpenAI
  • MCP
  • ChromaDB
  • Selenium
  • C# / .NET
  • Redmine REST API
  • MySQL / MariaDB
  • Azure VM