Files
Gnuboard7/tests/scenarios/ext-static-cache.yaml
T
HeuJung 0bb33e3baa fix(core): root 프로세스의 terminating 정적 게시를 웹 자가 치유로 위임
sudo 코어 업데이트(7.0.9→7.0.10 실서버)에서 자식 프로세스(신코드, root)의
terminating 게시가 소유권 복원 이후 시점에 실행되며 캐시 락 샤드 디렉토리
(storage/framework/cache/data/xx)와 병합 번들(storage/app/ext-bundles)을
root 소유로 남겼고, 이후 웹 프로세스의 캐시 쓰기가 그 샤드에 해시되는 순간
Permission denied 로 죽어 전면 500 이 됐다 (laravel.log 무기록 — 로거 도달 전).

normalizeOwnership 은 게시 트리(build/ext)만 다뤘다 — 게시의 간접 write-set
(캐시 락·번들 빌드)이 사각이었다. 뒤쫓아 chown 하는 대신 root 프로세스에서는
terminating 자동 게시를 예약하지 않고 다음 웹 렌더의 자가 치유(웹 계정)에
위임한다. 명시적 ext-static:publish 는 게이트 밖(운영자 책임 — 규정 §6).

- 시나리오 매니페스트에 process_user(web/root_cli) 축 신설 — uid 조합 실측
 부재가 이 결함을 통과시킨 구조적 원인 (기존 마커 전수에 web 부여)
- red 소급 증빙: 게이트 부재 커밋본에서 신규 케이스 실패 확인 → 15/15 green
2026-08-25 17:37:24 +09:00

