tirtc-device-builder 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tirtc-device-builder",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Codex workflows for building and validating TiRTC device firmware across supported chip platforms.",
5
5
  "author": {
6
6
  "name": "TangeAI",
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  This project follows Semantic Versioning.
4
4
 
5
+ ## 0.9.4
6
+
7
+ - Add a knowledge-only Waveshare ESP32-P4-WIFI6-Touch-LCD-3.5 package with
8
+ pinned source maps, media/resource lessons and explicit hardware/HIL limits.
9
+ - Expose validated knowledge references for model candidates without granting
10
+ adapter reuse; allow unknown PCB revisions only in knowledge-only packages.
11
+ - Add focused media/resource diagnosis guidance covering directional stream
12
+ contracts, WithCaps task cleanup, reserved codec ownership and UI cadence.
13
+ - Test candidate discovery, unknown revisions and reference path boundaries.
14
+
5
15
  ## 0.9.3
6
16
 
7
17
  - Pin ESP32 Device Kit 1.1.4 and use wildcard DNS plus HTTP probe redirects for
package/README.md CHANGED
@@ -961,8 +961,8 @@ metadata 中的版本、标签、上游 commit 和期望 SHA-256 必须与本地
961
961
 
962
962
  ```bash
963
963
  npm test
964
- git tag -a v0.9.3 -m "v0.9.3"
965
- git push origin v0.9.3
964
+ git tag -a v0.9.4 -m "v0.9.4"
965
+ git push origin v0.9.4
966
966
  ```
967
967
 
968
968
  不要重复发布已经存在的 npm 版本。版本变化同步更新 `package.json`、`.codex-plugin/plugin.json` 和发布说明。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tirtc-device-builder",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Install and run TiRTC device-development Agent Skills across popular coding clients.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -43,6 +43,12 @@ A probable match supplies hypotheses; a component match supplies only component
43
43
  lessons. Any identity conflict creates a new variant and keeps concrete GPIO,
44
44
  clock, DMA and task values unresolved.
45
45
 
46
+ Read a match's `knowledge_refs` before scanning reference repositories. These
47
+ are source maps and evidence, not permission to install a candidate adapter.
48
+ For an existing project's media, memory or scheduling bug, read
49
+ [runtime-resource-review.md](references/runtime-resource-review.md); preserve
50
+ the user's business baseline separately from the hardware reference.
51
+
46
52
  ## 3. Prove the business flows before board porting
47
53
 
48
54
  When device-call or WeChat VoIP is requested, first build and run the pinned
@@ -1 +1 @@
1
- 0.9.3
1
+ 0.9.4
@@ -8,7 +8,7 @@
8
8
 
9
9
  ```bash
10
10
  export TIRTC_AGENT_CLIENT=codex
11
- npx --yes tirtc-device-builder@0.9.3 setup esp32 --install \
11
+ npx --yes tirtc-device-builder@0.9.4 setup esp32 --install \
12
12
  --client "$TIRTC_AGENT_CLIENT" --force-skill
13
13
  ```
14
14
 
@@ -17,11 +17,11 @@ npx --yes tirtc-device-builder@0.9.3 setup esp32 --install \
17
17
  本轮第一步先只读运行:
18
18
 
19
19
  ```bash
20
- npx --yes tirtc-device-builder@0.9.3 --version
21
- npx --yes tirtc-device-builder@0.9.3 setup esp32 --client "$TIRTC_AGENT_CLIENT"
20
+ npx --yes tirtc-device-builder@0.9.4 --version
21
+ npx --yes tirtc-device-builder@0.9.4 setup esp32 --client "$TIRTC_AGENT_CLIENT"
22
22
  ```
23
23
 
24
- 必须根据命令的实际输出和本机文件确认:npm 包为 0.9.3、已安装 Skill 的 `VERSION` 为 0.9.3、所选 Device Kit 的 `manifest.json` 中 `kit_version` 为 1.1.4,并且 Doctor 对 `--expected-kit 1.1.4` 输出 `OVERALL: PASS`。Plugin manifest 不属于这种 npm 安装方式的运行时前置条件,不得把不可访问的 Plugin 版本当作阻塞项。如果版本不一致、Skill 是在当前会话启动后才安装,或环境检查未通过,停止并报告前置条件不成立;不要在当前会话中替换 Skill 后继续生成工程。
24
+ 必须根据命令的实际输出和本机文件确认:npm 包为 0.9.4、已安装 Skill 的 `VERSION` 为 0.9.4、所选 Device Kit 的 `manifest.json` 中 `kit_version` 为 1.1.4,并且 Doctor 对 `--expected-kit 1.1.4` 输出 `OVERALL: PASS`。Plugin manifest 不属于这种 npm 安装方式的运行时前置条件,不得把不可访问的 Plugin 版本当作阻塞项。如果版本不一致、Skill 是在当前会话启动后才安装,或环境检查未通过,停止并报告前置条件不成立;不要在当前会话中替换 Skill 后继续生成工程。
25
25
 
26
26
  工作区与 clean-room 边界:
27
27
  - 将启动 Agent 客户端时的当前目录定义为 `WORKSPACE_ROOT`。
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "boards": [
4
+ {
5
+ "package_id": "waveshare_esp32p4_wifi6_touch_lcd_35_unknown_rev",
6
+ "status": "knowledge_only",
7
+ "identity": {
8
+ "vendor": "Waveshare",
9
+ "model": "ESP32-P4-WIFI6-Touch-LCD-3.5",
10
+ "aliases": ["微雪ESP32-P4-WIFI6-Touch-LCD-3.5", "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.5"],
11
+ "hardware_revision": null,
12
+ "target": "esp32p4",
13
+ "module": null,
14
+ "flash_mb": 16,
15
+ "psram_mb": 32,
16
+ "components": [
17
+ {"kind": "camera", "model": "OV5647"},
18
+ {"kind": "audio_codec", "model": "ES8311"},
19
+ {"kind": "display", "model": "ST7796"},
20
+ {"kind": "touch", "model": "FT6336"},
21
+ {"kind": "network", "model": "ESP32-C6"}
22
+ ],
23
+ "probes": []
24
+ },
25
+ "compatibility": {"idf": "5.5.4", "tirtc_sdk": "2.3.0"},
26
+ "artifacts": {},
27
+ "knowledge_refs": ["waveshare-esp32-p4-wifi6-touch-lcd-3.5/knowledge.md"],
28
+ "lessons": [
29
+ {
30
+ "id": "waveshare_p4_media_receive_identity",
31
+ "scope": "board",
32
+ "subject": "VoIP receive admission",
33
+ "guidance": "Observed WeChat JPEG stream1 differs from subscribe/uplink stream11. Check active connection, generation and codec; reproduce with actual ingress before editing filters. See knowledge_refs for evidence and limitations.",
34
+ "action": "warn",
35
+ "verification": "corroborated",
36
+ "applies_to": {"package_id": "waveshare_esp32p4_wifi6_touch_lcd_35_unknown_rev"},
37
+ "evidence": [{"board_package_id": "waveshare_esp32p4_wifi6_touch_lcd_35_unknown_rev", "source_ref": "waveshare-esp32-p4-wifi6-touch-lcd-3.5/knowledge.md#media-and-failures-worth-remembering"}]
38
+ }
39
+ ]
40
+ },
4
41
  {"package_id":"lckfb_szpi_esp32s3_v1_0_1","status":"knowledge_only","identity":{"vendor":"深圳嘉立创科技集团股份有限公司(立创开发板)","model":"立创·实战派 ESP32-S3 开发板","aliases":["LCKFB SZPI ESP32-S3"],"hardware_revision":"V1.0.1","target":"esp32s3","module":"ESP32-S3 module","flash_mb":16,"psram_mb":8,"components":[{"kind":"camera","model":"GC2145"},{"kind":"aec_reference","model":"ES7210 MIC3 hardware reference"}],"probes":[]},"compatibility":{"idf":"5.5.4","tirtc_sdk":"2.3.0"},"artifacts":{},"lessons":[]},
5
42
  {"package_id":"alientek_atk_dnesp32s3_v1","status":"knowledge_only","identity":{"vendor":"正点原子 (ALIENTEK)","model":"ATK-DNESP32S3","aliases":[],"hardware_revision":"V1.0","target":"esp32s3","module":"ATK-MWS3S","flash_mb":16,"psram_mb":8,"components":[{"kind":"camera","model":"OV5640 PID 0x5640"},{"kind":"audio_codec","model":"ES8388"}],"probes":[]},"compatibility":{"idf":"5.5.4","tirtc_sdk":"2.3.0"},"artifacts":{},"lessons":[]}
6
43
  ]
@@ -0,0 +1,84 @@
1
+ # Waveshare ESP32-P4-WIFI6-Touch-LCD-3.5
2
+
3
+ 2026-09-07. Knowledge only; PCB and silicon revisions unresolved. This package
4
+ contains source-correlated observations and targeted regressions, not an
5
+ installable adapter or complete Hardware IR. A matching sales name does not
6
+ authorize pin reuse. No credentials, raw call logs or user media are retained.
7
+
8
+ ## Read only the needed source
9
+
10
+ | Baseline | Pinned source | Locate next |
11
+ | --- | --- | --- |
12
+ | Product business | xiaotai, baseline `8d67f0fe1b7385485e9d3de57065be53d10639ed` plus uncommitted P4 port | `lckfb-szpi-esp32s3-tirtc/components/starter_runtime/src/starter_runtime.c`, shared `starter_product`, `starter_tirtc` |
13
+ | Hardware/media | tirtc-device-example `cad4cbe58c3ff451906322930ceede19e6bf6a07`, app 1.5.3 | `complete-applications/esp32-p4/device-monitor/main/` |
14
+ | Vendor | waveshareteam/ESP32-P4-WIFI6-Touch-LCD-3.5 `870588e62daaf723f3c52f0060d8dc053665ed30` | `examples/esp-idf/06_I2SCodec`, `docs/revisions.md`, schematic |
15
+ | Cross-check | xiaozhi-esp32 `1ce658bcb9ce9aac69d3f87af9894a715b7175f6` | `main/boards/waveshare/esp32-p4-wifi6-touch-lcd-3.5/` |
16
+
17
+ Source paths are repository-relative, not prerequisites for a particular home
18
+ directory. Product HEAD alone does not identify dirty source; retain the local
19
+ diff and BIN/ELF identity when promoting this package. Project source maps live
20
+ under `docs/boards/waveshare-esp32-p4-wifi6-touch-lcd-3.5/`; current build status
21
+ is in `waveshare-esp32p4-xiaotai/PORT_STATUS.md` and `BUILD_IDENTITY.md`.
22
+
23
+ ## Hardware candidates (source-correlated, re-probe on new hardware)
24
+
25
+ - P4, 32 MiB PSRAM, 16 MiB flash; ESP32-C6 over Hosted SDIO. P4 requires its own
26
+ RISC-V SDK; the tested IDF is 5.5.4-dirty, TiRTC 2.3.0.
27
+ - ST7796 **SPI**, physical 320x480 / landscape 480x320; FT6336 uses the FT5x06
28
+ driver family. CSI camera does not imply a DSI display.
29
+ - No onboard IMU is documented for this model: vendor hardware inventory omits
30
+ one, pinned monitor BSP declares `BSP_CAPS_IMU=0`, and vendor BSP 2.0.1 lists
31
+ IMU unavailable. External sensing is needed for motion-based rotation; BSP
32
+ support flags alone are not proof of hardware absence. Sources checked
33
+ 2026-09-07: https://docs.waveshare.com/ESP32-P4-WIFI6-Touch-LCD-3.5 and
34
+ https://components.espressif.com/components/waveshare/esp32_p4_wifi6_touch_lcd_3_5/versions/2.0.1/readme
35
+ - Shared I2C1 SDA7/SCL8: pass the existing bus to camera SCCB (`init_sccb=false`),
36
+ codec and touch; an unused I2C0 macro is not evidence for a second owner.
37
+ - ES8311 I2S1 MCLK13/BCLK12/WS10/DOUT9/DIN11; PA53 high. Shared TX/RX,
38
+ 16 kHz stereo 16-bit, left MIC/right DAC reference. Monitor uses
39
+ `.no_dac_ref=false`, codec register 0x44=0x58. Validate reference energy and
40
+ double-talk; a board-level reference macro in Xiaozhi does not prove its
41
+ actual codec object enables reference input.
42
+ - LCD MOSI20/CLK21/CS23/DC26/RST27/BL28; touch RST29/INT50; BOOT35.
43
+ Verify combined rotation/mirror and all four touch corners. BOOT is distinct
44
+ from AXP2101 power controls; PMIC power sequencing remains unverified.
45
+ - C6 SDIO CLK18/CMD19/D0..3=14..17/RST54. Read host/slave versions; STA working
46
+ does not verify SoftAP/captive portal. Read the remote STA MAC after got-IP.
47
+ - OV5647 source: `main/drivers/camera/camera_driver.c`. Sensor mode may run
48
+ faster than requested output: observed 800x640@50 sensor paced to 15 fps.
49
+ Verify PID and actual silicon revision before selecting rev<3 defaults.
50
+
51
+ ## Media and failures worth remembering
52
+
53
+ | Symptom | Evidence and correction | Regression / next source |
54
+ | --- | --- | --- |
55
+ | VoIP audio but no remote image | Callback metadata stream=1/media=65 (JPEG), rx=37 and decoded=0; adapter only allowed stream11. Receive by active connection/generation/codec, not uplink ID. User later reported image visible. | P4 `tools/test_video_ingress.py`, actual `p4_video_submit`/`drain_video`; monitor `tirtc_session_on_video` |
56
+ | Remote producer not requested | Monitor subscribes then sends its version-specific video-enable command `(SN<<16)\u007c0x1105`, byte1. Added missing request, but this alone was not proven to fix image. | `tools/test_p4_video.py`; monitor `tirtc_session.c`, `tirtc_commands.c` |
57
+ | H264 open fails with >8 MiB free | Singleton output workspace remained reserved for old profile. Stop worker, apply profile, reconcile reservation, restart. | `tools/test_p4_profile_switch.py`; `camera_pipeline_on_rtc_video_config_changed` |
58
+ | Repeated WHIP attempts leak | Shared worker used WithCaps creation and ordinary deletion. Fixed to paired deletion; 24 KiB stack plus TCB per completed attempt was retained. | S3 `tools/test_voip_task_cleanup.py`; installed IDF `esp_additions/idf_additions.c` |
59
+ | UI slow / video not smooth | Product UI tick100ms limits presentation near10fps; renderer input24x256KiB plus output20x480x320x2 dominate pools. Retained pools are intentional, not proven optimal for this product. | `call_video_renderer_config.h`, `p4_video_ui_tick`; measure before tuning |
60
+
61
+ Product-specific media: device CALL H264 both directions; WeChat VOIP H264 up /
62
+ MJPEG down, profile video enabled with 640x480, 8k mono A-law. Preserve explicit
63
+ voice calls. Camera privacy disables local capture/send while keeping remote
64
+ decode/audio; microphone is independent. Profile declaration is not proof of
65
+ actual packets or display. P4 H264 encode is hardware; downstream H264 here is
66
+ software, MJPEG uses hardware JPEG plus scaling.
67
+
68
+ ## Evidence boundary
69
+
70
+ P4 `xiaotai-p4-voip.8` compiled with host regressions including 100 simulated
71
+ worker exits; no repeated-call heap HIL yet.
72
+
73
+ - BIN SHA256: `8bb2e46f3ce68318ebdd544c6cff979e6885e1c40b70ff0ee1731342b07c55e1`
74
+ - ELF SHA256: `6a138dede2fe20e7980937801c48a0206b0a78a3634a49a9ef3e31f9bce811aa`
75
+ - P4 SDK archive SHA256: `a7a01ffd496a55364c7e4d665ff3884d078147bba96752a965d97befca12e451`
76
+
77
+ These identify historical local artifacts, not bundled binaries or a reproducible
78
+ release. User reports video visible after the stream-filter correction, without
79
+ a complete artifact-bound capture. Startup logs prove one successful media
80
+ start, not long-run stability. Earlier HMAC signing -> invalid LVGL event crash
81
+ is unresolved; no claim it was fixed by task cleanup. AEC/double-talk, weak
82
+ network, repeated calls, PMIC controls and full product parity remain pending.
83
+ Promote only after exact identity, portable adapter/IR/contracts and per-flow
84
+ artifact-bound acceptance are retained.
@@ -51,6 +51,13 @@ Registry package status controls reuse:
51
51
 
52
52
  An older HIL result is provenance, not proof for a newly built artifact.
53
53
 
54
+ Knowledge-only packages may record an unknown PCB revision as `null`; verified
55
+ packages still require an explicit revision. `match` returns registry-relative
56
+ `knowledge_refs` for exact/model candidates, including knowledge-only packages.
57
+ Read that source map first and inspect only relevant source deltas. A probable
58
+ match provides investigation leads, not validated pins or an installable adapter.
59
+ The Waveshare ESP32-P4-WIFI6-Touch-LCD-3.5 package follows this boundary.
60
+
54
61
  ## Learning loop
55
62
 
56
63
  After the final assessment, create a project-local candidate:
@@ -0,0 +1,58 @@
1
+ # Existing-project media and resource review
2
+
3
+ Use for missing remote video, allocation failures, task leaks, slow UI or call
4
+ switching. This is a focused diagnostic route, not a requirement to redo board
5
+ intake, replay every simulator flow or regenerate a working product.
6
+
7
+ ## Establish a narrow baseline
8
+
9
+ Record business baseline and hardware reference separately. Keep the user's
10
+ contacts, call arbitration, onboarding and UI semantics in the former; borrow
11
+ drivers, capture and codec resource ownership from the latter. Read the matched
12
+ knowledge package's source map, compare pinned commits and relevant dirty files,
13
+ then inspect only the path under investigation. Reuse prior build evidence only
14
+ when artifact identity matches; otherwise label it historical.
15
+
16
+ ## Receive-to-display contract
17
+
18
+ For each active mode record uplink codec, subscribe ID, downlink codec and
19
+ observed receive metadata independently. H5 video-up stream 11 remains valid;
20
+ it does not establish a universal receive stream ID. Authenticate the active
21
+ connection, validate generation and negotiated media, and test actual ingress
22
+ with captured non-sensitive frame metadata before changing filters.
23
+
24
+ Use bounded counters at callback, admission, queue, decoder, converter and
25
+ presentation boundaries. Count admission rejection separately from overflow.
26
+ `rx>0, decoded=0` is not automatically a codec failure. Test enabling remote
27
+ production separately from subscription; protocol commands are SDK/reference
28
+ specific, not generic magic constants.
29
+
30
+ ## Resource lifecycle
31
+
32
+ - Inventory task creation and deletion pairs. ESP-IDF `xTaskCreate*WithCaps`
33
+ requires `vTaskDeleteWithCaps`; ordinary deletion retains its statically
34
+ registered stack/TCB. Confirm the installed IDF implementation. Self-deletion
35
+ may allocate a helper task; under tight internal RAM prefer owner-driven
36
+ cleanup after the worker has stopped. A mock API-pair test is not heap HIL.
37
+ - Distinguish free capacity, largest contiguous block, DMA capability and
38
+ reserved-workspace ownership. Stop the old codec owner before reconfiguring
39
+ or acquiring a singleton workspace. Retry cannot repair stale ownership.
40
+ - Budget internal RAM and PSRAM independently for idle, handshake, duplex media
41
+ and stop/reconnect overlap. Include codec pools, ingress copies, UI surfaces,
42
+ worker stacks and SDK queues; a retained pool is not a per-call leak.
43
+ - Measure receive, decode and presentation cadence independently. A 100 ms UI
44
+ timer caps presentation near 10 fps even if decode runs at 15 fps. Evaluate
45
+ dedicated video presentation and smaller MJPEG latest-frame pools against
46
+ audio deadlines; H264 recovery buffers have different dependencies.
47
+
48
+ ## Completion evidence
49
+
50
+ Run an actual-code regression for the failing seam. Report build/host tests
51
+ separately from hardware. For resource changes, compare post-warmup baselines
52
+ over repeated connect/cancel/hangup cycles and a sustained duplex call: heap
53
+ free/largest/minimum by capability, task stack low-water marks, queue drops,
54
+ decode/present rates and audio starvation. Sample cheaply outside realtime
55
+ callbacks; choose intervals for the experiment, not permanent noisy polling.
56
+ Unreproduced crashes remain unresolved even if a later call succeeds. Change
57
+ one high-risk variable per hardware comparison and retain redacted artifact-
58
+ bound results; user-reported video is evidence of video, not AEC or stability.
@@ -95,12 +95,16 @@ def validate_probe(item: Any, path: str, errors: list[str]) -> None:
95
95
  errors.append(f"{path}.required_for_exact must be true or false")
96
96
 
97
97
 
98
- def validate_identity(identity: Any, path: str, errors: list[str]) -> None:
98
+ def validate_identity(identity: Any, path: str, errors: list[str], *, allow_unknown_revision: bool = False) -> None:
99
99
  if not isinstance(identity, dict):
100
100
  errors.append(f"{path} must be an object")
101
101
  return
102
- for field in ("vendor", "model", "hardware_revision", "target"):
102
+ for field in ("vendor", "model", "target"):
103
103
  nonempty(identity.get(field), f"{path}.{field}", errors)
104
+ if allow_unknown_revision:
105
+ nullable_nonempty(identity.get("hardware_revision"), f"{path}.hardware_revision", errors)
106
+ else:
107
+ nonempty(identity.get("hardware_revision"), f"{path}.hardware_revision", errors)
104
108
  aliases = string_array(identity.get("aliases", []), f"{path}.aliases", errors)
105
109
  names = [identity.get("model"), *aliases]
106
110
  normalized_names = [normalized(value) for value in names if normalized(value)]
@@ -244,7 +248,15 @@ def validate_registry(data: dict[str, Any], registry_path: Path) -> list[str]:
244
248
  errors.append(
245
249
  f"{prefix}.status must be one of {', '.join(sorted(PACKAGE_STATUSES))}"
246
250
  )
247
- validate_identity(board.get("identity"), f"{prefix}.identity", errors)
251
+ validate_identity(board.get("identity"), f"{prefix}.identity", errors,
252
+ allow_unknown_revision=status == "knowledge_only")
253
+ for ref_index, value in enumerate(string_array(
254
+ board.get("knowledge_refs", []), f"{prefix}.knowledge_refs", errors
255
+ )):
256
+ resolved = safe_registry_path(registry_path, value,
257
+ f"{prefix}.knowledge_refs[{ref_index}]", errors)
258
+ if resolved is not None and not resolved.is_file():
259
+ errors.append(f"{prefix}.knowledge_refs[{ref_index}] does not exist")
248
260
  compatibility = board.get("compatibility")
249
261
  if not isinstance(compatibility, dict):
250
262
  errors.append(f"{prefix}.compatibility must be an object")
@@ -453,6 +465,10 @@ def match_board(board: dict[str, Any], query: dict[str, Any]) -> dict[str, Any]
453
465
  {"kind": kind, "model": model} for kind, model in shared_components
454
466
  ],
455
467
  "applicable_lessons": applicable_lessons,
468
+ # A model candidate can read its evidence without acquiring GPIO/adapter
469
+ # authority. Component-only matches must not inherit board-specific docs.
470
+ "knowledge_refs": board.get("knowledge_refs", [])
471
+ if classification in {"exact", "probable"} else [],
456
472
  "artifacts": board.get("artifacts", {}) if reuse == "registered_board" else {},
457
473
  }
458
474