Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8164f03
fix: QnA 이미지 키 전송 오류 수정
Hyeonjun0527 May 16, 2026
3153604
Merge pull request #606 from code-zero-to-one/fix/e2e
HA-SEUNG-JEONG May 17, 2026
3ed8b2d
fix: QnA 제출 성공 시 blob URL 정리
Hyeonjun0527 May 17, 2026
4d311a6
fix: QnA 이미지 미리보기 해제
Hyeonjun0527 May 17, 2026
86a7cd4
fix: 커리큘럼 배지 테스트 locator 고정
Hyeonjun0527 May 17, 2026
1bf3cfe
fix: 커리큘럼 테스트 충돌 해결
Hyeonjun0527 May 17, 2026
08a5d12
Merge pull request #603 from code-zero-to-one/fix/lesson-qna-image-ke…
Hyeonjun0527 May 17, 2026
5bb61f6
fix: 노션 복붙 본문 유실 방지
Hyeonjun0527 May 17, 2026
d70f7ad
Merge pull request #609 from code-zero-to-one/fix/admin-lesson-editor…
Hyeonjun0527 May 17, 2026
b873a29
[builder-feed-update-and-delete] feat : chore(types): BuilderFeedUpda…
HA-SEUNG-JEONG May 17, 2026
b51a51a
[builder-feed-update-and-delete] feat : style(feed): biome import 정렬
HA-SEUNG-JEONG May 17, 2026
35c1941
[builder-feed-update-and-delete] fix : NaN feedId edit 모드 오분기 및 캐시 무효…
HA-SEUNG-JEONG May 17, 2026
8aef3ae
chore: 운영 릴리즈 기록 자동화
Hyeonjun0527 May 17, 2026
ecac9d1
fix: 릴리즈 리뷰 후속 보완 (#612)
Hyeonjun0527 May 17, 2026
f3a8189
fix: PR 613 main 병합 충돌 해소
Hyeonjun0527 May 17, 2026
8329b31
fix: 복합 HTML 붙여넣기 보존
Hyeonjun0527 May 17, 2026
0f72ddd
fix: 복합 붙여넣기 이미지 위치 보존
Hyeonjun0527 May 17, 2026
0946e33
fix: 복합 붙여넣기 표와 노션 이미지 안내 보존
Hyeonjun0527 May 17, 2026
1c29db5
chore: 운영 릴리즈 기록 수동 반영
Hyeonjun0527 May 17, 2026
f5ce0dc
fix: 운영 릴리즈 기록 메타데이터 보정
Hyeonjun0527 May 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .claude/rules/version-management-frontend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# ZERO-ONE Version Management Rule - Frontend Repository

Source of truth for this repository: `ops/version-management.md`.

This rule is a version-management rule, not a frontend coding-style rule. Do not duplicate the full rule body here; keep the durable policy in `ops/version-management.md` so Claude/Codex/project agents share the same repository-level source of truth.
10 changes: 10 additions & 0 deletions .claude/skills/zeroone-version-management/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: zeroone-version-management
description: Use when working on ZERO-ONE production release records, PR release intent labels/body, main-branch production deploy workflow, rollback metadata, or releases/prod-*.yaml in study-platform-client.
---

# ZERO-ONE Version Management Wrapper for Claude

Source of truth: `ops/agent-skills/zeroone-version-management.md`.

Immediately read that file and follow it. Do not duplicate or reinterpret the rule in this wrapper.
10 changes: 10 additions & 0 deletions .codex/skills/zeroone-version-management/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: zeroone-version-management
description: Use when working on ZERO-ONE production release records, PR release intent labels/body, main-branch production deploy workflow, rollback metadata, or releases/prod-*.yaml in study-platform-client.
---

# ZERO-ONE Version Management Wrapper for Codex

Source of truth: `ops/agent-skills/zeroone-version-management.md`.

Immediately read that file and follow it. Do not duplicate or reinterpret the rule in this wrapper.
260 changes: 225 additions & 35 deletions .github/workflows/deploy-prod.yml

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions .github/workflows/record-backend-prod-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# 백엔드 prod 배포 fact(repository_dispatch)를 받아 프론트 레포 releases/에 최종 atomic release record를 남긴다.

name: Record Backend Production Release

on:
repository_dispatch:
types:
- backend-prod-deployed
workflow_dispatch:
inputs:
backend_payload_json:
description: 'Backend dispatch wrapper or client_payload JSON for validation/dry-run'
required: true
dry_run:
description: 'true면 record 생성/검증만 하고 commit/push하지 않는다'
required: true
default: 'true'

permissions:
contents: write

concurrency:
group: prod-release-record
cancel-in-progress: false

jobs:
record-backend-release:
runs-on: ubuntu-latest
env:
BACKEND_RELEASE_PAYLOAD_JSON: ${{ github.event_name == 'workflow_dispatch' && inputs.backend_payload_json || toJSON(github.event.client_payload) }}
BACKEND_RELEASE_DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || 'false' }}

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'

