---
title: {{TITLE}} — 작업 일정
date: {{TASK_DATE}}
lang: "ko"
tags: ["schedule"]
status: new
---

<!-- `lang` decides which labels the validator checks. Section headings and
     field labels (`## At a Glance`, `**Problem**:`, `| Stage | Title | …`) stay
     English literals in every language; the words around them follow `lang`.
     Known languages and their labels live in `SCHEDULE_LABELS` in
     `validators/validate-schedule.py` — add a language there before writing one.

     ko: title `— 작업 일정`, meta `> 작성일 … · 대상 저장소 …`,
         summary `**목적**` / `**달라지는 것**`, scale `| Priority | 기준 |`
     fr: title `— Planning de travail`, meta `> Rédigé le … · Dépôt concerné …`,
         summary `**Objectif**` / `**Ce qui aura changé à la fin**`,
         scale `| Priority | Critère |` -->

<!-- The schedule is shared with people who do not run okstra. Neither the tool
     nor its vocabulary — task-group, task-key, taskType, the run's phase names —
     appears anywhere in the body, and the validator refuses a body that carries
     them. Frontmatter stays tooling-only and is not rendered. -->

# <작업 이름> — 작업 일정

> 작성일 <YYYY-MM-DD> · 대상 저장소 <repo>

---

## At a Glance

**<N> tasks total / estimated effort: <X.X> ~ <Y.Y> days (Effort sum)**

<!-- `Days` is the task's own range and MUST equal the sum of its Work Breakdown
     stage rows. Append ` (est)` when the range was derived from `step_count`
     rather than stated by the plan — which is every range today, because
     implementation-planning does not estimate duration. `[NEEDS-PLANNING]` is
     reserved for a task with NO planning report at all. -->

| # | 작업 | Category | Priority | Effort | Days | Risk |
|---|------|----------|----------|--------|------|------|
| 1 | <작업 이름> | <category> | <P0~P3> | <S/M/L/XL/XXL> | <X.X> ~ <Y.Y> (est) | <risk> |

**Effort distribution**: S × <n> / M × <n> / L × <n> / XL × <n> / XXL × <n>
**Risk distribution**: Very Low × <n> / Low × <n> / Medium × <n> / Med-High × <n> / High × <n>
**Impact by repo**: <repo1> (<n>) / <repo2> (<n>)

---

## Executive Summary

<!-- A reader who opens only this section must learn why the work is being done
     and what is true when it ends. Both labels are required and the validator
     checks for them.

     `목적` — the problem in today's system, in its own terms. Not "migrate to
     NestJS" (that is the what); rather what is wrong or missing now and what
     this changes about it. Source: the report's `humanSummary.headline` /
     `whyItMatters`, rewritten for someone who never saw the report — it is
     written at an approver ("your two questions", "the option you approved")
     and that framing must not survive into the schedule.

     `달라지는 것` — a bullet list of the end state, each item something a
     reader could later check. State the limits too: if the work stops short of
     what its name suggests, say so here rather than letting the reader assume
     otherwise. -->

**목적** — <오늘 무엇이 문제이고 이 작업이 그것을 어떻게 바꾸는지>

**달라지는 것**

- <완료 시점에 참이 되는 것, 확인 가능한 형태로>
- <미완으로 남는 것이 있으면 그것도>

**범위와 순서** — <몇 개 stage / step, 의존 형태, 병행 가능한 준비 작업>

### Effort Sizing Criteria

| Size | Criteria | Day(s) |
|------|------|--------|
| **S** | 1-2 files, 1 repo, test changes only, no DB changes | 0.5 - 1 |
| **M** | 3-5 files, 1 repo, includes new tests, no DB changes | 2 - 3 |
| **L** | 5-15 files, 2-3 repos, sequential deploy, may include package release | 3 - 5 |
| **XL** | 15+ files, 3+ repos + infra, concurrent frontend changes, architecture migration | 5 - 10 |
| **XXL** | decomposed into stages; the day range is the stage sum | 10 - |

<!-- XXL has no finite upper bound in this table on purpose: an XXL task is
     sized by its own stage decomposition, so its `Days` range comes from the
     Work Breakdown sum and NOT from this row. Every other size takes its range
     from here. -->

### Priority & Risk Scale

<!-- Controlled vocabulary the At a Glance cells draw from. The schedule is read by people who never saw the source report,
     so a `P0` or a `High` that is never defined is an opaque code with a
     friendlier shape. Keep both tables verbatim. -->