102 lines
6.2 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
feature: ext-static-cache
description: |
부트스트랩 리소스 정적 게시(bake) — 공개 #122.
초기 부트스트랩 리소스(다국어 병합·컴포넌트 정의·라우트·확장 번들·템플릿 dist
에셋)를 캐시 버전 디렉토리(`public/build/ext/{v}/`)에 실파일로 게시해 웹서버가
rewrite 전에 직접 서빙한다. 병합 입력이 바뀌는 수명주기 이벤트마다
`incrementExtensionCacheVersion()` 단일 지점의 terminating 예약으로 재게시되고,
누락 시 blade 렌더의 자가 치유가 보충한다. 미게시/부분게시/GC 직후에는 프론트
fetch 계층(fetchStaticFirst)과 태그 계층(asset-url-recovery 파샬)이 종전 API 로
폴백한다.
핵심 동작:
- 게시 원자성: {v}.tmp 쓰기 → rename → manifest 마지막 기록 (manifest 존재 = 완료)
- 서빙 게이트 3조건: 프로덕션 + core.static_cache.enabled + 게시 완료
- 태그 계층은 개별 파일 file_exists 까지 확인 후 정적 URL 방출
- blade 주입 cache_version 시드로 stale localStorage 이중 로드 제거 (작업 A)
- components.json `?v` 부착 + 버전 키드 manifestCache (작업 B)
- 폴백 API 품질: ETag/304 + 환경 분기 (작업 C·D)
axes:
publish_state: [published, unpublished, partial]
environment: [production, dev]
trigger: [lifecycle, self_heal, manual_command, kill_switch]
# 실행 주체(uid) 축 — sudo CLI(root)와 웹 계정이 갈리는 파일 산출물 기능의 필수 축.
# 7.0.10 업그레이드 실사례: root terminating 게시가 캐시 락 샤드를 root 소유로 남겨
# 웹 캐시 쓰기가 전면 500. 이 축이 없어 24축 매트릭스가 그 조합을 못 잡았다.
process_user: [web, root_cli]
exclusions:
- { environment: dev, publish_state: published, reason: "dev 는 staticBase 미주입 — 게시 상태와 무관하게 전 리소스 API 직행" }
- { environment: dev, publish_state: partial, reason: "동일 — dev 는 정적 경로 자체가 없다" }
- { trigger: kill_switch, publish_state: published, reason: "kill-switch off 는 게이트에서 base 자체를 차단 — 게시 상태 무관" }
- { trigger: kill_switch, publish_state: partial, reason: "동일" }
- { process_user: root_cli, trigger: self_heal, reason: "자가 치유는 웹 렌더 경로 전용 — root 웹 프로세스는 존재하지 않는 구성" }
- { process_user: root_cli, environment: dev, reason: "dev 는 게시 자체가 무의미 (terminating 게이트가 프로덕션 한정)" }
- { process_user: root_cli, trigger: kill_switch, reason: "kill-switch 는 uid 판정 이전의 선행 게이트 — uid 무관" }
- { process_user: root_cli, trigger: manual_command, reason: "명시적 ext-static:publish 는 root 게이트 밖 (운영자 책임 — 규정 §6). 게시 로직 자체는 web 축 케이스가 검증" }
- { process_user: root_cli, publish_state: published, reason: "root 게이트는 게시 상태 판정 이전에 위치 — 상태와 직교, 대표 조합(unpublished×lifecycle)으로 고정" }
- { process_user: root_cli, publish_state: partial, reason: "동일 — 상태 직교" }
effects:
- published_lang_matches_api_payload
- published_routes_has_success_envelope
- published_tree_excludes_inactive_extensions
- published_tree_excludes_sourcemaps
- identifier_outside_pattern_rejected
- publish_is_idempotent_until_forced
- write_failure_cleans_tmp_and_falls_back
- cleanup_keeps_current_and_previous_versions
- kill_switch_disables_publish_and_gate
- terminating_publish_gated_to_production
- terminating_publish_uses_final_version_after_burst
- static_gate_requires_manifest
- tag_layer_checks_individual_file_existence
- static_first_fetch_falls_back_to_api_on_miss
- fallback_is_observable_via_console_warn
- static_asset_tag_recovers_to_api_url
- no_duplicate_boot_requests
- versioned_boot_urls
- handshake_reload_preserved_on_version_mismatch
- manifest_cache_keys_are_version_scoped
- fallback_api_serves_etag_304
- fallback_api_no_cache_in_dev
- degraded_snapshot_gets_no_public_cache
- seo_html_never_references_gc_target_static_path
- static_miss_skips_spa_catch_all
- terminating_schedule_rearms_after_execution
- published_htaccess_declares_compression
- bundle_script_static_miss_falls_back_to_api
- root_cli_defers_publish_to_web_self_heal
test_files:
- tests/Feature/Services/ExtensionStaticCacheServiceTest.php
- tests/Feature/Api/Public/PublicComponentsCachingTest.php
- tests/Feature/Template/PublicTemplateControllerTest.php
- tests/Feature/Template/TemplateAssetServingTest.php
- tests/Feature/Template/TemplateLanguageServingTest.php
- tests/Feature/Api/Public/LayoutServingTest.php
- tests/Feature/Http/BuildPathCatchAllExclusionTest.php
- tests/Unit/Support/AssetUrlTest.php
- tests/Unit/Seo/SeoRendererStaticAssetBypassTest.php
- resources/js/core/__tests__/TemplateApp.cacheVersionSeed.test.ts
- resources/js/core/support/__tests__/fetchStaticFirst.test.ts
- resources/js/core/support/__tests__/assetUrlRecovery.test.ts
- resources/js/core/template-engine/__tests__/ComponentRegistry.manifestVersion.test.ts
- resources/js/core/modules/__tests__/ModuleAssetLoader.test.ts
- tests/Playwright/specs/smoke/bootstrap-request-dedup.spec.ts
- tests/Playwright/specs/smoke/static-cache-fallback.spec.ts
notes: |
리소스 5종(lang/components/routes/번들/dist)은 축이 아니라 effects 로 커버한다 —
게시·게이트·폴백은 리소스 무관 공통 메커니즘이고, 리소스별 형상 차이는
published_* effects (payload/봉투/소스맵 제외) 가 각각 단언한다.
environment=dev 축은 PHPUnit(환경 분기 케이스)과 AssetUrl 게이트 단위가 담당한다 —
E2E 대상 사이트(g7_2.dev)는 production 이라 dev 축을 브라우저로 실측할 수 없다.
trigger=lifecycle 의 실기기 검증(확장 비활성/활성 → 재게시 → 새 staticBase)은
Chrome MCP 매트릭스 T10a~T10d 가 수행한다 (원상 복구 의무 포함).
publish_state=partial 은 태그 계층(개별 file_exists 게이트)과 fetch 계층
(fetchStaticFirst 404 폴백) 양쪽에서 단위/브라우저로 커버된다.