Selected cases across payments, apps, roaming, internal AI agents, pricing, membership, promotions, DX, and personal products

Project Portfolio

This page highlights projects such as multi-vendor payments, app/WebView bridge work, roaming reliability, internal AI-agent routing and log diagnosis, pricing APIs, membership migration, point-wallet design, DX automation, and Commit Map as a personal product built from a real planning problem.

Projects

13

Primary Stack

Kotlin Spring Boot MySQL

Focus

Architecture, state transitions, operational resilience

01

Backend development experience centered on Kotlin and Spring Boot

02

Experience integrating microservices with OAuth2, payment gateways, API gateways, and external certificate services

03

Experience connecting U+ VIP partnership coupons, external membership gateways, and operations-console tooling

04

Hybrid app and app-validation tooling experience with Flutter/WebView

05

Operational stabilization of public roaming integrations using events, retries, and monthly resync

06

Kept homepage statistics updated under public traffic by caching prior-month cumulative totals in Redis for 24h, upserting daily deltas into cumulative DB state, and protecting same-day sums with Redis/local cache plus distributed locks

07

Operational stabilization with Pub/Sub DLQ, Athena batches, monthly partitions, and circuit-breaker patterns

08

Chat-based AI agents, LLM routing, tool calling, WebSocket streaming, and production-log diagnosis automation

09

DX improvements using LLM review workflows, Docusaurus, Firebase App Distribution, capture/replay extensions, and Jenkins/TestFlight

Project

01

LG Uplus VoltUp / Oct 2024 - Present

Multi-Vendor Payment System and Arrears Recovery

Owned the service from initial design to implementation

Led the end-to-end architecture and implementation of VoltUp's payment platform, evolving a single-provider system into a multi-provider module featuring DLQ-based recovery and atomic database approval locks to eliminate duplicate charges. Connected stranded READY and AUTHORIZED states to automated recovery and operator-review paths so indeterminate outcomes remain traceable.

Kotlin Spring Boot Spring Batch GCP Pub/Sub Cloud SQL

Design Context

Beyond supporting multiple providers through one extension point, the service needed a final safety boundary that prevents reapproval across Redis-lock expiry, lost payment-gateway responses, database rollbacks, and provider changes, with clear recovery ownership.

Key Point

A project that demonstrates multi-provider extensibility, payment state machines, atomic approval ownership, and recovery boundaries for indeterminate outcomes.

Core Implementation

  • Built a payment-gateway integration architecture using an abstract-class-based provider strategy.
  • Implemented a GCP Pub/Sub dead-letter-queue pattern that isolates failed events and keeps failed-payment recovery traceable.
  • Added database-backed approval ownership that conditionally transitions `payments.status` to `AUTHORIZED` immediately before the payment-gateway call, letting the database atomically claim the single approver for an order.
  • Separated unpaid-balance bookkeeping from payment confirmation so ledger-lock contention cannot roll back an approval, while correcting duplicate point deductions and preventing current settings from being applied retroactively during retries.
  • Added state sync/event republishing for READY orphans and resolution batches/manual repair APIs for AUTHORIZED holds, separating automatic recovery from operator review.

Engineering Lens

  • Centered request orchestration, point hold/confirm, and success/failure transitions around PaymentProcessor so state changes remain traceable in one place.
  • Kept Redis locks as a first-line concurrency control, while making a conditional DB update the final serialization boundary immediately before money moves.
  • Used a fail-closed model: transition to PAID/FAILED only for definitive outcomes and retain AUTHORIZED when uncertain, prioritizing duplicate-charge prevention over automatic availability.
  • Verified with a real-MySQL 8-thread concurrency test that exactly one contender wins approval ownership, locking the conditional UPDATE serialization guarantee into a regression test rather than relying on mocks.

Architecture Snapshot

Mermaid View

Multi-vendor orchestration: lock, hold, and state transitions

Shows request data, lock keys, point holds, READY-state creation, provider approval, and success or failure transitions in one integrated diagram.

flowchart TD
  Req["pay / rePay<br/>order=ORD-240915-001<br/>user=421 method=17 point=2000"] --> Lock["distributed lock<br/>payment-user-process:421"]
  Lock --> Hold["PointUpdater.hold()<br/>wallet -> HOLD 2000P"]
  Hold --> Ready["createWithReady()<br/>payment READY"]
  Ready --> Sub["resolve subscription<br/>methodId=17 or primary"]
  Sub --> Vendor["Payment Provider Router<br/>Kakao Pay / Toss Payments / Kakao T<br/>selected: Kakao T"]
  subgraph PGV["Payment Provider Layer"]
    direction TD
    Vendor --> Keys["read vendor keys<br/>pgPayKey + token"]
    Keys --> Api["vendor client.pay(...)"]
    Api --> Tx["save pgTransactionId<br/>paymentId / tid / paymentKey"]
  end
  Tx --> Result{"approval result"}
  Result -->|success| Done["updateSuccess<br/>payment PAID<br/>point HOLD->CONFIRM"]
  Result -->|fail| Fail["updateFailed<br/>releaseHold(order)"]
  Fail --> Recovery["repair / retry / failover"]
  classDef vendor fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  class Vendor,Keys,Api,Tx vendor;
  style PGV fill:#eef7fb,stroke:#0f4c81,stroke-width:2px,color:#0f172a;

Mermaid View

Multi-vendor system: mandatory contracts and optional extensions

Places `VendorChecker.select()` on top of the `VendorType` extension point, separates contracts required for every vendor from features needed by only some vendors, and uses `@RequiredVendor` plus `VendorRequirementsValidator` to catch missing mandatory implementations at startup.

flowchart TD
  Vendors["VendorType<br/>Kakao Pay / Toss Payments / Kakao T"] --> Select["VendorChecker.select(vendorType)"]
  Select --> Required["Required on all vendors<br/>VendorPaymentProcessor<br/>VendorMethodProcessor"]
  Select --> Partial["Required on some vendors<br/>VendorPaymentOnceProcessor<br/>(KakaoPay only)"]
  Select --> Optional["Optional extensions<br/>RepairService / vendor hooks"]
  Required --> Validate["@RequiredVendor<br/>+ VendorRequirementsValidator"]
  Partial --> Validate
  Validate --> Boot{"startup validation"}
  Boot -->|missing| Error["application start fail"]
  Boot -->|ok| Route["route to concrete impl"]
  classDef core fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef optional fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  classDef error fill:#fff1f2,stroke:#be123c,stroke-width:2px,color:#0f172a;
  class Vendors,Select,Required,Partial,Validate,Route core;
  class Optional optional;
  class Error error;

Operational Outcomes

  • Built a payment architecture that integrates multiple payment providers behind one interface and remains extensible as providers are added.
  • Applied dead-letter-queue handling and state-specific recovery paths for READY and AUTHORIZED payments, making each failure either automatically recoverable or explicitly reviewable by an operator.
  • Blocked paths where post-approval database contention, lost responses, or retries through a different payment provider could lead to duplicate charges.
Project

02

LG Uplus VoltUp / Jul 2025 - Present

Kakao T Account Linking and Payment Method Registration

Unified member identity, linked AuthMethod records, and designed card-registration state around the partner customer token

Designed account linking and payment-method integration between VoltUp and Kakao T, a South Korean ride-hailing and mobility platform. Unified member identities through encrypted identity data and built an end-to-end flow from session creation through payment-method authorization and activation.

Resume Link Points

Customer-Facing Backend and Kakao T Integration

This section expands on the resume entry covering Kakao T integration, vehicle and Plug & Charge flows, certificate reliability, and isolation of external membership and points services.

Kotlin Spring Boot OAuth2 Flyway T Partner API

Reference Views

Preview

Linked sign-in methods: connecting Kakao T to the same member

A screen showing multiple sign-in methods unified under one member before entering the Kakao T payment-registration flow.

VoltUp linked auth-method screen

Linked sign-in methods: connecting Kakao T to the same member

A screen showing multiple sign-in methods unified under one member before entering the Kakao T payment-registration flow.

Payment registration: Kakao T, Kakao Pay, and card options

A bottom sheet that presents Kakao T, Kakao Pay, and standard card registration in one place.

VoltUp payment-method registration bottom sheet

Payment registration: Kakao T, Kakao Pay, and card options

A bottom sheet that presents Kakao T, Kakao Pay, and standard card registration in one place.

Design Context

The system had to link existing VoltUp members with Kakao T accounts without creating duplicate identities, then keep registration sessions and final payment-method state under the same user context.

Key Point

A strong project for explaining both identity unification and payment-method state transitions.

Core Implementation

  • Designed the linking flow that identifies an existing VoltUp member from Kakao T OAuth data and encrypted identity information, then adds the linked authentication method.
  • Built a single mobile-gateway endpoint that uses the current member’s encrypted identity data to create a Kakao T payment-linking session.
  • In payment-service, implemented the state transition that stores the `session_key` in the payment payload and finalizes `pgPayKey` plus the partner customer token during confirm.
  • Added partner-customer-token filters and a composite index, and hardened unlink validation so Kakao T disconnection verifies that the payment method belongs to the current member.
  • Separated the activation endpoint used by app callbacks and added DTO aliases, `@JsonProperty`, and search logs to absorb external schema differences and improve operational traceability.
  • Wrapped external certificate, U+ Membership, and Bluemembers point calls with dedicated pools, timeouts, circuit breakers, and CallGuard so latency cannot consume all request threads, explicitly failing uncertain approval/point outcomes instead of using silent fallbacks.

