初始化奇妙小屏幕控制器项目

This commit is contained in:
2026-09-08 22:56:52 +08:00
commit 8d368de3b5
491 changed files with 67678 additions and 0 deletions
@@ -0,0 +1,27 @@
# 1.1.0 OTA 运行目录修复验收
2026-09-08 已在测试设备实际完成 1.0.6 → 1.1.0。磁盘 VERSION、运行 API 均为 1.1.0,OTA worker Result=success,用户数据 421 个非 OTA 文件摘要完全一致,frpc 二进制摘要保持,服务仍 inactive / disabled。组件完整性校验通过,恢复事务、临时保护、恢复 unit/helper、上传包与工作目录均已清理。
正式产物为 `发布更新相关/OTA数据包/1.1.0/matrix-screen-controller-1.1.0.ota`,27998979 字节,SHA-256 为 `7f33a417181633e1f4c77f48a3030d33f6c6e9578002e22f3b0487aff535eef8`。发布时直接复制实机验收候选,逐字节确认一致,未重新构建。VERSION 保持 1.1.0,FEATURE_UPDATED_AT 为 `2026-09-08T10:19+08:00`。旧包摘要 `379f80a30157e23d717157005ea246fa822529010c0f0295dcad5db3ceff42c5`,完整旧产物和旧发布记录归档位置见根发布记录的 repairs 字段。
## 故障与修复
原 1.1.0 安装过程中,旧 unit 的 RuntimeDirectoryPreserve=restart 在独立 stop 时删除共享 /run 目录,升级日志和回滚日志连续 FileNotFoundError;独立组件监护恢复了 1.0.6。因此显示 1.0.6 代表升级失败恢复,不能宣称升级成功。
主服务改为 Preserve=yes。候选迁移入口在停止旧服务之前创建标准运行期 drop-in、daemon-reload 并核验有效策略;保护直到提交或恢复完成,随后清理。日志目录丢失会重建,写入失败保留有限缓冲,避免日志异常阻止恢复。该约束已写入 AGENTS.md、源码 README、稳定需求和测试流程。
首轮修复候选发现保护残留检查位置错误,安装后校验误拒绝本次事务保护。该候选未发布;完整板端测试 420 passed / 2 skipped 后进入迁移失败恢复,421 个用户文件和 frp 状态保持,原始失败原因和日志保留,临时事务清理通过。随后将残留检查移至事务开始之前,并新增组件校验允许本事务保护的回归测试。
## 验证及证据
- 最终候选隔离目录完整 pytest:414 passed、9 skipped;完整其他离线依赖复制后验证,未引用历史镜像导出目录。最初仅解包 OTA 的隔离目录缺少镜像构建材料,已补全离线依赖后重新完整测试通过,无跳过失败用例。
- 最终候选前端测试:76 passed;本机完整前端和 JavaScript 语法、文案目录检查通过。隔离目录重新打包通过,其测试构建不用于发布。
- 最终实机 OTA 的完整 pytest:421 passed、2 skipped,358.28 秒。跳过为源码 OTA 不含镜像材料及已登记平台条件;保留完整测试门槛。
- 真实 systemd 专用临时 unit 复现 restart 在独立 stop 时删除目录,生产保护函数验证 stop、restart、启动失败后请求/进度/日志字节不变,测试 unit 清理通过。该项未使用 systemctl 替身。
- 实际 OTA 采样 7 次观察到临时保护存在、有效 Preserve=yes,请求/状态/日志均存在;最终保护清理成功。见 ota-evidence.json。
- 从原 1.0.3 镜像已只读提取的真实解析器再次校验和解包最终候选通过,仅 software / wheelhouse 顶层,恰好一份 frpc 离线二进制。
- 原有组件失败恢复、升级门槛、日志故障、同版本发布与发布失败原产物保留回归均包含在完整 pytest 中。原 1.0.3 worker 的隔离故障矩阵证据仍保留在 20260907 归档,该矩阵使用过 systemctl 替身,不替代此次真实 systemd 与真实 OTA 验收。
本次没有执行人工实屏视觉、断电或整机重启验收,不把软件成功等同于这些结果通过。升级前完整持久数据备份仅在设备受限临时目录保存,验收及发布完成后清理,不复制用户配置或凭据到普通归档。
设备临时验证目录及受限备份已清理。本机 `如何测试/本机测试环境/work/ota-runtime-repair/` 的递归清理被自动安全策略拒绝(blocked by policy,未提供更具体原因),因此仍保留候选和隔离测试文件;它们不属于正式产物,不被后续构建引用。
@@ -0,0 +1 @@
<仓库根目录>\测试相关资料\如何测试\本机测试环境\work\ota-runtime-repair\final-isolated-build\1.1.0
@@ -0,0 +1,84 @@
✔ derives automatic frame names from current animation name and position (0.6403ms)
✔ keeps custom frame names and restores automatic names when cleared (0.0937ms)
✔ builds non-repeating animation edit context names (0.0948ms)
✔ selects saved animation preview frames by ordered durations (0.6116ms)
✔ loads every preview frame through revision-checked pagination (1.3323ms)
✔ validates configurable animation preview concurrency (0.2673ms)
✔ rejects mixed revisions instead of showing a partial animation (0.3507ms)
✔ validates frame duration only when explicitly called (0.3759ms)
✔ moves selected frames as one ordered group (0.2669ms)
✔ protects an eight pixel ring around interactive drag controls (0.1664ms)
✔ reorders mixed library keys without mutating the source list (0.1138ms)
✔ restores drag eligibility after protected pointer cancellation and drag end (0.3556ms)
✔ animation workspace exposes two previews and the simplified control order (6.8075ms)
✔ normal and pixel modes expose independent ranges and presets (1.1726ms)
✔ pixel stamps are exact squares from 1x1 through 4x4 (0.2138ms)
✔ pixel stamps clip at every canvas edge (0.1787ms)
✔ normal circle geometry remains unchanged (0.0899ms)
✔ line interpolation includes endpoints without coordinate gaps (0.1821ms)
✔ v1 tools migrate into normal mode while v2 restores both sizes (0.2555ms)
✔ pixel mode markup and grid stay accessible and non-interactive (6.9068ms)
✔ pixel undo history stores changed snapshots, caps entries, and clears (0.5017ms)
✔ canvas view clamps scale and keeps a visible section in the viewport (0.4704ms)
✔ zooming around an anchor preserves its logical position (0.2555ms)
✔ floating editor button stays inside its safe viewport bounds (0.1233ms)
✔ canvas editor markup, guarded input, and checkpoint events stay wired (1.9429ms)
✔ normalizes and converts hexadecimal colors (1.1363ms)
✔ converts RGB and HSV boundaries (0.2535ms)
✔ round trips representative colors within 8-bit rounding (0.9763ms)
✔ recent colors deduplicate, normalize, and evict the oldest (0.2321ms)
✔ formats and projects looping playback without moving paused content (1.0057ms)
✔ seek dispatcher keeps one request in flight and flushes the newest position (2.948ms)
✔ topbar current display dialog exposes static close and animation controls (3.2173ms)
✔ device workspace exposes the compact immediate pure-color test flow (5.2123ms)
✔ font catalog normalization, grouping, and search are stable (1.7571ms)
✔ font picker implements commit-only searchable combobox keyboard behavior (2.9703ms)
✔ settings places unrestricted FRP maintenance at the bottom (4.612ms)
✔ FRP editor uses raw server storage and official validation errors (1.0223ms)
✔ network diagnostics renders layered evidence (1.448ms)
✔ unavailable controls never masquerade as indefinite loading (5.6542ms)
✔ action controls do not bypass the shared state module (6.0127ms)
✔ HTTP requests time out with a stable user-facing error (18.0299ms)
✔ demo library actions stay black, explain restrictions, and preserve copy (1.1572ms)
✔ sortable cards protect controls and keep demo cards fixed (1.0358ms)
✔ media import workspace exposes streaming upload, crop controls, and queue polling (1.1099ms)
✔ free framing starts from the complete source and keeps one pixel visible (0.6589ms)
✔ free framing copy is stable and catalogued (0.0881ms)
✔ upload action has visible ordinary-build copy (0.193ms)
✔ media naming happens after analysis and conflicts keep the draft (0.1455ms)
✔ awaiting-settings cards survive polling without hiding server transitions (0.1182ms)
✔ creates a fixed 64x64 RGB scene (1.9085ms)
✔ serializes pixelRgb as base64 and round trips an independent scene (6.1668ms)
✔ moves a valid v1 draft to the stable key before removing exact legacy keys (9.1938ms)
✔ keeps the v1 draft and all legacy keys when stable-key migration cannot write (2.3183ms)
✔ preserves and blocks a future stable draft instead of falling back or writing blank (1.224ms)
✔ preserves and blocks a damaged stable draft (0.4343ms)
✔ does not discard a damaged higher-priority legacy key during fallback migration (1.1675ms)
✔ rejects damaged scene roots while skipping only damaged elements (0.6392ms)
✔ normalizes text fields and creates a centered lowercase ok element (0.2164ms)
✔ migrates the first valid canvas by v3, v2, legacy priority and merges text v1 (8.2449ms)
✔ supports independent add, update, and delete operations for multiple text elements (0.5292ms)
✔ duplicates with a deep copy, new id, 2px offset, and topmost order (0.3276ms)
✔ alpha-composites full RGBA layers in array order without mutating inputs (1.1463ms)
✔ settings brightness explains an active temporary display-test override (5.305ms)
✔ settings exposes full OTA upload progress and service recovery polling (2.9085ms)
✔ settings automatically opens and safely copies a detailed OTA failure log (1.5356ms)
✔ settings exposes a transactional performance-mode switch with the fixed warning (1.3568ms)
✔ settings exposes persisted animation preview concurrency below performance mode (1.3258ms)
✔ template edit baseline distinguishes clean, dirty, and unbound scenes (0.9501ms)
✔ template manager uses a monitor and unified four-row card actions (6.2269ms)
✔ animation editing backs up and restores template edit ownership (1.0747ms)
✔ special editor exposes the agreed modes and guarded editing flow (6.7948ms)
✔ latched WiFi draft state remains dirty until explicitly cleared (1.022ms)
✔ WiFi settings expose independent network and prompt-delay controls (2.1695ms)
✔ workspace order resolves saved ids and appends new registrations (1.0454ms)
✔ workspace order movement is immutable and respects both boundaries (0.2573ms)
✔ drawer exposes one editable list and commits only from the save action (5.2391ms)
ℹ tests 76
ℹ suites 0
ℹ pass 76
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 502.5316
@@ -0,0 +1,91 @@
........................................................................ [ 17%]
........................................................................ [ 34%]
........................................................................ [ 51%]
.............................sssssss.......s............................ [ 68%]
...............s........................................................ [ 85%]
............................................................... [100%]
============================== warnings summary ===============================
..\..\..\..\.venv\Lib\site-packages\fastapi\routing.py:234: 81 warnings
tests/test_animations.py: 891 warnings
tests/test_api.py: 2025 warnings
tests/test_fonts.py: 243 warnings
tests/test_frp.py: 324 warnings
tests/test_library_order.py: 324 warnings
tests/test_media_conversion.py: 810 warnings
tests/test_ota.py: 243 warnings
tests/test_performance_mode.py: 81 warnings
tests/test_templates.py: 648 warnings
tests/test_ui_copy_editor.py: 585 warnings
<仓库根目录>\测试相关资料\如何测试\本机测试环境\.venv\Lib\site-packages\fastapi\routing.py:234: DeprecationWarning: 'asyncio.iscoroutinefunction' is deprecated and slated for removal in Python 3.16; use inspect.iscoroutinefunction() instead
is_coroutine = asyncio.iscoroutinefunction(dependant.call)
app\main.py:2024: 1 warning
tests/test_animations.py: 11 warnings
tests/test_api.py: 25 warnings
tests/test_fonts.py: 3 warnings
tests/test_frp.py: 4 warnings
tests/test_library_order.py: 4 warnings
tests/test_media_conversion.py: 10 warnings
tests/test_ota.py: 3 warnings
tests/test_performance_mode.py: 1 warning
tests/test_templates.py: 8 warnings
tests/test_ui_copy_editor.py: 7 warnings
<仓库根目录>\测试相关资料\如何测试\本机测试环境\work\ota-runtime-repair\isolated-final\software\app\main.py:2024: DeprecationWarning:
on_event is deprecated, use lifespan event handlers instead.
Read more about it in the
[FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/).
@app.on_event("startup")
..\..\..\..\.venv\Lib\site-packages\fastapi\applications.py:4495: 2 warnings
tests/test_animations.py: 22 warnings
tests/test_api.py: 50 warnings
tests/test_fonts.py: 6 warnings
tests/test_frp.py: 8 warnings
tests/test_library_order.py: 8 warnings
tests/test_media_conversion.py: 20 warnings
tests/test_ota.py: 6 warnings
tests/test_performance_mode.py: 2 warnings
tests/test_templates.py: 16 warnings
tests/test_ui_copy_editor.py: 14 warnings
<仓库根目录>\测试相关资料\如何测试\本机测试环境\.venv\Lib\site-packages\fastapi\applications.py:4495: DeprecationWarning:
on_event is deprecated, use lifespan event handlers instead.
Read more about it in the
[FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/).
return self.router.on_event(event_type)
app\main.py:2047: 1 warning
tests/test_animations.py: 11 warnings
tests/test_api.py: 25 warnings
tests/test_fonts.py: 3 warnings
tests/test_frp.py: 4 warnings
tests/test_library_order.py: 4 warnings
tests/test_media_conversion.py: 10 warnings
tests/test_ota.py: 3 warnings
tests/test_performance_mode.py: 1 warning
tests/test_templates.py: 8 warnings
tests/test_ui_copy_editor.py: 7 warnings
<仓库根目录>\测试相关资料\如何测试\本机测试环境\work\ota-runtime-repair\isolated-final\software\app\main.py:2047: DeprecationWarning:
on_event is deprecated, use lifespan event handlers instead.
Read more about it in the
[FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/).
@app.on_event("shutdown")
tests/test_media_conversion.py::test_free_crop_can_leave_only_one_source_pixel_visible_at_each_corner
tests/test_media_conversion.py::test_free_crop_can_leave_only_one_source_pixel_visible_at_each_corner
tests/test_media_conversion.py::test_free_crop_can_leave_only_one_source_pixel_visible_at_each_corner
tests/test_media_conversion.py::test_free_crop_can_leave_only_one_source_pixel_visible_at_each_corner
<仓库根目录>\测试相关资料\如何测试\本机测试环境\work\ota-runtime-repair\isolated-final\software\tests\test_media_conversion.py:134: DeprecationWarning: Image.Image.getdata is deprecated and will be removed in Pillow 14 (2027-10-15). Use get_flattened_data instead.
assert sum(pixel[0] > 240 for pixel in output.getdata()) == 1
tests/test_templates.py::test_template_crud_copy_persistence_and_thumbnail_cleanup
<仓库根目录>\测试相关资料\如何测试\本机测试环境\work\ota-runtime-repair\isolated-final\software\tests\test_templates.py:165: DeprecationWarning: Image.Image.getdata is deprecated and will be removed in Pillow 14 (2027-10-15). Use get_flattened_data instead.
assert len(set(image.getdata())) > 1
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
414 passed, 9 skipped, 6568 warnings in 65.73s (0:01:05)
@@ -0,0 +1,7 @@
{
"original_failure_matches": [
"FileNotFoundError: [Errno 2] No such file or directory: '/run/matrix-screen-controller/ota-worker.log'",
"FileNotFoundError: [Errno 2] No such file or directory: '/run/matrix-screen-controller/ota-worker.log'"
],
"window_utc": "2026-09-07T14:20:00Z/2026-09-07T14:40:00Z"
}
@@ -0,0 +1,58 @@
{
"pytest_summary": [
"421 passed, 2 skipped, 313 warnings in 358.28s (0:05:58)"
],
"guard_observations": [
{
"guard_exists": true,
"request_exists": true,
"status_exists": true,
"log_exists": true,
"preserve": "yes"
},
{
"guard_exists": true,
"request_exists": true,
"status_exists": true,
"log_exists": true,
"preserve": "yes"
},
{
"guard_exists": true,
"request_exists": true,
"status_exists": true,
"log_exists": true,
"preserve": "yes"
},
{
"guard_exists": true,
"request_exists": true,
"status_exists": true,
"log_exists": true,
"preserve": "yes"
},
{
"guard_exists": true,
"request_exists": true,
"status_exists": true,
"log_exists": true,
"preserve": "yes"
},
{
"guard_exists": true,
"request_exists": true,
"status_exists": true,
"log_exists": true,
"preserve": "yes"
},
{
"guard_exists": true,
"request_exists": false,
"status_exists": true,
"log_exists": true,
"preserve": "yes"
}
],
"service_state": "active",
"ota_worker_result": "success"
}
@@ -0,0 +1,10 @@
{
"legacy_103_parser_and_extraction_passed": true,
"top_level": [
"software",
"wheelhouse"
],
"frpc_count": 1,
"package_sha256": "7f33a417181633e1f4c77f48a3030d33f6c6e9578002e22f3b0487aff535eef8",
"file_count": 249
}
@@ -0,0 +1,9 @@
/usr/bin/python3
/usr/bin/systemctl
/usr/bin/tar
/usr/bin/sha256sum
/usr/bin/curl
Python 3.11.2
PASS: real systemd Preserve=restart deletes runtime directory on explicit stop
PASS: real stop/restart/start-failure preserve OTA request/status/log; temporary protection cleaned
{"runtime_lifecycle_passed": true}
@@ -0,0 +1,12 @@
{
"recovery_passed": true,
"disk_version": "1.0.6",
"user_files_preserved": 421,
"frp_preserved": true,
"cleanup_passed": true,
"failure_log_preserved": true,
"pytest_summary": [
"420 passed, 2 skipped, 313 warnings in 359.96s (0:05:59)"
],
"original_failure_preserved": true
}
@@ -0,0 +1,18 @@
{
"package_sha256": "7f33a417181633e1f4c77f48a3030d33f6c6e9578002e22f3b0487aff535eef8",
"installed_version": "1.1.0",
"status": "success",
"runtime_lifecycle_passed": true,
"user_data_preserved": true,
"frp_state_preserved": true,
"transaction_cleanup_passed": true,
"user_file_count": 421,
"job_id": "86cbcfe541894bb6b07c52b17fb5f3f3",
"verified_at": "2026-09-08T02:31:22.720144+00:00",
"frp_active": "inactive",
"frp_enabled": "disabled",
"disk_version": "1.1.0",
"api_version": "1.1.0",
"feature_updated_at": "2026-09-08T10:19+08:00",
"screen_visual_acceptance": "not_performed"
}