- name: Generate backend release record
run: |
set -euo pipefail
RECORD_PATH="$(node scripts/release/generate-backend-prod-release-record.mjs)"
node scripts/release/validate-release-record.mjs "$RECORD_PATH"
echo "RECORD_PATH=$RECORD_PATH" >> "$GITHUB_ENV"
echo "Generated $RECORD_PATH"
sed -n '1,220p' "$RECORD_PATH"

- name: Upload dry-run release record
if: ${{ env.BACKEND_RELEASE_DRY_RUN == 'true' }}
uses: actions/upload-artifact@v4
with:
name: backend-release-record-dry-run
path: ${{ env.RECORD_PATH }}
if-no-files-found: error

- name: Commit release record
if: ${{ env.BACKEND_RELEASE_DRY_RUN != 'true' }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add "$RECORD_PATH"
git commit -m "chore(release): record backend prod release [release-record]"
git pull --rebase origin main
git push origin HEAD:main
26 changes: 26 additions & 0 deletions .github/workflows/release-record-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Release Record Check

on:
pull_request:
paths:
- 'releases/**/*.yaml'
- 'scripts/release/**'
- 'ops/**'
- '.github/workflows/deploy-prod.yml'
- '.github/workflows/release-record-check.yml'
workflow_dispatch:

jobs:
validate-release-records:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Validate release records
run: node scripts/release/validate-release-record.mjs releases
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,9 @@ yarn typecheck # tsc --noEmit
- `@radix-ui/*` — headless UI primitives (Dialog, DropdownMenu, Avatar, etc.)

<!-- MANUAL: Any manually added notes below this line are preserved on regeneration -->

### Production Version Management
- Main-branch production deployments must follow `ops/version-management.md` (ZERO-ONE Version Management Rule - Frontend Repository).
- Agent skill SSOT for this workflow is `ops/agent-skills/zeroone-version-management.md`; Codex/Claude skill files are thin wrappers only.
- `releases/` is the source of truth for successful production FE/BE/DB/rollback combinations.
- Develop/test-server deployment keeps the existing flow and does not create release records.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ yarn typecheck # No type errors
- No hardcoded colors/spacing. Use only `@theme inline` tokens from `global.css`.

@.claude/rules/no-img-no-eslint-disable.md
@.claude/rules/version-management-frontend.md

---

Expand Down
30 changes: 10 additions & 20 deletions e2e/class/curriculum-drawer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,34 +185,24 @@ test.describe('커리큘럼 드로어 배지 렌더링 @auth', () => {
});

test('isFree=false, isLocked=true → 잠금 배지 표시', async ({ page }) => {
const lessonLockBadge = page.getByRole('link', {
name: '잠금 Lesson 02심화 레슨',
exact: true,
});
await expect(
page
.getByRole('img', { name: '잠금', exact: true })
.or(
page
.locator('a')
.filter({ hasText: '심화 레슨' })
.locator('div')
.first(),
)
.first(),
lessonLockBadge.getByRole('img', { name: '잠금', exact: true }),
).toBeVisible();
});

test('isFree=false, isLocked=false → 잠금 해제 배지 표시', async ({
page,
}) => {
const lessonUnlockBadge = page.getByRole('link', {
name: '잠금 해제 Lesson 03결제 완료 레슨',
exact: true,
});
await expect(
page
.getByRole('img', { name: '잠금 해제', exact: true })
.or(
page
.locator('a')
.filter({ hasText: '결제 완료 레슨' })
.locator('div')
.first(),
)
.first(),
lessonUnlockBadge.getByRole('img', { name: '잠금 해제', exact: true }),
).toBeVisible();
});
});
60 changes: 60 additions & 0 deletions ops/agent-skills/zeroone-version-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# ZERO-ONE Version Management Agent Skill SSOT