Engineering Lens

  • Split responsibilities so auth-domain owns external account acquisition, identity-service owns identity resolution and AuthMethod ownership, and payment-service owns payment-method state.
  • Prevented identity mismatches by not starting card registration until account linking is complete, then creating sessions and activate calls only afterward.
  • Grouped the `session_key`, partner customer token, and `pgPayKey` around the same subscription row during the `READY -> ACTIVE` transition so later approve/cancel calls can reuse them.
  • Treated the partner customer token not as a response field but as an operational key for user-to-external-payment consistency, so lookup and unlink validation share the same basis.
  • External-dependency isolation limits concurrency and wait time per integration rather than making failures look successful, leaving compensation or retry decisions to the owning domain when outcomes are uncertain.

Architecture Snapshot

Mermaid View

Card registration after linking VoltUp and Kakao T accounts

Summarizes identity linking, encrypted-CI based account matching, link-session creation, and the READY-state to ACTIVE-state transition in one pass.

flowchart TD
  User["VoltUp member<br/>encrypted CI"] --> OAuth["Kakao T OAuth<br/>external account + encrypted CI"]
  OAuth --> Link["identity-service<br/>linked auth method"]
  Link --> Session["mobile-gateway link session<br/>ACCOUNT + PAYMENT"]
  Session --> Ready["payment-service init<br/>READY transition"]
  Ready --> Active["confirm success<br/>ACTIVE transition"]

Operational Outcomes

  • Established a flow that links an existing VoltUp member to a Kakao T account before card registration begins.
  • Made subscriptions stay in the ACTIVE state after registration so approve, cancel, and lookup operations can reuse the same identity context.
  • Kept member and payment-method matching consistent across app callbacks, web sign-in, and unlink validation.
  • Contained external membership/point API latency so it cannot cascade into a user-side outage, while making indeterminate outcomes explicit to callers.
Project

03

LG Uplus VoltUp / Jul 2025 - Present

Unified Promotion Platform for Coupons and Points Buildout and Enhancement

Extended promotion-service policy, payment-vendor restrictions, and point-wallet structure

In VoltUp `promotion-service`, implemented code issuance, code registration, direct coupon assignment, and expiry reminders based on coupon-pack registration and usage windows. Applied partner-specific payment-method restrictions consistently across issuance, lookup, redemption, and operations-console creation, and designed point wallets with per-accrual expiration and deterministic redemption order.

Kotlin Spring Boot Spring Batch MySQL JPA QueryDSL JDBC Distributed Lock

Design Context

The project had to support partner-specific promotion rules, both code-based issuance and direct coupon assignment, and payment-method restrictions such as Kakao T, standard cards, or Kakao Pay. The points model also had to apply expiration dates to each accrual reliably.

Key Point

A good project for explaining promotion-service issuance, direct assignment, expiry reminders, and point-wallet redemption structure together.

Core Implementation

  • Managed coupons with separate `registerStartAt/registerEndAt` and `usableStartAt/usableEndAt` windows on `couponPack`, separating registration timing from usage timing.
  • Issued code-based coupons through `batchIssue`, converting Snowflake IDs into 10-character base36 codes after SHA-256 hashing, bulk-saving them first and falling back to individual saves on conflicts.
  • For direct assignment without coupon codes, created coupon rows through `mapping(code=null)` or `batchMapping(userIds)`, while code registration linked the user inside a `coupon-mapping:{code}` lock.
  • Added allowed payment methods as a coupon-pack policy, treating an empty value as allowing all methods to preserve compatibility while applying the same restriction across issuance, lookup, and usage.
  • Added allowed-payment-method selection and encoded ID visibility to the operations-console coupon-pack form so operators can verify policy settings at creation time.
  • In `addBulk`, created a new `PointWallet` whenever `expiredAt` exists, while merging into the same `type + chargeType` wallet when it does not, so accrual and expiration units stay aligned.
  • When points are used, paged through active wallets and sorted them by `FREE -> earliest expiredAt`, holding across multiple wallets sequentially and restoring only the wallets that participated in the hold on failure.
  • Handled coupon expiry through an expiry reminder batch that reads a `usableEndAt` window and publishes `EXPIRED` events, while point expiration is reflected by the next-month history batch.

Engineering Lens

  • Split coupon responsibilities so `couponPack` owns timing and discount policy while each `coupon` owns user mapping and usage state, allowing both code-based and code-less issuance within the same model.
  • Protected code registration with `coupon-mapping:{code}` locks, usage with `coupon-process:{userId}` locks, and reinforced both with `unique(code)` plus `unique(userId,couponPackId)` constraints to prevent duplicate registration and duplicate issuance.
  • Handled expiry without adding another online coupon state: the batch reads coupon packs by `usableEndAt` and emits `EXPIRED` events only for unused assigned coupons.
  • Moved payment-method restrictions from UI-only conditions into coupon-pack domain policy, keeping rules created in the operations console consistent across issuance, lookup, and redemption.
  • Split points into `PointWallets` entities instead of one balance, creating new wallets for expiring accruals and merging non-expiring ones into the same `type + chargeType` wallet so expiration rules are visible in the data model.
  • During usage, distributed holds across active wallets in `FREE -> expiredAt asc` order, and made `releaseHold(order)` restore only the actually held `pointWalletId`s so deduction and recovery follow the same sequence.

Architecture Snapshot

Mermaid View

promotion-service: code issuance, direct assignment, and expiry reminders

Shows the actual `promotion-service` flow from coupon-pack based code issuance and code registration to direct assignment without codes and `usableEndAt`-based expiry reminder batches.

flowchart TD
  Pack["couponPack 71<br/>register 10/01~10/31<br/>usable 10/01~11/30"] --> Code["batchIssue(size=1000)<br/>Snowflake -> SHA-256/base36<br/>code=37PRPT85WA"]
  Pack --> Direct["mapping(code=null) / batchMapping<br/>user=421 or [421,422]"]
  Pack --> VendorPolicy["allowed payment methods<br/>partner / card / wallet"]
  Code --> Claim["mapping(user=421, code=37PRPT85WA)<br/>lock coupon-mapping:37PRPT85WA"]
  Claim --> Guard["DB unique guard<br/>code / (userId,couponPackId)"]
  Direct --> Guard
  Guard --> Ready["coupon row<br/>userId=421 status=READY"]
  Ready --> Process["process(price=32000, user=421)<br/>lock coupon-process:421<br/>READY -> PROCESSING"]
  VendorPolicy --> Process
  Process --> Finish["complete -> COMPLETE<br/>rollback -> READY"]
  Pack --> Expire["expiry reminder batch<br/>usableEndAt D+3 window"]
  Expire --> Scan["getAllByCouponPackId<br/>completeAt is null"]
  Scan --> Event["publish promotion event<br/>eventType=EXPIRED"]

Mermaid View

Point wallets: partner accrual and expiration-ordered deduction

Separates point handling from coupon flow and shows, with concrete example data, how partner points split into wallets and move through active-wallet scan, ordering, hold, and confirm/release.

flowchart TD
  Grant["addBulk / partner accrual<br/>BASE 1200P exp 10-18<br/>TOYOTA 3000P exp 10-20<br/>BLUEMEMBERS 800P exp 10-22<br/>NEXEN 5000P exp 10-31<br/>EVENT 700P exp 11-15<br/>BASE 900P exp null"] --> Rule["wallet rule<br/>new wallet if expiredAt exists<br/>merge by same type+chargeType if null"]
  Rule --> Wallets["wallet #11 BASE/FREE 1200 exp 10-18<br/>wallet #12 TOYOTA/FREE 3000 exp 10-20<br/>wallet #13 BLUEMEMBERS/FREE 800 exp 10-22<br/>wallet #14 NEXEN/CHARGE 5000 exp 10-31<br/>wallet #15 EVENT/FREE 700 exp 11-15<br/>wallet #16 BASE/FREE 900 exp null"]
  Wallets --> Active["active wallet scan<br/>expiredAt > now only<br/>page query by createdAt asc"]
  Active --> Order["deduction order<br/>FREE first<br/>then expiredAt asc"]
  Order --> Hold["hold 4500P<br/>#11 -1200<br/>#12 -3000<br/>#13 -300"]
  Hold --> Usage["point_usage rows<br/>order=ORD-240915-001<br/>walletId=11,12,13<br/>status=HOLD"]
  Usage --> Result{"payment result"}
  Result -->|success| Confirm["confirm<br/>HOLD -> CONFIRM<br/>wallet amount final"]
  Result -->|fail| Release["releaseHold(order)<br/>wallet 11 +1200<br/>wallet 12 +3000<br/>wallet 13 +300<br/>usage -> FAILED"]
  Wallets --> Expire["expiry handling<br/>expired wallet excluded from active<br/>expiringSoon queried separately"]
  classDef wallet fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef state fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Wallets,Active,Order,Hold,Usage wallet;
  class Confirm,Release,Expire state;

Operational Outcomes

  • Organized code issuance, code registration, direct assignment without codes, and expiry reminder batches under the same `promotion-service` model.
  • Absorbed partner-promotion payment-vendor requirements into coupon-pack policy, reducing the chance that discount policy drifts from payment and settlement conditions.
  • Kept point wallets, expiration-order redemption, and next-month history batching in the same backend so coupons and points can be explained together as one promotion platform.
Project

04

LG Uplus VoltUp / Dec 2024 - Present

VoltUp Hybrid App: WebView Bridge and Native Features

Flutter hybrid launch, JSBridge, and QR/permission/push/forced-update flows