| Priority | 기준 |
|----------|------|
| **P0** | 다른 작업을 막고 있거나 운영 중인 서비스에 직접 영향 — 이번 주기에 착수 |
| **P1** | 다음 주기 안에 착수해야 하며, 미루면 P0 로 올라갈 사유가 있음 |
| **P2** | 계획된 개선 — 일정 여유에 맞춰 배치 |
| **P3** | 가치는 인정되나 착수 시점이 정해지지 않음 |

| Risk | 기준 |
|------|------|
| **High** | 되돌림 비용이 크거나, 실패가 사용자에게 보이는 형태로 드러남 |
| **Med-High** | 회귀 가능성이 있고 감지 장치가 일부 구간에만 있음 |
| **Medium** | 표준 절차로 감지·복구 가능 |
| **Low** | 영향 범위가 좁고 즉시 되돌릴 수 있음 |
| **Very Low** | 실패해도 관측 가능한 영향이 없음 |

---

## Task Dependency Graph

<!-- OPTIONAL — omit the whole section, heading included, when no task depends on
     another. That is always the case for a single-task schedule, and a section
     whose entire body says "the relation is in that other table" is noise the
     reader steps over. Stage order is NEVER drawn here: the Work Breakdown's
     `Depends On` column owns it.
     When two or more tasks are coupled, render a plain ``` fenced adjacency
     list, `<TASK-ID> -> <TASK-ID>[, …]`, one edge per line, `# <prose>`
     comments allowed. ASCII `->` only; Unicode `→` is rejected. The fence takes
     no language tag — ```mermaid / ```plantuml / ```graphviz / ```dot are all
     rejected. -->

```
DEV-1 -> DEV-2, DEV-3
# release order
DEV-2 -> DEV-4
```

---

## Gantt Chart

<!-- OPTIONAL: include only when per-task day estimates are available.
     If included, MUST use this exact heading literal `## Gantt Chart`.
     Render as ASCII inside a fenced ``` block with NO language tag.
     Mermaid / PlantUML / Graphviz are forbidden.

     GEOMETRY — the validator checks these, they are not decoration:
     - One column = 0.5 day. This is what lets a 2.5-day stage draw honestly.
     - The axis starts at Day 1 and its last tick is the smallest multiple of 5
       that covers the schedule's upper bound. Do not draw empty months.
     - A row's bar starts at the cumulative LOWER bound of everything it waits
       on, and runs `lower / 0.5` filled cells `█` then `(upper - lower) / 0.5`
       open cells `░`.
     - Row label is `Stage <n>` for a single-task schedule, spelled out so the
       reader never cross-references an abbreviation. With 2+ tasks, prefix the
       task-id: `<TASK-ID> Stage <n>`.
     - A row is a label and a bar, with NOTHING after it. No `days=` (the Work
       Breakdown's Days column owns those numbers and the validator compares the
       bar against it), and no per-row `! crit` / `est` markers — when every row
       carries the same marker it distinguishes nothing.
     If day estimates are missing, omit this whole section and replace it with one blockquote: `> _Gantt Chart omitted: <reason>_`. -->

> 한 칸은 반나절이다. `█` 는 하한 일수만큼, `░` 는 거기서 상한까지의 여유만큼 그린다.

```
Day:      1       5         10        15
          |       |         |         |
Stage 1   ████░░
Stage 2       ████░░
Stage 3           ██████░░░░
```

---

## Task Details

<!-- One `### <n>. <Title>` block per in-scope task, numbered from 1 in At a
     Glance order — which is Priority-first, so that order carries priority.
     There are no phase buckets and no separate priority table; stages carry
     sequence.

     The heading is the work's NAME, not its task-id. A task-id is a machine key
     (`nestjs-migration-nlpvibe-to-nestjs-and-org-standard-structure`); printing
     it as the reader's heading makes them decode a slug to learn what the work
     is. The id stays in the At a Glance row, and the block binds to that row by
     its number — which is also what the validator matches on. -->

### 1. <Title>

| Item | Detail |
|------|--------|
| **Category** | <category> |
| **Priority** | <P0~P3> |
| **Effort** | **<S/M/L/XL/XXL>** (<scope summary>) |
| **Status** | <작업이 지금 어느 단계인지, 사람 말로> |
| **Risk** | <risk> |
| **Scope** | <files / repos summary> |
| **Repo** | <repos> |