Use this skill when working on production release records, release intent labels/body, `main` production deployment workflow, rollback metadata, or `releases/prod-*.yaml` in `study-platform-client`.

This file is the shared skill source of truth. Codex and Claude wrappers must stay thin and point here instead of duplicating the workflow.

## Required reading order

1. `ops/release-record-shared-contract.md` - FE/BE shared payload and final record contract.
2. `ops/version-management.md` - frontend repository rule and release-record policy.
3. `ops/release-intent.md` - human usage for PR labels/body and bootstrap.
4. `ops/deploy-checklist.md` or `ops/rollback.md` only when deploying or rolling back.
5. Relevant scripts/workflows only after the docs above:
- `.github/workflows/deploy-prod.yml`
- `.github/workflows/release-record-check.yml`
- `scripts/release/resolve-prod-release-intent.mjs`
- `scripts/release/generate-prod-release-record.mjs`
- `scripts/release/generate-backend-prod-release-record.mjs`
- `scripts/release/validate-release-record.mjs`
- `ops/backend-release-dispatch.md`
- `ops/release-record-shared-contract.md`

## Non-negotiable rules

- This skill applies to `main` production releases only. Do not change `develop` deployment behavior unless the user explicitly asks.
- `ops/release-record-shared-contract.md` is the shared FE/BE contract; `releases/` is the frontend repository source of truth for successful production FE/BE/DB/rollback combinations.
- Frontend repo owns only the frontend version-management rule. Do not add the backend repository rule here.
- Production version metadata comes from PR intent or backend dispatch payload, not per-release repository variables.
- Exactly one release intent is allowed: `release:major`, `release:minor`, or `release:patch`. Use `N/A` for no DB migration version.
- If multiple `release:*` labels are present, or label intent conflicts with body `release`, fail instead of guessing.
- First recorded frontend production release requires explicit bootstrap approval metadata in the PR body: `bootstrap: approved`, `base_version` or `version`, backend image/commit/version, and rollback frontend/backend fixed image tags.
- `prod` and `latest-prod` are pointer tags only. They are never valid rollback targets and must fail if used as inherited or supplied backend/rollback images.
- Image dates belong in `release_id`, not image tags.

## Implementation workflow for agents

1. Inspect current branch and changed files.
2. Read the docs in the required reading order.
3. For workflow/script changes, add deterministic checks for:
- first-release bootstrap without accidental defaulting,
- duplicate release label failure,
- pointer-tag rejection,
- docs/examples matching supported script keys,
- backend dispatch schema validation,
- duplicate `metadata.backend_deploy_id` rejection.
4. Validate with targeted commands first:
- `node --check scripts/release/resolve-prod-release-intent.mjs`
- local resolver smoke cases for bootstrap, duplicate labels, pointer tags, and normal latest-release inheritance.
- `node scripts/release/validate-release-record.mjs releases`
5. Then run repository checks required by the project for the changed scope.

## Human handoff format

Report:

- changed files,
- what happens on `main` merge,
- what the PR author must put in labels/body,
- verification commands and results,
- any remaining manual setup such as optional `PROD_E2E_BASE_URL`.
126 changes: 126 additions & 0 deletions ops/backend-release-dispatch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Backend Production Release Dispatch Contract