Built the Flutter-based Android/iOS hybrid app for the VoltUp 2.0 launch and designed JSBridge plus core app flows so WebView surfaces can call native capabilities reliably. Continued improving production quality through QR scanning, camera permission, FCM, forced-update handling, and Crashlytics-driven stabilization.

Flutter Dart Kotlin Swift WebView JSBridge ML Kit FCM Crashlytics

Design Context

The app had to ship quickly on Android and iOS while keeping service screens flexible through WebView. At the same time, app-only capabilities such as QR scanning, camera permission, new-window handling, push notifications, and forced updates needed reliable native support.

Key Point

A strong project for explaining rapid user-app delivery together with WebView-native bridge design and app-specific flows such as QR, permissions, push, and updates.

Core Implementation

  • Defined the Flutter hybrid structure and designed the boundary between WebView screens and native capability calls for a two-month Android/iOS launch.
  • Implemented the JSBridge contract that lets the frontend call native features such as new-window handling, external URLs, QR scanning, camera permission, app messages, and forced updates.
  • Built a custom ML Kit-based QR scanner page with responsive scan UI to control the QR recognition experience and reduce dependency on the previous scanner package.
  • Connected Crashlytics for Dart/native error collection and stabilized camera and FCM flows.

Engineering Lens

  • In the hybrid app, WebView owns fast surface iteration while the native layer owns OS permissions and hardware capabilities. I treated JSBridge as the product contract between them and organized callable frontend features into explicit message flows.
  • Because QR scanning and camera permission are key entry points for starting a charge, I focused on controlling device layout, lifecycle, and permission states inside the app UX rather than just wrapping a scanner package.
  • Production stability improvements were driven by Crashlytics signals. I grouped camera exceptions and repeated FCM calls that affected user entry flows, improving reliability around those paths.

Architecture Snapshot

Mermaid View

App bridge between WebView surfaces and native features

Shows how WebView surfaces call native features such as new-window handling, QR scanning, camera permission, FCM, and forced updates through JSBridge, then feed stability improvements through Crashlytics.

flowchart TD
  Web["VoltUp WebView surface"] --> Bridge["JSBridge contract<br/>frontend -> native"]
  Bridge --> Window["new-window / external URL handling"]
  Bridge --> QR["QR scanning<br/>ML Kit custom scanner"]
  Bridge --> Camera["camera permission / lifecycle"]
  Bridge --> Push["FCM push token"]
  Bridge --> Version["forced update / version branch"]
  QR --> Native["Android / iOS native layer"]
  Camera --> Native
  Push --> Native
  Version --> Native
  Native --> Observe["Crashlytics<br/>Dart + native error tracking"]
  Observe --> Fix["camera / FCM stabilization"]
  classDef web fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef native fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef ops fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Web,Bridge web;
  class Window,QR,Camera,Push,Version,Native native;
  class Observe,Fix ops;

Operational Outcomes

  • Launched the 2.0 app quickly across Android and iOS and established an operational contract for WebView surfaces to call native features.
  • Stabilized core app-owned capabilities such as QR scanning, camera permission, push notifications, and forced-update handling in the native layer.
  • Used Crashlytics to track and fix production crashes, continuously improving stability around core app entry points.
Project

05

LG Uplus VoltUp / May 2026 - Present

VoltUp App Validation and Ops Correction Extension

Browser-based app validation, API capture and replay, and targeted operational corrections

Reduced validation time by reproducing app-dependent flows—new-window handling, QR scanning, camera permissions, and forced-update branches—in a browser extension. The same capture-and-replay foundation later supported targeted operational corrections that were unavailable in the operations console.

TypeScript Chrome Extension API Replay WebView Debugging

Design Context

Validating even simple APIs or WebView-to-app bridge behavior required a full app setup. Operations teams also encountered narrow correction cases, such as fixing a charging-zone record, that the operations console did not support even though a single API call could resolve them.

Key Point

A tooling project for speeding up app development and operations response rather than an app feature itself. It is useful for showing a working style of spotting bottlenecks and turning them into small internal tools.

Core Implementation

  • Built Chrome Extension flows that can adjust or recreate app-provided behaviors such as new windows, QR scanning, camera permission, and forced-update version conditions.
  • Implemented API capture plus row-based replay so repeated QA and API-flow checks can be performed quickly without attaching the app.
  • Added variable templates, row parsing, and an executor so targeted corrections unavailable in the operations console can run through Bulk Replay.

Engineering Lens

  • The tool began by removing app-setup delays from development validation, then expanded when the same capture-and-replay model proved useful for operational corrections.
  • After handling a charge-zone creation issue with a hand-written JS `fetch` script, I turned that pattern into a row-based execution tool the team can reuse instead of writing one-off scripts every time.
  • Because app/admin hosts coexist, I separated host-specific UI to avoid exposing the wrong operation in the wrong context.

Architecture Snapshot

Mermaid View

From app-validation bottlenecks to ops-correction replay

Shows how the extension recreates app-dependent flows without connecting a mobile app, then converts captured API requests into row-based replay for development QA and targeted operational corrections.

flowchart TD
  Pain["full app setup for every check<br/>new window / QR / camera / version"] --> Extension["Chrome Extension<br/>app-like controls"]
  Extension --> Sim["reproduce app-dependent flows in browser"]
  Extension --> Capture["API request capture"]
  Capture --> Template["row parser<br/>variable template"]
  Template --> Replay["Bulk Replay executor"]
  Replay --> QA["faster repeatable QA"]
  Replay --> Ops["targeted API corrections<br/>outside the operations console"]
  Ops --> Share["one-off JS fetch -> reusable team tool"]
  classDef pain fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef tool fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Pain pain;
  class Extension,Sim,Capture,Template,Replay tool;
  class QA,Ops,Share result;

Operational Outcomes

  • Reduced waiting and repeated interactions by making app-dependent flows quickly verifiable from the browser without the app.
  • Turned targeted single-API corrections outside the operations console from one-off scripts into a repeatable internal workflow.
  • Became a concrete example of spotting bottlenecks and sharing small tools that improve real app development and operations workflows.
Project

06

LG Uplus VoltUp / Jun 2026 - Present

Voltbot: Internal Work-Agent Platform and Log Diagnosis Automation

Designed and implemented the chat-based work-agent platform, automatic specialist-agent routing, and log-diagnosis agent

Voltbot is an internal work platform where employees use specialist agents for code policy, BigQuery lookup, legal support, operations, and log diagnosis through chat. It routes by intent and permissions, then uses native function calling and parallel tools to accumulate log, data, and code evidence in shared context. An evaluation harness compared the new loop with the legacy path and verified groundedness and cost improvements.

Kotlin Spring Boot React TypeScript WebSocket GCP Cloud Logging LLM GitHub API Google OAuth MySQL Multi-Agent

Product Preview

Internal work agents in a chat surface

The Agent Router classifies the request first, then the right specialized agent traverses logs, guides, and code.

Role-based access PII masking Token/cost display Session sharing
VOLTBOT
Customer 224514 failed payment yesterday. Can you find the cause?

Agent Router

Auto-routed

Payment failure / evidence request → Log Diagnosis Agent

Permission check · agent availability · session context handoff

Execution Trail

  1. 1 Classify intent and route agent
  2. 2 Search production logs
  3. 3 Read diagnosis guides
  4. 4 Find code evidence
  5. 5 Build linked flow

Diagnosis

The failure is likely a temporary rejection from an external PG during card or bank maintenance.

Evidence Logs

  • svc=payment-service-worker | code=4902
  • order_number=260706170149MSGD
  • Re-query by traceId, user_id, then order_number

Recommended Action

  • Ask the customer to retry after maintenance
  • Check PG schedule if failures continue
  • Mask natural-person data at display time
Usage 61.2K tokens · Context 23.6K · Estimated cost $0.09

Design Context

When customer payment or charging failures arrived, operators could not easily inspect error codes, cross-service correlation keys, or whether a case was expected policy behavior without asking engineers for manual triage. The internal AI tool also needed to expose multiple agents safely through one chat surface and reduce the decision cost of choosing between code policy, log diagnosis, and data analysis.

Key Point

This project groups my contributions inside the single Voltbot service: automatic specialist-agent routing and shared-context operations diagnosis. It is less about replacing internal work with AI and more about turning developer-dependent triage into a productized work tool with permissions, evidence, guides, and code/data exploration.

Core Implementation

  • Designed and implemented `Voltbot Crew` as a single user-facing entry point, where `AgentRouter` automatically assigns a specialist such as log diagnosis, data analysis, legal support, or code policy based on the request, conversation context, agent descriptions, and user permissions.
  • Connected `AgentRunner`, `AgentRouter`, and `ToolHandler` on top of the shared `Agent`/`Tool` contract so sessions, context, permissions, quotas, interruptions, tool calls, and tool results flow through WebSocket responses.
  • Replaced regex-parsed text-tag calls with Gemini native function calling, redesigning the loop to run independent tools in parallel while keeping approval and user-question tools sequential.
  • Added customer/operations modes plus pivots across traceId, user_id, order_number, time windows, and report signatures, enforcing a hypothesis→trace→query→verdict loop to prevent anchoring and unverified conclusions.
  • Implemented `searchUserLogs` for GCP production logs using `services`, `severity`, `excludeIstio`, `range/from/to`, `pageToken`, and raw LQL, with retries for widening ranges or strengthening queries.
  • Connected guide upload/search/edit screens with `listLogDiagnosisGuides` and `readLogDiagnosisGuide`, so operational knowledge can be consulted directly at runtime.
  • Designed the code-policy agent to add expected conditions, state transitions, and error codes; the BigQuery agent to add aggregate, history, and pattern evidence; and the log-diagnosis agent to add runtime logs into the same context. The next agent can then continue from prior evidence to narrow services, time ranges, and correlation keys before classifying a first-pass cause.
  • Added correlation-key pivot rules across traceId, user_id, and order_number, plus answer rules that render a Mermaid sequence diagram when a case spans multiple services.
  • Defined a display policy that masks natural-person identifiers in quoted logs and diagnosis text while keeping system identifiers such as user_id, order_number, and traceId traceable.

