release: develop 변경사항 main 운영 반영#613
Conversation
Why: 레슨 질문 모달에서 업로드 공개 URL을 imageKeys로 보내 백엔드가 잘못된 R2 key를 재서명하던 문제를 막기 위함. Constraint: origin/develop 기반 클린브랜치에서 레슨 QnA 이미지 key 변환만 수정하고 API 계약은 변경하지 않음. Tested: yarn eslint src/app/(class-lesson)/class/vibe-intro/lesson/[id]/_components/lesson-qna-submission-modal.tsx --fix Tested: yarn biome format --write src/app/(class-lesson)/class/vibe-intro/lesson/[id]/_components/lesson-qna-submission-modal.tsx Tested: yarn typecheck Tested: git diff --check Co-authored-by: OmX <omx@oh-my-codex.dev>
class E2E 테스트 추가 및 버그 수정
Co-authored-by: OmX <omx@oh-my-codex.dev>
Co-authored-by: OmX <omx@oh-my-codex.dev>
Co-authored-by: OmX <omx@oh-my-codex.dev>
Co-authored-by: OmX <omx@oh-my-codex.dev>
…y-20260516 QnA 이미지 키 전송 오류 수정
Co-authored-by: OmX <omx@oh-my-codex.dev>
…-notion-paste fix: 노션 복붙 본문 유실 방지
…teRequest 타입 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…화 범위 보완 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
프론트 레포를 운영 릴리즈 기록 SSOT로 두고 main prod 배포 및 backend-prod-deployed dispatch 기반 release record 자동 기록을 추가합니다.\n\n검증:\n- lint\n- prettier\n- typecheck\n- build\n- storybook\n- e2e\n- security\n- validate-release-records\n- Chromatic
Co-authored-by: OmX <omx@oh-my-codex.dev>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthrough프로덕션 릴리즈 의도 해석·검증·배포 워크플로우와 릴리즈 레코드 생성·검증을 추가하고, 에디터의 클립보드 이미지/표 처리·이미지 업로드 치환 및 preview 정리, 관리자용 클립보드 검사 페이지와 관련 문서를 도입합니다. 변경 사항프로덕션 릴리즈 자동화 시스템
클립보드 이미지 및 첨부 파일 관리
시퀀스 다이어그램sequenceDiagram
participant GH as GitHub Push
participant Intent as resolve-prod-release-intent.mjs
participant Deploy as deploy-prod.yml
participant Backend as Remote Backend (docker inspect)
participant Record as generate-prod-release-record.mjs
participant Validate as validate-release-record.mjs
participant Commit as Git Commit/Push
GH->>Intent: PR 라벨/바디에서 의도 추출
Intent->>Intent: 버전 계산 및 출력
Deploy->>Backend: SSH docker inspect 메타 조회
Deploy->>Deploy: 의도·백엔드 메타 검증
Deploy->>Deploy: 프론트엔드 이미지 pull & compose up
Deploy->>Record: 빌드 메타 + 백엔드 상태로 레코드 생성 호출
Record->>Validate: 생성된 YAML 검증
Deploy->>Commit: 검증된 레코드 main 커밋 & push
sequenceDiagram
participant BE as Backend (배포 완료)
participant Dispatch as repository_dispatch
participant FEWorkflow as record-backend-prod-release.yml
participant GenBE as generate-backend-prod-release-record.mjs
participant ValidateRec as validate-release-record.mjs
participant Git as Git Commit/Push
BE->>Dispatch: backend-prod-deployed 이벤트 (client_payload)
Dispatch->>FEWorkflow: 워크플로우 트리거
FEWorkflow->>GenBE: payload 전달
GenBE->>GenBE: 최신 FE 상태 조회 & 중복 backend_deploy_id 검사
GenBE->>ValidateRec: 생성 레코드 검증
alt dry_run=true
FEWorkflow->>FEWorkflow: 아티팩트 업로드
else
FEWorkflow->>Git: main 브랜치에 커밋 & push
end
Estimated code review effort: Possibly related PRs:
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR #613의 develop -> main 병합 전 충돌을 해소하기 위해 origin/main 변경사항을 develop에 반영합니다. 커리큘럼 드로어 배지 E2E는 develop의 scoped locator 방식을 유지해 Playwright strict mode 중복 매칭을 방지합니다. Constraint: PR #613은 develop에서 main으로 올리는 운영 반영 PR이므로 develop에 main 최신 변경사항을 먼저 흡수한다 Rejected: page-global or locator 복원 | strict mode에서 배지 컨테이너와 아이콘이 동시에 매칭될 수 있음 Confidence: high Scope-risk: moderate Tested: yarn eslint e2e/class/curriculum-drawer.spec.ts Tested: git diff --check Related: PR #613 Co-authored-by: OmX <omx@oh-my-codex.dev>
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/deploy-prod.yml:
- Around line 218-224: The workflow allows BACKEND_COMMIT and BACKEND_VERSION to
silently fall back to "unknown" which lets bad release intents proceed; update
the validation after computing BACKEND_COMMIT and BACKEND_VERSION to treat empty
or "unknown" as fatal: check the variables BACKEND_COMMIT and BACKEND_VERSION
(and similarly the second block's
INSPECTED_BACKEND_COMMIT/INSPECTED_BACKEND_VERSION fallback logic) and if either
is empty or equals "unknown" emit a clear "::error::" message and exit 1
(similar to the existing BACKEND_IMAGE check) so releases fail fast when
commit/version metadata is missing.
In `@ops/release-record-shared-contract.md`:
- Line 292: This file uses the token `bootstrap:approved` which conflicts with
the other docs that use `bootstrap: approved`; update every occurrence of
`bootstrap:approved` in this document to `bootstrap: approved` (with the space)
and scan for the alternate form elsewhere (e.g., the version-management and
release-intent docs) to ensure all release/approval token strings are normalized
to `bootstrap: approved`.
In `@scripts/release/generate-prod-release-record.mjs`:
- Around line 113-115: The code reads metadata.release_intent from
BACKEND_RELEASE_INTENT (via the backendReleaseIntent variable) which can be
empty for FE-only releases; change it to use the RELEASE_TYPE env var instead:
add or use a variable from getEnv('RELEASE_TYPE') and assign that value to
metadata.release_intent wherever backendReleaseIntent is currently used
(including the other occurrence around the block referenced by lines 185-186).
Ensure you keep getEnv usage consistent and replace references to
backendReleaseIntent with the RELEASE_TYPE-sourced variable so FE-only releases
record the correct release intent.
In `@scripts/release/resolve-prod-release-intent.mjs`:
- Around line 19-36: The parseField function currently only extracts scalar
values, so YAML array entries like database.migration_files are skipped; update
parseField (in resolve-prod-release-intent.mjs) to detect when the matched key
is followed by indented list items (lines matching /^\s*-\s*(.*)$/ at a greater
indent) and collect those list values until the indent drops, then return them
(for compatibility either as a comma-separated string or as an array depending
on the caller) instead of an empty string; ensure you reference parseField and
the path parameter when locating the change and preserve existing scalar
behavior for non-list fields.
In
`@src/app/`(class-lesson)/class/vibe-intro/lesson/[id]/_components/lesson-qna-submission-modal.tsx:
- Around line 90-99: The upload flow (setIsUploadingImage → await
uploadCommunityMarkdownImage → setImages) lacks a guard that the modal session
is still active, so a finished upload can resurrect images after the modal was
cleared; to fix, introduce and check a local session/active flag (or compare a
modalSessionId captured before awaiting) right after the await returned from
uploadCommunityMarkdownImage and before calling setImages or creating/keeping
the previewUrl, and if the modal is no longer active skip updating state (and
revoke the created preview URL if you created one but won’t use it); update
functions referenced: setIsUploadingImage, uploadCommunityMarkdownImage,
setImages, and the modal clear logic around where state is emptied to use the
same session/active marker.
In `@src/components/common/ui/editor/clipboard-utils.ts`:
- Around line 131-158: The isClipboardImageOnly function currently checks
text/plain first which can mask a HTML-only <img> clipboard entry; reorder the
logic so pastedHtml is inspected before the text/plain branch: call
removeHtmlWrappers(pastedHtml) and if it yields empty (or if pastedHtml contains
only image markup) return true or defer to
extractClipboardImageFiles(clipboardData) as appropriate, and only then proceed
to evaluate pastedText using the existing IMAGE_URL_PART, DATA_IMAGE_TEXT_PART
replacements and isAllowedUrl checks; this preserves HTML image paste while
keeping the existing text-based image URL heuristics.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 318d1248-77d9-4f45-ab54-5e0e621a93be
📒 Files selected for processing (25)
.claude/rules/version-management-frontend.md.claude/skills/zeroone-version-management/SKILL.md.codex/skills/zeroone-version-management/SKILL.md.github/workflows/deploy-prod.yml.github/workflows/record-backend-prod-release.yml.github/workflows/release-record-check.ymlAGENTS.mdCLAUDE.mde2e/class/curriculum-drawer.spec.tsops/agent-skills/zeroone-version-management.mdops/backend-release-dispatch.mdops/deploy-checklist.mdops/release-intent.mdops/release-record-shared-contract.mdops/rollback.mdops/version-management.mdpackage.jsonreleases/.gitkeepscripts/release/generate-backend-prod-release-record.mjsscripts/release/generate-prod-release-record.mjsscripts/release/resolve-prod-release-intent.mjsscripts/release/validate-release-record.mjssrc/app/(class-lesson)/class/vibe-intro/lesson/[id]/_components/lesson-qna-submission-modal.tsxsrc/components/common/ui/editor/clipboard-utils.tssrc/components/common/ui/editor/markdown-editor.tsx
| BACKEND_COMMIT="${RELEASE_BACKEND_COMMIT:-${INSPECTED_BACKEND_COMMIT:-unknown}}" | ||
| BACKEND_VERSION="${RELEASE_BACKEND_VERSION:-${INSPECTED_BACKEND_VERSION:-unknown}}" | ||
|
|
||
| if [ -z "$BACKEND_IMAGE" ]; then | ||
| echo "::error::Backend image metadata is required. Add backend_image/backend_commit/backend_version to the release intent body for the first recorded production release." | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
백엔드 커밋/버전이 unknown으로 통과될 수 있습니다
Line 218-219에서 unknown fallback을 허용하고, Line 221-224는 이미지만 검증합니다. 이 상태로 진행되면 릴리즈 레코드 품질이 깨지거나, 배포 이후 검증 단계에서 늦게 실패할 수 있습니다. BACKEND_COMMIT/BACKEND_VERSION도 여기서 즉시 실패 처리하는 게 안전합니다.
제안 수정안
BACKEND_IMAGE="${RELEASE_BACKEND_IMAGE:-${INSPECTED_BACKEND_IMAGE:-}}"
BACKEND_COMMIT="${RELEASE_BACKEND_COMMIT:-${INSPECTED_BACKEND_COMMIT:-unknown}}"
BACKEND_VERSION="${RELEASE_BACKEND_VERSION:-${INSPECTED_BACKEND_VERSION:-unknown}}"
if [ -z "$BACKEND_IMAGE" ]; then
echo "::error::Backend image metadata is required. Add backend_image/backend_commit/backend_version to the release intent body for the first recorded production release."
exit 1
fi
+ if [ -z "$BACKEND_COMMIT" ] || [ "$BACKEND_COMMIT" = "unknown" ]; then
+ echo "::error::Backend commit metadata is required and must not be unknown."
+ exit 1
+ fi
+ if [ -z "$BACKEND_VERSION" ] || [ "$BACKEND_VERSION" = "unknown" ]; then
+ echo "::error::Backend version metadata is required and must not be unknown."
+ exit 1
+ fiAlso applies to: 230-245
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/deploy-prod.yml around lines 218 - 224, The workflow
allows BACKEND_COMMIT and BACKEND_VERSION to silently fall back to "unknown"
which lets bad release intents proceed; update the validation after computing
BACKEND_COMMIT and BACKEND_VERSION to treat empty or "unknown" as fatal: check
the variables BACKEND_COMMIT and BACKEND_VERSION (and similarly the second
block's INSPECTED_BACKEND_COMMIT/INSPECTED_BACKEND_VERSION fallback logic) and
if either is empty or equals "unknown" emit a clear "::error::" message and exit
1 (similar to the existing BACKEND_IMAGE check) so releases fail fast when
commit/version metadata is missing.
|
|
||
| ## 8. Bootstrap | ||
|
|
||
| If current production is not yet on canonical image tags, backend may use bootstrap mode only with explicit `bootstrap:approved` approval on the backend side. |
There was a problem hiding this comment.
bootstrap 승인 토큰 표기를 다른 운영 문서와 일치시켜 주세요.
여기서는 bootstrap:approved를 사용하지만, ops/version-management.md와 ops/release-intent.md는 bootstrap: approved를 사용합니다. 배포 승인 절차 문서가 이중 표기를 가지면 운영/자동화 해석이 갈려 릴리즈 기록 생성이 실패할 수 있습니다. 한 가지 표기로 통일해 주세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@ops/release-record-shared-contract.md` at line 292, This file uses the token
`bootstrap:approved` which conflicts with the other docs that use `bootstrap:
approved`; update every occurrence of `bootstrap:approved` in this document to
`bootstrap: approved` (with the space) and scan for the alternate form elsewhere
(e.g., the version-management and release-intent docs) to ensure all
release/approval token strings are normalized to `bootstrap: approved`.
| setIsUploadingImage(true); | ||
| try { | ||
| const publicUrl = await uploadCommunityMarkdownImage(file); | ||
| const key = new URL(publicUrl).pathname.slice(1); | ||
| const previewUrl = URL.createObjectURL(file); | ||
| setImages((prev) => [...prev, { previewUrl, key: publicUrl }]); | ||
| setImages((prev) => [...prev, { previewUrl, key }]); | ||
| } catch { | ||
| showToast('이미지 업로드에 실패했습니다.', 'error'); | ||
| } finally { | ||
| setIsUploadingImage(false); |
There was a problem hiding this comment.
모달을 닫은 뒤 완료된 업로드가 첨부 이미지를 다시 살릴 수 있습니다.
Line 92-95의 await 뒤에는 현재 모달 세션을 확인하는 가드가 없습니다. 사용자가 업로드 중에 닫으면 Line 77-80에서 상태를 비운 뒤에도, 나중에 완료된 업로드가 setImages로 새 blob URL을 다시 넣어 버립니다. 그러면 다음에 모달을 열었을 때 이전 세션의 첨부가 되살아나고, 그 preview URL도 다음 정리 시점까지 해제되지 않습니다.
🔧 수정 예시
const fileInputRef = useRef<HTMLInputElement>(null);
const imageListRef = useRef<AttachedImage[]>([]);
+ const openRef = useRef(open);
+ const modalSessionRef = useRef(0);
@@
useEffect(() => {
+ openRef.current = open;
if (!open) {
+ modalSessionRef.current += 1;
clearImagePreviews();
}
}, [open]);
async function handleImageAdd(file: File) {
+ const modalSession = modalSessionRef.current;
if (images.length >= 10) {
showToast('최대 10장까지 첨부 가능합니다.', 'error');
return;
}
setIsUploadingImage(true);
try {
const publicUrl = await uploadCommunityMarkdownImage(file);
+ if (!openRef.current || modalSession !== modalSessionRef.current) {
+ return;
+ }
const key = new URL(publicUrl).pathname.slice(1);
const previewUrl = URL.createObjectURL(file);
setImages((prev) => [...prev, { previewUrl, key }]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/app/`(class-lesson)/class/vibe-intro/lesson/[id]/_components/lesson-qna-submission-modal.tsx
around lines 90 - 99, The upload flow (setIsUploadingImage → await
uploadCommunityMarkdownImage → setImages) lacks a guard that the modal session
is still active, so a finished upload can resurrect images after the modal was
cleared; to fix, introduce and check a local session/active flag (or compare a
modalSessionId captured before awaiting) right after the await returned from
uploadCommunityMarkdownImage and before calling setImages or creating/keeping
the previewUrl, and if the modal is no longer active skip updating state (and
revoke the created preview URL if you created one but won’t use it); update
functions referenced: setIsUploadingImage, uploadCommunityMarkdownImage,
setImages, and the modal clear logic around where state is emptied to use the
same session/active marker.
| export const isClipboardImageOnly = (clipboardData: DataTransfer) => { | ||
| const pastedText = clipboardData.getData('text/plain').trim(); | ||
| const pastedHtml = clipboardData.getData('text/html').trim(); | ||
|
|
||
| if (pastedText) { | ||
| const textWithoutImageRefs = pastedText | ||
| .replace(IMAGE_URL_PART, '') | ||
| .replace(DATA_IMAGE_TEXT_PART, '') | ||
| .replace(/\s+/g, '') | ||
| .trim(); | ||
|
|
||
| if (textWithoutImageRefs) { | ||
| return false; | ||
| } | ||
|
|
||
| return !!isAllowedUrl(pastedText, ['image', 'data-image']); | ||
| } | ||
|
|
||
| if (!pastedHtml) { | ||
| return extractClipboardImageFiles(clipboardData).length > 0; | ||
| } | ||
|
|
||
| const htmlText = removeHtmlWrappers(pastedHtml); | ||
| if (htmlText) { | ||
| return false; | ||
| } | ||
|
|
||
| return true; |
There was a problem hiding this comment.
text/plain 우선 분기 때문에 HTML 이미지 붙여넣기가 막힐 수 있습니다.
Line 135-147이 먼저 false를 반환하면, 클립보드에 <img>만 들어 있는 text/html이 있어도 HTML 분기를 보지 못합니다. 그래서 브라우저가 text/plain으로 페이지 URL이나 확장자 없는 CDN URL을 같이 넣는 경우, 이제 이미지 붙여넣기가 일반 텍스트 붙여넣기로 퇴행할 수 있습니다. HTML에 실제 이미지가 있고 래퍼 제거 후 잔여 텍스트가 없으면 그쪽을 먼저 우선해야 합니다.
🔧 수정 예시
export const isClipboardImageOnly = (clipboardData: DataTransfer) => {
- const pastedText = clipboardData.getData('text/plain').trim();
const pastedHtml = clipboardData.getData('text/html').trim();
- if (pastedText) {
+ if (pastedHtml) {
+ const htmlText = removeHtmlWrappers(pastedHtml);
+ if (htmlText) {
+ return false;
+ }
+
+ if (extractHtmlImageUrls(pastedHtml).length > 0) {
+ return true;
+ }
+ }
+
+ const pastedText = clipboardData.getData('text/plain').trim();
+ if (pastedText) {
const textWithoutImageRefs = pastedText
.replace(IMAGE_URL_PART, '')
.replace(DATA_IMAGE_TEXT_PART, '')
.replace(/\s+/g, '')
.trim();
@@
- if (!pastedHtml) {
- return extractClipboardImageFiles(clipboardData).length > 0;
- }
-
- const htmlText = removeHtmlWrappers(pastedHtml);
- if (htmlText) {
- return false;
- }
-
- return true;
+ return extractClipboardImageFiles(clipboardData).length > 0;
};🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/common/ui/editor/clipboard-utils.ts` around lines 131 - 158,
The isClipboardImageOnly function currently checks text/plain first which can
mask a HTML-only <img> clipboard entry; reorder the logic so pastedHtml is
inspected before the text/plain branch: call removeHtmlWrappers(pastedHtml) and
if it yields empty (or if pastedHtml contains only image markup) return true or
defer to extractClipboardImageFiles(clipboardData) as appropriate, and only then
proceed to evaluate pastedText using the existing IMAGE_URL_PART,
DATA_IMAGE_TEXT_PART replacements and isAllowedUrl checks; this preserves HTML
image paste while keeping the existing text-based image URL heuristics.
Notion 등 복합 HTML 붙여넣기에서 텍스트/표/이미지 순서를 보존하고, 실제 클립보드 payload 확인을 위한 어드민 진단 페이지를 추가합니다.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/common/ui/editor/markdown-editor.tsx`:
- Around line 397-414: The current paste flow lets the browser insert mixed HTML
(including <img>) before replaceMixedClipboardImagesAfterDefaultPaste runs,
allowing disallowed or too-many images to remain; before calling
replaceMixedClipboardImagesAfterDefaultPaste (and before allowing default paste
when markdownTable is undefined), pre-scan pastedHtml for image nodes and
validate against the editor's imageConfig (e.g., in AdminCourseMarkdownEditor)
and the uploader limits: if any image is disallowed or would exceed limits, call
event.preventDefault() and either block the paste entirely or sanitize the HTML
by removing invalid/overflowing <img> nodes then insert the cleaned content
programmatically via insertMarkdownTable/Editor API; otherwise allow the default
paste and continue to call replaceMixedClipboardImagesAfterDefaultPaste for
upload/replace handling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 28f1cd7d-f862-4d0d-91b2-e30313ea37ab
📒 Files selected for processing (6)
src/app/(admin)/admin/clipboard-inspector/page.tsxsrc/components/admin/courses/admin-course-markdown-editor.tsxsrc/components/common/ui/editor/markdown-editor.tsxsrc/components/common/ui/editor/markdown-table-utils.tssrc/components/common/ui/editor/use-image-upload.tssrc/features/admin/course-management/model/admin-course-markdown.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/common/ui/editor/markdown-table-utils.ts
공용 MarkdownEditor의 복합 붙여넣기에서 표를 마크다운 표로 보존하고 Notion attachment 이미지는 위치 안내 문구로 남깁니다.
Co-authored-by: OmX <omx@oh-my-codex.dev>
Co-authored-by: OmX <omx@oh-my-codex.dev>
요약
develop에 누적된 변경사항을main운영 배포 대상으로 올리는 PR입니다.release:major라벨로 표시합니다.릴리즈/버전관리 참고
main대상이므로 운영 release/version-management 규칙이 적용됩니다.release:major를 기준으로 main 배포 자동화가 frontend version bump 의도를 읽습니다.releases/prod-*.yaml에 기록됩니다.backend-prod-deployeddispatch로 기록해야 합니다.PR 생성 전 확인
origin/main..origin/develop차이를 확인했습니다.develop -> main오픈 PR이 없는 것을 확인했습니다.AGENTS.mdops/agent-skills/zeroone-version-management.mdops/release-record-shared-contract.mdops/version-management.mdops/release-intent.md머지 전 운영 체크리스트
major가 맞는지 확인하세요.운영 릴리즈 기록 순서
main에 record workflow/script가 없어서 run 25997420948에서 실패했습니다.releases/prod-20260517-1941.yaml로 기존 frontend/backend baseline을 수동 기록하고,releases/prod-20260518-0210.yaml로 backend-prod-56 결과를 수동 기록합니다.prod-20260518-0210을 상속해 frontend-onlyrelease:major배포 record를 추가합니다.record-backend-prod-release.yml이main에 존재하므로backend-prod-deployeddispatch가 자동 release record를 남기는 경로로 동작합니다.Summary by CodeRabbit
릴리스 노트
New Features
Bug Fixes
Documentation
Tests