diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 22cc202..b825210 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -242,6 +242,11 @@ agent or engineer to remove them without reconstructing context from chat. `plan_asset_file_load` wrapper still performs the retained `stat` probe for mtime platforms until asset/file watching is owned by injected storage services. +- 2026-06-12: DEBT-0054 was narrowed again. The retained mtime probe is now an + explicit `pp_platform_api::probe_asset_file_timestamp` boundary with missing + and existing file coverage, and `plan_asset_file_load_for_platform` consumes + that probe instead of owning the `stat` call inline. Injected file-watch or + storage services still need to own the live probe before the debt closes. - 2026-06-05: DEBT-0056 was narrowed. `src/asset.h` no longer exposes Android SDK types or forward declarations; retained Android asset-manager and asset handles are stored as opaque pointers and cast only inside `src/asset.cpp`, diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 70dee25..9b63d04 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -644,6 +644,8 @@ mobile platform keyboard bridges continue. `pano_cli plan-cursor-visibility` exposes the app-core cursor visibility decision used by live canvas cursor requests before retained desktop platform cursor bridges continue. +`pp_platform_api::probe_asset_file_timestamp` now owns the retained asset mtime +probe used by layout reload planning, with missing/existing file coverage. `pano_cli plan-clipboard-read` and `pano_cli plan-clipboard-write` expose the app-core clipboard text decisions used by live clipboard get/set requests before retained platform clipboard bridges continue. diff --git a/src/platform_api/asset_file_load_policy.cpp b/src/platform_api/asset_file_load_policy.cpp index 66c4d16..c658444 100644 --- a/src/platform_api/asset_file_load_policy.cpp +++ b/src/platform_api/asset_file_load_policy.cpp @@ -5,6 +5,19 @@ namespace pp::platform { +AssetFileTimestampProbe probe_asset_file_timestamp(std::string_view absolute_path) +{ + struct stat file_info {}; + const std::string path(absolute_path); + if (stat(path.c_str(), &file_info) != 0) + return {}; + + return { + true, + static_cast(file_info.st_mtime), + }; +} + AssetFileLoadDecision plan_asset_file_load_with_probe( PlatformFamily family, bool already_loaded, @@ -43,25 +56,11 @@ AssetFileLoadDecision plan_asset_file_load_for_platform( {}); } - struct stat file_info {}; - const std::string path(absolute_path); - if (stat(path.c_str(), &file_info) != 0) - { - return plan_asset_file_load_with_probe( - family, - already_loaded, - previous_last_write_time, - {}); - } - return plan_asset_file_load_with_probe( family, already_loaded, previous_last_write_time, - { - true, - static_cast(file_info.st_mtime), - }); + probe_asset_file_timestamp(absolute_path)); } AssetFileLoadDecision plan_asset_file_load( diff --git a/src/platform_api/asset_file_load_policy.h b/src/platform_api/asset_file_load_policy.h index 813c685..c04471a 100644 --- a/src/platform_api/asset_file_load_policy.h +++ b/src/platform_api/asset_file_load_policy.h @@ -18,6 +18,9 @@ struct AssetFileTimestampProbe { std::int64_t last_write_time = 0; }; +[[nodiscard]] AssetFileTimestampProbe probe_asset_file_timestamp( + std::string_view absolute_path); + [[nodiscard]] AssetFileLoadDecision plan_asset_file_load_with_probe( PlatformFamily family, bool already_loaded, diff --git a/tests/platform_api/platform_services_tests.cpp b/tests/platform_api/platform_services_tests.cpp index 91bd69e..15bb2be 100644 --- a/tests/platform_api/platform_services_tests.cpp +++ b/tests/platform_api/platform_services_tests.cpp @@ -799,6 +799,27 @@ void asset_file_load_policy_preserves_platform_reload_behavior(pp::tests::Harnes std::filesystem::remove(temp_path); } +void asset_file_load_policy_exposes_timestamp_probe(pp::tests::Harness& harness) +{ + const auto temp_path = std::filesystem::temp_directory_path() + / "panopainter-platform-api-timestamp-probe.xml"; + std::filesystem::remove(temp_path); + + const auto missing = pp::platform::probe_asset_file_timestamp(temp_path.string()); + PP_EXPECT(harness, !missing.file_exists); + + { + std::ofstream file(temp_path); + file << ""; + } + + const auto existing = pp::platform::probe_asset_file_timestamp(temp_path.string()); + PP_EXPECT(harness, existing.file_exists); + PP_EXPECT(harness, existing.last_write_time > 0); + + std::filesystem::remove(temp_path); +} + void asset_file_load_policy_preserves_family_reload_behavior(pp::tests::Harness& harness) { const auto missing_desktop = pp::platform::plan_asset_file_load_with_probe( @@ -1074,6 +1095,9 @@ int main() harness.run( "asset file load policy preserves platform reload behavior", asset_file_load_policy_preserves_platform_reload_behavior); + harness.run( + "asset file load policy exposes timestamp probe", + asset_file_load_policy_exposes_timestamp_probe); harness.run( "asset file load policy preserves family reload behavior", asset_file_load_policy_preserves_family_reload_behavior);