Engineering Lens

  • The Agent Router was designed as more than a menu shortcut: it lets internal users describe the problem they want to solve instead of knowing which agent to choose. Clear intent is routed automatically, while ambiguous cases still allow explicit agent selection.
  • `Voltbot Crew` acts as a router rather than a direct answering agent. The selected specialist still owns the actual response, keeping existing tool permissions and system-prompt boundaries intact while simplifying the entry point.
  • The important part was not just a chatbot that answers well, but an agent that calls work tools and leaves evidence. Tool trails, evidence logs, and guide/code basis therefore remain visible before the final answer is trusted.
  • Log diagnosis cannot rely on a single key across the whole system, so I paired traceId with user_id and order_number views. The key point is keeping the causal chain traceable across generic services such as `payment-service`, `order-service`, and `mobile-gateway`, even when traces break at async worker boundaries.
  • When multiple agents share one chat surface, operational states such as permissions, developing status, pending approval, and usage need to be part of the product UI.
  • For customer-issue diagnosis, the system selects the relevant code-policy, log, and BigQuery agents, then compares their evidence in shared context to distinguish expected policy blocks, external API or payment-gateway failures, and internal state mismatches.

Architecture Snapshot

Mermaid View

Automatic specialist-agent routing: question-based selection and shared context

Shows how `Voltbot Crew` receives customer context, selects the needed work among authorized agents, accumulates code-policy, log, and BigQuery findings into shared context, and continues through next-agent decisions and first-pass triage.

flowchart TD
  Voc["Customer issue<br/>context / time range / identifiers"] --> Crew["automatic specialist-agent routing<br/>intent-based handoff"]
  Crew --> Auth["candidate agents<br/>limited by user permission"]
  Auth --> Policy["Code-policy agent<br/>expected behavior / exception rules"]
  Auth --> Log["Log agent<br/>trace / order / user search"]
  Auth --> Data["BigQuery agent<br/>aggregation / history / pattern checks"]
  Policy --> Shared["shared context<br/>policy / logs / data results"]
  Log --> Shared
  Data --> Shared
  Shared --> Next["decide next needed agent<br/>carry context forward"]
  Next --> Policy
  Next --> Log
  Next --> Data
  Shared --> Triage{"first-pass triage"}
  Triage --> Expected["expected policy block"]
  Triage --> External["external API / payment-gateway failure"]
  Triage --> Internal["internal state mismatch"]
  Triage --> Reply["operations response draft<br/>shorter developer wait"]
  classDef ops fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef ai fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Voc,Crew,Auth ops;
  class Policy,Log,Data,Shared,Next ai;
  class Triage,Expected,External,Internal,Reply result;

Mermaid View

Voltbot platform structure for chat-based multi-agent work

Shows how a chat request passes through permissions and agent selection, then AgentRunner and ToolHandler execute log, guide, and code tools before streaming tool trails and the final answer over WebSocket.

flowchart TD
  User["Internal users<br/>CS / operations / engineers"] --> Chat["Voltbot Web Chat<br/>sessions / attachments / sharing"]
  Chat --> Auth["Google OAuth + permissions<br/>role/user based agent access"]
  Auth --> Router["Intent-based AgentRouter<br/>classify request / choose agent"]
  Router --> Select["AgentSelector<br/>manual choice / routing result"]
  Select --> Runner["AgentRunner<br/>context / quota / interruption"]
  Runner --> Tools["ToolHandler<br/>tool_call / approval / answer"]
  Tools --> Log["GCP production-log search"]
  Tools --> Guide["log-diagnosis guides"]
  Tools --> Code["GitHub code evidence"]
  Runner --> Stream["WebSocket streaming<br/>tool trail + final answer"]
  Stream --> Ops["diagnosis / evidence / actions"]
  classDef user fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef core fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef tool fill:#eef7fb,stroke:#3b556b,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class User user;
  class Chat,Auth,Router,Select,Runner,Stream core;
  class Tools,Log,Guide,Code tool;
  class Ops result;

Mermaid View

Agent execution loop: routing, tool calls, and next-action decisions

Shows the runtime loop where AgentRouter chooses a specialist, AgentRunner lets the LLM decide the next action, and ToolHandler results are appended back into context until the final answer is ready.

flowchart TD
  Input["User message<br/>question / file / session context"] --> Guard["session, permission,<br/>and quota checks"]
  Guard --> Route{"AgentRouter<br/>auto routing needed?"}
  Route -->|auto| Pick["classify intent<br/>choose authorized agent candidate"]
  Route -->|manual| Selected["selected specialist agent"]
  Pick --> Selected
  Selected --> Runner["AgentRunner<br/>system prompt + history + context"]
  Runner --> Turn{"LLM turn<br/>decide next action"}
  Turn -->|tool_call| ToolHandler["ToolHandler<br/>schema validation / approval check"]
  ToolHandler --> Approval{"user approval needed?"}
  Approval -->|yes| Wait["wait for approval<br/>stream status over WebSocket"]
  Wait -->|approved| Execute["execute tool<br/>logs / guides / code / files"]
  Approval -->|no| Execute
  Execute --> Append["append tool result<br/>to conversation context"]
  Append --> Budget{"context / token limit?"}
  Budget -->|compress| Summary["create compressed context"]
  Summary --> Runner
  Budget -->|continue| Runner
  Turn -->|ask_user| Clarify["ask follow-up<br/>request missing info"]
  Clarify --> Input
  Turn -->|final_answer| Answer["final answer<br/>diagnosis / evidence / action"]
  Answer --> Stream["WebSocket streaming<br/>tool trail + cost + answer"]
  Guard --> Block["interrupted / unauthorized / quota exceeded"]
  classDef input fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef core fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef loop fill:#eef7fb,stroke:#3b556b,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  classDef stop fill:#fff1f2,stroke:#be123c,stroke-width:2px,color:#0f172a;
  class Input input;
  class Guard,Route,Pick,Selected,Runner core;
  class Turn,ToolHandler,Approval,Wait,Execute,Append,Budget,Summary,Clarify loop;
  class Answer,Stream result;
  class Block stop;

Mermaid View

Log diagnosis agent: joining logs, guides, and code evidence

Splits customer and operations modes, pivots across traceId/user_id/order_number, then combines logs with guide and code evidence to produce diagnosis and recommended actions.

flowchart TD
  Ask["Operations question<br/>payment failure / service error"] --> Mode{"customer mode<br/>or operations mode"}
  Mode -->|userId exists| Timeline["user_id timeline search"]
  Mode -->|pattern search| Pattern["service/error-pattern search"]
  Timeline --> Signal["extract error codes, exceptions, correlation keys"]
  Pattern --> Signal
  Signal --> Pivot["multi-angle pivot<br/>traceId / user_id / order_number"]
  Pivot --> Flow["cross-service flow<br/>Mermaid sequenceDiagram"]
  Signal --> Guide["compare diagnosis guides"]
  Signal --> Github["check GitHub code evidence"]
  Flow --> Answer["final answer<br/>diagnosis / evidence logs / actions"]
  Guide --> Answer
  Github --> Answer
  Answer --> Mask["PII masking<br/>keep system identifiers"]
  classDef ask fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef search fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef evidence fill:#eef7fb,stroke:#3b556b,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Ask,Mode ask;
  class Timeline,Pattern,Signal,Pivot search;
  class Flow,Guide,Github evidence;
  class Answer,Mask result;

Operational Outcomes

  • Across 9 evaluation cases run 3 times each, preserved statistically equivalent answer quality while improving average groundedness from 87.7% to 100%, cutting token/cost by 46%, and reducing runtime by 10%.
  • Lowered the entry barrier for the internal AI tool by letting users reach the right work agent from the question itself, even when they do not know the available agent types in advance.
  • Created a workflow where operations and CS can ask about customer payment failures, service errors, or incident patterns in chat and receive evidence logs plus recommended actions.
  • Turned manual triage across Cloud Logging, internal guides, and GitHub code search into an agent tool flow, creating a foundation for reducing repeated developer-dependent diagnosis work.
  • Organized specialized agents such as code policy, data analysis, legal, operations, and log diagnosis into a permission-aware multi-agent platform.
  • Validated the path from a team-agent prototype to the final `AUTO_ROUTING` model and shaped multi-agent collaboration into a product fit for the single Voltbot work platform.
Project

07

Kakao Style / Dec 2023 - Sep 2024

Pricing Platform: Product Matching, Dynamic Pricing, Final Pricing, and Catalog Pipelines

Built four core pricing capabilities across PIM and Promotion services

Built four core capabilities across the Product Information Management (PIM) and Promotion services: external-to-internal product matching, dynamic pricing, the Final Pricing API, and Catalog Engine Page pipelines for Naver and YouTube Shopping. PIM combines promotion-adjusted prices with external market prices to select the best price shown to customers.

