# audit:allow test-scenario-coverage reason: 본 매니페스트는 코어 업데이트 spawn 실패 / silent skip / stale 메모리 가드 인프라의 시나리오 매트릭스 SSoT. 핵심 회귀 가드는 test_files 의 통과 테스트로 커버. feature: 코어 업데이트 spawn 자식 실패 fail-fast + [STEPS_EXECUTED] silent skip 가드 + stale 메모리 가드 description: | 공개 이슈 gnuboard/g7#28 (beta.3 → beta.4 업그레이드 도중 `Call to undefined method ensureWritableDirectories()` fatal) 의 발현 메커니즘은 spawn 자식 프로세스 실패 → 부모 in-process fallback 진입 → 부모 메모리의 stale 클래스가 신규 메서드 호출 시 fatal. 본 매니페스트는 spawn 자식 실패의 4가지 분기 (proc_open 비활성 / 자원 생성 실패 / 자식 비정상 종료 / 자식 silent skip) 각각에 대해 spawn_failure_mode (abort/fallback) 가 정상 동작하는지 + stale 메모리 가드가 in-process fallback 진입 시점에 abort/fallback 분기를 일관 적용하는지의 cross product 회귀 가드. 구성 (정책 결정 2026-05-11): 1. `spawn_failure_mode` 기본값 `abort` — 4분기 모두 UpgradeHandoffException throw. 2. `[STEPS_EXECUTED]` stdout 라인 프로토콜 — 자식이 실행한 step 수(count) + 범위 내 발견된 스텝 파일 수(discovered) 명시. 부모는 신호 미수신 또는 executed=0 && discovered>0 (스텝 파일이 있는데 실행 못함) 을 silent skip 으로 판정. executed=0 && discovered=0 (스텝 파일 부재) 은 정상 통과 — from0 — zero 모순" } - { parent_generation: 7_0_11_plus, package_manifest_state: stale_dev_provider, reason: "7.0.11+ 부모는 spawn 직전에 매니페스트를 비우므로 자식이 stale 을 보는 상태 자체가 성립하지 않는다 — 계층 ② 검증은 pre_7_0_11 부모에서만 의미가 있다" } - { process_context: long_lived_outside_update, child_self_heal_flag: present, reason: "플래그를 물고 있으면 정의상 업데이트 트리 안이다 — 모순" } - { vendor_dev_packages: unknown, composer_step_branch: skipped_unchanged, reason: "판정 불가(installed.json 부재)에서는 감지 로그 자체가 출력되지 않아 분기가 구분되지 않는다" } - { argv_sapi: web_with_forged_query_argv, child_self_heal_flag: present, reason: "env 플래그는 웹 요청으로 주입할 수 없다 — 위조 argv 축은 플래그 부재 상태에서만 의미가 있다" } effects: # §2 spawn_failure_mode + failSpawnWithMode - failSpawnWithMode_abort_throws_UpgradeHandoffException_with_resume_command - failSpawnWithMode_fallback_returns_false_with_warning_log - spawnUpgradeStepsProcess_proc_open_disabled_dispatches_to_failSpawnWithMode - spawnUpgradeStepsProcess_proc_open_resource_fail_dispatches_to_failSpawnWithMode - spawnUpgradeStepsProcess_child_abnormal_exit_dispatches_to_failSpawnWithMode # §2.1 [STEPS_EXECUTED] silent skip 가드 (count + discovered) - ExecuteUpgradeStepsCommand_emits_STEPS_EXECUTED_with_count_and_discovered_on_normal_completion - ExecuteUpgradeStepsCommand_does_not_emit_STEPS_EXECUTED_on_handoff_exit - spawnUpgradeStepsProcess_parses_STEPS_EXECUTED_with_positive_count_returns_true - spawnUpgradeStepsProcess_missing_STEPS_EXECUTED_dispatches_to_failSpawnWithMode # discovered 기반 분기 — executed=0 을 discovered 로 구분 - handleSpawnExit_zero_executed_zero_discovered_returns_true # 케이스 B: 스텝 파일 부재 정상 통과 - handleSpawnExit_zero_executed_positive_discovered_dispatches_to_failSpawnWithMode # 케이스 A: gnuboard/g7#28 silent skip - handleSpawnExit_zero_executed_null_discovered_from_lt_to_dispatches_to_failSpawnWithMode # 구버전 자식 레거시 판정 - spawnUpgradeStepsProcess_no_step_files_returns_true_even_when_from_lt_to # 7.0.0→7.0.1 실증 - runUpgradeSteps_notifies_discovered_zero_when_no_step_in_range - spawnUpgradeStepsProcess_zero_steps_with_from_eq_to_forced_returns_true # §6 stale 메모리 가드 - runUpgradeSteps_throws_UpgradeHandoffException_when_memory_lt_to_with_abort_mode - runUpgradeSteps_logs_warning_when_memory_lt_to_with_fallback_mode - runUpgradeSteps_proceeds_silently_when_memory_eq_or_gt_to - runUpgradeSteps_reads_env_APP_VERSION_before_config_so_spawn_child_with_stale_config_cache_passes_guard - runUpgradeSteps_falls_back_to_config_version_when_env_APP_VERSION_absent # config 캐시 부팅 자식 (2026-09-06 전수조사) — 부모는 spawn 전에 캐시를 비우고, 자식은 캐시 부팅이면 디스크 config 를 읽는다 - spawnUpgradeStepsProcess_clears_config_cache_before_proc_open - execute_upgrade_steps_child_reads_update_config_from_disk_when_config_is_cached - route_cache_rebuild_preserves_container_instance - runUpgradeSteps_resume_command_format_matches_execute_upgrade_steps_signature # §1 symlink 보존 (CoreBackupHelper 위임 경로 포함) - copyDirectory_preserves_symlink_target_pointer_on_linux - copyDirectory_falls_back_to_directory_copy_on_symlink_failure - copyDirectory_replaces_existing_directory_with_symlink_when_source_is_symlink - removeOrphanItems_unlinks_orphan_symlinks_without_recursive_delete # §7 V-1 audit rule - upgrade_step_vone_safety_warns_on_app_service_call_in_upgrade_step - upgrade_step_vone_safety_warns_on_app_manager_call_in_upgrade_step - upgrade_step_vone_safety_warns_on_app_repository_call_in_upgrade_step - upgrade_step_vone_safety_skips_when_audit_allow_inline_present # §8 핸드오프 재실행 권한 안내 (sudo/root × 웹서버 계정 식별성 4분기) - renderResumeGuidance_non_root_prints_command_verbatim_without_permission_warning - renderResumeGuidance_root_web_known_prefixes_sudo_u_and_warns_with_account_name - renderResumeGuidance_root_web_symmetric_prints_command_verbatim - renderResumeGuidance_root_web_unknown_uses_placeholder_and_generic_warning # §9 격리 디렉토리 정리 3층 (부모 루트째 삭제 / 자식 빈 껍데기 청소 / 스냅샷 제외) + 완료 안내문 - cleanupPending_removes_whole_staging_root_when_given_inner_source_path - sweepEmptyStagingDirectories_removes_empty_core_dirs_and_keeps_dirs_with_files - snapshotOwnershipDetailed_excludes_current_run_staging_root - execute_bundled_updates_child_sweeps_empty_staging_directories_left_by_parent - apply_mode_incremental_prune_hint_does_not_reference_rollback_command # #658 stale 패키지 매니페스트 3계층 (부모 선정리 / 자식 자가 치유 / 버전 판독 범위) - spawnUpgradeStepsProcess_clears_package_manifests_before_proc_open - spawn_child_boots_with_regenerated_package_manifest_when_parent_left_stale_dev_manifest - bootstrap_app_unlinks_stale_package_manifest_when_update_flag_present - bootstrap_app_leaves_package_manifest_untouched_without_update_flag - PackageManifestCacheHelper_clear_unlinks_packages_and_services_at_configured_paths - clearAllCaches_rebuilds_package_manifest_via_helper - CoreUpdateContext_isInProgress_detects_env_flag_or_update_argv - CoreUpdateContext_ignores_argv_outside_console_sapi - CoreUpdateContext_env_flag_is_honored_regardless_of_sapi - PackageManifestCacheHelper_clear_returns_paths_it_could_not_remove - spawnUpgradeStepsProcess_logs_manifest_files_it_could_not_remove - getCoreVersion_prefers_env_APP_VERSION_only_inside_update_tree - getCoreVersion_ignores_env_APP_VERSION_outside_update_tree - getCoreVersion_falls_back_to_config_when_env_absent_inside_update_tree # #658 상주 큐 워커 재시작 신호 - core_update_step11_signals_queue_restart test_files: - tests/Feature/Console/CoreUpdateCommandSpawnFailureTest.php - tests/Feature/Console/Commands/ExecuteUpgradeStepsStandaloneTest.php - tests/Unit/Services/CoreUpdateServiceFreshDiskConfigTest.php - tests/Unit/Support/RouteCacheHelperContainerTest.php - tests/Feature/Console/CoreUpdateCommandHandoffTest.php - tests/Feature/Console/CoreUpdateResumeGuidanceTest.php - tests/Feature/Upgrades/MultiVersionUpgradePathTest.php - tests/Unit/Extension/Helpers/FilePermissionHelperSymlinkTest.php - tests/Unit/Services/CoreUpdateServiceStagingCleanupTest.php - tests/Feature/Console/ExecuteBundledUpdatesCommandTest.php - tests/Feature/Console/CoreUpdateCommandStalePackageManifestTest.php - tests/Unit/Support/PackageManifestCacheHelperTest.php - tests/Unit/Support/CoreUpdateContextTest.php - tests/Unit/Extension/CoreVersionCheckerEnvPriorityTest.php - tests/Unit/Services/CoreUpdateServiceQueueRestartTest.php # 본 매니페스트의 axes cross product 는 800+ 케이스이나, 실제 회귀 가드는 test_files 의 # 통과 테스트들이 SSoT — 매니페스트는 매트릭스 SSoT 역할. # # 잔존 결함 (계획서 §9 명시, 본 매트릭스에서 제외): # 9.1: proc_open 비활성 + spawn_failure_mode=fallback 사용자의 V-1 fatal # → mode=fallback 자체가 호환 옵션이며, V-1 위험 잔존은 fallback 의 본질. # 매트릭스 검증 대상 아님. # 9.2: Windows 환경의 public/storage symlink 보존 # → axes.symlink_target_state=windows_no_privilege 케이스로 표현되며, # symlink 생성 자체가 PHP 권한 부족으로 실패 → 일반 디렉토리 폴백. # 회귀 테스트가 markTestSkipped 로 표시. # 9.3: OPCache file_cache 활성 환경의 stale 자식 디스크 캐시 # → 본 매니페스트 범위 밖 (인프라 설정). # 9.6: beta.1/2 사용자의 beta.5 직접 점프 fatal # → axes.parent_memory_version=less_than_to 케이스 일부 시뮬레이션 가능하나, # 실 fatal 은 이전 버전 디스크의 CoreUpdateCommand 가 호출되는 시점이라 # 본 브랜치 코드의 영향력 범위 밖. 단계적 업그레이드 가이드 (CHANGELOG Upgrade Notice).