**Problem**: <…>

**Solution**: <…>

<!-- Everything below this line is transcribed from the planning report the CLI
     resolved — `okstra stage-map <task-key> --text` carries fixed `Stages` and
     `Planning` count/name/value rows for each stage's acceptance, exit contract,
     slice value, and the task-level rollback, validation, and migration-risk
     fields. Transcribe those rows; do not re-summarise the report body. -->

**Work Breakdown**:

<!-- `Depends On` spells the stage out: `None`, `Stage 2`, `Stage 1 (done)`.
     A bare number makes the reader match a digit against the Stage column of
     the same table. -->

| Stage | Title | Steps | Depends On | Days |
|---:|---|---:|---|---:|
| 2 | Build adapter | 3 | Stage 1 (done) | 2.0 ~ 3.0 |
| 3 | Wire consumer | 2 | Stage 2 | 1.0 ~ 2.0 |

<!-- One `#### Stage <n> — <title>` per SELECTED stage, in Work Breakdown order.

     `Steps` comes from that stage's `stepwiseExecution`: what the step does
     (keep a leading RED:/GREEN: marker), the files it touches, and what the
     executor should see when it succeeds — the step's own `expected`, written
     as an outcome they can check, not pasted.

     The 대상 column separates entries with `<br>`, never a comma — a
     comma-joined list stretches the column and pushes the two text columns
     into a narrow gutter. It lists repository paths only. A plan that scaffolds under
     the run's working tree (`.okstra/…`, `qa/…`) names the artefact there
     instead — "계약 캡처 도구", "위반 대장" — because the reader neither owns
     nor can open that tree. The validator refuses such a path.

     `Exit criteria` is one line from `exitContract`: when the stage is done.

     There is no risk section, no cross-task section and no "what to do next"
     list. Every row those carried restated a stage — a risk's mitigation is
     the stage that answers it, an upstream dependency is the step that gates
     on it, and the next action is Stage 1's first step. Anything that is
     genuinely not a stage (a freeze agreed with another team, a coupling the
     stages only imply) belongs in that task's **Solution** or **Scope**.

     Do NOT render `sliceValue`. It argues why the plan sliced the work this
     way, which is a reviewer's question; an implementer gets nothing from it.
     Do NOT render `acceptance` next to `exitContract` — they restate each
     other, `exitContract` being the wider of the two. -->

#### Stage 2 — Build adapter

**Steps**:

| Step | 작업 | 대상 | 기대 결과 |
|---:|---|---|---|
| 1 | RED: <무엇을 왜 하는지> | `<file>`<br>`<file>` | <성공했을 때 보이는 것> |
| 2 | GREEN: <무엇을 왜 하는지> | `<file>` | <성공했을 때 보이는 것> |

**Exit criteria**: <이 stage 가 끝났다고 판정하는 상태>

**Verification Commands**:

<!-- One row per `validationChecklist` entry whose stage is in scope, SORTED
     `pre` → `mid` → `post`. Source order interleaves them, and a reader working
     down the table then runs a precondition after the post checks it gates.
     Introduce the three values in one line above the table — they are
     controlled vocabulary like `P0`, and just as opaque undefined.
     Do NOT carry the source row's `id`: a schedule reader has no access to the
     report those codes index, and the self-contained rule rejects them. -->

`Phase` 는 이 검사를 언제 돌리는지를 뜻한다 — **pre**: 첫 stage 착수 전, **mid**: 매 stage 진행 중 상시, **post**: 각 stage 종료 조건으로.

| Phase | Check | Command / Observation | Expected |
|-------|-------|-----------------------|----------|
| pre | <check> | `<commandOrObservation>` | <expectedOutcome> |

**Rollback**:

<!-- One row per `rollbackStrategy` entry, in source order. The source `id` is
     dropped for the same reason as the verification table. -->

| Stage | Action | Trigger Signal | Verification |
|------:|--------|----------------|--------------|
| 1 | <action> | <triggerSignal> | <verificationMethod> |

---

<!-- OPTIONAL: ## Glossary
     Include ONLY when the body uses opaque codes (FC-N, UC-N, M-N, ...).
     Header literal `| Code | Description |` is required. Every code in body
     MUST have a row. Decision-item letters (A1, B2, C3, D4) are forbidden
     even with a glossary entry — they belong only in internal reports.

## Glossary

| Code | Description |
|------|-------------|
| FC-5 | Missing payment gateway timeout handling |
-->