Resume Link Points

External-to-Internal Product Matching

Builds stable comparison groups from image similarity, exact matches within the same shop, and canonical-product scores.

Resume Link Points

Dynamic Pricing

Scores competitive position from external lowest-price data and internal catalog data, then feeds those signals into pricing adjustments and reporting.

Resume Link Points

Final Pricing API

Standardizes membership, coupon, promotion, and shipping adjustments behind one pricing contract.

Resume Link Points

Catalog Engine Page and Lowest-Price Update Pipelines

Generates Engine Page outputs, shopping-feed CSVs, and synchronization datasets from changed products only for Naver and YouTube Shopping.

Kotlin Spring Boot AWS Athena

Design Context

External market prices, internal optimization signals, and membership or coupon benefits were spread across multiple services while pricing rules changed frequently. The platform needed clear ownership boundaries between PIM and Promotion while still producing one consistent customer-facing price.

Key Point

Demonstrates how product matching, dynamic pricing, Final Pricing, and catalog pipelines work together to produce a consistent customer-facing price.

Core Implementation

  • Used versioned matching caches in PIM to resolve `productId -> matchingId`, then combined exact same-shop matches with canonical-product scores to build stable comparison groups.
  • Ran dynamic-pricing batches that loaded eligible products from Athena, rebuilt external-to-internal comparison sets, and updated price scores using rules such as `SUPERIOR / EQUAL = 100` and `UNKNOWN = 50`.
  • Built a shared Catalog Engine Page pipeline that consumes product and price-update events, filters changed items, and generates Engine Page outputs, shopping-feed CSVs, and synchronization datasets.
  • Separated membership eligibility from `product / item / order` Final Pricing APIs, then composed shipping fees through `MappedBatchLoader` into the final customer price.

Engineering Lens

  • Defined clear ownership boundaries: PIM handles external-to-internal product matching, dynamic pricing, and catalog pipelines, while Promotion owns membership and Final Pricing. PIM then compares promotion-adjusted prices with external market prices to select the customer-facing price.
  • Stabilized comparable product groups through versioned caches and exact same-shop matching, while exposing canonical-product scores for operational review.
  • Replaced full-catalog scans with an event-driven pipeline that processes only changed products and generates Engine Page outputs, the Naver Shopping feed CSV, and synchronization datasets. This met the two-hour CPS refresh interval and made the same pipeline reusable for Google Engine Page and YouTube Shopping.
  • Separated Final Pricing by `product / item / order` boundary and combined membership, coupons, promotions, and shipping in one response while controlling shipping-query cost through DataLoader.

Architecture Snapshot

Mermaid View

Pricing flow from the product-management system to the promotion service

Shows how PIM owns external-to-internal matching, dynamic pricing, and Catalog Engine Page pipelines while Promotion owns membership and Final Pricing, and how both services contribute to the customer-facing price.

flowchart TD
  Req["request<br/>product=421 user=3001 site=KR"] --> Match
  Req --> Member
  subgraph PIMSYS["Product Management System (PIM)"]
    direction TD
    Match["External-to-internal product matching<br/>matchingId / same-shop / winner score"]
    Optimize["Dynamic pricing<br/>price score / compare set"]
    Catalog["Catalog Engine Page pipelines<br/>Naver / YouTube feed sync"]
    External["External product values<br/>lowest price / sync dataset"]
    Match --> Optimize --> Catalog --> External
  end
  subgraph PROMO["Promotion Service"]
    direction TD
    Member["Membership<br/>grade / eligibility"]
    Final["Final Pricing API<br/>coupon / promotion / shipping"]
    Member --> Final
  end
  External --> Expose
  Final --> Expose
  Expose["Customer-facing price selection<br/>promotion-adjusted + external price"] --> Resp["response<br/>best available price"]
  classDef pim fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef promo fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef expose fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Match,Optimize,Catalog,External pim;
  class Member,Final promo;
  class Expose,Resp expose;

Operational Outcomes

  • Enabled PIM to compare Promotion Final Pricing results with external market prices and select the best price shown to customers.
  • Replaced a roughly six-hour full-catalog refresh with an event-driven changed-product path that generates the Naver Shopping feed CSV and synchronization dataset within an hour.
  • Standardized the Engine Page and lowest-price update pipeline built for Naver Shopping so Google Engine Page and YouTube Shopping could reuse the same foundation.
  • Standardized Final Pricing responses across product, item, and order boundaries so customer-facing services and operational batches can reuse one contract for membership, coupon, and promotion-adjusted prices.
  • Allowed pricing policies inside PIM to change without breaking the customer-facing response contract owned by Promotion.
Project

08

Kakao Style / Apr 2023 - Jun 2023

Membership & Mileage Migration

Migrated the legacy service to Spring Boot through API-response parity verification

Redesigned membership tiers for retention and migrated the legacy Node.js service to Spring Boot without downtime while preserving its database contract. I captured real request-and-response pairs as regression cases, replayed them through the Spring implementation, compared outputs, and gradually shifted traffic at the gateway. I also rebuilt monthly tier calculations around partitioned Athena data.

Kotlin Spring Boot Spring Batch DGS Framework(GraphQL) JPA MySQL Kafka AWS Athena

Reference Views

Preview

Zigzag membership: tier-benefit screen

A reference view showing how the expanded membership-tier system and tier benefits were exposed in the actual user-facing UI.

Zigzag membership benefit screen

Zigzag membership: tier-benefit screen

A reference view showing how the expanded membership-tier system and tier benefits were exposed in the actual user-facing UI.

Design Context

The migration had to preserve the legacy database contract and user-facing responses while preventing monthly tier jobs from scanning an ever-growing history as users and months accumulated.

Key Point

A strong project for explaining zero-downtime migration through legacy-response parity checks together with Athena-based membership-batch optimization.

Core Implementation

  • Collected real request/response sets from the legacy membership API, organized them into test cases across query, body, and edge conditions, and replayed the same inputs through the Spring Boot implementation to compare output diffs.
  • Expanded the membership tier calculation period from 3 to 6 months.
  • After passing response-parity verification, gradually switched the gateway so the Spring Boot service could be opened without breaking user responses.
  • Queried a monthly payment partition in Athena by target date, paged results with a continuation token, and converted them into monthly payment snapshots as batch input.
  • Calculated tiers from the six-month confirmed cumulative amount, batch-upserted the result, and bounded reads to the needed period with a recent-month set plus monthly range partitioning.

Engineering Lens

  • Treated zero-downtime migration as a response-parity problem first, turning real legacy API request/response sets into reusable test assets that were repeatedly replayed against the Spring implementation.
  • Built the monthly batch to read only the required `stamp_date` from a partitioned Athena source and page through results with `queryExecutionId + nextToken`, avoiding a full in-memory load for large target sets.
  • Stored both six-month confirmed totals and current-month-inclusive totals in a monthly payment snapshot, then persisted them as cumulative fields for tier calculation so later reads could rely on the model directly.
  • Grouped writes into JDBC batch insert/update, limited reads to recent month sets, and managed history data with monthly range partitions so only the needed months are touched as data grows.

Architecture Snapshot

Mermaid View

Zero-downtime migration through legacy API response-parity checks

Shows the zero-downtime migration flow that collects request/response sets from the legacy membership API, replays the same inputs through the Spring Boot implementation, compares output diffs, and then gradually switches the gateway.

flowchart TD
  Legacy["legacy membership API<br/>request / response set capture"] --> Cases["test case conversion<br/>query / body / edge case"]
  Cases --> Replay["replay the same input<br/>into Spring Boot logic"]
  Replay --> Compare{"same as legacy response?"}
  Compare -->|yes| Ready["ready for rollout"]
  Compare -->|no| Fix["fix logic / serializer diff"]
  Fix --> Replay
  Ready --> Switch["gradual gateway switch"]
  Switch --> Open["zero-downtime release"]

Mermaid View

Membership batch with monthly cumulative sums and monthly partitioning

Shows the flow from an Athena monthly payment partition by target date, through monthly payment snapshots and membership batch upserts, into recent-month-scoped lookups and monthly history partitioning.

flowchart TD
  Source["Athena source<br/>monthly paid partition<br/>target date=2023-10-08"] --> Reader["paged query<br/>continuation token"]
  Reader --> Paid["monthly payment snapshot<br/>user=421 confirmed=330000<br/>predicted=350000"]
  Paid --> Calc["level calc<br/>latest 6-month cumulative sum"]
  Calc --> Upsert["membership upsert<br/>dateAppliedYm=202310"]
  Upsert --> Current["current membership data<br/>batch insert / update"]
  Upsert --> Archive["monthly history partition<br/>RANGE(applied month)"]
  Current --> Query["recent Ym lookup<br/>202310, 202309, 202308"]
  Archive --> Query

Operational Outcomes

  • Reduced DB load from a 70% threshold to under 30% through monthly cumulative sums and monthly partitioning.
  • Achieved zero-downtime deployment by passing request/response-set-based parity tests between the legacy API and the Spring implementation, then gradually switching the gateway.
  • Combined a partitioned Athena source, paged reader, JDBC batch upsert, and recent-month-scoped lookups to keep monthly tier-calculation performance stable as customer data accumulated.
Project

09

Personal Project / Ongoing

Commit Map

Designed an authoring flow that turns natural-language travel routes into structured map content

A map-based travel planning service built to share personal itineraries with friends. Natural-language destinations and routes are turned into a rough itinerary draft through an AI workflow, then refined manually in Markdown.

