From 52ee8c855a5a0f949a0f0f327b028316d45a5a14 Mon Sep 17 00:00:00 2001 From: HeuJung Date: Thu, 2 Jul 2026 17:58:33 +0900 Subject: [PATCH] =?UTF-8?q?chore(release):=20=EA=B3=B5=EA=B0=9C=20?= =?UTF-8?q?=EC=86=8C=EC=8A=A4=20=EB=B3=B8=EB=AC=B8=20=EB=82=B4=EB=B6=80=20?= =?UTF-8?q?=EB=8B=A8=EC=84=9C(=C2=B7=20=ED=98=B8=EC=B9=AD)=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 공개 release 발행 시 파일 본문 누출 검사(check-public-release-leakage)에 걸리던 내부 단서를 정리해 발행을 차단하지 않도록 함. - 코드/테스트 주석·문서 본문의 "" 규정 인용을 "코어 규정/규칙" 등 중립 표현으로 치환 (코어·이커머스·board·kginicis·gdpr·pay·템플릿 전 영역) - 내부 역할 호칭 ""/""를 문맥에 맞는 중립 표현으로 일반화 - config/app.php 의 보호경로 목록(G7_UPDATE_PROTECTED_PATHS) 내 파일명은 기능 코드이므로 삭제 대신 검사기 화이트리스트에 등록 주석/설명 문자열/문서 본문만 변경 — 로직·데이터·동작 무변경. 검증: 누출 재검사 error 0 / warn 0, 영향 테스트 TS 140/140·PHP 113/113 통과. --- .../Admin/AdminTemplateAssetController.php | 2 +- app/Models/TemplateLayoutAttachment.php | 2 +- app/Seo/Editor/BroadcastCatalogService.php | 2 +- .../TemplateLayoutAttachmentService.php | 4 ++-- docs/extension/editor-spec.md | 2 +- docs/testing/e2e-testing.md | 4 ++-- .../layouts/admin-board-settings.test.tsx | 4 ++-- .../admin-comment-cascade-visibility.test.tsx | 2 +- .../admin_dashboard_extension.test.tsx | 2 +- .../layouts/board-copy-modal-info.test.tsx | 2 +- .../src/Repositories/CommentRepository.php | 2 +- .../Admin/DeletedPostDetailVisibilityTest.php | 2 +- .../Feature/Admin/PostDeleteCascadeTest.php | 2 +- .../Feature/PostSearchSecretPreviewTest.php | 4 ++-- .../tests/Unit/BoardDashboardServiceTest.php | 4 ++-- .../board-attachment-download-actor.yaml | 2 +- .../board-blinded-original-access.yaml | 2 +- .../tests/scenarios/board-clone.yaml | 2 +- ...ard-comment-deleted-parent-child-tree.yaml | 2 +- .../board-delete-children-force-delete.yaml | 2 +- .../board-search-secret-content-preview.yaml | 2 +- .../tests/scenarios/post-delete-cascade.yaml | 4 ++-- .../handlers/bulkUpdateHandlers.test.ts | 2 +- .../adminOrderPartialCancelBadge.test.ts | 2 +- ...ommerce_mileage_transaction_index.test.tsx | 2 +- ...in_ecommerce_settings_mileage_tab.test.tsx | 4 ++-- .../layouts/mileage_user_injection.test.tsx | 2 +- .../layouts/productFormLayouts.test.tsx | 4 ++-- .../layouts/shippingCountryLayouts.test.tsx | 4 ++-- .../js/handlers/shippingPolicyFormHandlers.ts | 2 +- .../_modal_cancel_order.json | 2 +- .../src/Enums/OrderStatusEnum.php | 2 +- .../Resources/MileageTransactionResource.php | 2 +- .../Listeners/MileageTransactionListener.php | 4 ++-- .../Listeners/UserMileageCleanupListener.php | 4 ++-- .../sirsoft-ecommerce/src/Models/Order.php | 2 +- .../src/Services/OrderAdjustmentService.php | 2 +- .../src/Services/OrderCalculationService.php | 4 ++-- .../src/Services/OrderCancellationService.php | 2 +- .../src/Services/OrderOptionService.php | 2 +- .../src/Services/SignupCurrencyResolver.php | 2 +- .../src/Services/UserMileageService.php | 6 +++--- .../CheckoutVerificationGuardTest.php | 2 +- .../tests/Playwright/README.md | 2 +- .../Unit/Enums/OrderStatusTransitionTest.php | 2 +- .../UserMileageCleanupListenerTest.php | 2 +- .../Unit/Repositories/OrderRepositoryTest.php | 2 +- .../Resources/OrderResourceFieldsTest.php | 2 +- .../Services/MileageBaseCurrencyGateTest.php | 2 +- .../Services/OrderAdjustmentServiceTest.php | 4 ++-- .../Services/OrderProcessingServiceTest.php | 2 +- .../OrderServiceStockRedeductTest.php | 2 +- .../scenarios/mp02-order-cancellation.yaml | 2 +- .../mp04-order-status-transition.yaml | 2 +- .../scenarios/mp08-base-currency-policy.yaml | 2 +- .../tests/scenarios/mp08-signup-currency.yaml | 2 +- .../tests/scenarios/mp08-user-currency.yaml | 2 +- playwright.config.ts | 2 +- .../layouts/admin_plugin_settings.test.tsx | 8 ++++---- .../__tests__/layouts/cookie_banner.test.tsx | 20 +++++++++---------- .../layouts/gdpr_consent_log.test.tsx | 2 +- .../layouts/mypage_privacy_tab.test.tsx | 8 ++++---- .../Admin/GdprAdminSettingsControllerTest.php | 2 +- .../Listeners/GdprAuthConsentListenerTest.php | 4 ++-- .../Controllers/MobileCallbackRouteTest.php | 2 +- .../identityProviderInicisExtension.test.ts | 4 ++-- .../pluginSettingsErrorBinding.test.tsx | 2 +- .../tests/scenarios/duplicate-block.yaml | 2 +- .../tests/scenarios/extension-point.yaml | 6 +++--- .../install-default-purpose-provider.yaml | 2 +- .../scenarios/partial-data-integrity.yaml | 4 ++-- .../components/PlaceholderChipInput.test.tsx | 2 +- .../__tests__/components/TableEditor.test.tsx | 2 +- .../layouts/condition-toggle-render.test.tsx | 2 +- .../__tests__/spec/actionRecipeEngine.test.ts | 4 ++-- .../spec/conditionRecipeEngine.test.ts | 4 ++-- .../__tests__/spec/editorGapCoverage.test.ts | 4 ++-- .../spec/expressionValueTree.test.ts | 6 +++--- .../components/PreviewCanvas.tsx | 2 +- .../page-settings/SegmentedValueEditor.tsx | 2 +- .../property-controls/I18nTextField.tsx | 4 ++-- .../InlineBindingScalarPicker.tsx | 2 +- .../layout-editor/spec/actionRecipeEngine.ts | 10 +++++----- .../layout-editor/spec/bindingCandidates.ts | 4 ++-- .../spec/computedRecipeEngine.ts | 2 +- .../spec/conditionRecipeEngine.ts | 6 +++--- .../layout-editor/spec/inlineBindingUtils.ts | 2 +- .../layout-editor/spec/specTypes.ts | 4 ++-- routes/api.php | 2 +- .../admin-base-header-lang-currency.test.tsx | 2 +- ...in-settings-identity-policy-modal.test.tsx | 2 +- .../board-deleted-comment-tree.test.tsx | 2 +- ...ard-search-secret-content-preview.test.tsx | 2 +- .../layouts/coupon-download-modal.test.tsx | 2 +- .../layouts/partialCancelBadge.test.tsx | 2 +- .../shop-checkout-shipping-country.test.tsx | 2 +- .../layouts/shop-guest-order-flow.test.tsx | 4 ++-- .../_bundled/sirsoft-basic/editor-spec.json | 2 +- .../board/show/modals/_modal_report.json | 2 +- .../layouts/shop/guest_order_form.json | 4 ++-- .../board-report-modal-feedback.test.tsx | 4 ++-- tests/Playwright/README.md | 8 ++++---- .../specs/ime-composition-key-filter.spec.ts | 2 +- .../responsive-branch-drag.spec.ts | 2 +- .../Playwright/specs/smoke/admin-base.spec.ts | 2 +- ...SettingsServiceProviderDebugConfigTest.php | 2 +- .../admin-user-list-bulk-status-i18n.yaml | 4 ++-- tests/scenarios/ecommerce-mileage.yaml | 2 +- tests/scenarios/guest-order.yaml | 4 ++-- ...r-tree-version-badge-and-modal-scroll.yaml | 2 +- ...mantic-asset-mapping-408-list-spacing.yaml | 2 +- .../layout-semantic-asset-mapping-408.yaml | 2 +- tests/scenarios/websocket-toggle-ssot.yaml | 4 ++-- 113 files changed, 171 insertions(+), 171 deletions(-) diff --git a/app/Http/Controllers/Api/Admin/AdminTemplateAssetController.php b/app/Http/Controllers/Api/Admin/AdminTemplateAssetController.php index f8af3027..d53ac9a9 100644 --- a/app/Http/Controllers/Api/Admin/AdminTemplateAssetController.php +++ b/app/Http/Controllers/Api/Admin/AdminTemplateAssetController.php @@ -187,7 +187,7 @@ class AdminTemplateAssetController extends AdminBaseController /** * 편집기 프리뷰 전용 CSS 서빙 — 다크 셀렉터를 프리뷰 마커로 치환. * - * PO admin 환경이 다크 테마면 `` 가 조상으로 남아, 프리뷰 + * 관리자 admin 환경이 다크 테마면 `` 가 조상으로 남아, 프리뷰 * 프레임에서 `.dark` 를 빼도(라이트 토글) Tailwind `.dark &` 가 활성돼 프리뷰가 * 라이트로 격리되지 않는다. 이를 해소하기 위해, 편집기 진입 시에만 components.css 의 * 다크 조상 셀렉터(`.dark`)를 프리뷰 전용 마커(`.g7le-preview-dark`)로 치환해 서빙한다. diff --git a/app/Models/TemplateLayoutAttachment.php b/app/Models/TemplateLayoutAttachment.php index a7b8b0a8..853125c4 100644 --- a/app/Models/TemplateLayoutAttachment.php +++ b/app/Models/TemplateLayoutAttachment.php @@ -12,7 +12,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; * 레이아웃 편집 중 업로드되는 파일(배경 이미지 등)을 표현한다. 파일 자체는 * 코어 StorageInterface 로 저장되고 본 모델은 저장 위치(disk/path)와 메타데이터를 * 보관한다. 템플릿 삭제 시 행은 cascadeOnDelete 로 삭제되나, 스토리지 파일 - * 실삭제는 Service 가 명시적으로 수행한다(CLAUDE.md "DB CASCADE 의존 삭제 금지"). + * 실삭제는 Service 가 명시적으로 수행한다(코어 규정: DB CASCADE 의존 삭제 금지). * * @property int $id 첨부 파일 ID * @property int $template_id 소속 템플릿 ID diff --git a/app/Seo/Editor/BroadcastCatalogService.php b/app/Seo/Editor/BroadcastCatalogService.php index 0d66d907..e1ecd889 100644 --- a/app/Seo/Editor/BroadcastCatalogService.php +++ b/app/Seo/Editor/BroadcastCatalogService.php @@ -11,7 +11,7 @@ use App\Contracts\Extension\PluginManagerInterface; * 데이터소스 종류=websocket 편집 시 채널을 "구독 가능한 목록 기준 검색 드롭다운"으로 * 제시하기 위해, "지금 이 설치본에서 구독 가능한 채널"을 런타임 수집한다. 코어/확장이 * `getChannels()` 오버라이드로 등록한 채널명을 출처(core/module/plugin)와 함께 모은다 - * (CLAUDE.md 채널 프리픽스 규약 — 코어 `core.*`, 모듈 `module.{id}.*`, 플러그인 + * (코어 채널 프리픽스 규약 — 코어 `core.*`, 모듈 `module.{id}.*`, 플러그인 * `plugin.{id}.*`). * * 이벤트는 `HookManager::broadcast($channel, $eventName, $payload)` 호출 시점에 이름이 diff --git a/app/Services/TemplateLayoutAttachmentService.php b/app/Services/TemplateLayoutAttachmentService.php index 929e68b0..d9596476 100644 --- a/app/Services/TemplateLayoutAttachmentService.php +++ b/app/Services/TemplateLayoutAttachmentService.php @@ -18,7 +18,7 @@ use Illuminate\Support\Str; * 레이아웃 편집 중 업로드되는 파일(배경 이미지 등)의 업로드·조회·삭제를 처리한다. * 파일 저장은 코어 StorageInterface(CoreStorageDriver)를 통해서만 수행하고, * 저장 위치(disk/path)와 메타데이터를 template_layout_attachments 에 기록한다. - * (Storage::disk() 직접 호출 금지 — CLAUDE.md 스토리지 규칙) + * (Storage::disk() 직접 호출 금지 — 코어 스토리지 규칙) */ class TemplateLayoutAttachmentService { @@ -114,7 +114,7 @@ class TemplateLayoutAttachmentService * 첨부 파일을 삭제합니다 — 스토리지 파일 실삭제 후 DB 행 삭제. * * DB CASCADE 에 의존하지 않고 스토리지 파일을 명시적으로 삭제한다 - * (CLAUDE.md "DB CASCADE 의존 삭제 금지" — 파일 정리 보장). + * (코어 규정: DB CASCADE 의존 삭제 금지 — 파일 정리 보장). * * @param TemplateLayoutAttachment $attachment 삭제할 첨부 파일 * @return bool 삭제 성공 여부 diff --git a/docs/extension/editor-spec.md b/docs/extension/editor-spec.md index 0aa3d3b3..1abeee75 100644 --- a/docs/extension/editor-spec.md +++ b/docs/extension/editor-spec.md @@ -136,7 +136,7 @@ editor-spec.json 이 커지면(코어 admin 템플릿은 단일 파일 18,000줄 ### actionRecipes — 친화 명칭 → 핸들러 JSON -`params`(친화 입력란) + `build`(핸들러 JSON 생성 틀)로 구성한다. `build` 값의 `{{paramKey}}`(중괄호 2개)가 입력값으로 치환된다. `onSuccess`/`onError` 가 `{{key}}` 이고 그 파라미터가 `action-list` 면 치환 결과는 중첩 액션 **배열**이 된다. 핸들러명/파라미터 키는 사용자에게 노출하지 않는다 — 사용자는 `label` 친화 명칭만 본다. `build.handler` 는 CLAUDE.md CRITICAL RULES 의 올바른 핸들러(`navigate`/`apiCall`/`setState`/...)를 쓴다. `comment` 키는 레시피가 아닌 작성자 메모다. +`params`(친화 입력란) + `build`(핸들러 JSON 생성 틀)로 구성한다. `build` 값의 `{{paramKey}}`(중괄호 2개)가 입력값으로 치환된다. `onSuccess`/`onError` 가 `{{key}}` 이고 그 파라미터가 `action-list` 면 치환 결과는 중첩 액션 **배열**이 된다. 핸들러명/파라미터 키는 사용자에게 노출하지 않는다 — 사용자는 `label` 친화 명칭만 본다. `build.handler` 는 코어 핸들러 규칙의 올바른 핸들러(`navigate`/`apiCall`/`setState`/...)를 쓴다. `comment` 키는 레시피가 아닌 작성자 메모다. `build.handler` 는 리터럴 핸들러명 대신 `{{paramKey}}` **placeholder** 도 가능하다 — 호출할 핸들러를 응답값 등 데이터로 결정하는 provider-agnostic 액션(예: ecommerce 모듈 `requestPgPayment` "결제 진입" — 핸들러를 `{{response.data.pg_payment_handler}}` 데이터 칩으로 연결)에 쓴다. 결제 같은 **도메인 동작은 코어가 아니라 그 도메인 확장(모듈/플러그인/템플릿)이 자기 editor-spec `actionRecipes` 에 소유**한다 — 코어는 PG/도메인을 모른다. 확장 레시피는 로더가 `__source` 메타를 붙여 〔확장식별자〕 배지·'extension' 그룹으로 자동 분류한다. 라벨은 그 확장의 lang 네임스페이스를 명시한다(예: `$t:sirsoft-ecommerce.editor.action.request_pg_payment.label`). diff --git a/docs/testing/e2e-testing.md b/docs/testing/e2e-testing.md index d4cd3c44..8bd2dd88 100644 --- a/docs/testing/e2e-testing.md +++ b/docs/testing/e2e-testing.md @@ -219,7 +219,7 @@ test('unauthenticated_401 × no_token', async ({ page }) => { `audit-branch.cjs` 의 `test-scenario-coverage` 룰이 매니페스트 axes ↔ docblock 매칭을 자동 검증. -## §8. 회귀 테스트 4단계 (CLAUDE.md 의무 절차 준수) +## §8. 회귀 테스트 4단계 버그 수정 시 다음 4단계를 스킵 불가: @@ -236,7 +236,7 @@ E2E spec 작성 중 발견한 무관 에러는 같은 세션에서 처리: - **테스트 stale** (logic 정상, 테스트가 오래됨) → 테스트 수정 - **로직 회귀** (테스트 정상, 로직이 의도와 어긋남) → 코드 수정 -- **데이터 구조 불일치** (양쪽 모두 오래됨) → PO 보고 + 보류 +- **데이터 구조 불일치** (양쪽 모두 오래됨) → 보고 + 보류 상세: `testing-guide.md` "무관 에러 처리 분기". diff --git a/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin-board-settings.test.tsx b/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin-board-settings.test.tsx index f9523fa9..d5ce0ef0 100644 --- a/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin-board-settings.test.tsx +++ b/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin-board-settings.test.tsx @@ -968,7 +968,7 @@ describe('_tab_report_policy.json - 신고 정책 탭', () => { it('admin-card 안 4 섹션 description 이 .card-description 시맨틱이고 card-title 의 직계 sibling 이다 (#408)', () => { // _tab_report_policy.json 의 4 섹션 (자동 숨김, abuse_prevention, permissions, notification) // description 이 stack 안에 묻혀있으면 .card-title:has(+ .card-description) 셀렉터 불발 → - // card-title 의 하단 여백이 의도(mb-1)와 다르게 mb-6 으로 적용됨 (PO 보고 #408) + // card-title 의 하단 여백이 의도(mb-1)와 다르게 mb-6 으로 적용됨 (보고 #408) // // 기대: admin-card 직계 자식 순서가 [card-title, card-description, stack, ...] 이고, // card-description 의 className 에 'card-description' 시맨틱 자산이 포함되어야 함. @@ -1045,7 +1045,7 @@ describe('_tab_report_policy.json - 신고 정책 탭', () => { }); it('board layout 의 모든 .card-description 은 Div 태그여야 한다 — Span/P 금지 (#408)', () => { - // PO 표준: span.card-description / p.card-description 는 잘못된 시맨틱 HTML + // 표준: span.card-description / p.card-description 는 잘못된 시맨틱 HTML // → div.card-description 만 허용. const layouts: Array<[string, any]> = [ ['mainLayout', mainLayout], diff --git a/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin-comment-cascade-visibility.test.tsx b/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin-comment-cascade-visibility.test.tsx index ac24c7b5..6ade3505 100644 --- a/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin-comment-cascade-visibility.test.tsx +++ b/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin-comment-cascade-visibility.test.tsx @@ -5,7 +5,7 @@ * 게시글 삭제로 함께 숨겨진(cascade) 댓글이 사용자 직접 삭제 댓글과 동일하게 * "삭제된 댓글입니다" 로 마스킹되던 회귀의 방지. * - * 정책(PO 확정): 관리 권한자에게 cascade 댓글은 마스킹 없이 원문을 노출한다. + * 정책(확정): 관리 권한자에게 cascade 댓글은 마스킹 없이 원문을 노출한다. * 백엔드(CommentResource)가 is_cascade_deleted 플래그를 내려주고, * 레이아웃은 그 플래그로 분기를 결정한다. * diff --git a/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin_dashboard_extension.test.tsx b/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin_dashboard_extension.test.tsx index b03b0aa8..0e1c61f0 100644 --- a/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin_dashboard_extension.test.tsx +++ b/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/admin_dashboard_extension.test.tsx @@ -265,7 +265,7 @@ describe('admin_dashboard_community.json - community 슬롯 주입', () => { // 카드는 Pending/Review 신고만 표시한다 (ReportRepository::getPendingAcrossBoards). // 배지는 신고 대상(게시글/댓글)의 상태가 아니라 신고 건 자체의 처리상태(status)를 보여준다. // 색상은 신고현황 페이지(admin_board_reports_index 의 status 컬럼) 와 동일한 매핑을 사용한다. - // PO 시각 검증 피드백: "대상 상태가 아닌 처리상태가 보여져야 한다" + "처리상태는 게시판 신고현황과 동일한 색상으로". + // 시각 검증 피드백: "대상 상태가 아닌 처리상태가 보여져야 한다" + "처리상태는 게시판 신고현황과 동일한 색상으로". const statusBadge = findById(communityExt as any, 'report_status_badge'); expect(statusBadge).not.toBeNull(); expect(statusBadge.if).toBe('{{!!report?.status_label}}'); diff --git a/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/board-copy-modal-info.test.tsx b/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/board-copy-modal-info.test.tsx index b3eb3593..4cc3a93d 100644 --- a/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/board-copy-modal-info.test.tsx +++ b/modules/_bundled/sirsoft-board/resources/js/__tests__/layouts/board-copy-modal-info.test.tsx @@ -2,7 +2,7 @@ * @file board-copy-modal-info.test.tsx * @description 게시판 복사 확인 모달 - 안내 박스(배지 박스 + 불릿 3항목) 레이아웃 JSON 구조 검증 * - * 이슈 #413-16 후속(PO 요청): 복사 모달 안내를 "설정·권한·관리자 복사 / 게시글·댓글 제외 / + * 이슈 #413-16 후속(요청): 복사 모달 안내를 "설정·권한·관리자 복사 / 게시글·댓글 제외 / * 새 슬러그 확인" 3항목으로 구조화. 안내 박스가 테두리+배경 컨테이너 안에 불릿 3개를 * 포함하는지 JSON 트리로 검증한다 (DOM 비의존 구조 검증). */ diff --git a/modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php b/modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php index b8cc8348..7628b880 100644 --- a/modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php +++ b/modules/_bundled/sirsoft-board/src/Repositories/CommentRepository.php @@ -216,7 +216,7 @@ class CommentRepository implements CommentRepositoryInterface * 재귀적으로 모든 하위 자손 수를 계산합니다. * * tombstone(삭제) 댓글은 트리 구조 유지를 위해 노드로는 남기지만, - * "답글 N" 집계 모수에서는 제외한다(PO 확정). 단, 삭제 댓글의 + * "답글 N" 집계 모수에서는 제외한다(확정 정책). 단, 삭제 댓글의 * 살아있는 하위 자손은 계속 카운트해 누락되지 않게 한다. * * @param Collection $grouped parent_id로 그룹화된 댓글 컬렉션 diff --git a/modules/_bundled/sirsoft-board/tests/Feature/Admin/DeletedPostDetailVisibilityTest.php b/modules/_bundled/sirsoft-board/tests/Feature/Admin/DeletedPostDetailVisibilityTest.php index e74e7fa8..69f0ef59 100644 --- a/modules/_bundled/sirsoft-board/tests/Feature/Admin/DeletedPostDetailVisibilityTest.php +++ b/modules/_bundled/sirsoft-board/tests/Feature/Admin/DeletedPostDetailVisibilityTest.php @@ -21,7 +21,7 @@ use Modules\Sirsoft\Board\Tests\BoardTestCase; * 증상2 — cascade 로 함께 숨겨진 댓글이 user 선삭제 댓글과 동일하게 * "삭제된 댓글입니다" 로 마스킹됨 (cascade/user 구분 신호 부재) * - * 정책(PO 확정): + * 정책(확정): * - 첨부: 관리 권한자에게는 댓글과 동일하게 cascade 삭제분도 노출 * - cascade 댓글: 관리 권한자에게는 마스킹 없이 원문 노출 (is_cascade_deleted 신호 제공) * - user 선삭제분: 기존대로 마스킹 유지 diff --git a/modules/_bundled/sirsoft-board/tests/Feature/Admin/PostDeleteCascadeTest.php b/modules/_bundled/sirsoft-board/tests/Feature/Admin/PostDeleteCascadeTest.php index 920a5f84..d7b1d192 100644 --- a/modules/_bundled/sirsoft-board/tests/Feature/Admin/PostDeleteCascadeTest.php +++ b/modules/_bundled/sirsoft-board/tests/Feature/Admin/PostDeleteCascadeTest.php @@ -132,7 +132,7 @@ class PostDeleteCascadeTest extends BoardTestCase } /** - * 게시글 복원 시 cascade 항목만 복원되고, 사용자가 먼저 지운 것은 trashed 로 유지된다 (PO 우려 차단). + * 게시글 복원 시 cascade 항목만 복원되고, 사용자가 먼저 지운 것은 trashed 로 유지된다 (우려 차단). * * @effects cascaded_comment_restored_on_post_restore,cascaded_attachment_restored_on_post_restore,user_pre_deleted_comment_stays_trashed,user_pre_deleted_attachment_stays_trashed */ diff --git a/modules/_bundled/sirsoft-board/tests/Feature/PostSearchSecretPreviewTest.php b/modules/_bundled/sirsoft-board/tests/Feature/PostSearchSecretPreviewTest.php index 6ff0040d..2585e0d5 100644 --- a/modules/_bundled/sirsoft-board/tests/Feature/PostSearchSecretPreviewTest.php +++ b/modules/_bundled/sirsoft-board/tests/Feature/PostSearchSecretPreviewTest.php @@ -16,7 +16,7 @@ use PHPUnit\Framework\Attributes\Test; * * 게시판 내부 검색은 별도 엔드포인트가 아니라 목록 조회(index)에 search 필터를 얹은 것이다. * 비밀글(is_secret)·블라인드(status=blinded) 게시글은 목록/검색 응답에서 본문 요약이 - * 권한과 무관하게 빈 문자열로 차단되어야 한다 (PO 확정: 목록 미리보기는 권한자도 숨김). + * 권한과 무관하게 빈 문자열로 차단되어야 한다 (확정 정책: 목록 미리보기는 권한자도 숨김). * * 제목은 현행 유지(노출), 차단 대상은 content_preview 뿐. * 상세 본문(content) 권한자 노출은 기존 정책(PostBlindedAccessControlTest)이 보장 — 본 테스트 범위 밖. @@ -237,7 +237,7 @@ class PostSearchSecretPreviewTest extends BoardTestCase /** * 비밀글 미리보기는 작성자 본인에게도 목록에서는 차단된다(빈 문자열). - * (PO 확정: 목록 미리보기는 권한자도 숨김 — 본문은 상세에서만 노출) + * (확정 정책: 목록 미리보기는 권한자도 숨김 — 본문은 상세에서만 노출) * * @scenario viewer=author * diff --git a/modules/_bundled/sirsoft-board/tests/Unit/BoardDashboardServiceTest.php b/modules/_bundled/sirsoft-board/tests/Unit/BoardDashboardServiceTest.php index 4ad8085e..7501bc64 100644 --- a/modules/_bundled/sirsoft-board/tests/Unit/BoardDashboardServiceTest.php +++ b/modules/_bundled/sirsoft-board/tests/Unit/BoardDashboardServiceTest.php @@ -183,7 +183,7 @@ class BoardDashboardServiceTest extends ModuleTestCase // FormatsBoardDate 표준형 규칙: // - 24시간 이상 차이: 올해는 'MM-DD', 작년 이전은 'YY-MM-DD' // 캡션이 게시판 날짜 표시 규칙과 일관되어야 한다는 회귀 가드. - // (게시글 created_at 표시와 동일 컨벤션 — PO 시각 검증 피드백) + // (게시글 created_at 표시와 동일 컨벤션 — 시각 검증 피드백) $today = CarbonImmutable::today(); // 어제 자정에 마지막 스케줄러가 돈 상황을 시뮬레이션 $yesterdayBoundary = $today->subDay()->setTime(0, 0, 0); @@ -223,7 +223,7 @@ class BoardDashboardServiceTest extends ModuleTestCase public function test_get_recent_posts_excludes_replies(): void { // 대시보드 "최신 게시글" 카드는 본문 글만 보여줘야 한다 (답글 Re: 제외). - // PO 시각 검증 피드백: "최신 게시글이 최신순으로 안 나오는 것 같다" — 원인은 답글이 섞여 표시된 것. + // 시각 검증 피드백: "최신 게시글이 최신순으로 안 나오는 것 같다" — 원인은 답글이 섞여 표시된 것. // PostRepository::getRecentAcrossBoards 가 whereNull('parent_id') 누락했던 회귀 가드. $parent = $this->makePost('원글'); $reply = $this->makePost('Re: 원글'); diff --git a/modules/_bundled/sirsoft-board/tests/scenarios/board-attachment-download-actor.yaml b/modules/_bundled/sirsoft-board/tests/scenarios/board-attachment-download-actor.yaml index 46e92833..885a7041 100644 --- a/modules/_bundled/sirsoft-board/tests/scenarios/board-attachment-download-actor.yaml +++ b/modules/_bundled/sirsoft-board/tests/scenarios/board-attachment-download-actor.yaml @@ -17,7 +17,7 @@ description: | (composite/ImageGallery.tsx 의 downloadAuthenticatedFile 과 동일 패턴). · 레이아웃 카드 3곳 전환: 사용자 _post_attachments(권한 분기 보존), 관리자 _post_card_content / _reply_card_content(무분기) 의 → Div + click 핸들러. - - PO 정책 확정: + - 정책 확정: · 범위 = 게시판만 3곳 (페이지 첨부는 별도). 비회원 = NULL 유지(현행). 성공 토스트 없음. · 서버(라우트/컨트롤러/서비스) 무변경 — optional.sanctum 유지가 비회원 NULL 정책과 정합. diff --git a/modules/_bundled/sirsoft-board/tests/scenarios/board-blinded-original-access.yaml b/modules/_bundled/sirsoft-board/tests/scenarios/board-blinded-original-access.yaml index 48f42830..fc25c682 100644 --- a/modules/_bundled/sirsoft-board/tests/scenarios/board-blinded-original-access.yaml +++ b/modules/_bundled/sirsoft-board/tests/scenarios/board-blinded-original-access.yaml @@ -16,7 +16,7 @@ description: | canViewBlindedContent(관리자 OR 작성자 본인) 게이트 추가, 권한 없으면 null · CommentResource::getFilteredContent — 동일 결함 → 동일 게이트 추가 · PostResource::toListArray — 블라인드 글 content_preview 를 빈 문자열로 차단 - - PO 정책 확정 (일반 관례): 원문 = 관리자 + 작성자 본인. 안내/사유/제목은 전원 노출. + - 정책 확정 (일반 관례): 원문 = 관리자 + 작성자 본인. 안내/사유/제목은 전원 노출. - 비밀글(canViewSecretContent)과 달리 비밀번호 검증·posts.read-secret 은 인정하지 않음 (블라인드는 운영 제재이므로 비밀번호로 해제되지 않음). diff --git a/modules/_bundled/sirsoft-board/tests/scenarios/board-clone.yaml b/modules/_bundled/sirsoft-board/tests/scenarios/board-clone.yaml index 31ac4bd4..84ac0e08 100644 --- a/modules/_bundled/sirsoft-board/tests/scenarios/board-clone.yaml +++ b/modules/_bundled/sirsoft-board/tests/scenarios/board-clone.yaml @@ -13,7 +13,7 @@ description: | identifier 는 stripBoardScopeRoles 로 제거 (attachRoles 의 전역 whereIn 으로 인한 교차 누수 차단) · 새 게시판 manager/step 역할은 생성 시 injectBoardRolesToPermissions 가 자동 주입 · 비-스코프 역할(member 등) 은 그대로 보존 - - PO 정책 확정: 복제는 원본 manager/step 명단을 승계 (순수 생성 모드는 본인 단독) + - 정책 확정: 복제는 원본 manager/step 명단을 승계 (순수 생성 모드는 본인 단독) - role 조회 로직은 BoardResource::getBoardRoleData 를 public static 으로 공유 (SSoT) axes: diff --git a/modules/_bundled/sirsoft-board/tests/scenarios/board-comment-deleted-parent-child-tree.yaml b/modules/_bundled/sirsoft-board/tests/scenarios/board-comment-deleted-parent-child-tree.yaml index 9b317f6d..5c550187 100644 --- a/modules/_bundled/sirsoft-board/tests/scenarios/board-comment-deleted-parent-child-tree.yaml +++ b/modules/_bundled/sirsoft-board/tests/scenarios/board-comment-deleted-parent-child-tree.yaml @@ -16,7 +16,7 @@ description: | 고아 자식의 조상 체인을 따라 누락된 삭제 부모를 일괄 IN 조회(N+1 회피)로 tombstone 노드 복구. 자식 없는 삭제 부모는 고아 자식이 없어 자연 제외. · countDescendants() 보정 — tombstone(삭제) 댓글은 "답글 N" 집계 모수에서 제외 - (트리 노드로는 유지, 살아있는 자손은 계속 카운트). PO 확정. + (트리 노드로는 유지, 살아있는 자손은 계속 카운트). 확정 정책. · content 권한·표시(tombstone 안내 + 관리자 원문 토글)는 #413-50 기구현 재사용. · withTrashed=true(manager del_cmt 토글) 경로는 이미 전체 노출 → 고아 복구 비적용. diff --git a/modules/_bundled/sirsoft-board/tests/scenarios/board-delete-children-force-delete.yaml b/modules/_bundled/sirsoft-board/tests/scenarios/board-delete-children-force-delete.yaml index 8da9ee96..9822a08f 100644 --- a/modules/_bundled/sirsoft-board/tests/scenarios/board-delete-children-force-delete.yaml +++ b/modules/_bundled/sirsoft-board/tests/scenarios/board-delete-children-force-delete.yaml @@ -15,7 +15,7 @@ description: | (b) 저장 경로는 slug 기준({slug}/...)인데 삭제는 board_id 기준 디렉토리를 지우려 해 경로 불일치로 아무 파일도 안 지워짐. - 조치 (PO 결정 B안): + 조치 (결정 B안): · Repository 3종(Post/Comment/Attachment)에 forceDeleteByBoardId 신설 (기존 softDeleteByBoardId 의 delete → forceDelete). 인터페이스 + 구현. · BoardService::deleteBoard() 의 자식 정리를 force 로 교체. diff --git a/modules/_bundled/sirsoft-board/tests/scenarios/board-search-secret-content-preview.yaml b/modules/_bundled/sirsoft-board/tests/scenarios/board-search-secret-content-preview.yaml index f356e057..3e51e0f7 100644 --- a/modules/_bundled/sirsoft-board/tests/scenarios/board-search-secret-content-preview.yaml +++ b/modules/_bundled/sirsoft-board/tests/scenarios/board-search-secret-content-preview.yaml @@ -14,7 +14,7 @@ description: | 미처리 → 비로그인·제3자도 게시판 내부 검색/목록 응답에서 비밀글 본문 요약 열람 가능 (보안 결함). - 수정: PostResource::getMaskedContentPreviewForList 신설 — status=Blinded 또는 is_secret 이면 권한 무관 빈 문자열. toListArray 가 이 헬퍼를 사용. - - PO 정책 확정: 목록 미리보기는 권한자(작성자/관리자)에게도 숨김(상세에서만 본문 노출). + - 정책 확정: 목록 미리보기는 권한자(작성자/관리자)에게도 숨김(상세에서만 본문 노출). DB select 심층방어는 미포함(응답 계층 차단만 — 노출 경계 차단 관례). - 통합검색(/api/search)은 쿼리에서 published+non-secret 만 검색하므로 누수 없음(무관). diff --git a/modules/_bundled/sirsoft-board/tests/scenarios/post-delete-cascade.yaml b/modules/_bundled/sirsoft-board/tests/scenarios/post-delete-cascade.yaml index 4f14c034..b249eb63 100644 --- a/modules/_bundled/sirsoft-board/tests/scenarios/post-delete-cascade.yaml +++ b/modules/_bundled/sirsoft-board/tests/scenarios/post-delete-cascade.yaml @@ -5,7 +5,7 @@ description: | 정리되지 않아, 게시글만 trashed 되고 댓글·첨부는 살아남아 목록/상세에서 고아로 노출되던 문제를 수정한다. 동시에 게시글 복원 시 cascade 로 지워진 항목만 선택 복원하여, 삭제 이전에 사용자가 직접 지운 항목이 게시글 복원과 함께 부활하는 - 문제(PO 우려)를 차단한다. + 문제(우려)를 차단한다. 근본 원인: · PostService::deletePost() 가 게시글 자신만 soft delete 하고 댓글·첨부는 @@ -14,7 +14,7 @@ description: | · 첨부 테이블(board_attachments)에는 cascade 와 사용자 삭제를 구분할 trigger_type 컬럼 자체가 없었다. - 조치 (PO 결정 방법1: 연쇄 soft delete + cascade 플래그 기반 선별 복원): + 조치 (결정 방법1: 연쇄 soft delete + cascade 플래그 기반 선별 복원): · board_comments.trigger_type enum 에 'cascade' 추가 (마이그레이션). · board_attachments 에 trigger_type enum 컬럼 신설 (+ board_id/post_id/trigger_type 복합 인덱스). diff --git a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/handlers/bulkUpdateHandlers.test.ts b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/handlers/bulkUpdateHandlers.test.ts index 18fad849..308a7acb 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/handlers/bulkUpdateHandlers.test.ts +++ b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/handlers/bulkUpdateHandlers.test.ts @@ -1,4 +1,4 @@ -// e2e:allow MP13 — i18n/UX 소품 묶음(라벨 표시·링크 분기·모달 조건부 필드·placeholder 치환). 핵심 회귀는 Vitest(모듈 22 + 템플릿 13)로 커버, 라이브 검수는 계획서 §14 Chrome MCP 매트릭스(PO 환경)로 수행. +// e2e:allow MP13 — i18n/UX 소품 묶음(라벨 표시·링크 분기·모달 조건부 필드·placeholder 치환). 핵심 회귀는 Vitest(모듈 22 + 템플릿 13)로 커버, 라이브 검수는 계획서 §14 Chrome MCP 매트릭스(검수 환경)로 수행. /** * bulkUpdateHandlers 테스트 * diff --git a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/adminOrderPartialCancelBadge.test.ts b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/adminOrderPartialCancelBadge.test.ts index 30f8c3d4..d2916189 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/adminOrderPartialCancelBadge.test.ts +++ b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/adminOrderPartialCancelBadge.test.ts @@ -4,7 +4,7 @@ * 배경: * - partial_cancelled 주문상태 제거 후, 일부 취소 사실은 파생 플래그 is_partially_cancelled 로 표시한다. * - DataGrid order_status 컬럼 cellChildren 에 StatusBadge + 보조 배지를 평면 형제로 두면 - * 목록 셀과 모바일 카드(responsiveBreakpoint)에서 한 줄로 나란히 떠 깨진다(PO 발견). + * 목록 셀과 모바일 카드(responsiveBreakpoint)에서 한 줄로 나란히 떠 깨진다(검수 발견). * → 둘을 flex-col 묶음 Div 로 감싸 상태 배지 아래(두 줄)에 보조 배지를 배치한다. * DataGrid 는 동일 columns 정의를 목록·카드 양쪽에 재사용하므로 한 곳 수정으로 둘 다 적용된다. * diff --git a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin_ecommerce_mileage_transaction_index.test.tsx b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin_ecommerce_mileage_transaction_index.test.tsx index a1dd5b63..121d99c6 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin_ecommerce_mileage_transaction_index.test.tsx +++ b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin_ecommerce_mileage_transaction_index.test.tsx @@ -406,7 +406,7 @@ describe('DataGrid (_transactions_table.json)', () => { expect(edit).toBeTruthy(); // 비적립계 또는 권한없음이면 비활성 — Resource 가 can_edit = can_manage && isEarning 으로 계산 expect(edit.disabledField).toBe('abilities.can_edit'); - // 원장 불변: 삭제 액션은 존재하지 않는다 (PO 확정) + // 원장 불변: 삭제 액션은 존재하지 않는다 (정책 확정) const ids = grid.props.rowActions.map((a: any) => a.id); expect(ids).not.toContain('delete'); expect(ids).not.toContain('manual'); diff --git a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin_ecommerce_settings_mileage_tab.test.tsx b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin_ecommerce_settings_mileage_tab.test.tsx index 693905be..38668dc4 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin_ecommerce_settings_mileage_tab.test.tsx +++ b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin_ecommerce_settings_mileage_tab.test.tsx @@ -3,7 +3,7 @@ * * @description * - 마일리지 설정은 이커머스 환경설정(admin_ecommerce_settings.json)의 서브탭으로 통합됨 - * (별도 독립 화면 아님 — PO 합의사항) + * (별도 독립 화면 아님 — 합의사항) * - 환경설정 메인: mileage 탭 등록 + 탭 콘텐츠 partial(_tab_mileage.json) 참조 + 탭 if 가드 * - 탭 콘텐츠 partial 5종(탭/기본/통화테이블/통화카드/유효기간/소멸알림) 구조 검증 * - 카드 4종(기본/통화/유효기간/소멸알림), 토글 3종, 적립시점 기본=구매확정 @@ -180,7 +180,7 @@ describe('마일리지 탭 콘텐츠 (_tab_mileage.json)', () => { }); it('마일리지 내역 보기 버튼이 기본 설정 카드 헤더에 있고 내역 화면으로 이동한다', () => { - // 공간 절약 위해 별도 헤더 행 대신 기본 설정 카드 우측 상단으로 이동 (PO 요청) + // 공간 절약 위해 별도 헤더 행 대신 기본 설정 카드 우측 상단으로 이동 (요청) const btn = findById(basicCard, 'view_transactions_button'); expect(btn, '내역 보기 버튼이 기본 설정 카드에 없다').toBeTruthy(); expect(btn.actions[0].handler).toBe('navigate'); diff --git a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/mileage_user_injection.test.tsx b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/mileage_user_injection.test.tsx index f7d1de5d..4809601a 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/mileage_user_injection.test.tsx +++ b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/mileage_user_injection.test.tsx @@ -102,7 +102,7 @@ describe('마이페이지 프로필 내 마일리지 카드 주입 (mypage-profi it('사용자 화면(마이페이지) 카드는 ExtensionBadge 를 사용하지 않는다 (회귀 차단)', () => { // ExtensionBadge 는 관리자 템플릿(sirsoft-admin_basic)에만 존재하는 컴포넌트로, // 사용자 템플릿(sirsoft-basic)에는 미등록이라 사용자 화면에 주입 시 "컴포넌트를 찾을 수 없습니다" 렌더 실패. - // PO 정책: 사용자 화면에서 ExtensionBadge 사용 금지. + // 정책: 사용자 화면에서 ExtensionBadge 사용 금지. const json = jsonOf(mypageCard); expect(json).not.toContain('ExtensionBadge'); }); diff --git a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/productFormLayouts.test.tsx b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/productFormLayouts.test.tsx index 7c985641..1db68b5c 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/productFormLayouts.test.tsx +++ b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/productFormLayouts.test.tsx @@ -258,7 +258,7 @@ describe('productFormLayouts', () => { * "Failed to execute action: navigate" 오류 토스트가 추가로 발생하고 * 목록 페이지로 이동하지 않음 (성공 토스트 + 오류 토스트 2건 동시 노출). * 원인: onSuccess 시퀀스의 navigate 핸들러가 params.url 로 경로를 전달했으나, - * G7 navigate 핸들러는 params.path 만 인식한다 (CLAUDE.md "navigate path 필수"). + * G7 navigate 핸들러는 params.path 만 인식한다 (코어 규정: navigate path 필수). * url 키는 무시되어 finalPath=undefined → navigate throw → 오류 토스트. * 동일 모듈의 다른 navigate(주문 상세/브랜드 목록 등)는 모두 path 사용. * 해결: onSuccess navigate 의 params.url → params.path 로 정정. @@ -527,7 +527,7 @@ describe('productFormLayouts', () => { findNodeById(otherInfoPartial, 'field_max_purchase_qty'), ]); // $event.target.value 는 허용하되, parseInt($event)/Number($event) 같은 - // 이벤트 객체 직접 숫자화 패턴은 금지 (CLAUDE.md 데이터 바인딩 규정) + // 이벤트 객체 직접 숫자화 패턴은 금지 (코어 데이터 바인딩 규정) expect(serialized).not.toMatch(/parseInt\(\$event\)/); expect(serialized).not.toMatch(/Number\(\$event\)/); }); diff --git a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/shippingCountryLayouts.test.tsx b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/shippingCountryLayouts.test.tsx index a8dd5a66..c66cc957 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/shippingCountryLayouts.test.tsx +++ b/modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/shippingCountryLayouts.test.tsx @@ -95,7 +95,7 @@ describe('header-currency-selector-user.json — 헤더 공용 셀렉터 2섹션 }); it('루트는 if(마운트 게이트) 없이 항상 마운트 + className 으로 기본 hidden 토글 (깜빡임 방지)', () => { - // PO 회귀(유저 헤더): if 마운트/언마운트 대신 항상 마운트 + className 기본 hidden(display:none) → 조건 충족 시 해제. + // 회귀(유저 헤더): if 마운트/언마운트 대신 항상 마운트 + className 기본 hidden(display:none) → 조건 충족 시 해제. expect(root.if).toBeUndefined(); const cls = root.props?.className ?? ''; expect(cls).toContain("'hidden'"); @@ -160,7 +160,7 @@ describe('header-currency-selector-admin.json — 관리자 헤더 2섹션', () }); it('루트는 if(마운트 게이트) 없이 항상 마운트 + className 으로 기본 hidden 토글 (깜빡임 방지)', () => { - // PO 회귀: if 로 마운트/언마운트하면 첫 페인트에 잠깐 보였다 사라진다. + // 회귀: if 로 마운트/언마운트하면 첫 페인트에 잠깐 보였다 사라진다. // 항상 마운트하되 className 에 기본 hidden(display:none) 을 깔고 조건 충족 시에만 해제한다. expect(root.if).toBeUndefined(); const cls = root.props?.className ?? ''; diff --git a/modules/_bundled/sirsoft-ecommerce/resources/js/handlers/shippingPolicyFormHandlers.ts b/modules/_bundled/sirsoft-ecommerce/resources/js/handlers/shippingPolicyFormHandlers.ts index fe6c88f6..e2ada8ab 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/js/handlers/shippingPolicyFormHandlers.ts +++ b/modules/_bundled/sirsoft-ecommerce/resources/js/handlers/shippingPolicyFormHandlers.ts @@ -1201,7 +1201,7 @@ export function testShippingApiHandler( G7Core.state.set?.({ apiTestLoading: true, apiTestResult: null }); // apiCall 구조: URL 은 액션 top-level target, method/body 는 params, - // onSuccess/onError 도 top-level (CLAUDE.md apiCall 규약). 백엔드 test-api-call + // onSuccess/onError 도 top-level (코어 apiCall 규약). 백엔드 test-api-call // 엔드포인트가 사용자가 입력한 설정으로 외부 API 를 대신 호출하고 결과를 서빙한다. G7Core.dispatch({ handler: 'apiCall', diff --git a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_detail/_modal_cancel_order.json b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_detail/_modal_cancel_order.json index c2e7e4dd..d3c81786 100644 --- a/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_detail/_modal_cancel_order.json +++ b/modules/_bundled/sirsoft-ecommerce/resources/layouts/admin/partials/admin_ecommerce_order_detail/_modal_cancel_order.json @@ -421,7 +421,7 @@ ] }, { - "comment": "환불 우선순위 라디오 — PG 환불 가능(requires_pg_cancellation)일 때 노출한다. 포인트 사용분이 없으면(total_points_used_amount=0) PG↔포인트 배분 순서가 무의미하므로 노출은 하되 선택 불가(disabled)로 둔다 (A28, PO 2026-06-22).", + "comment": "환불 우선순위 라디오 — PG 환불 가능(requires_pg_cancellation)일 때 노출한다. 포인트 사용분이 없으면(total_points_used_amount=0) PG↔포인트 배분 순서가 무의미하므로 노출은 하되 선택 불가(disabled)로 둔다 (A28, 2026-06-22).", "type": "basic", "name": "Div", "if": "{{order.data?.payment?.requires_pg_cancellation === true}}", diff --git a/modules/_bundled/sirsoft-ecommerce/src/Enums/OrderStatusEnum.php b/modules/_bundled/sirsoft-ecommerce/src/Enums/OrderStatusEnum.php index bd4c848e..0c5f7ca9 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Enums/OrderStatusEnum.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Enums/OrderStatusEnum.php @@ -339,7 +339,7 @@ enum OrderStatusEnum: string * - 취소(CANCELLED) 진입은 이 목록에 포함하지 않는다(canTransitionTo 가 별도 통과 처리). * - 현재 상태가 CANCELLED 면 판매 반영 상태(salesEligibleStatuses)로만 복원 허용 * (취소된 주문/옵션을 다시 판매 진행 상태로 되살리는 reactivation 운영 기능 — 재고 재차감 동반. - * 결제 전 상태(pending_order/pending_payment)로의 복원은 차단. PO 2026-06-23). + * 결제 전 상태(pending_order/pending_payment)로의 복원은 차단. 2026-06-23). * * @return array */ diff --git a/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/MileageTransactionResource.php b/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/MileageTransactionResource.php index fc453e02..22b746a7 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/MileageTransactionResource.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Http/Resources/MileageTransactionResource.php @@ -116,7 +116,7 @@ class MileageTransactionResource extends BaseApiResource * 행 레벨 능력을 계산합니다. * * 표준 can_manage 에 더해, 행 편집 메뉴('사유·기간 변경') 게이팅용 can_edit 를 노출합니다. - * can_edit = manage 권한 보유 && 적립계 거래 (비적립계 행은 메뉴 비활성 — PO 합의 C-2). + * can_edit = manage 권한 보유 && 적립계 거래 (비적립계 행은 메뉴 비활성 — 합의 C-2). * * @param Request $request HTTP 요청 * @return array 능력 불리언 맵 diff --git a/modules/_bundled/sirsoft-ecommerce/src/Listeners/MileageTransactionListener.php b/modules/_bundled/sirsoft-ecommerce/src/Listeners/MileageTransactionListener.php index 8e416396..0dd22248 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Listeners/MileageTransactionListener.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Listeners/MileageTransactionListener.php @@ -175,7 +175,7 @@ class MileageTransactionListener implements HookListenerInterface // 취소 전이: 기적립 건 회수. // 취소는 옵션 단위로 처리되므로 회수도 옵션(CANCELLED) 기준이다 — 부분취소든 전체취소든 - // 취소된 옵션마다 이 분기를 타 개별 회수된다(별도 partial_cancelled 상태 불요). PO 2026-06-22 + // 취소된 옵션마다 이 분기를 타 개별 회수된다(별도 partial_cancelled 상태 불요). 2026-06-22 if ($newStatus === OrderStatusEnum::CANCELLED) { $this->mileageService->cancelEarnForOption($order, $option); } @@ -186,7 +186,7 @@ class MileageTransactionListener implements HookListenerInterface * * mileage.enabled 토글은 주문 당시 적립액 "계산"(OrderCalculationService) 단계에서만 작용한다. * 리스너는 이미 계산·저장된 subtotal_earned_points_amount 를 실행할 뿐이므로 enabled 를 재검사하지 않는다. - * (저장값이 0 이면 Service 가 자연 no-op — PO 확정: 계산 완료된 적립은 토글로 막지 않음) + * (저장값이 0 이면 Service 가 자연 no-op — 정책 확정: 계산 완료된 적립은 토글로 막지 않음) * * @param Order $order 주문 * @param OrderOption $option 주문옵션 diff --git a/modules/_bundled/sirsoft-ecommerce/src/Listeners/UserMileageCleanupListener.php b/modules/_bundled/sirsoft-ecommerce/src/Listeners/UserMileageCleanupListener.php index 2093c51f..27bcaa22 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Listeners/UserMileageCleanupListener.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Listeners/UserMileageCleanupListener.php @@ -7,12 +7,12 @@ use App\Models\User; use Modules\Sirsoft\Ecommerce\Services\UserMileageService; /** - * 회원 탈퇴/삭제 시 마일리지 데이터 정리 리스너 (PO 확정 #8) + * 회원 탈퇴/삭제 시 마일리지 데이터 정리 리스너 (정책 확정 #8) * * 코어 UserService 의 탈퇴(before_withdraw)·삭제(before_delete) 훅을 구독해, * 활성 lot 을 expired 거래로 소멸 기록(감사 흐름 보존)한 뒤 원장·잔액 캐시 행을 명시 삭제합니다. * 탈퇴는 soft 상태 변경이라 cascadeOnDelete 가 발화하지 않으므로 명시 정리가 필수입니다 - * (CLAUDE.md "DB CASCADE 의존 삭제 금지"). cascade 는 삭제 경로의 안전망으로만 둡니다. + * (코어 규정: DB CASCADE 의존 삭제 금지). cascade 는 삭제 경로의 안전망으로만 둡니다. */ class UserMileageCleanupListener implements HookListenerInterface { diff --git a/modules/_bundled/sirsoft-ecommerce/src/Models/Order.php b/modules/_bundled/sirsoft-ecommerce/src/Models/Order.php index 7137e98b..807545fd 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Models/Order.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Models/Order.php @@ -526,7 +526,7 @@ class Order extends Model * * 부분취소는 별도 주문 상태(partial_cancelled)가 아니라 "취소된 옵션이 있으면서 * 잔여 활성 옵션도 존재"하는 파생 상태다. 주문 상태(order_status)는 잔여 활성 옵션 기준으로 - * 결정되고, "일부가 취소되었다"는 사실은 이 플래그로 노출한다 (PO 2026-06-22, partial_cancelled 제거). + * 결정되고, "일부가 취소되었다"는 사실은 이 플래그로 노출한다 (2026-06-22, partial_cancelled 제거). * * @return bool 일부 옵션만 취소된 주문이면 true */ diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/OrderAdjustmentService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/OrderAdjustmentService.php index 05fd4916..4321afe7 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/OrderAdjustmentService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/OrderAdjustmentService.php @@ -664,7 +664,7 @@ class OrderAdjustmentService { $updates = []; - // 부분취소 재계산 시 옵션 레벨 다통화(mc_) 컬럼도 재산출 (PO 확정) + // 부분취소 재계산 시 옵션 레벨 다통화(mc_) 컬럼도 재산출 (정책 확정) $currencySnapshot = $order->currency_snapshot; // 재계산 결과에서 옵션별 금액 반영 diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/OrderCalculationService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/OrderCalculationService.php index c4019f5a..f3ecca4f 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/OrderCalculationService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/OrderCalculationService.php @@ -118,7 +118,7 @@ class OrderCalculationService // 쿠폰 조합 가능 여부 검증 (is_combinable) — 전체 슬롯 통합 1회 (A15/MP06) // 슬롯별 독립 검증은 "상품별(false) 1개 + 주문(true) 1개" 같은 슬롯 교차 조합을 // 각 슬롯 count=1 이라 놓친다. 적용 대상 전체 쿠폰을 한 집합으로 모아 1회 검증한다. - // 스냅샷 모드(환불 재계산)에서도 동일 검증한다(PO 2026-06-19 확정). + // 스냅샷 모드(환불 재계산)에서도 동일 검증한다(2026-06-19 확정). $allAppliedCoupons = $this->collectAllCouponsForCombination($coupons, $itemCouponIssues); $combinationViolations = $this->findNonCombinableViolations($allAppliedCoupons); foreach ($combinationViolations as $violationIssue) { @@ -874,7 +874,7 @@ class OrderCalculationService // 마일리지 기능 비활성 시 주문 시점 적립 "계산" 자체를 수행하지 않는다 (전부 0). // mileage_value/mileage_type 은 상품 옵션(상품 등록 당시) 속성이므로, - // 전역 OFF 면 상품옵션 명시 적립률·기본 적립률 모두 계산 대상에서 제외된다 (PO 확정). + // 전역 OFF 면 상품옵션 명시 적립률·기본 적립률 모두 계산 대상에서 제외된다 (정책 확정). if (! (bool) $this->settingsService->getSetting('mileage.enabled', false)) { $pointsPerItem[$optionId] = 0; diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/OrderCancellationService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/OrderCancellationService.php index 7b2adcf7..7be48aec 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/OrderCancellationService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/OrderCancellationService.php @@ -683,7 +683,7 @@ class OrderCancellationService } // 부분취소는 별도 주문 상태(partial_cancelled)를 두지 않는다 — 옵션은 이미 CANCELLED 로 전이됐고, // 주문 상태는 아래 syncParentOrderStatus 가 "취소 제외 잔여 활성 옵션" 기준으로 파생 결정한다. - // (잔여 옵션이 모두 취소된 경우 syncParentOrderStatus 가 CANCELLED 로 전이.) — PO 2026-06-22 + // (잔여 옵션이 모두 취소된 경우 syncParentOrderStatus 가 CANCELLED 로 전이.) — 2026-06-22 $order->update($updateData); diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/OrderOptionService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/OrderOptionService.php index 234b8f7c..e4338d52 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/OrderOptionService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/OrderOptionService.php @@ -603,7 +603,7 @@ class OrderOptionService * * 부분취소는 별도 주문 상태가 아니라 "취소 옵션 + 잔여 활성 옵션 공존" 파생 상태다. * 따라서 주문 상태는 항상 잔여 활성 옵션 기준으로 결정하고, 부분취소 여부는 - * Order::isPartiallyCancelled() 파생 플래그로 노출한다 (PO 2026-06-22, partial_cancelled 제거). + * Order::isPartiallyCancelled() 파생 플래그로 노출한다 (2026-06-22, partial_cancelled 제거). * * @param int $orderId 동기화할 주문 ID */ diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/SignupCurrencyResolver.php b/modules/_bundled/sirsoft-ecommerce/src/Services/SignupCurrencyResolver.php index d7253360..0cbf4896 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/SignupCurrencyResolver.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/SignupCurrencyResolver.php @@ -7,7 +7,7 @@ namespace Modules\Sirsoft\Ecommerce\Services; * * 유저 locale 을 환경설정의 통화별 언어(currencies[].locales) 매핑과 대조해 부여 통화를 결정한다. * - * 매칭 우선순위(D-SIGNUP, PO 확정): + * 매칭 우선순위(D-SIGNUP, 정책 확정): * 1) locale 이 매칭되는 통화가 1개 → 그 통화 * 2) 여러 통화에 중복 매칭 → 매칭된 통화 중 is_default 통화 우선 * 3) 매칭 없음/모호 → is_default 통화 → 그래도 없으면 default_currency 폴백 diff --git a/modules/_bundled/sirsoft-ecommerce/src/Services/UserMileageService.php b/modules/_bundled/sirsoft-ecommerce/src/Services/UserMileageService.php index ab76f02f..2460eecb 100644 --- a/modules/_bundled/sirsoft-ecommerce/src/Services/UserMileageService.php +++ b/modules/_bundled/sirsoft-ecommerce/src/Services/UserMileageService.php @@ -731,7 +731,7 @@ class UserMileageService } /** - * 회원 탈퇴/삭제 시 활성 lot 소멸 기록 (감사 흐름 보존 — PO 확정 #8) + * 회원 탈퇴/삭제 시 활성 lot 소멸 기록 (감사 흐름 보존 — 정책 확정 #8) * * 활성 lot 을 expired 거래로 소멸 기록(활동로그에 금액·통화·회원ID 잔존)합니다. * 거래/캐시 행은 이후 cascade 또는 명시 삭제로 함께 사라집니다. @@ -764,7 +764,7 @@ class UserMileageService } /** - * 회원 탈퇴/삭제 시 마일리지 데이터를 명시적으로 정리합니다 (PO 확정 #8). + * 회원 탈퇴/삭제 시 마일리지 데이터를 명시적으로 정리합니다 (정책 확정 #8). * * 활성 lot 을 expired 거래로 소멸 기록(활동로그 보존)한 뒤, 원장·잔액 캐시 행을 * 동일 트랜잭션에서 삭제합니다. CASCADE 는 안전망으로만 두고 명시 삭제합니다 @@ -986,7 +986,7 @@ class UserMileageService * 마일리지 사용이 가능한지 여부를 반환합니다. * * 기본 통화(base_currency)에 대한 마일리지 통화별 사용 단위 규칙이 설정되어 있지 않으면 - * 마일리지 사용을 허용하지 않는다(PO 정책). 마일리지는 base_currency 기준으로 사용/정산되므로 + * 마일리지 사용을 허용하지 않는다(정책). 마일리지는 base_currency 기준으로 사용/정산되므로 * base 규칙이 없으면 사용 단위·최소 사용액·한도를 알 수 없어 사용 자체가 불가하다. * * @return bool 기본 통화 마일리지 사용 규칙이 설정되어 있으면 true diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Feature/Identity/CheckoutVerificationGuardTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Feature/Identity/CheckoutVerificationGuardTest.php index 210926f2..b86a05c6 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Feature/Identity/CheckoutVerificationGuardTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Feature/Identity/CheckoutVerificationGuardTest.php @@ -164,7 +164,7 @@ class CheckoutVerificationGuardTest extends ModuleTestCase * * 기존 G1(훅 발화)·코어 가드(enforce 예외)·리스너 구독 테스트는 각 조각만 따로 검증한다. * 이 테스트는 그 셋이 실제 HTTP 흐름에서 연결되어 최종적으로 428 이 나오는지를 통합 검증한다. - * (PO 검수: 조각 테스트는 green 인데 실제로 본인인증이 발동하지 않던 결함의 회귀 가드) + * (검수: 조각 테스트는 green 인데 실제로 본인인증이 발동하지 않던 결함의 회귀 가드) * * @scenario policy=checkout, enabled=on, actor=member, verified_state=unverified, path=http_order_create * diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Playwright/README.md b/modules/_bundled/sirsoft-ecommerce/tests/Playwright/README.md index e97a015a..6eadf69f 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Playwright/README.md +++ b/modules/_bundled/sirsoft-ecommerce/tests/Playwright/README.md @@ -120,7 +120,7 @@ await page.route('https://api.tosspayments.com/v1/payments/confirm', route => ro ``` 분기별 가맹점 측 처리(주문 상태 전환, 재고 차감, 알림 발송 등) 가 정확한지 검증. -실 카드 결제 자동화는 권장하지 않음 — PO 수동 검증. +실 카드 결제 자동화는 권장하지 않음 — 수동 검증. ## 참고 diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Enums/OrderStatusTransitionTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Enums/OrderStatusTransitionTest.php index bd4b8e68..dd952863 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Enums/OrderStatusTransitionTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Enums/OrderStatusTransitionTest.php @@ -134,7 +134,7 @@ class OrderStatusTransitionTest extends ModuleTestCase /** * 취소 → 판매 반영 상태 복원(reactivation)은 허용, 결제 전 상태 복귀는 차단됨을 명시 단언합니다. - * (취소 주문/옵션을 다시 판매 진행 상태로 되살리는 운영 기능 — 재고 재차감 동반. PO 2026-06-23) + * (취소 주문/옵션을 다시 판매 진행 상태로 되살리는 운영 기능 — 재고 재차감 동반. 2026-06-23) */ public function test_cancelled_reactivation_to_sales_status_allowed_but_pre_payment_blocked(): void { diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Listeners/UserMileageCleanupListenerTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Listeners/UserMileageCleanupListenerTest.php index 58c6e94f..935f858c 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Listeners/UserMileageCleanupListenerTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Listeners/UserMileageCleanupListenerTest.php @@ -11,7 +11,7 @@ use Modules\Sirsoft\Ecommerce\Repositories\Contracts\MileageBalanceRepositoryInt use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase; /** - * UserMileageCleanupListener 테스트 (회원 탈퇴/삭제 시 마일리지 정리 — PO 확정 #8) + * UserMileageCleanupListener 테스트 (회원 탈퇴/삭제 시 마일리지 정리 — 정책 확정 #8) */ class UserMileageCleanupListenerTest extends ModuleTestCase { diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Repositories/OrderRepositoryTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Repositories/OrderRepositoryTest.php index 8d9cac78..6d0fe886 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Repositories/OrderRepositoryTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Repositories/OrderRepositoryTest.php @@ -482,7 +482,7 @@ class OrderRepositoryTest extends ModuleTestCase } /** - * 주문대기(pending_order)는 통계에서 숨김 유지된다 (PO 확정, listHiddenValues). + * 주문대기(pending_order)는 통계에서 숨김 유지된다 (정책 확정, listHiddenValues). */ public function test_user_statistics_hides_pending_order(): void { diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Resources/OrderResourceFieldsTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Resources/OrderResourceFieldsTest.php index 296632c6..42dc4d6a 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Resources/OrderResourceFieldsTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Resources/OrderResourceFieldsTest.php @@ -367,7 +367,7 @@ class OrderResourceFieldsTest extends ModuleTestCase /** * OrderResource 의 is_partially_cancelled 파생 플래그가 "취소 옵션 ∧ 잔여 활성 옵션" 일 때만 true 인지 확인. - * (부분취소는 별도 order_status 가 아니라 파생 플래그로 표시 — PO 2026-06-22.) + * (부분취소는 별도 order_status 가 아니라 파생 플래그로 표시 — 2026-06-22.) */ public function test_order_resource_exposes_is_partially_cancelled_flag(): void { diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/MileageBaseCurrencyGateTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/MileageBaseCurrencyGateTest.php index 7d0c7543..7e43138f 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/MileageBaseCurrencyGateTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/MileageBaseCurrencyGateTest.php @@ -13,7 +13,7 @@ use Modules\Sirsoft\Ecommerce\Tests\ModuleTestCase; * 마일리지 기본 통화 사용 규칙 게이트 테스트 (M2) * * 기본 통화(base_currency)에 대한 마일리지 통화별 사용 단위 규칙이 설정되어 있지 않으면 - * 마일리지 사용이 불가해야 한다(PO 정책). + * 마일리지 사용이 불가해야 한다(정책). * - isMileageUsable(): base 규칙 존재 시 true, 없으면 false * - getMaxUsable(): base 규칙 없으면 0 * - validateUsage(): base 규칙 없으면 MileageValidationException diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderAdjustmentServiceTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderAdjustmentServiceTest.php index 70e13c42..b2885dc8 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderAdjustmentServiceTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderAdjustmentServiceTest.php @@ -6820,7 +6820,7 @@ class OrderAdjustmentServiceTest extends ModuleTestCase } /** - * §6.3 회귀: 부분취소 재계산 시 옵션 레벨 다통화(mc_) 컬럼이 재안분값으로 갱신된다 (PO 확정). + * §6.3 회귀: 부분취소 재계산 시 옵션 레벨 다통화(mc_) 컬럼이 재안분값으로 갱신된다 (정책 확정). * * 결함: buildOptionUpdates 가 plain subtotal_* 만 갱신하고 mc_subtotal_* 는 부분취소 후 stale. */ @@ -6869,7 +6869,7 @@ class OrderAdjustmentServiceTest extends ModuleTestCase } // ================================================================ - // §6.3 복합 상황: 배송정책 · 할인쿠폰 · 도서산간 · 상품별 정책 + 마일리지 (PO 추가 요구) + // §6.3 복합 상황: 배송정책 · 할인쿠폰 · 도서산간 · 상품별 정책 + 마일리지 (추가 요구) // OrderCalculation 테스트 수준의 정확값 검증. 각 케이스는 적립 재안분값이 // 환불 우선순위에 무관(우선순위는 refund split 만 변경)함을 함께 단언한다. // ================================================================ diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderProcessingServiceTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderProcessingServiceTest.php index f5d0ca6f..6112fa41 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderProcessingServiceTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderProcessingServiceTest.php @@ -1208,7 +1208,7 @@ class OrderProcessingServiceTest extends ModuleTestCase } /** - * PO 검증 4조합: base/order 통화 전 경우의 수. + * 검증 4조합: base/order 통화 전 경우의 수. * * @return array */ diff --git a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderServiceStockRedeductTest.php b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderServiceStockRedeductTest.php index cad63e79..ca65eaa3 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderServiceStockRedeductTest.php +++ b/modules/_bundled/sirsoft-ecommerce/tests/Unit/Services/OrderServiceStockRedeductTest.php @@ -98,7 +98,7 @@ class OrderServiceStockRedeductTest extends ModuleTestCase public function test_update_to_non_sales_status_is_blocked_by_transition_rule(): void { // 전이 규칙 도입(A30) 이후: 취소 → 결제 전 상태(결제대기) 복귀는 전이 게이트가 차단한다. - // (취소 복원은 판매 반영 상태로만 허용 — PO 2026-06-23.) + // (취소 복원은 판매 반영 상태로만 허용 — 2026-06-23.) // 차단되므로 update 가 OrderProcessingException 을 던지고, 재차감은 물론 상태/재고 모두 불변. [$order, $option] = $this->createCancelledOrderWithRestoredStock(stock: 8, quantity: 3); diff --git a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp02-order-cancellation.yaml b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp02-order-cancellation.yaml index 3d7c8a2d..5c8c833e 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp02-order-cancellation.yaml +++ b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp02-order-cancellation.yaml @@ -19,7 +19,7 @@ description: | shouldConvertToFullCancel 이 담당(프론트 isFullCancel 휴리스틱 제거). - U20③: 취소 차단 메시지를 쿠폰 단정 없이 일반화. - U20④: cancelled_at native 컬럼에 전체·부분취소 모두 기록 + Resource 노출(cancelled_at_formatted). - - 부분취소 모델 변경(PO 2026-06-22): partial_cancelled 주문 상태를 제거. 부분취소는 별도 상태가 아니라 + - 부분취소 모델 변경(2026-06-22): partial_cancelled 주문 상태를 제거. 부분취소는 별도 상태가 아니라 "취소 옵션 + 잔여 활성 옵션 공존"이며, 주문 상태는 잔여 활성 옵션 기준으로 파생(syncParentOrderStatus). 잔여 항목 재취소는 진행 상태(payment_complete 등)가 취소 가능 목록에 있어 자연 허용. 잔여 전량 취소 시 CANCELLED 전이. "일부 취소됨" 표시는 파생 플래그 is_partially_cancelled(취소옵션∧활성옵션)로 노출(목록·상세 보조 배지). diff --git a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp04-order-status-transition.yaml b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp04-order-status-transition.yaml index 5c0f7dd3..f8e4c344 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp04-order-status-transition.yaml +++ b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp04-order-status-transition.yaml @@ -17,7 +17,7 @@ description: | (SHIPPING_HOLD↔PREPARING, SHIPPING→{SHIPPING_READY,PREPARING}, DELIVERED→SHIPPING)만 추가 허용. - CONFIRMED 되돌림 전부 금지. - CANCELLED 발 전이는 판매 반영 상태(salesEligibleStatuses)로의 복원(reactivation)만 허용 - (재고 재차감 동반 운영 기능, PO 2026-06-23). 결제 전 상태(pending_order/pending_payment) 복귀는 차단. + (재고 재차감 동반 운영 기능, 2026-06-23). 결제 전 상태(pending_order/pending_payment) 복귀는 차단. - 취소(CANCELLED) 진입은 게이트가 통과 처리(OrderCancellationService 전담, 이중 관리 방지). - 3경로 모두 FormRequest withValidator 1차 차단(422 + 다국어) + Service 진입부 2차 도메인 불변식 가드. - 일괄은 all-or-nothing: 1건이라도 위반이면 전체 422, DB 무변경. diff --git a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-base-currency-policy.yaml b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-base-currency-policy.yaml index a47fd8e6..6aff77ea 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-base-currency-policy.yaml +++ b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-base-currency-policy.yaml @@ -2,7 +2,7 @@ # cross product 자동 전개는 audit 실행 환경에 js-yaml 부재로 검출되지 않는다(다른 번들 동일). # base 통화 정책 axis 는 PHPUnit(OrderProcessingService/buildCurrencySnapshot/OrderAdjustmentService/ # StoreEcommerceSettingsRequest)·Playwright 가 커버하며 green 이다. -feature: base 통화 정책 + 변경 가드 + 결제/통지/부분환불 base 기준 (A2 — PO 신규) +feature: base 통화 정책 + 변경 가드 + 결제/통지/부분환불 base 기준 (A2 — 신규) description: | 기본통화 = 모든 금액 테이블의 base(진실값). 비-base 선택 시 base 정수 × 스냅샷 환율로 PG 청구·통지·환불. diff --git a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-signup-currency.yaml b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-signup-currency.yaml index f7b5d8ab..e21a7306 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-signup-currency.yaml +++ b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-signup-currency.yaml @@ -2,7 +2,7 @@ # cross product 자동 전개는 audit 실행 환경에 js-yaml 부재로 검출되지 않는다(다른 번들 동일). # 가입 통화 부여 axis 는 PHPUnit(AssignDefaultCurrencyOnRegisterListener/StoreEcommerceSettingsRequest # locales 검증)·Vitest(통화별 언어 Tag UI)가 커버하며 green 이다. -feature: 가입 시 locale 기반 통화 부여 + 통화별 언어 다중 매핑 (A4 — PO 신규) +feature: 가입 시 locale 기반 통화 부여 + 통화별 언어 다중 매핑 (A4 — 신규) description: | 가입/회원생성 시 브라우저/locale 기반 기본통화 부여(환경설정>언어/통화의 통화별 언어 Tag 매핑 기준). diff --git a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-user-currency.yaml b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-user-currency.yaml index 53a347bb..c2fe781e 100644 --- a/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-user-currency.yaml +++ b/modules/_bundled/sirsoft-ecommerce/tests/scenarios/mp08-user-currency.yaml @@ -2,7 +2,7 @@ # cross product 자동 전개는 audit 실행 환경에 js-yaml 부재로 검출되지 않는다(다른 번들 동일). # 유저별 영속 통화 axis 는 PHPUnit(EcommerceUserProfile/Repository/통화 저장 컨트롤러)· # Vitest(설정 UI)·Playwright(설치 게이트)가 커버하며 green 이다. -feature: 유저별 영속 통화 — user-profile 확장 + 4곳 설정 (A3·A5 — PO 신규) +feature: 유저별 영속 통화 — user-profile 확장 + 4곳 설정 (A3·A5 — 신규) description: | 유저별 통화 = 커머스 책임. 코어 users 미변경, 모듈 ecommerce_user_profiles 테이블 + layout_extensions. diff --git a/playwright.config.ts b/playwright.config.ts index 2ba2c116..a77f2396 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,7 +1,7 @@ /** * Playwright 코어 E2E 설정 (G7 코어 영역) * - * 위치 규약 (CLAUDE.md 코어/확장 분리 원칙): + * 위치 규약 (코어/확장 분리 원칙): * - 코어 spec : tests/Playwright/specs/ * - 모듈 spec : modules/_bundled/{id}/tests/Playwright/specs/ (모듈 자체 config) * - 플러그인 : plugins/_bundled/{id}/tests/Playwright/specs/ (플러그인 자체 config) diff --git a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/admin_plugin_settings.test.tsx b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/admin_plugin_settings.test.tsx index f48b0376..f7a3a964 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/admin_plugin_settings.test.tsx +++ b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/admin_plugin_settings.test.tsx @@ -490,7 +490,7 @@ describe('admin/plugin_settings.json — 3 카드 (operator / cookie_banner / au const modalText = JSON.stringify(publishModal); it('발행 버튼은 isPublishing 동안 spinner + "발행 중..." 라벨 노출 + 양쪽 버튼 disabled', () => { - // PO 피드백: 이전엔 closeModal 이 apiCall 앞에 있어 클릭 즉시 모달이 닫혀 사용자가 + // 피드백: 이전엔 closeModal 이 apiCall 앞에 있어 클릭 즉시 모달이 닫혀 사용자가 // 발행 진행 상태를 알 수 없었음. _local.isPublishing 플래그로 발행 중 시각 피드백 + // 양쪽 버튼 disabled (취소 버튼도 발행 중엔 막아 도중 닫힘 방지). // JSON.stringify 기본 출력은 공백 없는 형식 (콜론 뒤 공백 없음). @@ -516,7 +516,7 @@ describe('admin/plugin_settings.json — 3 카드 (operator / cookie_banner / au }); }); - it('정책 버전 가이드는 카드 hint 영역에 2줄 P 로 통합된다 (PO 피드백: 토글/박스 제거)', () => { + it('정책 버전 가이드는 카드 hint 영역에 2줄 P 로 통합된다 (피드백: 토글/박스 제거)', () => { const box = findById(root, 'policy_version_guide_box'); expect(box).not.toBeNull(); const serialized = JSON.stringify(box); @@ -666,7 +666,7 @@ describe('admin/plugin_settings.json — 3 카드 (operator / cookie_banner / au expect(layoutJson).toContain('partials/_shared/_policy_version_snapshot_modal.json'); }); - it('현재 버전 v배지 옆에 본문 보기 Button 이 노출된다 (PO 피드백 — 평문 + 명시 액션 분리)', () => { + it('현재 버전 v배지 옆에 본문 보기 Button 이 노출된다 (피드백 — 평문 + 명시 액션 분리)', () => { // v배지는 평문 Span (text 'v...' 으로 항상 표시), 본문 보기 Button 은 if 분기 + sequence expect(layoutJson).toContain('"text":"v{{gdprPolicyVersionCurrent?.data?.data?.version ?? \'?\'}}"'); expect(layoutJson).toContain('"if":"{{gdprPolicyVersionCurrent?.data?.data?.version}}"'); @@ -683,7 +683,7 @@ describe('admin/plugin_settings.json — 3 카드 (operator / cookie_banner / au expect(layoutJson).toContain('"viewingPolicyVersion":"{{row?.version}}"'); }); - it('policy_version_guide_box wrapper 는 라벨 + hint 2줄 구조의 단순 Div (PO 피드백: 토글/박스 제거)', () => { + it('policy_version_guide_box wrapper 는 라벨 + hint 2줄 구조의 단순 Div (피드백: 토글/박스 제거)', () => { const box = findById(root, 'policy_version_guide_box'); expect(box).not.toBeNull(); // wrapper 는 className 없음 (라벨 + hint 두 줄을 단순 그룹핑) diff --git a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/cookie_banner.test.tsx b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/cookie_banner.test.tsx index 97a2cf2f..70f8bbfc 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/cookie_banner.test.tsx +++ b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/cookie_banner.test.tsx @@ -84,13 +84,13 @@ describe('extensions/cookie_banner.json — 쿠키 동의 배너', () => { // 1. ResponseHelper::success($msg, $data) 응답은 데이터소스에서 그대로 보존되므로 단일 객체 응답은 `.data.` 한 단계 경로. // 과거 통합본에서 `?.data?.data?` 두 단계로 작성되어 항상 undefined → if false → 배너 미출력 사고. // 2. _global.gdprBannerDismissed 만으로는 새로고침 시 휘발 — 서버 조회 (gdprMyConsent.has_consented) 가 우선이며 클릭 직후 즉시 사라지는 효과만 글로벌 상태가 보장. - // 3. needs_renewal=true 일 때도 배너만 노출 (모달 폐기 — PO 결정 모달/배너 중복 제거). 배너 본문에 사유 안내 + 회원 한정 "현 상태 유지" 액션 통합. + // 3. needs_renewal=true 일 때도 배너만 노출 (모달 폐기 — 결정 모달/배너 중복 제거). 배너 본문에 사유 안내 + 회원 한정 "현 상태 유지" 액션 통합. expect(banner?.if).toBe( '{{gdprPublicSettings?.data?.banner_enabled === true && gdprMyConsent?.data?.has_consented !== true && _global.gdprBannerDismissed !== true}}' ); }); - it('needs_renewal 강제 모달 (gdpr_needs_renewal_modal) 이 더 이상 존재하지 않는다 (PO 결정 — 배너 통합)', () => { + it('needs_renewal 강제 모달 (gdpr_needs_renewal_modal) 이 더 이상 존재하지 않는다 (결정 — 배너 통합)', () => { // 모달 폐기 회귀 가드: 모달과 배너의 시각적 중복 + UX 혼란 + dark pattern 회피 위해 // 모달 통째 제거하고 배너에 사유 안내 + 회원 한정 "현 상태 유지" 액션 통합. const modal = findById(root, 'gdpr_needs_renewal_modal'); @@ -114,7 +114,7 @@ describe('extensions/cookie_banner.json — 쿠키 동의 배너', () => { expect(json).toContain('|renewed='); }); - it('게스트 전용 갱신 버튼 (gdpr_keep_consent_button_guest) 은 더 이상 존재하지 않는다 (PO 결정 — 게스트는 status 미사용 + history 기반이라 이전 의사 자동 복원 인프라 부재 → "모두 동의" 그대로 사용)', () => { + it('게스트 전용 갱신 버튼 (gdpr_keep_consent_button_guest) 은 더 이상 존재하지 않는다 (결정 — 게스트는 status 미사용 + history 기반이라 이전 의사 자동 복원 인프라 부재 → "모두 동의" 그대로 사용)', () => { const guestBtn = findById(root, 'gdpr_keep_consent_button_guest'); expect(guestBtn).toBeNull(); }); @@ -143,12 +143,12 @@ describe('extensions/cookie_banner.json — 쿠키 동의 배너', () => { it('기본(bottom_bar) 위치는 풀폭 팝업 카드 (bottom-4 left-4 right-4 rounded-lg)', () => { // 회귀 가드: 이전엔 화면 하단에 딱 붙는 띠(bottom-0)였지만, - // PO 결정으로 화면에서 살짝 떠 있는 풀폭 카드 형태로 변경됨. + // 설계 결정으로 화면에서 살짝 떠 있는 풀폭 카드 형태로 변경됨. expect(className).toContain('bottom-4 left-4 right-4 rounded-lg'); }); it('top_bar 분기는 제거되어 있다', () => { - // 회귀 가드: PO 결정으로 banner_position 옵션에서 top_bar 제거됨. + // 회귀 가드: 설계 결정으로 banner_position 옵션에서 top_bar 제거됨. expect(className).not.toContain("'top_bar'"); }); @@ -170,9 +170,9 @@ describe('extensions/cookie_banner.json — 쿠키 동의 배너', () => { } }); - it('centered_modal 에서 dim 오버레이를 사용하지 않는다 (PO 의도: 다른 팝업과 동일 외형)', () => { + it('centered_modal 에서 dim 오버레이를 사용하지 않는다 (의도: 다른 팝업과 동일 외형)', () => { // 회귀 가드: 이전 구현은 외곽에 bg-black/40 dim 을 깔았으나 자식 박스가 dim 위에 떠야 하는 - // 구조 — 외곽이 bg-white 와 함께 정의되어 dim 의도가 깨졌었다. PO 가 dim 강제 모달이 아닌 + // 구조 — 외곽이 bg-white 와 함께 정의되어 dim 의도가 깨졌었다. dim 강제 모달이 아닌 // 단순 중앙 배치만 원하므로 dim 클래스 자체를 제거. expect(className).not.toContain('bg-black/40'); expect(className).not.toContain('bg-black/50'); @@ -280,7 +280,7 @@ describe('extensions/cookie_banner.json — 쿠키 동의 배너', () => { }); it('배너 4버튼은 gdprBannerSubmittingAction 식별자 패턴으로 클릭한 버튼만 spinner + 다른 버튼은 원래 라벨 유지', () => { - // 회귀 가드: PO 피드백 — 단일 boolean 플래그면 모든 버튼이 "동의 중..." 으로 동시 변경되어 + // 회귀 가드: 피드백 — 단일 boolean 플래그면 모든 버튼이 "동의 중..." 으로 동시 변경되어 // 어느 버튼을 클릭했는지 시각적으로 알 수 없음. 식별자 string 으로 변경하여 클릭한 버튼만 // 스피너 + 라벨 변경, 다른 버튼은 disabled + 원래 라벨 유지. // 4 action 식별자: accept_all / reject_all / save_selection / renew_consent @@ -365,7 +365,7 @@ describe('extensions/cookie_banner.json — 쿠키 동의 배너', () => { }); it('"자세히 보기" 토글 + 펼침 영역이 통째로 제거되었다 (카테고리 description 한 줄로 일원화)', () => { - // PO 결정: 펼침 영역의 user_info description 이 카테고리 카드 자체의 description 과 + // 설계 결정: 펼침 영역의 user_info description 이 카테고리 카드 자체의 description 과 // 의미상 중복 → "자세히 보기" 토글 자체를 없애고, 카테고리 description 을 사용자 친화 // 표현으로 갈아끼움 (시더 plugin.php / CookieCategoryService 의 기본값 갱신). expect(text).not.toContain('banner.category_details_expand'); @@ -398,7 +398,7 @@ describe('extensions/cookie_banner.json — 쿠키 동의 배너', () => { }); describe('카테고리 카드 — 임의값 회귀 가드 (#14 글씨 키우기)', () => { - // PO 결정: 카테고리 카드 자체의 보조 텍스트 (필수 배지, 카테고리 설명) 도 너무 작아 + // 설계 결정: 카테고리 카드 자체의 보조 텍스트 (필수 배지, 카테고리 설명) 도 너무 작아 // 가독성 저하. 임의값 text-[10px] 11건을 모두 표준 text-xs 로 통일. it('cookie_banner.json 전체에서 text-[10px] 임의값이 사용되지 않는다', () => { const fullText = serializeForSearch(extension); diff --git a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/gdpr_consent_log.test.tsx b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/gdpr_consent_log.test.tsx index cb216211..89d81463 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/gdpr_consent_log.test.tsx +++ b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/gdpr_consent_log.test.tsx @@ -192,7 +192,7 @@ describe('layouts/admin/gdpr_consent_log.json — #27 체크박스 즉시 적용 /** * 작업 2 (B-3 옵션 X) — policy_version 컬럼이 평문 Span + 본문 보기 Button 분리. - * PO 피드백: v배지 클릭만으로는 직관성 부족 — 평문 + 명시 버튼 패턴으로 변경. + * 피드백: v배지 클릭만으로는 직관성 부족 — 평문 + 명시 버튼 패턴으로 변경. */ describe('작업 2: policy_version 컬럼 → 평문 + 본문 보기 Button', () => { const layoutJson = JSON.stringify(layout); diff --git a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/mypage_privacy_tab.test.tsx b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/mypage_privacy_tab.test.tsx index 9dad8627..5a1173b7 100644 --- a/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/mypage_privacy_tab.test.tsx +++ b/plugins/_bundled/sirsoft-gdpr/resources/js/__tests__/layouts/mypage_privacy_tab.test.tsx @@ -79,7 +79,7 @@ describe('extensions/mypage_privacy_tab.json — 마이페이지 GDPR 동의 매 }); it('카드 헤더(타이틀 + 구분선) 다음 형제 위치에 배치 (헤더 내부 X)', () => { - // PO UX 결정: 메타 라인은 H2 타이틀 + 구분선 다음에 자연어 문장으로 노출. + // UX 결정: 메타 라인은 H2 타이틀 + 구분선 다음에 자연어 문장으로 노출. // 헤더 내부(border-b 위)에 두면 타이틀과 같은 영역으로 보여 시각적 혼잡 → 헤더 외부로 분리. const header = findById(card ?? null, 'gdpr_card_header'); const headerChildren = (header?.children ?? []) as AnyNode[]; @@ -129,7 +129,7 @@ describe('extensions/mypage_privacy_tab.json — 마이페이지 GDPR 동의 매 }); it('가운데 점(·) 구분자 미사용 (자연어 한 줄 패턴이라 구분자 불필요)', () => { - // 회귀 가드: 이전 디자인은 가로 인라인 + · 구분자였으나, PO 결정으로 자연어 한 줄로 재설계. + // 회귀 가드: 이전 디자인은 가로 인라인 + · 구분자였으나, 설계 결정으로 자연어 한 줄로 재설계. const children = (meta?.children ?? []) as AnyNode[]; const separator = children.find((c) => String(c.text ?? '') === '·'); expect(separator).toBeUndefined(); @@ -223,7 +223,7 @@ describe('extensions/mypage_privacy_tab.json — 마이페이지 GDPR 동의 매 const modal = (root.modals ?? []).find((m) => m.id === 'revoke_confirm_modal'); it('모달 본문에 raw key (cookie_functional 등) 대신 사용자 친화 description 노출', () => { - // PO 피드백 — raw key 는 사용자에게 무의미하여 모달 본문에서 제거. + // 피드백 — raw key 는 사용자에게 무의미하여 모달 본문에서 제거. // 라벨 + description 2줄 구조로 변경됨. const modal = (root.modals ?? []).find((m) => m.id === 'revoke_confirm_modal'); const text = serializeForSearch(modal); @@ -329,7 +329,7 @@ describe('extensions/mypage_privacy_tab.json — 마이페이지 GDPR 동의 매 }); describe('정책 갱신 amber 안내 박스 (마이페이지 카드 내 needs_renewal 안내)', () => { - // PO 결정 — 마이페이지 카드 안에 amber 박스 (gdpr_needs_renewal_banner) 유지. + // 설계 결정 — 마이페이지 카드 안에 amber 박스 (gdpr_needs_renewal_banner) 유지. // 조건: gdprMeConsents.data.needs_renewal === true 일 때만 노출. // 본문: 현 정책 버전 안내 (#23 informed consent) + 필수 쿠키 면제 hint // 액션: 「전체 항목 다시 동의」 (POST /consent/renew-all) diff --git a/plugins/_bundled/sirsoft-gdpr/tests/Feature/Api/Admin/GdprAdminSettingsControllerTest.php b/plugins/_bundled/sirsoft-gdpr/tests/Feature/Api/Admin/GdprAdminSettingsControllerTest.php index 1a91c3ab..1a6df9a1 100644 --- a/plugins/_bundled/sirsoft-gdpr/tests/Feature/Api/Admin/GdprAdminSettingsControllerTest.php +++ b/plugins/_bundled/sirsoft-gdpr/tests/Feature/Api/Admin/GdprAdminSettingsControllerTest.php @@ -295,7 +295,7 @@ class GdprAdminSettingsControllerTest extends PluginTestCase * 노출하여 사용자가 어느 필드를 의미하는지 인지하기 어려움. FormRequest::attributes() 메서드로 * 각 필드에 lang 의 사용자 친화 라벨을 매핑하여 메시지 안에 노출되도록 함. * - * 검토 #10d — PO 가 사용자에게 영문 키 노출이 부적절하다는 피드백 후 추가. + * 검토 #10d — 사용자에게 영문 키 노출이 부적절하다는 피드백 후 추가. */ public function test_update_validation_messages_use_friendly_attribute_names(): void { diff --git a/plugins/_bundled/sirsoft-gdpr/tests/Unit/Listeners/GdprAuthConsentListenerTest.php b/plugins/_bundled/sirsoft-gdpr/tests/Unit/Listeners/GdprAuthConsentListenerTest.php index 69e48147..ea9bc7b9 100644 --- a/plugins/_bundled/sirsoft-gdpr/tests/Unit/Listeners/GdprAuthConsentListenerTest.php +++ b/plugins/_bundled/sirsoft-gdpr/tests/Unit/Listeners/GdprAuthConsentListenerTest.php @@ -75,7 +75,7 @@ class GdprAuthConsentListenerTest extends PluginTestCase * * 실제 훅 체인: HookManager::doAction('core.auth.record_consents', User, array, string $agreedAt, string $ip) * 를 발화했을 때 listener 가 TypeError 없이 받고 폼 동의를 status 에 반영해야 함. - * 회귀 발견 시점: 2026-05-08 PO 가입 시 Argument #3 ($agreedAt) must be of type Carbon\Carbon, string given. + * 회귀 발견 시점: 2026-05-08 관리자 가입 시 Argument #3 ($agreedAt) must be of type Carbon\Carbon, string given. * * @return void */ @@ -139,7 +139,7 @@ class GdprAuthConsentListenerTest extends PluginTestCase * Listener 메서드는 현재 HTTP 요청 컨텍스트 (`request()->ip()`) + Carbon 인스턴스에 * 의존한다. HookListenerRegistrar 의 기본 동작은 큐 디스패치인데, 큐 잡은 별도 컨텍스트라 * 쿠키 소실 + Carbon → string 직렬화 mismatch 발생 → 폼 동의 기록 실패 - * (실제 회귀 — 2026-05-08 PO 가입 시도 시 발견). + * (실제 회귀 — 2026-05-08 관리자 가입 시도 시 발견). */ public function test_subscribed_hook_forces_sync_to_preserve_request_context(): void { diff --git a/plugins/_bundled/sirsoft-pay_kginicis/tests/Feature/Controllers/MobileCallbackRouteTest.php b/plugins/_bundled/sirsoft-pay_kginicis/tests/Feature/Controllers/MobileCallbackRouteTest.php index 2163fcb2..04baa0c7 100644 --- a/plugins/_bundled/sirsoft-pay_kginicis/tests/Feature/Controllers/MobileCallbackRouteTest.php +++ b/plugins/_bundled/sirsoft-pay_kginicis/tests/Feature/Controllers/MobileCallbackRouteTest.php @@ -9,7 +9,7 @@ class MobileCallbackRouteTest extends PluginTestCase /** * KG 이니시스 모바일 표준결제는 P_NEXT_URL 로 POST 콜백을 보낸다. * 라우트가 GET 만 허용하면 405 Method Not Allowed 회귀가 발생한다. - * 가상계좌 결제 시 PO 운영 검증으로 발견된 회귀. + * 가상계좌 결제 시 운영 검증으로 발견된 회귀. */ public function test_mobile_callback_route_accepts_post(): void { diff --git a/plugins/_bundled/sirsoft-verification_kginicis/resources/js/__tests__/identityProviderInicisExtension.test.ts b/plugins/_bundled/sirsoft-verification_kginicis/resources/js/__tests__/identityProviderInicisExtension.test.ts index 60a2e6b4..d37a9f83 100644 --- a/plugins/_bundled/sirsoft-verification_kginicis/resources/js/__tests__/identityProviderInicisExtension.test.ts +++ b/plugins/_bundled/sirsoft-verification_kginicis/resources/js/__tests__/identityProviderInicisExtension.test.ts @@ -3,7 +3,7 @@ * * 본 회차에서 추가한 두 가지 회귀 차단 포인트: * 1. 모달 헤더 제목/설명에 `purpose === 'inicis.adult_verification'` 분기 두 쌍 존재 - * 2. if 표현식이 CLAUDE.md 규정 (`{{식}}` 한 쌍 안에 비교 식 전체) 부합 + * 2. if 표현식이 코어 규정 (`{{식}}` 한 쌍 안에 비교 식 전체) 부합 * * 레이아웃 JSON 파일을 직접 읽어 구조를 단언하는 가벼운 단위 테스트. * 코어 DynamicRenderer 환경 셋업 없이도 회귀 차단 가능. @@ -128,7 +128,7 @@ describe('identity_provider_inicis Extension Point — 본 회차 회귀 차단' expect(texts).toContain('$t:sirsoft-verification_kginicis.modal.description'); }); - it('모든 purpose 분기 if 표현식이 CLAUDE.md 규정 (식 전체를 `{{}}` 한 쌍에) 부합', () => { + it('모든 purpose 분기 if 표현식이 코어 규정 (식 전체를 `{{}}` 한 쌍에) 부합', () => { const purposeIfs: string[] = []; for (const root of spec.components) { diff --git a/plugins/_bundled/sirsoft-verification_kginicis/resources/js/__tests__/layouts/pluginSettingsErrorBinding.test.tsx b/plugins/_bundled/sirsoft-verification_kginicis/resources/js/__tests__/layouts/pluginSettingsErrorBinding.test.tsx index fd7ba10a..558d7707 100644 --- a/plugins/_bundled/sirsoft-verification_kginicis/resources/js/__tests__/layouts/pluginSettingsErrorBinding.test.tsx +++ b/plugins/_bundled/sirsoft-verification_kginicis/resources/js/__tests__/layouts/pluginSettingsErrorBinding.test.tsx @@ -5,7 +5,7 @@ * 배경: onError 의 errors 바인딩이 `{{$error.errors}}` ($ 접두사) 였을 때 ActionDispatcher * 컨텍스트 변수명과 불일치하여 _local.errors 가 null 로 남고, validation_error 박스가 * 렌더되지 않는 silent failure 회귀가 발생했다. 올바른 컨텍스트 변수명은 `{{error.errors}}`. - * (CLAUDE.md 데이터 바인딩 규칙: onSuccess/onError 컨텍스트는 $ 접두사 없음) + * (코어 데이터 바인딩 규칙: onSuccess/onError 컨텍스트는 $ 접두사 없음) */ import { describe, it, expect } from 'vitest'; diff --git a/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/duplicate-block.yaml b/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/duplicate-block.yaml index d2552a56..78bb6533 100644 --- a/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/duplicate-block.yaml +++ b/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/duplicate-block.yaml @@ -110,7 +110,7 @@ design_rationale: | 신규 가입자는 가입 시점에 record 생성되므로 시간 흐를수록 자연 수렴. - 외국인 (userDi 빈값) 통과 정책 — KG이니시스가 외국인 본인확인 시 DI 발급 못 하는 케이스 대응. - PO 결정에 따라 향후 차단 정책 전환 가능 (별도 이슈). + 정책 결정에 따라 향후 차단 정책 전환 가능 (별도 이슈). - PII 미노출 — IdentityDuplicateException / verify failure 메시지에 이메일/이름 절대 포함하지 않음. heads-up 공격 방어. i18n 키로만 처리. diff --git a/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/extension-point.yaml b/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/extension-point.yaml index 234938d1..4f6c97d3 100644 --- a/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/extension-point.yaml +++ b/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/extension-point.yaml @@ -155,7 +155,7 @@ design_rationale: | 주석 명시), 모달 닫힘은 명시적 closeModal 책임. 코어 모달 partial 의 verify 버튼이 이미 동일 sequence 패턴 사용 — 코어 default 와 일관성. 2. 사용자 안내 강화 — 기존 "본인인증 시작" 버튼만으로는 사용자가 "왜 이게 뜨지" 의문을 가질 수 - 있어 (PO 피드백), 사전 안내 카드 (아이콘 + 제목 + 설명 + 3단계 절차 안내) 로 확장. Phase E + 있어 (사용자 피드백), 사전 안내 카드 (아이콘 + 제목 + 설명 + 3단계 절차 안내) 로 확장. Phase E 폐기 사유 #1 의 "사용자 제스처 확보" 본질은 그대로 유지. 3. 진행 중 상태 표시 — 팝업이 열린 후 모달이 정적 상태로 남아있어 사용자가 "지금 뭘 해야 하지" 혼란을 느낄 수 있음. providerInProgress 플래그로 슬롯 if 분기하여 "팝업창에서 진행 중" 스피너 @@ -164,7 +164,7 @@ design_rationale: | providerInProgress 만 false 로 set → 모달은 열린 채 시작 버튼이 재활성화되어 사용자가 다시 시도 가능. 강제 cancelled 처리 시 사용자가 모달 처음부터 다시 열어야 하는 부담 회피. - 옵션 비교 — Phase E′-Redesign 의사결정 (PO 와 검토): + 옵션 비교 — Phase E′-Redesign 의사결정 (검토): - 옵션 1 (사전 안내 모달 + redirect): 페이지 전환 1회 발생, 데스크톱 UX 무거움 - 옵션 2 (모달 + 팝업 = 본 안): 모달 안에서 완결, 모바일 일반 브라우저 (Safari/Chrome) 도 동일 흐름 으로 동작 (window.open 이 새 탭으로 열려도 opener 유지 → bridge postMessage 동작) @@ -178,7 +178,7 @@ design_rationale: | - 코어 무수정 + 템플릿 minimal 수정 (모달 partial 에 if 분기 1줄 + 액션 버튼 if 조건 보강) - 모달 닫힘 결함만 fix 하면 작업 완결 - PO 피드백 반영 (sirsoft-basic 1.0.0-beta.6): + 피드백 반영 (sirsoft-basic 1.0.0-beta.6): 1. 외부 plugin 흐름에서 코어 모달의 [코드 재전송] / [확인] 버튼 숨김 — 외부 plugin 은 자기 흐름 안에서 verify 까지 책임지므로 코어 액션이 불필요. 코어 mail/SMS 등 코드 발송형 흐름은 기존대로 노출 (회귀 없음). diff --git a/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/install-default-purpose-provider.yaml b/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/install-default-purpose-provider.yaml index 0bbc47be..fc12e7b6 100644 --- a/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/install-default-purpose-provider.yaml +++ b/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/install-default-purpose-provider.yaml @@ -22,7 +22,7 @@ test_files: - plugins/_bundled/sirsoft-verification_kginicis/tests/Feature/Identity/InicisInstallDefaultPurposeProviderTest.php design_rationale: | - PO 결정: + 설계 결정: - 설치 시 1회만 세팅(install 훅), 활성화/업그레이드 시 재적용하지 않음. - 운영자 변경 가능(초기값 성격) — 코어 settings 에 저장하므로 환경설정 UI (본인인증 > 프로바이더 탭, 목적별 매핑)에서 그대로 변경된다. diff --git a/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/partial-data-integrity.yaml b/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/partial-data-integrity.yaml index 54d44392..afaefe92 100644 --- a/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/partial-data-integrity.yaml +++ b/plugins/_bundled/sirsoft-verification_kginicis/tests/scenarios/partial-data-integrity.yaml @@ -48,7 +48,7 @@ rules_layer_coverage: coverage: name/phone/birthday nullable 변경 마이그레이션에 down() (NOT NULL 복귀) + 한국어 comment 포함. design_rationale: | - PO 결정 (부분 데이터 처리 방향): + 설계 결정 (부분 데이터 처리 방향): - 차단 대상 = 신원 핵심값(이름·생년월일·휴대폰·설정 기준 식별값). Chrome MCP 실측(2026-06-23, 아이핀 채널)에서 이름·휴대폰·생년월일·CI·DI 가 모두 응답에 채워짐을 확인 → 휴대폰도 항상 오는 핵심값으로 포함. 법규(CI/DI 는 주민/외국인등록번호 기반 → 정상 본인확인이면 제공)·코드(식별값 @@ -61,7 +61,7 @@ design_rationale: | 휴대폰은 신원 핵심값이라 누락 시 차단 대상 (위 incomplete_identity 케이스에 포함). - 토큰 재발급 가드(isVerified enum 비교)는 1번 PR, verify 실패 toast 매핑은 2번 PR 에서 처리됨. 본 항목은 3번의 신원 가드 + 정합 저장 + INCOMPLETE_IDENTITY toast 1줄 보강. - - (저장 원자성 보강) 토큰 발급(log update)과 PII 저장을 한 단위로 묶는다 (PO 결정 A안). PII 저장이 + - (저장 원자성 보강) 토큰 발급(log update)과 PII 저장을 한 단위로 묶는다 (설계 결정 A안). PII 저장이 실패하면 토큰 발급까지 롤백하고 STORAGE_FAILED 로 인증을 실패시켜 "인증 성공=PII 저장됨" 무결성을 보장한다. 로그인 record upsert 는 DB 트랜잭션으로 묶고, 비로그인 cache stash 실패는 트랜잭션 콜백을 예외 종료시켜 log update 를 롤백한다. 비로그인 가입 backfill(after_register listener)은 record 가 diff --git a/resources/js/core/template-engine/layout-editor/__tests__/components/PlaceholderChipInput.test.tsx b/resources/js/core/template-engine/layout-editor/__tests__/components/PlaceholderChipInput.test.tsx index 5fd2b0a1..504a7a43 100644 --- a/resources/js/core/template-engine/layout-editor/__tests__/components/PlaceholderChipInput.test.tsx +++ b/resources/js/core/template-engine/layout-editor/__tests__/components/PlaceholderChipInput.test.tsx @@ -273,7 +273,7 @@ describe('insertChipInValue — 커서 위치 자리표시 삽입', () => { }); }); -describe('타이핑 안전성 — 비제어 평문(글자 뒤섞임 결함 회귀, PO)', () => { +describe('타이핑 안전성 — 비제어 평문(글자 뒤섞임 결함 회귀)', () => { it('평문 타이핑(연속 입력) 시 평문 span 을 React 가 덮어쓰지 않는다(비제어)', () => { // 결함 재현 방지: controlled 였다면 onChange→value 갱신→리렌더로 span textContent 가 // 매 입력마다 재설정되어 커서가 튀고 글자 순서가 깨졌다("하나둘"→"퉁둘낭나한하"). diff --git a/resources/js/core/template-engine/layout-editor/__tests__/components/TableEditor.test.tsx b/resources/js/core/template-engine/layout-editor/__tests__/components/TableEditor.test.tsx index b5af39b1..819447a0 100644 --- a/resources/js/core/template-engine/layout-editor/__tests__/components/TableEditor.test.tsx +++ b/resources/js/core/template-engine/layout-editor/__tests__/components/TableEditor.test.tsx @@ -278,7 +278,7 @@ describe('셀 테두리/텍스트', () => { colors: [{ value: 'red', swatch: '#ef4444', token: 'border-red-500' }], }; const paramsCat = { ...params, cellBorder: catalog }; - // 셀에 두께(border)를 미리 부여 → 색 피커가 즉시 노출(앵커 셀 className 기반). 실제 PO + // 셀에 두께(border)를 미리 부여 → 색 피커가 즉시 노출(앵커 셀 className 기반). 실제 운영자 // 흐름(얇게 선택된 상태)과 동일. (단위 테스트의 onPatchNode mock 은 node prop 을 // 되먹이지 않아 두께 클릭→리렌더로는 색 피커가 안 떠 별도 셀 className 으로 노출.) const bc = (text: string): EditorNode => ({ type: 'basic', name: 'Td', text, props: { className: 'border' } }); diff --git a/resources/js/core/template-engine/layout-editor/__tests__/layouts/condition-toggle-render.test.tsx b/resources/js/core/template-engine/layout-editor/__tests__/layouts/condition-toggle-render.test.tsx index f975c738..73875846 100644 --- a/resources/js/core/template-engine/layout-editor/__tests__/layouts/condition-toggle-render.test.tsx +++ b/resources/js/core/template-engine/layout-editor/__tests__/layouts/condition-toggle-render.test.tsx @@ -4,7 +4,7 @@ * * ConditionBuilder 가 combineConditions 로 만든 단일 `{{ }}` 식이 실제 런타임 * `evaluateStringCondition`(→ optional chaining 식이라 `evaluateExpression` 진입, - * CLAUDE.md `if` 평가 규칙)으로 샘플 상태에 따라 표시/숨김 토글되는지 검증한다. + * 코어 `if` 평가 규칙)으로 샘플 상태에 따라 표시/숨김 토글되는지 검증한다. * * 단위 시뮬레이션이 아닌 실제 평가 엔진 라운드트립 — 합성식의 문법 정합성을 잠근다. */ diff --git a/resources/js/core/template-engine/layout-editor/__tests__/spec/actionRecipeEngine.test.ts b/resources/js/core/template-engine/layout-editor/__tests__/spec/actionRecipeEngine.test.ts index 8d523d1c..cd1fc47a 100644 --- a/resources/js/core/template-engine/layout-editor/__tests__/spec/actionRecipeEngine.test.ts +++ b/resources/js/core/template-engine/layout-editor/__tests__/spec/actionRecipeEngine.test.ts @@ -1,7 +1,7 @@ /** * actionRecipeEngine.test.ts — 친화 액션 레시피 → 핸들러 JSON DataProvider * - * CLAUDE.md "CRITICAL RULES — API/핸들러 호출" 전수 회귀 가드 포함: + * 코어 핸들러 규칙(API/핸들러 호출) 전수 회귀 가드 포함: * - apiCall(api 아님)/navigate(nav 아님)/setState+target/toast(showToast 아님)/ * refetchDataSource.dataSourceId(id 아님). * - apiCall 의 target/onSuccess/onError 는 액션 top-level. @@ -70,7 +70,7 @@ describe('normalizeActionRecipes', () => { }); }); -describe('buildAction — CLAUDE.md 핸들러 규칙 준수', () => { +describe('buildAction — 코어 핸들러 규칙 준수', () => { const recipes = normalizeActionRecipes(RECIPES); const byId = (id: string) => recipes.find((r) => r.id === id)!; diff --git a/resources/js/core/template-engine/layout-editor/__tests__/spec/conditionRecipeEngine.test.ts b/resources/js/core/template-engine/layout-editor/__tests__/spec/conditionRecipeEngine.test.ts index a468e05a..96869158 100644 --- a/resources/js/core/template-engine/layout-editor/__tests__/spec/conditionRecipeEngine.test.ts +++ b/resources/js/core/template-engine/layout-editor/__tests__/spec/conditionRecipeEngine.test.ts @@ -1,7 +1,7 @@ /** * conditionRecipeEngine.test.ts — 친화 조건 → if 식 합성 DataProvider * - * CLAUDE.md `if` 규칙 회귀 가드: + * 코어 `if` 규칙 회귀 가드: * - 최종 결과 전체가 단일 `{{ }}` 한 쌍 — 중첩 보간 `{{ {{x}} }}` 미발생. * - 옵셔널 체이닝(`?.`) / 배열 길이 `(arr?.length ?? 0)` 형태 보존. * - 로그인 판정 경로 보존(임의 식 금지). @@ -65,7 +65,7 @@ describe('buildClauseExpr — {paramKey} 치환', () => { }); }); -describe('combineConditions — 단일 {{ }} 합성 (CLAUDE.md if 규칙)', () => { +describe('combineConditions — 단일 {{ }} 합성 (코어 if 규칙)', () => { it('단일 조건 → {{ 식 }}', () => { const r = combineConditions([{ operator: 'isLoggedIn', params: {} }], 'and', ops); expect(r).toBe('{{ _global?.currentUser?.uuid }}'); diff --git a/resources/js/core/template-engine/layout-editor/__tests__/spec/editorGapCoverage.test.ts b/resources/js/core/template-engine/layout-editor/__tests__/spec/editorGapCoverage.test.ts index 75e21d67..b0732c70 100644 --- a/resources/js/core/template-engine/layout-editor/__tests__/spec/editorGapCoverage.test.ts +++ b/resources/js/core/template-engine/layout-editor/__tests__/spec/editorGapCoverage.test.ts @@ -9,7 +9,7 @@ * * 가드 항목: * 1. 신규 6개 admin composite 가 nesting.draggable 등재 + capability 보유. - * 2. 그 6개 tsx 가 editorAttrs 를 시각 루트에 spread (선택/클릭 가능 — PO 가 직접 + * 2. 그 6개 tsx 가 editorAttrs 를 시각 루트에 spread (선택/클릭 가능 — 검수자가 직접 * 발견한 AdminSidebar/AdminFooter 선택 불가 결함의 회귀 가드). * 3. SectionHeader 가 componentPalette 에 등재 (팔레트 추가 가능). * 4. ecommerce editor-spec 에 신규 admin 화면 5개 states 그룹 등록 (상태 토글 노출). @@ -83,7 +83,7 @@ describe('편집기 갭 보강 — 신규 admin composite 편집 가능성', () describe('편집기 갭 보강 — editorAttrs 시각 루트 spread (선택/클릭 가능)', () => { // base 골격/위젯이 편집기 주입 editorAttrs 를 시각 루트에 흘려야 클릭이 그 노드에 닿는다. - // (PO 가 base 편집 모드에서 AdminSidebar/AdminFooter 클릭 시 부모 Div 만 선택되던 결함) + // (검수자가 base 편집 모드에서 AdminSidebar/AdminFooter 클릭 시 부모 Div 만 선택되던 결함) const FILES: Record = { AdminSidebar: `${ADMIN}/src/components/composite/AdminSidebar.tsx`, AdminFooter: `${ADMIN}/src/components/composite/AdminFooter.tsx`, diff --git a/resources/js/core/template-engine/layout-editor/__tests__/spec/expressionValueTree.test.ts b/resources/js/core/template-engine/layout-editor/__tests__/spec/expressionValueTree.test.ts index 743f653b..a0d6946e 100644 --- a/resources/js/core/template-engine/layout-editor/__tests__/spec/expressionValueTree.test.ts +++ b/resources/js/core/template-engine/layout-editor/__tests__/spec/expressionValueTree.test.ts @@ -283,7 +283,7 @@ describe('디그레이드 — 손상 0 폴백', () => { }); describe('hasDecomposableExpressionSegment — 다중 세그먼트 + 표현식 조각', () => { - // 양 템플릿 실측 9건(다중 세그먼트 + 진짜 삼항 분기 토큰) — PO board/form 제목 케이스 포함. + // 양 템플릿 실측 9건(다중 세그먼트 + 진짜 삼항 분기 토큰) — 실측 board/form 제목 케이스 포함. const REAL_MULTISEGMENT = [ "{{route.id ? '$t:board.edit_post' : '$t:board.new_post'}} - {{form_meta?.data?.board?.name || ''}}", "{{_local.collapsedReplies?.[comment?.id] === false ? '$t:board.hide_replies' : '$t:board.show_replies'}} ({{comment?.replies_count}})", @@ -324,7 +324,7 @@ describe('previewSegments — 접힌 미리보기 토큰("한 값만 해석")', expect(toks[1]).toEqual({ kind: 'ellipsis', text: '⋯' }); }); - it('PO board/form 다중 세그먼트 → 게시글 수정⋯ + 평문 + 데이터 칩', () => { + it('실측 board/form 다중 세그먼트 → 게시글 수정⋯ + 평문 + 데이터 칩', () => { const toks = previewSegments( "{{route.id ? '$t:board.edit_post' : '$t:board.new_post'}} - {{form_meta?.data?.board?.name || ''}}", resolve, @@ -606,7 +606,7 @@ describe('extractFirstLeafText / reduceExpressionToPlain — 표현식 → 일 // og.title `{{route.id ? '$t:edit': product.title}}` 류에서 첫 결과(then)가 // 데이터인 경우. 미리보기 "기본 값 → 🔗 product.title" 과 적용이 일치해야 한다. - it('조건식 첫 분기가 데이터면 그 데이터 바인딩으로 복구(PO og.title 케이스)', () => { + it('조건식 첫 분기가 데이터면 그 데이터 바인딩으로 복구(실측 og.title 케이스)', () => { expect(reduceExpressionToPlain("{{route.id ? product.title : '$t:shop.new'}}")).toBe('{{product.title}}'); }); diff --git a/resources/js/core/template-engine/layout-editor/components/PreviewCanvas.tsx b/resources/js/core/template-engine/layout-editor/components/PreviewCanvas.tsx index 82b192b5..34d08e34 100644 --- a/resources/js/core/template-engine/layout-editor/components/PreviewCanvas.tsx +++ b/resources/js/core/template-engine/layout-editor/components/PreviewCanvas.tsx @@ -1060,7 +1060,7 @@ export function PreviewCanvas(props: PreviewCanvasProps = {}): React.ReactElemen >
{ - // shape 안전 바인딩(`{{src?.path ?? ''}}`) — 데이터 미도착 시 런타임 에러 방지(CLAUDE.md + // shape 안전 바인딩(`{{src?.path ?? ''}}`) — 데이터 미도착 시 런타임 에러 방지(코어 // fallback 필수). buildBindingExpression 이 scalar 폴백(`?? ''`)을 붙인다. onPick(buildBindingExpression(c.sourceId, c.path, 'scalar')); }} diff --git a/resources/js/core/template-engine/layout-editor/components/property-controls/I18nTextField.tsx b/resources/js/core/template-engine/layout-editor/components/property-controls/I18nTextField.tsx index d8bc030a..b61b9042 100644 --- a/resources/js/core/template-engine/layout-editor/components/property-controls/I18nTextField.tsx +++ b/resources/js/core/template-engine/layout-editor/components/property-controls/I18nTextField.tsx @@ -829,7 +829,7 @@ function PlainInsertDataButton({ return (
{/* `+` 버튼은 항상 자리 유지(피커가 열려도 플로우 폭 불변). 피커는 공용 FloatingDropdown 으로 - 버튼 아래 띄우되 위치 자동 보정(flip/clamp)으로 좁은 입력 행 어디서든 잘리지 않는다(PO + 버튼 아래 띄우되 위치 자동 보정(flip/clamp)으로 좁은 입력 행 어디서든 잘리지 않는다(검수 2026-06-13 — 하드코딩 right:0 으로 좌측 쏠려 가려지던 결함 → 범용 위치 보정으로 통일). */}