This is the frontend repository contract for backend production deployments. Backend automation is the producer of backend deploy facts. The frontend repository is the final release-record writer.

This document follows the shared FE/BE contract in `ops/release-record-shared-contract.md`.

## Trigger

Backend production deploy success must call the frontend repository with `repository_dispatch` or an equivalent API trigger.

```json
{
"event_type": "backend-prod-deployed",
"client_payload": {
"release_id": "prod-20260517-2100",
"env": "prod",
"summary": "backend patch release",
"backend": {
"repo": "study-platform-mvp",
"image": "zeroone-backend:v1.4.3-b7c8d9e",
"commit": "b7c8d9e",
"version": "v1.4.3",
"changed": true
},
"database": {
"changed": true,
"migration_version": "V45",
"migration_files": [
"src/main/resources/db/migration/V45__create_course_refund.sql"
]
},
"rollback": {
"backend": "zeroone-backend:v1.4.2-a1b2c3d"
},
"metadata": {
"release_intent": "patch",
"bootstrap_mode": false,
"previous_deploy_image": "zeroone-backend:v1.4.2-a1b2c3d",
"pull_request_number": 1234,
"pull_request_labels": ["release:patch", "db:backup-confirmed"],
"backend_deploy_id": "backend-prod-123"
}
}
}
```

The frontend workflow that receives this event is:

```txt
.github/workflows/record-backend-prod-release.yml
```

## Required `client_payload`

```json
{
"release_id": "prod-20260517-2100",
"env": "prod",
"summary": "backend patch release",
"backend": {
"repo": "study-platform-mvp",
"image": "zeroone-backend:v1.4.3-b7c8d9e",
"commit": "b7c8d9e",
"version": "v1.4.3",
"changed": true
},
"database": {
"changed": true,
"migration_version": "V45",
"migration_files": [
"src/main/resources/db/migration/V45__create_course_refund.sql"
]
},
"rollback": {
"backend": "zeroone-backend:v1.4.2-a1b2c3d"
},
"metadata": {
"release_intent": "patch",
"bootstrap_mode": false,
"previous_deploy_image": "zeroone-backend:v1.4.2-a1b2c3d",
"pull_request_number": 1234,
"pull_request_labels": ["release:patch", "db:backup-confirmed"],
"backend_deploy_id": "backend-prod-123"
}
}
```

## Required fields

- `release_id` - `prod-YYYYMMDD-HHmm`
- `env` - must be `prod`
- `backend.image` - fixed immutable backend image tag
- `backend.commit` - backend short commit
- `backend.version` - `vMAJOR.MINOR.PATCH`
- `backend.changed` - must be `true`
- `database.changed` - boolean
- `database.migration_version` - migration version or `N/A`
- `database.migration_files` - array
- `rollback.backend` - fixed immutable backend rollback image tag
- `metadata.release_intent` - `patch`, `minor`, or `major`
- `metadata.bootstrap_mode` - boolean
- `metadata.backend_deploy_id` - unique backend deployment id

## Optional fields

- `summary`
- `metadata.previous_deploy_image`
- `metadata.pull_request_number`
- `metadata.pull_request_labels`

## Frontend behavior

When the dispatch arrives, the frontend repository workflow:

1. validates the payload strictly,
2. reads the latest `releases/prod-*.yaml` to identify current frontend production state,
3. creates a new release record with `frontend.changed=false` and `backend.changed=true`,
4. writes `metadata.backend_deploy_id`,
5. fails if the same `backend_deploy_id` is already recorded,
6. fails if image tags use `prod`, `latest-prod`, dates, or non-canonical versions.

The workflow does not deploy frontend code. It only records the new atomic production combination `FE(current) + BE(new)`.

## Failure principle

Do not guess. If the payload is missing, invalid, duplicated, or the frontend repository has no previous release record to identify current frontend production state, the workflow must fail instead of writing a wrong release record.
Loading
Loading