Astro React Leaflet TypeScript Markdown GitHub Pages Antigravity

Reference Views

Preview

Home screen: world map and trip cards

A reference view showing the country filters, world map, and trip-planning cards in one continuous screen.

Reference image of the Commit Map home screen

Home screen: world map and trip cards

Open service

A reference view showing the country filters, world map, and trip-planning cards in one continuous screen.

Design Context

Travel plans tend to scatter across chat messages, map links, and notes, making them hard to share and revise, while manually structuring locations and coordinates from scratch also costs too much effort.

Key Point

Although it started as a hobby project, it became a real product example of natural-language input flowing into a structured draft that a human refines further.

Core Implementation

  • Built a static web service with Astro, React, and Leaflet that combines travel cards, detailed maps, and timelines, visualizing routes by place type, order, and visit date.
  • Managed trip posts through Markdown frontmatter and a location schema so dates, coordinates, notes, and links remain structured and easy to refine manually later.
  • Added an AI workflow so natural-language destinations and routes can quickly produce a draft post, candidate places, and a starter itinerary, after which I refine the plan and content manually.
  • Deployed it statically on GitHub Pages so travel plans can be shared instantly, with each trip post addressable by its own URL.

Engineering Lens

  • Treated AI not as the final planner but as a draft generator for the first route pass, while keeping the final plan data in Markdown.
  • Designed the content model and visualizations together so maps, timelines, and location data reinforce the itinerary instead of appearing as disconnected additions to a text-only post.
  • Kept operating cost low with static deployment and file-based content so the personal project stays lightweight and can grow only where needed.

Operational Outcomes

  • Operates as a personal service where itineraries can be shared through a single link instead of scattered messages.
  • Built an authoring flow where just listing destinations and routes is enough for the AI workflow to produce a starter plan that can later be refined in detail.
  • Unified maps, timelines, and location metadata under one content model so both past trips and future plans can be operated in the same way.
Project

10

LG Uplus VoltUp / Jul 2025 - Present

Vehicle Registration, Vehicle Identifier Safe Mapping, and Plug & Charge Authorization

Designed vehicle information lookup hardening, vehicle reference tree, and safe vehicle identifier mapping

Normalized vehicle information lookup results into a `brand > category > model` reference tree used across registration, selection UI, warning display, and targeted notifications, then safely connected vehicle info and vehicle identifiers into the same user-vehicle context even when they arrive independently.

Kotlin Spring Boot JPA Redis 차량정보 조회 API

Reference Views

Preview

Vehicle management: registered car and Plug & Charge entry

A user-facing screen that leads from vehicle registration into Plug & Charge, showing how vehicle context and charging authorization meet in the product flow.

VoltUp vehicle registration and Plug & Charge screen

Vehicle management: registered car and Plug & Charge entry

A user-facing screen that leads from vehicle registration into Plug & Charge, showing how vehicle context and charging authorization meet in the product flow.

Design Context

Because external plate-number/owner-name lookup results, user-selected vehicle models, vehicle identifiers, and brand/category/model-level warning notices arrive through different paths, the system needed a consistent vehicle reference model plus conservative deduplication and auto-linking rules.

Key Point

A good project for explaining how external vehicle information lookup results become internal reference data, then when vehicle info and vehicle identifiers should auto-link or fall back to manual choice.

Core Implementation

  • Mapped vehicle information lookup values such as brand name, vehicle category, model name, release year, fuel type, and representative image into internal vehicle-registration data, while storing the original response with sensitive owner-name values masked as trace metadata.
  • Hardened vehicle information lookup calls with forced token refresh on token expiry, active-certificate iteration, and fallback-result-code filtering so external lookup failures do not easily block the full vehicle-registration flow.
  • Promoted vehicle information lookup results into a `brand > category > model` reference tree, automatically filling missing nodes at registration time so vehicle selection, user-vehicle registration, and brand/category/model warning notifications share the same reference.
  • Applied a bidirectional auto-mapping rule that checks whether exactly one unmatched counterpart exists from both the vehicle-info and PnC (Plug & Charge) registration sides.
  • Stored vehicle info around the plate number and PnC vehicles around the vehicle identifier, keeping charging authorization focused on the user and auth tag while plate-number context is preserved through the mapped app/admin vehicle info.

Engineering Lens

  • Treated the vehicle tree as operational reference data rather than a simple selection list, so one `Hyundai > electric SUV > Ioniq 5` structure supports user-vehicle registration, UI selection, ancestor-notice collection, and descendant-user expansion.
  • Combined parent-scoped uniqueness with a distributed lock so concurrent registrations do not create duplicate reference nodes for the same vehicle.
  • Auto-linking is convenient but risky when wrong, so vehicle info and PnC entities were stored separately and linked only under the conservative exactly-one-unmatched-counterpart rule.

Architecture Snapshot

Mermaid View

Vehicle reference tree connecting registration, selection, warnings, and notifications

Shows how vehicle information lookup results from plate number and owner name become brand, category, and model nodes, then the same tree is reused as the shared reference for user-vehicle registration, selection UI, optional brand/category/model notice-warning exposure, and targeted notifications. Dotted edges indicate conditional exposure or delivery.

flowchart TD
  Lookup["vehicle information lookup<br/>plate=12GA3456 owner=K**"] --> LookupResult["brand name=Hyundai<br/>category=electric SUV<br/>model name=Ioniq 5"]
  LookupResult --> Normalize["look up or create<br/>vehicle reference tree"]
  Normalize --> Brand["brand node<br/>Hyundai"]
  Brand --> Category["category node<br/>electric SUV"]
  Category --> ModelNode["model node<br/>Ioniq 5"]
  ModelNode --> Model["vehicle-model reference<br/>image / registration count"]
  Model --> VehicleInfo["user vehicle info<br/>plate + model link"]
  Brand --> Selection["vehicle selection UI<br/>shared reference"]
  Category --> Selection
  ModelNode --> Selection
  Notice["operational notice<br/>optional exposure"] -.-> Brand
  Notice -.-> Category
  Notice -.-> ModelNode
  Warning["vehicle warning<br/>attention exposure"] -.-> Brand
  Warning -.-> Category
  Warning -.-> ModelNode
  Brand -.-> Display["vehicle detail screen<br/>conditionally shown"]
  Category -.-> Display
  ModelNode -.-> Display
  Brand -.-> Notify["targeted notifications<br/>expand when matched"]
  Category -.-> Notify
  ModelNode -.-> Notify
  classDef notice fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f;
  classDef warning fill:#fee2e2,stroke:#dc2626,stroke-width:2px,color:#7f1d1d;
  class Notice notice;
  class Warning warning;

Mermaid View

Safe auto/manual mapping flow between vehicle info and vehicle identifiers

Shows how either vehicle-info or PnC registration can trigger auto-linking only when exactly one unmatched counterpart exists, with all other cases falling back to user confirmation.

flowchart TD
  VehicleInfoReg["vehicle info registration<br/>plate=12GA3456"] --> CheckA["try auto-link<br/>from vehicle info"]
  PncReg["PnC registration<br/>vehicle identifier=VID-7K3Q9M"] --> CheckB["try auto-link<br/>from vehicle identifier"]
  CheckA --> Match{"exactly one<br/>unlinked counterpart?"}
  CheckB --> Match
  Match -->|yes| Link["link vehicle info<br/>and identifier"]
  Match -->|no| Manual["switch to user selection"]
  Manual --> Confirm["user-confirmed link"]
  Link --> Auth["charging authorization<br/>user / auth tag"]
  Confirm --> Auth
  Auth --> Context["vehicle context<br/>for app and operations"]
  Auth --> Charge["start charging"]

Operational Outcomes

  • Connected manufacturer, vehicle category, detailed model, release year, fuel type, and image data from vehicle information lookup into the vehicle reference tree, then used the same tree for brand/category/model warning display and targeted notification delivery.
  • Enabled auto-mapping from either vehicle-info or PnC registration when conditions match, while keeping charging authorization stable around the vehicle identifier and user auth context.
Project

11

LG Uplus VoltUp / Jun 2026 - Present

U+ VIP Partnership Coupon

Designed U+ VIP coupon-pack policy, external membership approval, issuance, and failure recovery

Connected monthly coupon-pack policy, external membership approval, coupon issuance, compensating cancellation, and issuance history so LG U+ VIP and VVIP members can receive one benefit coupon per month. Added assisted-issuance and status-check screens so operations teams can handle customer inquiries safely.

Kotlin Spring Boot Spring Batch React TypeScript MySQL JPA QueryDSL Feign Flyway

Design Context

External membership approval and internal coupon issuance had to behave as one customer flow while enforcing pre-registered benefit-month coupon packs, monthly limits per user and card, date-of-birth verification, and compensating approval cancellation when coupon issuance failed. The project also required operations-console support for payment-method restrictions, assisted issuance, and scheduled customer messages.

Key Point

Demonstrates how external membership approval, internal coupon issuance, compensating cancellation, and operations tooling work together in a production benefit flow.

Core Implementation

  • Added U+ VIP coupon-pack policy to `promotion-service`, blocked overlapping active packs within the same benefit month, and kept payment-method rules consistent across preview, search, redemption, and operations-console creation.
  • Consolidated phone-based card lookup, date-of-birth verification, monthly limits per user and card, LG U+ approval, coupon issuance, and compensating cancellation in a dedicated U+ VIP issuance service.
  • Added diagnostics for LGU+ response codes and limit-exceeded branches while keeping card numbers and keys behind masking and encryption boundaries so sensitive values do not leak into operational logs.
  • Added operations-console APIs for phone-based card lookup, assisted issuance from member details, identity-mismatch pre-checks, and searchable issuance-to-coupon history.
  • Implemented an assisted-issuance panel in member details, searchable issuance history, U+ VIP coupon-pack settings, benefit-month autofill, fixed-discount minimum-spend validation, and payment-method selection in the operations console.
  • Built an operations-console tool for one-time SMS, push, and Kakao AlimTalk messages, using one delivery record for both immediate and scheduled sends with batch dispatch and searchable history.

Engineering Lens

  • U+ VIP benefits were not just another coupon type; they were a state-alignment problem between external approval and internal coupon issuance. I called LGU+ approval only after local pre-checks and attached compensating cancellation when internal coupon issuance failed.
  • Treated assisted issuance as a faster way to execute the same validation rules, not as a bypass. The member-detail panel and history search let operations teams review status, failure causes, and retry eligibility in one place.
  • Kept coupon-pack creation rules in `promotion-service` domain policy instead of scattering them across frontend conditions. The operations console edits and displays the policy, while customer issuance, lookup, and redemption use the same values.
  • For customer messages, I stored send requests as data instead of creating bespoke code for each campaign, letting immediate sends and scheduled dispatches share the same send ledger.

Architecture Snapshot

Mermaid View

U+ VIP: operational flow connecting external membership approval and internal coupon issuance

Shows the U+ VIP coupon flow from operations-console pre-registration through phone-based card lookup, identity verification, LG U+ approval, coupon issuance, compensating cancellation, and issuance-history search.

flowchart TD
  Pack["Operations console<br/>pre-register coupon pack<br/>benefit month + usable window"] --> Policy["promotion-service policy<br/>payment-method restriction<br/>no overlapping active pack in same month"]
  Policy --> UserFlow["Customer issue flow<br/>phone + birthday<br/>card lookup"]
  Policy --> AdminFlow["Assisted issuance<br/>member detail panel<br/>phone-based card lookup"]
  UserFlow --> Guard["Pre-checks<br/>member birthday match<br/>once per user per month<br/>once per card per month"]
  AdminFlow --> Guard
  Guard --> Approve["external membership gateway<br/>approval request"]
  Approve --> Issue["promotion-service issue<br/>U+ VIP coupon"]
  Issue --> History["Issuance/coupon mapping history<br/>operations-console search"]
  Issue --> Fail{"Coupon issue failed?"}
  Fail -->|yes| Cancel["Compensating LGU+ cancel<br/>response-code diagnostics"]
  Fail -->|no| Complete["Customer benefit granted"]
  History --> Ops["Customer inquiry check<br/>less developer manual lookup"]
  classDef policy fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef flow fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Pack,Policy policy;
  class UserFlow,AdminFlow,Guard,Approve,Issue,History flow;
  class Cancel,Complete,Ops result;

Mermaid View

Customer messaging: one delivery ledger for immediate and scheduled sends

Shows how SMS, push, and AlimTalk send requests are stored in a send ledger and processed by both immediate sends and scheduled dispatch batches.

flowchart TD
  Admin["Operations console<br/>SMS / push / AlimTalk"] --> Template["Custom template<br/>targets + variables"]
  Template --> Source["Send ledger<br/>same record for immediate/scheduled"]
  Source --> Immediate["Immediate send<br/>send orchestrator"]
  Source --> Reserved["Scheduled send<br/>customerMessageDispatchJob"]
  Reserved --> Dispatch["Dispatch tasklet<br/>queries sendable time"]
  Immediate --> Provider["Message provider call"]
  Dispatch --> Provider
  Provider --> History["Send history / failure status<br/>operations-console search"]
  History --> Ops["Operations campaign response<br/>fewer repeated dev requests"]
  classDef admin fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef process fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Admin,Template admin;
  class Source,Immediate,Reserved,Dispatch,Provider process;
  class History,Ops result;

Operational Outcomes

  • Connected U+ VIP and VVIP benefit issuance end to end across the customer flow, coupon policy, external LG U+ approval, compensating cancellation, and issuance history.
  • Moved manual log and policy checks from developers into operations-console search and pre-check screens, enabling faster customer-inquiry handling.
  • Kept promotion policy consistent between `promotion-service` and the operations console for payment-method restrictions, coupon preview, U+ VIP coupon-pack creation, and reissuance after soft deletion.
  • Extended operations tooling with customer-message composition and scheduled dispatch so recurring campaign and notice sends no longer require developer intervention.
Project

12

LG Uplus VoltUp / Feb 2026 - Present

Roaming Reliability: State Resync and Batch Operations

Public roaming state redesign and long-running Kakao T partner-roaming batch hardening

Redesigned public-roaming card-state updates around unpaid-balance events and added public-API retries plus a monthly full resync to correct long-term drift. For Kakao T partner roaming, corrected shutdown ordering so Redis remains available during active work and prepared a non-overlapping in-process scheduler for charger-sync jobs that run for 60–75 minutes.

Kotlin Spring Boot Spring Batch GCP Pub/Sub Scheduler

Design Context

Public roaming data needs to stay aligned with the external system, but online events alone cannot reliably recover after missed events or transient failures. Treating baseline-like member-card data and more tolerably lossy charger-status data with the same priority could also waste retry capacity.

Key Point

An operational reliability project that combines online events, retries, and full resync to keep internal state aligned with an external public system over time.

Core Implementation

  • Moved card-state updates from payment responses to unpaid-balance events, so state changes occur only when a failed payment requires correction.
  • Simplified the roaming card-state processing path and consolidated payment-state lookups to reduce transformation and lookup overhead.
  • For public API retries, prioritized baseline-like member-card data and separated more tolerably lossy charger-status data into a lower-priority path.
  • Added a monthly full-resync scheduler and task seed for public roaming member cards so differences missed by online events can be periodically restored.
  • Corrected Kakao T partner-roaming shutdown so active batch work completes before Redis connections close.
  • Added a disabled-by-default opt-in scheduler, fixed-delay execution, and shutdown guards so charger-sync jobs that run longer than their hourly schedule can migrate without racing the existing CronJob.

Engineering Lens

  • Tying state updates to every payment response allowed normal payment flows to alter roaming state, so I narrowed the trigger to failed-payment events that actually require correction.
  • I treated retries as an operational design problem rather than “try everything again.” Member cards recover first because they are baseline data, while charger status is lower priority to control retry cost.
  • The monthly full resync complements online event handling. Instead of trying to eliminate every missed event, it creates a recovery path that prevents long-term drift from accumulating.
  • Kept resident-mode migration disabled by code defaults and designed CronJob suspension plus Deployment activation as one deployment step, controlling duplicate-sweep risk.

Architecture Snapshot

Mermaid View

Event handling, retries, and monthly resync for public roaming data

Shows how failed-payment updates, priority-based public API retries, and a monthly full resync work together to reduce long-term drift from the external system.

flowchart TD
  Source["public roaming API<br/>member cards / charger status"] --> Online["online event handling<br/>card state update"]
  Online --> Arrears["failed-payment events<br/>update only required cases"]
  Source --> Retry["public API error retry<br/>priority by importance"]
  Retry --> Member["member-card retry first<br/>recover baseline data"]
  Retry --> Charger["charger status later<br/>separate tolerable loss"]
  Source --> Monthly["monthly full resync<br/>dynamic scheduler + seed"]
  Monthly --> Baseline["prevent long-term external drift"]
  Arrears --> Stable["operational data accuracy"]
  Member --> Stable
  Baseline --> Stable
  classDef external fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef process fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Source external;
  class Online,Arrears,Retry,Member,Charger,Monthly process;
  class Baseline,Stable result;

Operational Outcomes

  • Reduced unnecessary state-change risk and improved data accuracy by redesigning the card-state update basis.
  • Established an operational retry path where important data recovers first after public API errors.
  • Added a monthly full-resync safety net so member-card baseline data realigns with the external system after missed events or transient failures.
Project

13

LG Uplus VoltUp / Oct 2024 - Present

Voltup Workflow: CI Automation and DevOps Standardization

Org-wide PR automation workflow, repo-local operational context, Vault-local sync, and CI/CD standardization

Voltup Workflow packages recurring engineering work—PR reviews and descriptions, security checks, notifications, local setup, internal API integration, and deployments—into reusable workflows and CI automation. It centralizes LLM access through LiteLLM, applies shared defaults across repositories, and standardizes Pub/Sub shutdown across four microservices to improve review quality and release reliability.

Vault CLI Gradle Kotlin DSL GitHub Actions Jenkins ArgoCD Workload Identity Firebase CLI LLM GitHub Copilot Claude Code gcloud CLI

Example PR Run

Voltup Workflow starts from PR comments

A developer leaves a command on a PR, then the GitHub Actions bot writes change analysis, code review, and PR-body updates back into the same timeline.

Open

feat(admin): clean up partner settlement APIs and prepare release

sprint/feature -> deploy/prod · 64 commits · 27 files changed

DV

developer commented

/voltup-review -t security
GH

github-actions Bot

Workflow accepted
Command /voltup-review -t security
Context project-context, review-template, docs
Diff scope 27 files · 3 review clusters
GH

github-actions Bot

pr-changes-detector · comment upsert

PR Change Analysis

Flyway migrations (4)

OK
  • V28__settlement_category_index.sql - Add category index to settlement_config
  • V29__partner_ticket_id.sql - Add partner_ticket_id to support_ticket
  • V30__notice_pin_flag.sql - Add pinned flag and composite index to admin_notice

Admin API controllers (16)

review
  • SettlementCommandController.kt - Remove [POST] /admin/settlements/{settlementId}/retry
  • StoreAdminController.kt - Add channel-level QR mapping to store settlement responses
  • CustomerNoticeController.kt - Move notice-template lookup behind a notification bridge
GH

github-actions Bot

/voltup-review · review posted

AI Code Review Result
Critical 0
High 0
Medium 6
Low 1
  • Reviews settlement retry endpoint removal and caller impact in one cluster
  • Flags null/empty cases in the store QR response enrichment path
  • Separates low-risk deletions into Low priority comments

StoreAdminController.kt

If channel-level QR codes are missing, the admin response can drift from settlement status and store mapping data. Add an explicit fallback response.

Security agent loop

Find Context expand Refute
DV

developer commented

/voltup-pr
GH

github-actions Bot

/voltup-pr · marker block update

PR Body Generated

<!-- voltup-pr:begin -->

Summary

Clean up partner settlement APIs and prepare release merge

Changes

Settlement migrations, Admin API removals/refinements, store QR response updates

Validation

Check Flyway order and settlement retry caller paths before deployment

<!-- voltup-pr:end -->

Manual PR notes are preserved; only the voltup-pr marker block is replaced.

Design Context

As the service landscape grew, review standards, microservice conventions, recurring task patterns, local secret delivery, internal API usage, and release procedures became inconsistent across engineers. With development and operations owned by different teams, those inconsistencies increased the risk of missed reviews, environment drift, failed releases, and unclear access boundaries for internal tools.

Key Point

A workflow automation project that improves operational efficiency and release reliability by reducing repeated review work, environment drift, delivery failures, and ambiguity at internal-tool boundaries.

Core Implementation

  • Rolled out a LiteLLM proxy and centrally managed model defaults across microservice repositories, standardizing code review, PR-description generation, security-review drafts, and Slack notifications as reusable workflows.
  • Built a reusable GitHub Actions workflow in a shared workflow hub triggered by `/voltup-review`, connecting an LLM with per-repo `project-context`, `review-template`, and docs so PR diffs are automatically reviewed against repository-specific context.
  • Designed `/voltup-review -t security` as a security-agent loop with Find, context expansion, and Refute stages, so it can inspect authentication, caller, and configuration context outside the diff and reduce false positives.
  • Built `/voltup-pr` to generate and update PR descriptions from commit logs, changed files, and diffs while replacing only the `voltup-pr` marker block to preserve manual content. `pr-changes-detector` upserts PR change summaries for Flyway, controller, entity, and authorization changes on PR open/push.
  • Added `.agent/workflows`, `.github/skills`, `.github/prompts`, and `copilot-instructions.md` to microservice repositories, giving generative AI tools reusable, repository-specific context for service conventions, API-first development, security rules, and recurring operational tasks.
  • Added root `build.gradle.kts` logic that replaces base-yaml placeholders from Vault and generates a local config yaml, checking both service-specific Vault paths and shared dev paths. It also verifies Vault CLI login, handles non-interactive environments, and fills the DB user from the current `gcloud` account so local environments stay automatically aligned across developers even when new keys are added.
  • Added a sensitive-certificate refresh task in an app-domain repository that Base64-encodes certificates and app-config fields into Vault, so developers can refresh sensitive assets themselves without sharing files through the repo or chat.
  • Documented and applied a shared internal API client pattern plus a caller-identity header convention so growing operator-tool integrations keep a consistent trust boundary.
  • Standardized graceful shutdown across billing, feapp, messaging, and roaming Pub/Sub consumers: stop intake on SIGTERM, drain in-flight work, redeliver unfinished messages, and align application timing with Kubernetes termination budgets.
  • Standardized delivery on top of a shared Jenkins library so each service `Jenkinsfile` routes API/BATCH/CONSUMER/APP targets by job name and continues into Docker build/push plus ArgoCD deploy. The Android app pipeline follows the same pattern with cache restore/save, release track selection, and notifications.
  • Reduced long-lived key dependency in Android delivery and improved release stability with clearer logs, Slack notifications, and token-exposure guards.
  • Hardened iOS delivery conditions and authentication flow so releases are not unnecessarily blocked by commit text or external dependency failures.

Engineering Lens

  • Treated AI adoption as an operating-system design problem rather than just attaching a model. `/voltup-review`, `/voltup-pr`, and `pr-changes-detector` each handle a different PR operations problem while reading per-repo context and templates, keeping the workflow shared without losing service-specific operational context.
  • Approached local setup as “sync Vault directly into local environments so authenticated developers receive the same baseline automatically” rather than “who hands secrets out.” By automating yaml generation and certificate refresh behind Vault CLI authentication, the workflow keeps developer environments aligned even as new keys are added.
  • Converged delivery onto a shared-library model with job-name-based target routing so operational steps stay standardized while app/backend differences appear only at the target layer.
  • Reduced long-lived key dependency in mobile delivery authentication and made deployment failures easier to trace from Slack messages and logs.
  • When repetitive work started becoming an operational risk, I did not stop at documentation alone; I turned it into Gradle tasks, reusable workflows, and Jenkins pipelines the team could use immediately.

Architecture Snapshot

Mermaid View

Voltup Workflow: /voltup-review and /voltup-pr PR automation loop

Shows how `/voltup-review` and `/voltup-pr` start from PR comments, invoke org-wide workflows, read repo-local context plus the PR diff, and update inline reviews, PR descriptions, and change summaries.

flowchart TD
  ReviewComment["PR comment<br/>/voltup-review"] --> ReviewWorkflow["AI code/security review<br/>voltup-review workflow"]
  PrComment["PR comment<br/>/voltup-pr"] --> PrWorkflow["PR body generation/update<br/>voltup-pr.yml"]
  AutoDetect["PR open/push<br/>pr-changes-detector"] --> Summary["change summary comment<br/>Flyway / controller / entity / auth"]
  ReviewWorkflow --> LLM["LLM-backed review engine<br/>automated code/security review"]
  ReviewWorkflow --> RepoCtx["repo-local context<br/>project-context / review-template / docs"]
  PrWorkflow --> RepoCtx
  ReviewWorkflow --> Diff["PR diff + changed files"]
  PrWorkflow --> Diff
  PrWorkflow --> Marker["voltup-pr marker block<br/>manual body preserved"]
  LLM --> ReviewEngine["change-cluster analysis<br/>risk / gap / improvement candidates"]
  RepoCtx --> ReviewEngine
  Diff --> ReviewEngine
  ReviewEngine --> ReviewBack["inline review + summary<br/>risks / gaps / suggestions"]
  Diff --> PrDoc["PR description<br/>summary / changes / validation / review points"]
  Marker --> PrDoc
  ReviewBack --> Human["developer review<br/>human owns final judgment"]
  PrDoc --> Human
  Summary --> Human
  Human --> Update["follow-up commit or discussion"]
  Update --> ReviewComment
  Update --> PrComment
  classDef trigger fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef workflow fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef context fill:#eef7fb,stroke:#3b556b,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class ReviewComment,PrComment,AutoDetect trigger;
  class ReviewWorkflow,PrWorkflow,LLM,ReviewEngine workflow;
  class RepoCtx,Diff,Marker context;
  class ReviewBack,PrDoc,Summary,Human,Update result;

Mermaid View

AI review, Vault-to-local sync, and deployment standardization

Summarizes how repeated work was turned into three tracks: AI review workflows, Vault-to-local sync based yaml generation, and Jenkins/ArgoCD deployment standardization.

flowchart TD
  Pain["Repeated operational work<br/>PR review / PR docs / local env / deploy"] --> Review["voltup-workflow<br/>/voltup-review + /voltup-pr<br/>org-wide PR automation"]
  Review --> Context["project-context + prompts + skills<br/>repo-specific ops context injected"]
  Pain --> Local["Gradle local-config task<br/>local config yaml"]
  Local --> Vault["Vault CLI login<br/>service path + shared path<br/>no secret commits"]
  Local --> IAM["cloud account -><br/>DB user auto-fill"]
  Pain --> Internal["internal API client<br/>caller identity convention"]
  Pain --> Deploy["Jenkins shared library<br/>job name -> target routing"]
  Deploy --> Build["docker/app build<br/>cache / track / notifications"]
  Deploy --> Android["Android release<br/>less key burden"]
  Deploy --> IOS["iOS release<br/>fewer avoidable failures"]
  Build --> Argo["ArgoCD deploy"]
  Android --> Argo
  IOS --> Argo
  classDef ai fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef sec fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  classDef ops fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  class Review,Context ai;
  class Local,Vault,IAM,Internal sec;
  class Deploy,Build,Android,IOS,Argo ops;

Operational Outcomes

  • Removed the intake and in-flight loss window during normal node drains and rolling deployments, establishing a reusable shutdown contract across four services.
  • Established the org-wide `voltup-workflow` plus repo-local context patterns, allowing new repositories and workstreams to onboard under the same PR review, PR description, change-summary, and operational conventions.
  • Kept sensitive values out of the repository while syncing Vault directly into local environments, so developer configs stay aligned automatically even as new keys are introduced.
  • Simplified service and app delivery with Jenkins shared-library plus ArgoCD deployment patterns, reducing manual branching in release operations.
  • Standardized internal operations-console APIs and mobile delivery authentication, reducing recurring security and operational risks as internal tools and app-release workflows expanded.