tan-cli 0.7.0__py3-none-any.whl

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.
Files changed (330) hide show
  1. tan/__init__.py +0 -0
  2. tan/__main__.py +120 -0
  3. tan/cli.py +939 -0
  4. tan/command_table.py +265 -0
  5. tan/commands/__init__.py +8 -0
  6. tan/commands/bootstrap_cmd.py +4856 -0
  7. tan/commands/bootstrap_patches.py +164 -0
  8. tan/commands/bootstrap_toolchain_fetch.py +155 -0
  9. tan/commands/bootstrap_toolchain_pin.py +36 -0
  10. tan/commands/build/__init__.py +1 -0
  11. tan/commands/build/configure_inputs.py +277 -0
  12. tan/commands/build/execute.py +1852 -0
  13. tan/commands/build/host_python.py +59 -0
  14. tan/commands/build/link_stale.py +78 -0
  15. tan/commands/build/manifest.py +428 -0
  16. tan/commands/build/materialise.py +81 -0
  17. tan/commands/build/token_substitution.py +381 -0
  18. tan/commands/build/toolchain.py +482 -0
  19. tan/commands/build/user_defines_wipe.py +115 -0
  20. tan/commands/build/workspace_patches.py +65 -0
  21. tan/commands/build_cmd.py +2236 -0
  22. tan/commands/build_output.py +331 -0
  23. tan/commands/clean_cmd.py +1183 -0
  24. tan/commands/completion_cmd.py +786 -0
  25. tan/commands/debug_config_cmd.py +2425 -0
  26. tan/commands/diff_cmd.py +940 -0
  27. tan/commands/doctor_cmd.py +4767 -0
  28. tan/commands/examples_cmd.py +644 -0
  29. tan/commands/explain_cmd.py +1319 -0
  30. tan/commands/faultdecode_cmd.py +931 -0
  31. tan/commands/flash_atoc_guard.py +240 -0
  32. tan/commands/flash_cmd.py +5731 -0
  33. tan/commands/flash_mram_guard.py +150 -0
  34. tan/commands/flash_ram.py +578 -0
  35. tan/commands/flash_raw.py +583 -0
  36. tan/commands/generate_cmd.py +1456 -0
  37. tan/commands/image_cmd.py +753 -0
  38. tan/commands/init_cmd.py +1740 -0
  39. tan/commands/inspect_cmd.py +377 -0
  40. tan/commands/kconfig_cmd.py +734 -0
  41. tan/commands/model_cmd.py +1514 -0
  42. tan/commands/model_device_cmd.py +351 -0
  43. tan/commands/model_device_live.py +234 -0
  44. tan/commands/model_host_cmd.py +430 -0
  45. tan/commands/model_zoo_cmd.py +363 -0
  46. tan/commands/monitor_actions.py +125 -0
  47. tan/commands/monitor_cmd.py +626 -0
  48. tan/commands/monitor_session.py +505 -0
  49. tan/commands/new_som_cmd.py +1522 -0
  50. tan/commands/pinmux_cmd.py +568 -0
  51. tan/commands/presets_cmd.py +1123 -0
  52. tan/commands/probe_cmd.py +421 -0
  53. tan/commands/reset_cmd.py +354 -0
  54. tan/commands/reset_confirm.py +168 -0
  55. tan/commands/run_cmd.py +637 -0
  56. tan/commands/scaffold_cmd.py +490 -0
  57. tan/commands/sdk_cmd.py +1723 -0
  58. tan/commands/size_cmd.py +1020 -0
  59. tan/commands/support_bundle_cmd.py +1073 -0
  60. tan/commands/trace_cmd.py +341 -0
  61. tan/commands/validate_cmd.py +2009 -0
  62. tan/commands/west_forward_cmd.py +736 -0
  63. tan/commands/workspace_patch_check.py +296 -0
  64. tan/core/__init__.py +1 -0
  65. tan/core/alp_conf_pregen.py +100 -0
  66. tan/core/artifact_provenance.py +240 -0
  67. tan/core/atoc_guard.py +276 -0
  68. tan/core/atoc_guard_messages.py +232 -0
  69. tan/core/atoc_replacement.py +118 -0
  70. tan/core/atomic_write.py +178 -0
  71. tan/core/board_context.py +351 -0
  72. tan/core/board_diagnostic.py +111 -0
  73. tan/core/board_files.py +221 -0
  74. tan/core/board_validator.py +542 -0
  75. tan/core/board_validator_compat.py +196 -0
  76. tan/core/board_validator_run.py +129 -0
  77. tan/core/board_validator_schema.py +186 -0
  78. tan/core/board_validator_skew.py +46 -0
  79. tan/core/board_yaml_edit.py +295 -0
  80. tan/core/board_yaml_pos.py +118 -0
  81. tan/core/bootstrap.py +2572 -0
  82. tan/core/build_plan.py +625 -0
  83. tan/core/consent.py +104 -0
  84. tan/core/console_filter.py +245 -0
  85. tan/core/debug_launch.py +1803 -0
  86. tan/core/devicetree_lint.py +168 -0
  87. tan/core/dir_removal.py +229 -0
  88. tan/core/doctor_git.py +172 -0
  89. tan/core/doctor_libraries.py +253 -0
  90. tan/core/doctor_render.py +185 -0
  91. tan/core/doctor_scope.py +66 -0
  92. tan/core/doctor_setools.py +237 -0
  93. tan/core/doctor_stale.py +180 -0
  94. tan/core/document_guards.py +723 -0
  95. tan/core/dp_id.py +97 -0
  96. tan/core/elf_load.py +63 -0
  97. tan/core/error_catalog.py +171 -0
  98. tan/core/example_catalog.py +499 -0
  99. tan/core/example_facets.py +151 -0
  100. tan/core/faultdecode.py +521 -0
  101. tan/core/flash_plan.py +2501 -0
  102. tan/core/flow_d_report.py +381 -0
  103. tan/core/flow_d_uptime.py +164 -0
  104. tan/core/fs_confine.py +130 -0
  105. tan/core/global_flags.py +230 -0
  106. tan/core/host_python.py +210 -0
  107. tan/core/image_bundle.py +247 -0
  108. tan/core/inert.py +147 -0
  109. tan/core/jlink_binary.py +128 -0
  110. tan/core/jlink_probe.py +481 -0
  111. tan/core/jsonc_splice.py +404 -0
  112. tan/core/launch_provenance.py +257 -0
  113. tan/core/link_refusal.py +76 -0
  114. tan/core/metadata_schema.py +376 -0
  115. tan/core/model_check.py +227 -0
  116. tan/core/model_device.py +449 -0
  117. tan/core/model_doctor.py +198 -0
  118. tan/core/model_host.py +48 -0
  119. tan/core/model_list.py +112 -0
  120. tan/core/model_zoo.py +353 -0
  121. tan/core/module_template.py +340 -0
  122. tan/core/mram_link.py +87 -0
  123. tan/core/new_som_ospi.py +94 -0
  124. tan/core/os_class.py +159 -0
  125. tan/core/patch_paths.py +87 -0
  126. tan/core/pending.py +40 -0
  127. tan/core/plain_zephyr_plan.py +138 -0
  128. tan/core/plan_exec.py +373 -0
  129. tan/core/plan_tokens.py +600 -0
  130. tan/core/probe.py +155 -0
  131. tan/core/probe_plan.py +237 -0
  132. tan/core/proxy.py +168 -0
  133. tan/core/publish.py +31 -0
  134. tan/core/python_floor.py +178 -0
  135. tan/core/ram_run.py +694 -0
  136. tan/core/ram_watch.py +231 -0
  137. tan/core/raw_write.py +160 -0
  138. tan/core/reset_plan.py +56 -0
  139. tan/core/run.py +161 -0
  140. tan/core/scaffold.py +2203 -0
  141. tan/core/scaffold_selftest_identity.py +225 -0
  142. tan/core/sdk_default_registry.py +565 -0
  143. tan/core/sdk_discovery.py +996 -0
  144. tan/core/sdk_removal.py +465 -0
  145. tan/core/serial_actions.py +175 -0
  146. tan/core/serial_capture.py +185 -0
  147. tan/core/serial_url.py +45 -0
  148. tan/core/setools.py +608 -0
  149. tan/core/setools_scratch.py +243 -0
  150. tan/core/shapes.py +194 -0
  151. tan/core/size.py +610 -0
  152. tan/core/som_buildability.py +204 -0
  153. tan/core/som_schema_version.py +62 -0
  154. tan/core/subprocess_env.py +201 -0
  155. tan/core/sysbuild_image.py +94 -0
  156. tan/core/system_manifest.py +827 -0
  157. tan/core/text_layout.py +73 -0
  158. tan/core/timestamp.py +182 -0
  159. tan/core/tool_lookup.py +383 -0
  160. tan/core/toolchain_pin.py +117 -0
  161. tan/core/toolchain_provision.py +1224 -0
  162. tan/core/trusted_path.py +134 -0
  163. tan/core/uboot_breakin.py +154 -0
  164. tan/core/uri_reference.py +415 -0
  165. tan/core/user_defines.py +152 -0
  166. tan/core/venv.py +332 -0
  167. tan/core/west_patches.py +143 -0
  168. tan/core/west_workspace_refusal.py +159 -0
  169. tan/core/zephyr_env.py +124 -0
  170. tan/env.py +185 -0
  171. tan/envelope.py +647 -0
  172. tan/exit_codes.py +12 -0
  173. tan/lazy_typer.py +185 -0
  174. tan/model/__init__.py +2 -0
  175. tan/model/_gen_fixture.py +132 -0
  176. tan/model/adapters/__init__.py +116 -0
  177. tan/model/adapters/cpu.py +26 -0
  178. tan/model/adapters/deepx.py +94 -0
  179. tan/model/adapters/drpai.py +242 -0
  180. tan/model/adapters/ethos_u.py +951 -0
  181. tan/model/adapters/executorch.py +55 -0
  182. tan/model/analyze.py +694 -0
  183. tan/model/build.py +304 -0
  184. tan/model/check.py +682 -0
  185. tan/model/manifest.py +625 -0
  186. tan/model/measure.py +216 -0
  187. tan/model/npu_ops_validate.py +130 -0
  188. tan/model/package.py +125 -0
  189. tan/model/perf.py +635 -0
  190. tan/model/perf_apply.py +721 -0
  191. tan/model/prep.py +193 -0
  192. tan/model/sram_fit.py +502 -0
  193. tan/model/targets.py +374 -0
  194. tan/model/tensorio.py +287 -0
  195. tan/net.py +73 -0
  196. tan/output_format.py +213 -0
  197. tan/planner/__init__.py +219 -0
  198. tan/planner/aperture.py +202 -0
  199. tan/planner/buildplan.py +1092 -0
  200. tan/planner/camera_owner.py +192 -0
  201. tan/planner/cameras.py +132 -0
  202. tan/planner/carveout.py +755 -0
  203. tan/planner/cli.py +274 -0
  204. tan/planner/headers.py +315 -0
  205. tan/planner/kconfig.py +2435 -0
  206. tan/planner/kconfig_symbols.py +473 -0
  207. tan/planner/libraries.py +677 -0
  208. tan/planner/link_target.py +237 -0
  209. tan/planner/linux_ownership.py +216 -0
  210. tan/planner/loader.py +1525 -0
  211. tan/planner/manifest.py +116 -0
  212. tan/planner/memory.py +161 -0
  213. tan/planner/memregion.py +56 -0
  214. tan/planner/models.py +533 -0
  215. tan/planner/orchestrator.py +874 -0
  216. tan/planner/ownership.py +203 -0
  217. tan/planner/partition.py +1160 -0
  218. tan/planner/paths.py +32 -0
  219. tan/planner/plain_slice.py +139 -0
  220. tan/planner/project_emit/__init__.py +60 -0
  221. tan/planner/project_emit/bom_netlist.py +375 -0
  222. tan/planner/project_emit/dts.py +580 -0
  223. tan/planner/project_emit/hw_info.py +221 -0
  224. tan/planner/project_emit/native_sim.py +136 -0
  225. tan/planner/project_emit/west_libs.py +213 -0
  226. tan/planner/project_loader.py +264 -0
  227. tan/planner/sdk_capability.py +203 -0
  228. tan/planner/sdk_compat.py +395 -0
  229. tan/planner/secure.py +350 -0
  230. tan/planner/slugs.py +394 -0
  231. tan/planner/som_metadata.py +325 -0
  232. tan/planner/strict_loaders.py +131 -0
  233. tan/planner/template.py +1037 -0
  234. tan/planner/template_pins.py +535 -0
  235. tan/planner/template_rewrite.py +714 -0
  236. tan/planner/topology.py +208 -0
  237. tan/planner/validate.py +365 -0
  238. tan/planner/whole_device_alias.py +67 -0
  239. tan/planner/zephyr_board.py +3510 -0
  240. tan/planner_cli.py +97 -0
  241. tan/planner_emit.py +617 -0
  242. tan/planner_root.py +119 -0
  243. tan/soc_ref.py +58 -0
  244. tan/templates/__init__.py +34 -0
  245. tan/templates/vendored/MANIFEST.md +1862 -0
  246. tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
  247. tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
  248. tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
  249. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
  250. tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
  251. tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
  252. tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
  253. tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
  254. tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
  255. tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
  256. tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
  257. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
  258. tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
  259. tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
  260. tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
  261. tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
  262. tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
  263. tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
  264. tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
  265. tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
  266. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
  267. tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
  268. tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
  269. tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
  270. tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
  271. tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
  272. tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
  273. tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
  274. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
  275. tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
  276. tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
  277. tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
  278. tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
  279. tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
  280. tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
  281. tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
  282. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
  283. tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
  284. tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
  285. tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
  286. tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
  287. tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
  288. tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
  289. tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
  290. tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
  291. tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
  292. tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
  293. tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
  294. tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
  295. tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
  296. tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
  297. tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
  298. tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
  299. tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
  300. tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
  301. tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
  302. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
  303. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
  304. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
  305. tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
  306. tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
  307. tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
  308. tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
  309. tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
  310. tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
  311. tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
  312. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
  313. tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
  314. tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
  315. tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
  316. tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
  317. tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
  318. tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
  319. tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
  320. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
  321. tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
  322. tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
  323. tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
  324. tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
  325. tan/version.py +102 -0
  326. tan_cli-0.7.0.dist-info/METADATA +125 -0
  327. tan_cli-0.7.0.dist-info/RECORD +330 -0
  328. tan_cli-0.7.0.dist-info/WHEEL +5 -0
  329. tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
  330. tan_cli-0.7.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1862 @@
1
+ <!-- SPDX-License-Identifier: Apache-2.0 -->
2
+ # Vendored scaffold provenance (alp-sdk#864)
3
+
4
+ This tree is `alp-sdk --emit scaffold` output, captured byte-for-byte (LF, no
5
+ retouching) and checked in so `tan init`/`tan scaffold` can read it without
6
+ ever shelling the SDK. `tests/parity/scaffold_byte_parity.py` re-runs the live
7
+ emit against a reachable alp-sdk checkout and fails loudly if this tree drifts
8
+ from an un-revendored SDK change.
9
+
10
+ ## Source
11
+
12
+ - **tan-cli#501: `sensor` and `diagnostics` gained `boards/native_sim_native_64.{overlay,conf}`.**
13
+ Neither template's vendored tree carried the `boards/` overlay+conf pair
14
+ their canonical example (`examples/peripheral-io/i2c-master` for `sensor`,
15
+ `examples/bringup/board-selftest` for `diagnostics`) ships, even though
16
+ Zephyr auto-discovers that path with no CMakeLists wiring at all. Without
17
+ it, `west build -b native_sim/native/64` on a scaffolded project has no
18
+ `alp-i2c0` DT alias and no `CONFIG_EMUL`/`CONFIG_I2C_EMUL`, so the run does
19
+ not match either README's own "Expected output" block (measured: an
20
+ unaliased I2C open fails with `alp_last_error=-2` instead of the documented
21
+ NACK probe). `edge-ai` ships the same gap but is unaffected -- read from
22
+ `examples/ai/cold-chain-monitor/src/main.c`, whose `bus != NULL &&
23
+ bme280_init(...)` short-circuit lands on the same synthetic-data `LOG_WRN`
24
+ branch whether the I2C alias resolves or not, not independently re-measured
25
+ with a real Zephyr build -- and is deliberately left unvendored (declared in
26
+ `scaffold_byte_parity.py`'s `DELIBERATELY_MISSING_EXTRAS`, see below). Fixed
27
+ by vendoring the pair (byte-identical, from the pinned commit below) into
28
+ all four `sensor`/`diagnostics` × SKU trees and adding both paths to
29
+ `tests/parity/scaffold_byte_parity.py`'s `NON_ENVELOPE_EXTRAS` (the same
30
+ mechanism `native_sim.conf`/tan-cli#379 use).
31
+
32
+ Landed alongside two follow-on fixes the first cut of this change was
33
+ missing (both re-measured against the pinned SDK tag below):
34
+ 1. `native-sim-overlay`'s destination colliding with this vendored file
35
+ used to make a freshly scaffolded project's bare `tan generate` refuse
36
+ outright (`generate.would-overwrite`, exit 3, nothing written), and
37
+ `--all --force` — the refusal's own recommended remedy — silently
38
+ replaced the vendored `alp-i2c0` overlay with tan's GPIO-only emit.
39
+ `generate_cmd.py` now leaves `native-sim-overlay` alone (dropped from
40
+ the run, `generate.overlay-not-owned` warning, exit 0) whenever it
41
+ rides along in the bare/`--all` default set rather than being named by
42
+ an explicit `--target`; an explicit `--target native-sim-overlay`
43
+ still refuses without `--force` and still overwrites with it, exactly
44
+ as before. `python/tests/commands/test_generate_command.py` pins both
45
+ directions.
46
+ 2. `scaffold_byte_parity.py` could not have failed on a MISSING vendored
47
+ extra at all: `augment_with_example_extras` only ever reached for a
48
+ `NON_ENVELOPE_EXTRAS` name the vendored tree already carried, so
49
+ `--sdk <1a9f753c>` was **9/9 PASS with this whole change reverted**
50
+ (measured both ways). `missing_extras` closes that: any
51
+ `NON_ENVELOPE_EXTRAS` file the live example ships that the vendored
52
+ tree does not is now a hard failure unless declared in
53
+ `DELIBERATELY_MISSING_EXTRAS` (`edge-ai`'s pair is the one entry).
54
+ `python/tests/core/test_scaffold.py::
55
+ test_sensor_and_diagnostics_scaffolds_ship_the_native_sim_board_pair`
56
+ is the SDK-free pin of the same gap.
57
+
58
+ A first cut of this change also gave all four `sensor`/`diagnostics`
59
+ `CMakeLists.txt` files `list(PREPEND EXTRA_CONF_FILE ...)`, on the theory
60
+ that appending the generated `alp.conf` let it win over the vendored board
61
+ conf's `CONFIG_EMUL`/`CONFIG_I2C_EMUL`. MEASURED false with a real CMake
62
+ configure against Zephyr v4.4.1: `boards/native_sim_native_64.conf` joins
63
+ `CONF_FILE`, not `EXTRA_CONF_FILE` (Zephyr's board-dir auto-discovery), and
64
+ `CONF_FILE_AS_LIST` is merged strictly before `EXTRA_CONF_FILE_AS_LIST`
65
+ regardless of PREPEND/APPEND order within the latter -- PREPEND and APPEND
66
+ produced the identical merge order and identical `.config` either way.
67
+ Reverted to plain `list(APPEND ...)`, which is what `--emit scaffold`
68
+ produces unedited, so these four files carry no "Deliberate edit" anymore
69
+ (see below; this is unrelated to the real `iot`/tan-cli#379 PREPEND, whose
70
+ scenario is a caller's own `-DEXTRA_CONF_FILE=` build line, which neither
71
+ `sensor` nor `diagnostics` documents).
72
+
73
+ Verified against `PINNED_SDK_TAG` (`1a9f753c`) after the fixes above:
74
+ `scaffold_byte_parity.py --sdk <1a9f753c>` is rc 0, **9/9** PASS, `sensor`
75
+ and `diagnostics` reporting 8 files each (was 6) — and rc 1, **4 FAIL**
76
+ (the two vendored `boards/` files missing on each of the four trees) with
77
+ the vendoring reverted, so the gate now actually covers this change.
78
+
79
+ - **`PINNED_SDK_TAG` has since moved past this vendor point, deliberately.**
80
+ The tan-cli#552…#563 planner re-sync bumped `parity.yml`'s `PINNED_SDK_TAG`
81
+ (and `ci.yml`'s `sdk_parity` checkout `ref:`) from `f30f4d4b` to `ccd34f06`,
82
+ and **no re-vendoring was needed**: nothing under `examples/`, the scaffold
83
+ catalog or the templates themselves changed in that range — the whole
84
+ alp-sdk diff is `scripts/`, `metadata/schemas|socs`, `docs/`, `tests/` and
85
+ `CHANGELOG.md`. Verified, not assumed: `scaffold_byte_parity.py --sdk
86
+ <ccd34f06>` is rc 0, **9/9** (template, sku) pairs PASS against this tree
87
+ unchanged. So the vendor point below stays where it is, and the two refs are
88
+ allowed to differ for exactly this reason.
89
+
90
+ The tan-cli#493/#591 re-sync moved both refs again, `ccd34f06` →
91
+ `7d58ef32`, and again needed **no re-vendoring**. Six `examples/**` files
92
+ did move in this range (five `board.yaml`s and
93
+ `power-timing/littlefs-keyvalue/src/main.c`), but none is a scaffold
94
+ template or a catalog entry. Verified, not assumed: `scaffold_byte_parity.py
95
+ --sdk <7d58ef32>` is rc 0, **9/9** (template, sku) pairs PASS against this
96
+ tree unchanged.
97
+
98
+ - **Current vendor point (all templates):** **`84132b98a`**
99
+ (`84132b98a53a9f0e5ef9e7622a38c6d637439873`, alp-sdk tag `v0.17.0`) — the stable release
100
+ merge, for tan `0.7.0` (tan-cli#1258). Re-vendored links only: the emit's doc-link renderer
101
+ now renders `blob|tree/v0.17.0/`, so every vendored `v0.17.0-rc2` link (19 files) plus the
102
+ matching `DELIBERATE_EDITS` strings in `scaffold_byte_parity.py` moved to `v0.17.0`. No other
103
+ byte changed. `scaffold_byte_parity.py --sdk <v0.17.0>` is rc 0, **10/10**; before the swap,
104
+ 10/10 FAIL. The pin moved in the same change as every other pin site.
105
+
106
+ - **Prior vendor point (all templates):** **`84a6e0d21`**
107
+ (`84a6e0d211d6cc7898e1c3827cfff9b43ed309c7`, alp-sdk tag `v0.17.0-rc2`) — the release merge of `dev` at
108
+ `1d20103ba` (tree-identical to it except the release bump). See the note below.
109
+
110
+ The `84a6e0d21` -> `64481b8e9` planner re-sync (tan-cli#1525) moves ONE scaffold input:
111
+ alp-sdk#2826 (`b874b1334`, warm-aware CC3501E bring-up in every `cc3501e_bridge` copy, #2797)
112
+ rewrote `examples/aen/aen-cc3501e-*/src/cc3501e_bridge.c`, which the `iot` scaffold ships as
113
+ `iot/E1M-AEN801/src/cc3501e_bridge.c`. That one file was re-vendored byte-for-byte from the emit
114
+ (the bytes are the only change; the vendor-point label above is unchanged because the rest of the
115
+ tree is identical at both commits), and
116
+ `scaffold_byte_parity.py --sdk <64481b8e9 + local v0.17.0-rc2 tag>` is rc 0, **10/10**.
117
+
118
+ The `64481b8e9` -> `7584a3562` planner re-sync (tan-cli#1541) moves no scaffold input. Its
119
+ `examples/**` edits (`aen-power-stop`, `aen-trace-runner`, `aen-hp-core-smoke`) are not templates
120
+ or catalog entries, so nothing was re-vendored and the label above is unchanged.
121
+ `scaffold_byte_parity.py --sdk <7584a3562 + local v0.17.0-rc2 tag>` is rc 0, **10/10**.
122
+
123
+ - **Prior vendor point (all templates):** **`1d20103ba`**
124
+ (`1d20103ba367668ab4ed9b139dc69c246198d2f1`, alp-sdk `dev`) — the
125
+ `5a612e24a` -> `1d20103ba` planner re-sync (alp-sdk#2822: carveout.py
126
+ `_resolve_mailbox_channel`, kconfig.py RAM console size). The range moves no scaffold input, so nothing was re-vendored and `scaffold_byte_parity.py --sdk
127
+ <1d20103ba>` is rc 0, **10/10** (template, sku) pairs PASS against the tree
128
+ unchanged. The pin moves in the same change as `parity.yml`'s
129
+ `PINNED_SDK_TAG`/`PINNED_PLANNER_ORACLE_SDK_REF`, `ci.yml`'s `sdk_parity`
130
+ `ref:` and `test_planner_relocation_freshness.py`'s
131
+ `PINNED_SDK_COMMIT`/`HAND_PORT_PINNED_SDK_COMMIT`.
132
+
133
+ Re-vendored links only, for tan `v0.7.0-rc1` (tan-cli#1258): alp-sdk
134
+ `v0.17.0-rc2` (tag commit `84a6e0d211d6cc7898e1c3827cfff9b43ed309c7`,
135
+ tree-identical to this point plus the release bump) is cut from this commit, so the emit's doc-link renderer renders
136
+ `blob|tree/v0.17.0-rc2/`, and every vendored `v0.17.0-rc1` link (20 files)
137
+ plus the matching `DELIBERATE_EDITS` strings in `scaffold_byte_parity.py`
138
+ moved to `v0.17.0-rc2`. No other byte changed. Verified against a checkout
139
+ carrying a local `v0.17.0-rc2` tag (what `release-sdk-parity`'s
140
+ `fetch-tags: true` checkout sees): `scaffold_byte_parity.py` rc 0, **10/10**
141
+ pairs PASS; before the swap, 10/10 FAIL.
142
+
143
+ - **Prior vendor point (all templates):** **`5a612e24a`**
144
+ (`5a612e24ad895f35a1365792f9e52ee929926d2d`) — the
145
+ `a5a137c7b` -> `5a612e24a` planner re-sync (alp-sdk#2820); nothing re-vendored.
146
+
147
+ - **Prior vendor point (all templates):** **`a5a137c7b`**
148
+ (`a5a137c7b594ebb5d451177803c0eb324069e86b`) — the
149
+ `0dee99fb5` -> `a5a137c7b` planner re-sync (alp-sdk#2795, #2736); nothing re-vendored.
150
+
151
+ - **Prior vendor point (all templates):** **`183b05509`**
152
+ (`183b05509b37b44c25d3b8ae64c1aa1a10523ffd`, alp-sdk `dev`) — the
153
+ `0dee99fb5` -> `183b05509` planner re-sync (tan-cli#1393; alp-sdk#2736). The
154
+ range changes no `examples/**` or `templates/**` file, so nothing was
155
+ re-vendored and `scaffold_byte_parity.py --sdk <183b05509>` is rc 0,
156
+ **10/10** (template, sku) pairs PASS against the tree unchanged.
157
+
158
+ - **Prior vendor point (all templates):** **`0dee99fb5`**
159
+ (`0dee99fb519b511e0a7fe4da63b333b9246040b4`, alp-sdk `dev`) — the
160
+ `c4eb27645` -> `0dee99fb5` planner re-sync (alp-sdk#2791 `cameras:`, with
161
+ #2784 in range). The range moves no scaffold input, so nothing was re-vendored and `scaffold_byte_parity.py --sdk
162
+ <0dee99fb5>` is rc 0, **10/10** (template, sku) pairs PASS against the tree
163
+ unchanged. The pin moves in the same change as `parity.yml`'s
164
+ `PINNED_SDK_TAG`/`PINNED_PLANNER_ORACLE_SDK_REF`, `ci.yml`'s `sdk_parity`
165
+ `ref:` and `test_planner_relocation_freshness.py`'s
166
+ `PINNED_SDK_COMMIT`/`HAND_PORT_PINNED_SDK_COMMIT`.
167
+
168
+ - **Earlier vendor point (all templates):** **`6159a7b1a`**
169
+ (`6159a7b1a50c3267b98fed17c0e17d084143d22b`, alp-sdk `dev`) — the
170
+ `030de105d` -> `6159a7b1a` planner re-sync (tan-cli#1216; alp-sdk#2778, with
171
+ the E1M-NX9101 removal #2782 in range), the same change that moves
172
+ `parity.yml`'s `PINNED_SDK_TAG`/`PINNED_PLANNER_ORACLE_SDK_REF`, `ci.yml`'s
173
+ `sdk_parity` `ref:` and `test_planner_relocation_freshness.py`'s
174
+ `PINNED_SDK_COMMIT`/`HAND_PORT_PINNED_SDK_COMMIT`. **Re-vendored, one
175
+ file:** `examples/ai/cold-chain-monitor/src/cold_chain.c` (two comment edits)
176
+ into `edge-ai/E1M-AEN801/src/cold_chain.c` and
177
+ `edge-ai/E1M-V2N101/src/cold_chain.c`. `scaffold_byte_parity.py --sdk
178
+ <6159a7b1a>` was 8/10 against the old tree and is rc 0, **10/10** (template,
179
+ sku) pairs PASS after.
180
+
181
+ - **Earlier vendor point (all templates):** **`030de105d`**
182
+ (`030de105d3a106b3c10202854cf57d4e81e78c0f`, alp-sdk `dev`) — the
183
+ `2d2a85333` -> `030de105d` planner re-sync (tan-cli#1401; alp-sdk#2774, with
184
+ the v0.17.0 release merge #2763 in range), the same change that moves
185
+ `parity.yml`'s `PINNED_SDK_TAG`/`PINNED_PLANNER_ORACLE_SDK_REF`, `ci.yml`'s
186
+ `sdk_parity` `ref:` and `test_planner_relocation_freshness.py`'s
187
+ `PINNED_SDK_COMMIT`/`HAND_PORT_PINNED_SDK_COMMIT`. **Re-vendored, two
188
+ files:** `edge-ai/E1M-AEN801/src/cold_chain.c` and
189
+ `edge-ai/E1M-V2N101/src/cold_chain.c` — alp-sdk reworded two comments that
190
+ named a training script the example does not ship; nothing else in the emit
191
+ moved. `scaffold_byte_parity.py --sdk <c4eb27645>` is rc 0, **10/10**
192
+ (template, sku) pairs PASS.
193
+
194
+ - **Prior vendor point (all templates):** **`030de105d`**
195
+ (`030de105d3a106b3c10202854cf57d4e81e78c0f`, tan-cli#1401): the `2d2a85333` ->
196
+ `030de105d` planner re-sync (alp-sdk#2774, with the v0.17.0 release merge
197
+ #2763 in range). Re-vendored links only: alp-sdk cut `v0.17.0-rc1` inside the
198
+ range, so the emit's doc-link renderer now rendered `blob|tree/v0.17.0-rc1/`
199
+ instead of `v0.16.0` — 20 files, every difference a
200
+ `github.com/alplabai/alp-sdk/(blob|tree)/<ref>/` ref. `- Ref:` is
201
+ `v0.17.0-rc1` since this point.
202
+
203
+ - **Earlier vendor point (all templates):** **`2d2a85333`**
204
+ (`2d2a85333ed32d1bf4f2131aefd563f9a1a91cd0`), tan-cli#1216, no re-vendoring.
205
+
206
+ - **Earlier vendor point (all templates):** **`ac0e2a5e0`**
207
+ (`ac0e2a5e096a1c8c102818650a688d0f7e709066`, alp-sdk `dev`) — the
208
+ `34c11c9de` -> `ac0e2a5e0` planner re-sync (tan-cli#1309), no re-vendoring.
209
+
210
+ - **Prior vendor point (all templates):** **`34c11c9de`**
211
+ (`34c11c9de04e264fdcab2bc0d58b328d9d117ca8`, alp-sdk `dev`) — the
212
+ `79c834e65` -> `34c11c9de` planner re-sync (alp-sdk#866/#2469/#2024/#2316),
213
+ the same change that moves `parity.yml`'s `PINNED_SDK_TAG`/
214
+ `PINNED_PLANNER_ORACLE_SDK_REF`, `ci.yml`'s `sdk_parity` `ref:` and
215
+ `test_planner_relocation_freshness.py`'s `PINNED_SDK_COMMIT`/
216
+ `HAND_PORT_PINNED_SDK_COMMIT`. Still untagged (alp-sdk's newest tag is
217
+ `v0.16.0`), so `- Ref:` below stays `v0.16.0` for the same
218
+ `_tag_resolves()` reason as the `722320a1` bullet.
219
+
220
+ **All ten (template, sku) pairs moved, 49 files.** Re-vendored by running
221
+ `scaffold_byte_parity.py`'s own `emit_live_scaffold`/
222
+ `augment_with_example_extras`, then re-applying each file's surviving
223
+ `DELIBERATE_EDITS` with a three-way merge (base = the old emit recovered
224
+ by `undo_declared_edits`, ours = the old vendored file, theirs = the new
225
+ emit), so the bytes are by construction what the gate compares. Causes,
226
+ each read off the emit diff and matched to the upstream commit that
227
+ changed the catalog example:
228
+
229
+ - alp-sdk#866 (`423e0100d`), every pair: the `--emit zephyr-conf --core
230
+ <id>` CMakeLists bridge is GONE from all eleven vendored
231
+ `CMakeLists.txt` files (both `edge-ai` variants and `multicore-mailbox`'s
232
+ `peer/` included) — checked by search, no `zephyr-conf`/`alp_project.py`
233
+ string survives anywhere under this tree outside this file. This is the
234
+ change that has to land with the planner port: `tan build` now passes
235
+ `-D<image>_EXTRA_CONF_FILE` on a sysbuild slice and `-DEXTRA_CONF_FILE`
236
+ otherwise, so a scaffold still carrying the bridge would get the
237
+ per-core `alp.conf` twice. The same commit rewrites the `board.yaml`/
238
+ `prj.conf` "CMakeLists.txt invokes scripts/alp_project.py" comments,
239
+ adds `extra_args: EXTRA_CONF_FILE=generated/alp.conf` to every
240
+ `testcase.yaml` scenario, and adds a `gen_example_alp_conf.py` step plus
241
+ `-DEXTRA_CONF_FILE=generated/alp.conf` to every README `west build`
242
+ line.
243
+ - alp-sdk#2556 (`42b837a2a`/`31ae71570`), `multicore-mailbox` only:
244
+ `src/main.c`/`peer/main.c` drop the `.cacheable = false` field and the
245
+ README/comment prose says the carve-out is non-cacheable by design.
246
+ - alp-sdk#2192 (`34a128d09`), `iot` only: the README entropy paragraph and
247
+ `testcase.yaml` drop `CONFIG_ALP_SDK_ALLOW_TEST_ENTROPY=y` from the AEN
248
+ scenario (the SE TRNG now seeds every AEN board).
249
+ - alp-sdk#2173 (`01c347539`), `iot` only: `src/cc3501e_bridge.c`'s LP-pad
250
+ mux also runs on M55-HP, and `testcase.yaml` gains the
251
+ `alp_e1m_aen803_m55_hp` platform.
252
+
253
+ **Fourteen `DELIBERATE_EDITS` entries retired, forty-one down to
254
+ twenty-seven** — every `workflow_pointer` entry: `sensor` `board.yaml` x2
255
+ and `prj.conf` x2 (tan-cli#977), `minimal`/`diagnostics` `board.yaml` x4
256
+ and `prj.conf` x4, `iot` `prj.conf` and `multicore-mailbox` `prj.conf`
257
+ (tan-cli#1009). Each qualified the bare `scripts/alp_project.py` in the
258
+ bridge prose #866 deleted, so the three-way merge conflicted on exactly
259
+ those hunks (resolved to the new emit) and the gate's strict half then
260
+ named all fourteen "no longer there". Their `un_edit_*` functions and
261
+ constants are deleted with them (nothing reuses them); `self_check()`'s
262
+ mechanism demo moved to `multicore-mailbox`'s native_sim overlay
263
+ `orchestrator_pointer`, the one remaining entry that is alone on its path.
264
+
265
+ **Twenty-four `alp_conf_pregeneration` entries added, twenty-seven up to
266
+ fifty-one** — the new prose's replacement for the bridge. The emit tells
267
+ the reader to write `generated/alp.conf` with `python3
268
+ scripts/gen_example_alp_conf.py <dir>` (every README, ten files) and
269
+ points at that script from `board.yaml` (six) and `prj.conf` (eight). In a
270
+ scaffolded project that step does not work, measured on a fresh
271
+ `tan init --template zephyr-app --som E1M-AEN801` against `34c11c9de`: the
272
+ bare form fails (a scaffold has no `scripts/`), and
273
+ `$ALP_SDK_ROOT/scripts/gen_example_alp_conf.py .` exits 0 having written
274
+ nothing ("matches no Zephyr example core") because it only walks alp-sdk's
275
+ own `examples/`. Without `generated/alp.conf`, every vendored
276
+ `testcase.yaml` scenario (`EXTRA_CONF_FILE=generated/alp.conf`) and every
277
+ documented bare `west build` line names a missing overlay, which Zephyr
278
+ refuses. The README line is therefore re-anchored on
279
+ `tan generate --target zephyr-conf --core <id> --sdk-root "$ALP_SDK_ROOT"
280
+ --output [<dir>/]generated/alp.conf` (`m55_hp`/`m33_sm` for the project
281
+ root, `m55_he` for `multicore-mailbox`'s `./peer`), and the `board.yaml`/
282
+ `prj.conf` pointers name that README step. Measured on fresh scaffolds of
283
+ all ten (template, sku) pairs, each README command run as written: it
284
+ writes `generated/alp.conf` (and `peer/generated/alp.conf` for the
285
+ mailbox), byte-identical to the `build/<core>-zephyr/alp.conf` that
286
+ `tan build --materialise` writes from the plan. `--sdk-root` is spelled
287
+ out because `tan` has no `ALP_SDK_ROOT` env tier. `tan build` itself never
288
+ needed the file. `tests/core/test_template_integrity.py` accepts a
289
+ `generated/` `EXTRA_CONF_FILE` name only where a planned file carries the
290
+ `tan generate ... --output` command for that exact path. The upstream
291
+ prose stays wrong for anyone copying an example out of alp-sdk without
292
+ `tan`; that is alp-sdk's to fix, and these entries retire the moment it is.
293
+
294
+ Verified at `34c11c9de`, against a checkout with tags fetched:
295
+ `scaffold_byte_parity.py` **10/10 PASS** (rc 0).
296
+
297
+ - **Prior vendor point (all templates):** **`79c834e65`**
298
+ (`79c834e654f150816d09c7c92832a2104e76e309`, alp-sdk `dev`) — review of
299
+ tan-cli#1291 (the alp-sdk#2311/#2312/#2288 planner resync), the same
300
+ change that moves `parity.yml`'s `PINNED_SDK_TAG`/
301
+ `PINNED_PLANNER_ORACLE_SDK_REF`, `ci.yml`'s `sdk_parity` `ref:` and
302
+ `test_planner_relocation_freshness.py`'s `PINNED_SDK_COMMIT`/
303
+ `HAND_PORT_PINNED_SDK_COMMIT`. Still untagged (alp-sdk's newest tag is
304
+ `v0.16.0`), so `- Ref:` below stays `v0.16.0` for the same
305
+ `_tag_resolves()` reason as the `722320a1` bullet.
306
+
307
+ **Two files changed, one (template, sku) pair each** —
308
+ `edge-ai`/E1M-AEN801's `README.md` and `edge-ai`/E1M-V2N101's
309
+ `README.md`. Every other (template, sku) pair is untouched —
310
+ `scaffold_byte_parity.py` reported 0 diffs for the other eight before
311
+ this re-vendor and still does after.
312
+
313
+ **One upstream cause:** alp-sdk#2281 adds `E1M-V2M103`/`E1M-V2N103` to
314
+ the SKU catalogue, and `edge-ai`'s SKU-neutral DEEPX-population sentence
315
+ now names all three SKUs per family ("...`E1M-V2M101`/`E1M-V2M102`/
316
+ `E1M-V2M103` -- not on `E1M-V2N101`/`E1M-V2N102`/`E1M-V2N103`..."), where
317
+ it previously named two. `E1M-V2N101`'s `README.md` is a straight
318
+ re-vendor of the new sentence (no `DELIBERATE_EDITS` entry covers it —
319
+ that transform retired at the `722320a1` re-vendor, see
320
+ `scaffold_byte_parity.py`'s own `un_edit_edge_ai_v2n101_readme_deepx_note`
321
+ docstring). `E1M-AEN801`'s active `deepx_v2m_note` `DELIBERATE_EDITS`
322
+ entry needed its `_EDGE_AI_AEN801_README_DEEPX_NOTE_EMITTED` anchor moved
323
+ to the new three-SKU sentence — the DELIBERATE correction text itself
324
+ (the re-scaffold-not-edit advice) is unchanged, since `E1M-AEN801` still
325
+ cannot flip `som.sku` to any DEEPX SKU without re-scaffolding, three
326
+ siblings or two.
327
+
328
+ Verified at `79c834e65`, against a checkout with tags fetched:
329
+ `scaffold_byte_parity.py` **10/10 PASS** (rc 0).
330
+
331
+ - **Prior vendor point (all templates):** **`c81cb5db`**
332
+ (`c81cb5db9945c8f448a7bb952d374f874e2f42c0`, alp-sdk `dev`) — tan-cli#1275's
333
+ re-pin (the #1269 planner re-sync's hand-finish), the same change that
334
+ moves `parity.yml`'s `PINNED_SDK_TAG`/`PINNED_PLANNER_ORACLE_SDK_REF`,
335
+ `ci.yml`'s `sdk_parity` `ref:` and
336
+ `test_planner_relocation_freshness.py`'s `PINNED_SDK_COMMIT`/
337
+ `HAND_PORT_PINNED_SDK_COMMIT`. Still untagged (alp-sdk's newest tag is
338
+ `v0.16.0`), so `- Ref:` below stays `v0.16.0` for the same
339
+ `_tag_resolves()` reason as the `722320a1` bullet.
340
+
341
+ **Six files moved / one file removed / two files added, across two
342
+ (template, sku) pairs** — `iot`/E1M-AEN801 (`CMakeLists.txt`, `README.md`,
343
+ `board.yaml`, `prj.conf`, `src/main.c`, `testcase.yaml`; `native_sim.conf`
344
+ REMOVED, not moved; `src/cc3501e_bridge.c` + `src/cc3501e_bridge.h` ADDED
345
+ -- see the KNOWN UPSTREAM GAP note below for why an addition was needed
346
+ at all) and `multicore-mailbox`/E1M-AEN801 (`board.yaml` only). Every
347
+ other (template, sku) pair is untouched — `scaffold_byte_parity.py`
348
+ reported 0 diffs for all eight before this re-vendor and still does
349
+ after.
350
+
351
+ **Two upstream causes, both AEN-only:**
352
+ - **alp-sdk#2112/#2172** (`4e68133b3`) attaches the CC3501E bridge
353
+ (`cc3501e_bridge_bringup()`) before `alp_wifi_open()` on
354
+ `examples/connectivity/mqtt-telemetry` (the `iot` template's canonical
355
+ example) — the app never called it, so `alp_wifi_open()` returned
356
+ `NULL` on real E1M-AEN801 silicon and never reached
357
+ `alp_wifi_connect()`. `CMakeLists.txt` gains
358
+ `target_sources(app PRIVATE src/cc3501e_bridge.c)`.
359
+ - **alp-sdk#2173** (`c81cb5db9`, this pin's own tip commit) fixes the
360
+ mbedtls `ssl_misc.h`/PSA-crypto break at its root
361
+ (`ALP_SDK_MBEDTLS_PSA_CRYPTO` in `zephyr/Kconfig.alp-libraries`), so
362
+ `mqtt-telemetry` no longer needs a `native_sim.conf` to force mbedtls
363
+ off for the native_sim scenario — every target now builds the same
364
+ TLS-enabled configuration the SoM does. This is also what flips
365
+ `CMakeLists.txt`'s `EXTRA_CONF_FILE` ordering from `list(PREPEND ...)`
366
+ back to a plain `list(APPEND ...)` with no preceding rationale
367
+ comment: the caller-override problem `PREPEND` (tan-cli#379) existed
368
+ to solve was specific to the now-deleted `native_sim.conf` overlay.
369
+ - `multicore-mailbox`'s `board.yaml` change is unrelated to either: it is
370
+ alp-sdk#2053's `mram_main.base` resolution (`0x80000000`, not `"TBD"`)
371
+ reaching this template's own carve-out-blocked explanation, the exact
372
+ downstream effect `test_planner_relocation_freshness.py`'s
373
+ `PINNED_SDK_COMMIT` paragraph documents for the planner side.
374
+
375
+ **Four of `DELIBERATE_EDITS`' prior forty-five entries retired**, taking
376
+ 0 `un_edit_*` functions with them (each retired transform's mechanics-only
377
+ round-trip proof is kept, matching the `un_edit_doc_link_ref` precedent
378
+ this file's `scaffold_byte_parity.py` companion already set) and 41
379
+ entries remain: `iot`/E1M-AEN801's `extra_conf_order` (`CMakeLists.txt`),
380
+ `native_sim_conf_link` and `native_sim_conf_copy_comment` (`README.md`,
381
+ both `native_sim.conf`-referencing), and `fleet_ota_pointer` (`prj.conf`
382
+ — the whole migration-note paragraph it qualified was replaced by
383
+ alp-sdk#2173's new PSA-crypto paragraph). All four existed only to
384
+ qualify or reorder `native_sim.conf`-related content that alp-sdk#2173
385
+ deleted outright, so — like the alp-sdk#1855 retirement above — upstream
386
+ did not merely reword around them, it removed the mechanism entirely.
387
+ `iot`/E1M-AEN801's `workflow_pointer` (`prj.conf`) and
388
+ `multicore-mailbox`/E1M-AEN801's `e1m_modules_pointer` /
389
+ `zephyr_drv_pointer` (`board.yaml`) all survive unchanged, re-applied on
390
+ top of the new emit exactly as written.
391
+
392
+ **KNOWN UPSTREAM GAP, worked around here, not fixed at its source:**
393
+ alp-sdk's own `metadata/templates/catalog-v1.json` `iot` entry's
394
+ `files.user_owned` list was never updated for alp-sdk#2112 — it still
395
+ names only `board.yaml`/`prj.conf`/`CMakeLists.txt`/`src/main.c`/
396
+ `README.md`, so `alp_project.py --emit scaffold --template iot --sku
397
+ E1M-AEN801` never returns `src/cc3501e_bridge.{c,h}` even though the
398
+ live `CMakeLists.txt` now references `src/cc3501e_bridge.c`. Confirmed
399
+ by inspection: `rg cc3501e_bridge metadata/templates/catalog-v1.json
400
+ scripts/alp_template.py` returns zero hits in the bound `c81cb5db`
401
+ checkout. Measured, not theoretical: without this workaround,
402
+ `tan/core/scaffold.py`'s own `TemplateDataError` cross-check (every
403
+ `target_sources()` token in a vendored `CMakeLists.txt` must name a file
404
+ the tree actually carries) refuses this exact scaffold outright rather
405
+ than shipping one that fails to link. Both files are declared in
406
+ `scaffold_byte_parity.py`'s `NON_ENVELOPE_EXTRAS` (the same mechanism
407
+ `native_sim.conf`/tan-cli#379 uses) and vendored from the catalog
408
+ example's real, on-disk copy — real upstream bytes, not invented
409
+ content, so byte-parity holds. This is an alp-sdk CATALOG defect, not a
410
+ tan-side one; `scaffold_byte_parity.py`'s job is byte-parity with what
411
+ the live emit + declared extras produce, not correctness of alp-sdk's
412
+ own `user_owned` list. Filed as **alplabai/alp-sdk#2241** (this repo has
413
+ no standing to edit `metadata/templates/catalog-v1.json` itself); treat
414
+ this workaround as temporary, not the permanent shape, and drop it once
415
+ #2241 lands and re-vendoring picks the files up from the envelope
416
+ directly.
417
+
418
+ Verified at `c81cb5db`, against a checkout with tags fetched:
419
+ `scaffold_byte_parity.py` **10/10 PASS** (rc 0).
420
+
421
+ - **Prior vendor point (all templates):** **`ff27f179`**
422
+ (`ff27f179c3baa9e04e8b6a536a4e0b8cee7be7b2`, alp-sdk `dev`) —
423
+ tan-cli#1151's re-pin (the #1118 planner re-sync), the same change that
424
+ moves `parity.yml`'s `PINNED_SDK_TAG`/`PINNED_PLANNER_ORACLE_SDK_REF`,
425
+ `ci.yml`'s `sdk_parity` `ref:` and
426
+ `test_planner_relocation_freshness.py`'s `PINNED_SDK_COMMIT`/
427
+ `HAND_PORT_PINNED_SDK_COMMIT`. Still untagged at the time (alp-sdk's
428
+ newest tag was `v0.16.0`), so `- Ref:` below stays `v0.16.0` for the same
429
+ `_tag_resolves()` reason as the `722320a1` bullet.
430
+
431
+ **Nine files moved across six (template, sku) pairs** —
432
+ `edge-ai`/E1M-AEN801 (`src/main.c`), `edge-ai`/E1M-V2N101 (`src/main.c`),
433
+ `iot`/E1M-AEN801 (`board.yaml`, `src/main.c`),
434
+ `multicore-mailbox`/E1M-AEN801 (`src/main.c`), `sensor`/E1M-AEN801
435
+ (`board.yaml`, `src/main.c`) and `sensor`/E1M-V2N101 (`board.yaml`,
436
+ `src/main.c`). `diagnostics` and `minimal` are untouched at both SKUs.
437
+
438
+ **One upstream cause: alp-sdk#1855** (`5c33ef04`, alp-sdk#1906). Its
439
+ `scripts/alp_template.py` change makes the emit itself rewrite a bare
440
+ `docs/*.md` or `examples/<category>/<name>[/<subpath>]` mention in PROSE
441
+ — a `board.yaml` or `src/*.c` comment with no `[...](...)` around it —
442
+ into the same absolute GitHub URL a markdown link already got, and keeps a
443
+ trailing `/<subpath>` when collapsing a template's own example path to
444
+ `.`. Every byte that moved in these nine files is that one substitution.
445
+
446
+ **Seventeen of `DELIBERATE_EDITS`' prior sixty-two entries retired as a
447
+ direct result**, taking 11 `un_edit_*` functions and 22 constants with
448
+ them (45 entries remain). This is the cleanest retirement reason the table
449
+ has had: these entries exist *only* to qualify the bare referents the emit
450
+ now rewrites on its own, so upstream did not merely reword around them —
451
+ it took over their job. `multicore-mailbox`'s `peer_build_path`
452
+ (tan-cli#1009) is the proof: tan's hand-fix and alp-sdk#1855's fix produce
453
+ **byte-identical** output (`./peer`), so that file needed no re-vendor at
454
+ all, only the now-redundant entry dropped.
455
+
456
+ Retired: `edge-ai` `model_readme_pointer_1`/`_2` (x2 SKUs), `iot`
457
+ `cc3501e_bridge_pointer` (`board.yaml` + `src/main.c`) and
458
+ `sensor_template_pointer`, `multicore-mailbox` `peer_build_path` and
459
+ `peer_main_pointer`, `sensor` `historical_note_v2n_pointer`
460
+ (`board.yaml` + `src/main.c`, x2 SKUs), `init_fail_scanner_pointer` (x2
461
+ SKUs) and `pattern_paragraph` (x2 SKUs). That last one was tan-cli#924's
462
+ final live entry, so tan-cli#924 now has no entry in the table.
463
+
464
+ **NOT retired, and this is the entry that had to be walked rather than
465
+ batched:** `sensor`'s `historical_note_boards_pointer` (both SKUs). It
466
+ sits in the SAME re-flowed `NOTE (#1269):` paragraph as the retired
467
+ `historical_note_v2n_pointer`, so a `--theirs` merge of that hunk dropped
468
+ it silently — but it qualifies bare `metadata/boards/e1m-evk.yaml` /
469
+ `e1m-x-evk.yaml` mentions, and alp-sdk#1855 deliberately leaves
470
+ `metadata/` and `scripts/` alone (its own `_BARE_REPO_PATH_RE` comment
471
+ says so). Upstream did NOT take over this one's job, so it was re-applied
472
+ on top of the new emit rather than retired. `scaffold_byte_parity.py`'s
473
+ own "declares an edit that is no longer there" self-check is what caught
474
+ the drop; the 19 entries it flagged were 17 genuinely superseded plus
475
+ these 2 pieces of collateral, and the difference had to be read per entry.
476
+
477
+ Verified at `ff27f179`, against a checkout with tags fetched:
478
+ `scaffold_byte_parity.py` **10/10 PASS** (rc 0).
479
+
480
+ - **Prior vendor point (all templates):** **`722320a1`**
481
+ (`722320a1abe3cea675e99e97300b8a484b4e8464`, alp-sdk `dev`, 63 commits past
482
+ `v0.16.0`/`eb96112b`) — tan-cli#996/#1001's re-pin, the same change that
483
+ moves `parity.yml`'s `PINNED_SDK_TAG`/`PINNED_PLANNER_ORACLE_SDK_REF` and
484
+ `test_planner_relocation_freshness.py`'s `PINNED_SDK_COMMIT`/
485
+ `HAND_PORT_PINNED_SDK_COMMIT`. Not tagged (alp-sdk's newest tag is still
486
+ `v0.16.0`) — `- Ref:` below stays `v0.16.0` because `_docs_ref()`'s
487
+ `_tag_resolves()` guard still degrades to the last RESOLVING tag, not to
488
+ this untagged commit.
489
+
490
+ **Twenty files moved across seven (template, sku) pairs** —
491
+ `diagnostics`/E1M-AEN801 (`README.md`), `diagnostics`/E1M-V2N101
492
+ (`README.md`), `edge-ai`/E1M-AEN801 (`README.md`, `src/main.c`),
493
+ `edge-ai`/E1M-V2N101 (`README.md`, `src/main.c`), `iot`/E1M-AEN801
494
+ (`README.md`, `board.yaml`), `sensor`/E1M-AEN801 (`README.md`,
495
+ `board.yaml`, `boards/native_sim_native_64.conf`,
496
+ `boards/native_sim_native_64.overlay`, `src/main.c`, `testcase.yaml`) and
497
+ `sensor`/E1M-V2N101 (the same six paths as its sibling SKU). `minimal` and
498
+ `multicore-mailbox` are untouched at both SKUs (`scaffold_byte_parity.py`
499
+ reported 0 diffs for both before this re-vendor and still does after).
500
+
501
+ Unlike every prior vendor-point move recorded here, this one is NOT just a
502
+ doc-link ref bump — real `examples/**` content changed inside the
503
+ `eb96112b..722320a1` window and the vendored bytes follow it:
504
+ `examples/peripheral-io/i2c-master`'s underlying chip swapped TMP112 ->
505
+ BMP581 (alp-sdk#1269, `sensor`'s own catalog entry), which is why `sensor`
506
+ moved all six of its files rather than just `README.md`;
507
+ `examples/ai/cold-chain-monitor`'s README/`src/main.c` gained real
508
+ doc-link/units-test pointers on their own (superseding entry 4's
509
+ `DELIBERATE_EDITS`, see "Deliberate edits on top of the emit" below); and
510
+ `examples/connectivity/mqtt-telemetry`'s README gained a `tan init`/
511
+ `tan build` customer-workflow rewrite plus a TMP112 -> BMP581 sensor-link
512
+ swap of its own (`iot`). Twenty of `DELIBERATE_EDITS`' prior thirty-one
513
+ entries retired as a direct result — their declared anchors stopped
514
+ matching this reworded prose verbatim; see "Deliberate edits on top of the
515
+ emit" below for the full retirement accounting, and
516
+ `scaffold_byte_parity.py`'s own `DELIBERATE_EDITS` block comment for the
517
+ per-entry reasoning.
518
+
519
+ Verified at `722320a1`, against a checkout with tags fetched:
520
+ `scaffold_byte_parity.py` **10/10 PASS** (rc 0; `multicore-mailbox` is a
521
+ 10th (template, sku) pair not present at the `eb96112b` vendor point's own
522
+ 9/9 count above — tan-cli#996/#1001 adds it, unrelated to this re-vendor).
523
+
524
+ - **Prior vendor point:** **`eb96112b`**
525
+ (`eb96112ba7d1cc3b4084c985962ea31772177d74`, alp-sdk — the
526
+ `release/v0.16.0-merge` merge, tagged `v0.16.0` the same day, 2026-08-23) —
527
+ tan-cli#891's pin bump. NOT a planner audit: the vendored fixtures and the
528
+ checkout `parity.yml`'s seam1 job compares against have to name the same
529
+ commit, and `contract/fixtures/toolchains/toolchains.json` was re-vendored
530
+ from `v0.16.0` first (tan-cli#888), which left `PINNED_SDK_TAG` behind it.
531
+ **Seven** files moved, all `README.md`: `diagnostics`/E1M-AEN801,
532
+ `diagnostics`/E1M-V2N101, `iot`/E1M-AEN801, `minimal`/E1M-AEN801,
533
+ `minimal`/E1M-V2N101, `sensor`/E1M-AEN801 and `sensor`/E1M-V2N101. The only
534
+ change in each is the doc-link ref: `blob/main/` → `blob/v0.16.0/` (file
535
+ links) and `tree/main/` → `tree/v0.16.0/` (directory links) — 10 of the 40
536
+ changed links across these seven files are `tree/`, not `blob/`.
537
+
538
+ **This is exactly the move the `94378a05` entry below predicted, arriving
539
+ the day it said it would.** `eb96112b` is itself tagged `v0.16.0`, so
540
+ alp-sdk#1535's `_tag_resolves()` guard in `_docs_ref()` now finds the tag
541
+ and renders the version link instead of degrading to `main` — the first
542
+ vendor point where that guard actually fires with a resolving tag. `tan/
543
+ planner/template.py` carries the same guard (ported at tan-cli#846), so
544
+ tan's own emit agrees and no code moved, only the vendored bytes.
545
+
546
+ `iot`/E1M-AEN801's `CMakeLists.txt` still carries the standing
547
+ `DELIBERATE_EDITS` entry (tan-cli#379's `list(PREPEND EXTRA_CONF_FILE
548
+ ...)`) and was NOT re-vendored. Verified at `eb96112b`, against a checkout
549
+ with tags fetched: `scaffold_byte_parity.py` **9/9 PASS** (rc 0). The same
550
+ commit without tags reproduces 7 FAIL / 2 PASS every time — `_docs_ref()`'s
551
+ `_tag_resolves()` guard reads only the checkout's local refs, never the
552
+ network — which is what this table's own 9/9 claims depend on: `parity.yml`
553
+ and `ci.yml` now clone alp-sdk with `fetch-tags: true` (tan-cli#891), so a
554
+ tagged clone is what CI actually measures, not a hand-run artefact.
555
+
556
+ - **Prior vendor point:** **`94378a05`**
557
+ (`94378a056549c7377d714a7f2b68878aca8fea01`, alp-sdk `dev`) — tan-cli#846's
558
+ pin bump, which moves `parity.yml`'s `PINNED_SDK_TAG`, `ci.yml`'s
559
+ `sdk_parity` `ref:` and `PINNED_SDK_COMMIT` past alp-sdk#1535. **Seven**
560
+ files moved, all `README.md`: `diagnostics`/E1M-AEN801,
561
+ `diagnostics`/E1M-V2N101, `iot`/E1M-AEN801, `minimal`/E1M-AEN801,
562
+ `minimal`/E1M-V2N101, `sensor`/E1M-AEN801 and `sensor`/E1M-V2N101. The only
563
+ change in each is the doc-link ref: `blob/v0.15.0/` → `blob/main/`.
564
+
565
+ **This is a re-vendor ONTO `main`, deliberately, and it is not drift.**
566
+ `94378a05` sits past alp-sdk's `v0.16.0` version bump
567
+ (`metadata/sdk_version.yaml`: `version: 0.16.0`, `status: released`) while
568
+ alp-sdk's tag list holds `v0.14.0`, `v0.15.0`, `v0.15.0-rc1` and
569
+ `v0.16.0-rc1` — no `v0.16.0`. Before alp-sdk#1535 that combination made
570
+ `_docs_ref()` pin every scaffolded README to
571
+ `https://github.com/alplabai/alp-sdk/blob/v0.16.0/docs/...`, which 404s;
572
+ #1535 adds `_tag_resolves()` so a declared-but-unresolvable tag degrades to
573
+ `main` instead. tan-cli#846 ports the same guard into
574
+ `tan/planner/template.py`, so tan's own emit agrees. The vendored bytes here
575
+ follow the emit, not the other way round.
576
+
577
+ This DID move again — back to `v0.16.0` — the day alp-sdk cut that tag; see
578
+ the `eb96112b` entry above.
579
+
580
+ `iot`/E1M-AEN801's `CMakeLists.txt` still carries the standing
581
+ `DELIBERATE_EDITS` entry (tan-cli#379's `list(PREPEND EXTRA_CONF_FILE
582
+ ...)`) and was NOT re-vendored. Verified at `94378a05`:
583
+ `scaffold_byte_parity.py` **9/9 PASS** (rc 0); 7 FAIL / 2 PASS before the
584
+ re-vendor.
585
+
586
+ - **Vendor point before `94378a05`:** **`d00dbdc1`**
587
+ (`d00dbdc124491c89f68f404cd7ac9d26127f038f`, alp-sdk `dev`) — tan-cli#560's
588
+ re-sync, which moves `PINNED_SDK_COMMIT`/`HAND_PORT_PINNED_SDK_COMMIT` past
589
+ alp-sdk#1394/#1399 (`c07254b2`) and #1400 (`739e998b`), both landed in the
590
+ `HAND_PORT_HASHES` file `scripts/alp_template.py`. #1400 added
591
+ `_rewrite_stale_sdk_root_comment()`, which rewrites the `ALP_SDK_ROOT`
592
+ comment paragraph in every emitted `CMakeLists.txt` that carries the
593
+ in-tree `../../..` guess block. **Four** files moved, all `CMakeLists.txt`:
594
+ `edge-ai`/E1M-AEN801, `edge-ai`/E1M-V2N101, `minimal`/E1M-AEN801 and
595
+ `minimal`/E1M-V2N101; `edge-ai`'s pair also gains #1390's
596
+ `OUTPUT_VARIABLE`/`ERROR_VARIABLE` capture on `execute_process`, folded
597
+ into the same alp-sdk range.
598
+
599
+ **Re-vendoring was necessary but not sufficient.** `scripts/alp_template.py`
600
+ is a `HAND_PORT_HASHES` file, so this re-sync also ports
601
+ `_rewrite_stale_sdk_root_comment()` and the loop-based
602
+ `_scaffold_cmakelists` into `tan/planner/template.py` itself (kept in step
603
+ with `_derive_pin_doc_renames`'s alp-sdk#1394 collision guard, the other
604
+ behavioural delta in this same file across the range) — see that module's
605
+ own history. `scaffold_byte_parity.py` (vendored bytes vs. the SDK emit)
606
+ alone would have gone green on the re-vendor without the emitter port;
607
+ `test_planner_emit_parity.py` (tan's OWN emit vs. the SDK emit) is what
608
+ actually exercises the ported code.
609
+
610
+ `iot`/E1M-AEN801's `CMakeLists.txt` still carries the standing
611
+ `DELIBERATE_EDITS` entry (tan-cli#379's `list(PREPEND EXTRA_CONF_FILE
612
+ ...)`) and was NOT re-vendored — re-vendoring it would silently revert that
613
+ fix. Verified at `d00dbdc1`: `scaffold_byte_parity.py` **9/9 PASS** (rc 0).
614
+
615
+ - **Vendor point before `d00dbdc1`:** **`f30f4d4b`** (alp-sdk `dev`,
616
+ the commit `parity.yml`'s `PINNED_SDK_TAG` named when that was captured) —
617
+ re-vendored by the tan-cli#543/#544/#545 planner re-sync. **Eleven** files
618
+ moved:
619
+ - The **seven** `README.md` doc-link files (`diagnostics`, `minimal` and
620
+ `sensor` for both SKUs, plus `iot`/E1M-AEN801) change only
621
+ `blob|tree/v0.15.0-rc1/` → `blob|tree/v0.15.0/`. Those bytes are now the
622
+ EMIT'S OWN: alp-sdk has since cut the real `v0.15.0` tag (`e2928b9f`), so
623
+ tan-cli#384's seven `DELIBERATE_EDITS` entries in
624
+ `tests/parity/scaffold_byte_parity.py` are **RETIRED** rather than
625
+ re-pointed — that module's doctrine makes an `un_edit` with nothing left
626
+ to undo a hard failure, so a healed divergence must force its entry out.
627
+ The `un_edit_doc_link_ref` transform is kept for the next pre-release
628
+ vendor point.
629
+ - `edge-ai`'s **two** `README.md` files gain the alp-sdk#1266 board-target
630
+ rewrite, and its **two** `testcase.yaml` files follow their example's
631
+ current content. `edge-ai/*/testcase.yaml` is RETAINED, not dropped: the
632
+ catalog record still carries
633
+ `testcase_yaml: ["examples/ai/cold-chain-monitor/testcase.yaml"]` and the
634
+ file exists on disk, so `augment_with_example_extras` diffs it like any
635
+ other file (both `edge-ai` pairs report 8 files, not 6).
636
+ - `iot`/E1M-AEN801's `CMakeLists.txt` keeps its tan-cli#379 deliberate edit
637
+ and was NOT rewritten.
638
+
639
+ Re-vendored by re-running the live emit through
640
+ `tests/parity/scaffold_byte_parity.py`'s OWN `discover_vendored_matrix` +
641
+ `emit_live_scaffold` + `augment_with_example_extras`, writing each changed
642
+ path with `newline="\n"` and never touching a `DELIBERATE_EDITS` path — the
643
+ same throwaway-driver shape as the bumps below, run on Linux (Python
644
+ 3.12.3). Verified after: `scaffold_byte_parity.py --sdk <f30f4d4b>` rc 0,
645
+ **9/9** (template, sku) pairs PASS.
646
+
647
+ This bump lands in the SAME commit as the `PINNED_SDK_TAG` move and the
648
+ `tan/planner/` port it depends on, because either half alone reds a seam.
649
+
650
+ - **Previous vendor point:** **`v0.15.0-rc1`** (`996937ac`) —
651
+ the release tag, re-vendored to match `parity.yml`'s `PINNED_SDK_TAG` move
652
+ off `v0.14.0`. Same seven `README.md` files as the v0.14.0 bump below and
653
+ NOTHING else: every changed line differs only by the doc-version link
654
+ `blob/v0.14.0/` → `blob/v0.15.0/` (the SDK's own doc-link renderer drops the
655
+ `-rc1` suffix; verified line-for-line, `git diff | grep -vc
656
+ "v0\.1[45]\.0"` returns 0). No schema, core, peripheral or `board.yaml`
657
+ content changed — in particular alp-sdk#1068 (`CONFIG_USE_DT_CODE_PARTITION`
658
+ in the AEN board `_defconfig`) touches none of `--emit scaffold`'s output;
659
+ `edge-ai` (whose README carried no version-pinned links at this vendor
660
+ point — since superseded, see the v0.14.0 bullet below and entry 4 under
661
+ "Deliberate edits on top of the emit") diffed clean at 0/8 files, confirming
662
+ it. No `PINNED_SDK_COMMIT`/`PINNED_HASHES` re-audit was
663
+ needed either: `scripts/alp_orchestrate/` is byte-identical between
664
+ `0f3cefbe` (the planner's last audit point) and this tag.
665
+ - Re-vendored by re-running the live emit through
666
+ `tests/parity/scaffold_byte_parity.py`'s OWN `discover_vendored_matrix` +
667
+ `emit_live_scaffold`, writing each changed path with `newline="\n"` — the
668
+ same throwaway-driver shape as the v0.14.0 bump below, run on Windows
669
+ (Python 3.12.10; the "needs WSL" note two bumps below was already
670
+ superseded — see that entry).
671
+ - **Diverged from `crates/tan-core/src/wizard/vendored/` on purpose.**
672
+ That tree was frozen at `v0.14.0` (`ef79eab0`) and never re-vendored; this
673
+ one is what a Python `tan` actually reads, so it tracks `PINNED_SDK_TAG`.
674
+ tan-cli#269 has since deleted `crates/` outright, which makes this the only
675
+ vendored scaffold tree in the repo. The obsolete
676
+ Python-vs-Rust byte-identity test was retired; the shipping tree is guarded
677
+ by its LF-only unit test and by `tests/parity/scaffold_byte_parity.py`
678
+ against the live pinned SDK instead.
679
+ - **The vendor ref and `parity.yml`'s `PINNED_SDK_TAG` are NOT the same ref,
680
+ and that is deliberate.** The pin is `f4d87a1f` (tan-cli#320, a commit past
681
+ `v0.15.0-rc1` — see that variable's own comment for why), while this tree
682
+ stays captured at the `v0.15.0-rc1` TAG, because the links below have to
683
+ name a ref a customer can open in a browser and `v0.15.0` does not exist
684
+ (tan-cli#384). Re-measured at `f4d87a1f`: `--emit scaffold` is
685
+ byte-identical to this tree across all 9 (template, sku) pairs except the
686
+ two deliberate edits below — no `board.yaml`, `prj.conf`, `src/` or
687
+ `CMakeLists.txt` content moved in that range. That is what makes the split
688
+ safe; it is not a licence to let the two drift unchecked, and
689
+ `scaffold_byte_parity.py` is what re-checks it.
690
+ - Bumping that pin drives all FOUR parity gates — it re-vendors this tree,
691
+ the bootstrap manifest fixture, the toolchain lock and the kconfig golden
692
+ together, or the gates go red. They are one atomic unit, not four
693
+ independent bumps. (The toolchain lock and kconfig fixture needed no byte
694
+ change this round — see each gate's own re-run notes; only this tree and
695
+ the bootstrap manifest fixture had real drift.)
696
+ - Re-vendored by re-running the emit, not by editing these files. A
697
+ hand-edit that happens to match today is a copy that drifts tomorrow;
698
+ the point of this tree is that it is generated.
699
+ - Repo: `alplabai/alp-sdk`
700
+ - Ref: `v0.17.0` — the ref every shipped doc link in this tree pins, and the
701
+ one `tests/core/test_template_integrity.py` reads off THIS line to check
702
+ them against. It is the emit's OWN rendered ref rather than a hand-edit:
703
+ the emit renders the link ref from the SDK's `VERSION` (dropping any
704
+ pre-release suffix), so **tan-cli#384's hand-edit is retired and the seven
705
+ `DELIBERATE_EDITS` entries with it.** Links resolve as emitted.
706
+
707
+ It moved off `main` (tan-cli#846's pin bump) to `v0.16.0` (tan-cli#891) the
708
+ day alp-sdk actually cut that tag — the emit doing its job, not a lost pin.
709
+ alp-sdk#1535 added `_tag_resolves()` to `_docs_ref()`: the declared
710
+ `v<version>` has to RESOLVE before a scaffold pins to it. `eb96112b` is
711
+ itself tagged `v0.16.0`, so the guard finds it and renders the version link
712
+ instead of degrading to `main`. See the `eb96112b` bullet above for the
713
+ full re-vendor.
714
+ - Commit: **`84132b98a`** (alp-sdk tag `v0.17.0`, full sha
715
+ `84132b98a53a9f0e5ef9e7622a38c6d637439873`) — the checkout the emit was RUN
716
+ against, matching the "Current vendor point" bullet above, asserted equal
717
+ to it by
718
+ `python/tests/core/test_template_integrity.py::
719
+ test_the_manifest_states_one_vendor_point_not_two`.
720
+
721
+ This line used to say `1d20103ba` (tan-cli#1487), and before that
722
+ `5a612e24a` (tan-cli#1216), and before that
723
+ `a5a137c7b` (tan-cli#1393), and before that
724
+ `0dee99fb5` (tan-cli#1440), and before that
725
+ `c4eb27645` (tan-cli#1427), and before that
726
+ `030de105d` (tan-cli#1401), and before that
727
+ `2d2a85333` (tan-cli#1216), and before that
728
+ `ac0e2a5e0` (tan-cli#1309), and before that
729
+ `34c11c9de` (tan-cli#1278), and before that
730
+ `79c834e65` (the alp-sdk#2311/#2312 re-sync), and
731
+ before that `c81cb5db` (review of tan-cli#1291, this pin's own
732
+ prior value), and before that `ff27f179` (tan-cli#1275), and before that
733
+ `eb96112b` (tan-cli#996/#1001), and before that
734
+ `94378a05` (tan-cli#846) and, before that, `f30f4d4b`
735
+ and assert it was "the same commit `parity.yml`'s `PINNED_SDK_TAG` now
736
+ names". Both halves went stale, and in that order. tan-cli#582 wrote it on
737
+ 2026-08-09, setting the vendor point, this line and the pin all to
738
+ `f30f4d4b`. The IDENTITY broke the same day — tan-cli#593 moved the pin to
739
+ `ccd34f06` 21h44m later — and the COMMIT went stale on 2026-08-13, when
740
+ tan-cli#714 moved the tree to `d00dbdc1` and left this line behind.
741
+ tan-cli#851 then re-vendored onto `94378a05` and demoted `d00dbdc1` to
742
+ history, without this line moving either time. Nothing read it, so nothing
743
+ could say.
744
+
745
+ The two refs are not independent by design: #714 and #851 each set the
746
+ vendor point and the pin together, so they are equal at capture and diverge
747
+ only as the pin keeps moving afterwards — which is what "PINNED_SDK_TAG has
748
+ since moved past this vendor point, deliberately" above describes.
749
+
750
+ Distinct from `Ref:` on purpose, and the distinction is NOT tag-vs-commit:
751
+ `Ref:` is `main` today, for the reason the `Ref:` bullet gives. `Ref:` is
752
+ what the rendered LINKS name; `Commit:` is where the BYTES came from.
753
+ - Previous: `v0.15.0-rc1` (release tag) / **`996937ac`** — the seven READMEs
754
+ described in the entry below. History below.
755
+ - Previous: **v0.14.0 (`ef79eab0`)** — the release tag, re-vendored for tan
756
+ v0.4.1. Seven `README.md` files moved (`diagnostics`, `minimal` and `sensor`
757
+ for both SKUs, plus `iot`/E1M-AEN801), and NOTHING else: all 40 changed
758
+ lines differ only by the doc-version link `blob/v0.13.0/` → `blob/v0.14.0/`,
759
+ verified line-for-line. No schema, core, peripheral or `board.yaml` content
760
+ changed. `edge-ai` was untouched for both SKUs at this vendor point because
761
+ its README carried no version-pinned links at all — no longer true: the
762
+ current vendor point's `## Model`/`## Tests` rewrite (entry 4 under
763
+ "Deliberate edits on top of the emit") adds four, pinned at `v0.16.0`.
764
+ - Re-vendored by re-running the live emit through
765
+ `tests/parity/scaffold_byte_parity.py`'s OWN `emit_live_scaffold`, so the
766
+ bytes written are by construction the bytes that gate compares against —
767
+ not a hand-substitution that happens to match. Run from WSL: the emit needs
768
+ a Linux host, and the gate cannot be checked on Windows at all.
769
+ - Prior vendor point: `cdfe13684e362c75f6df2b190ec1c3e736c48731` —
770
+ alp-sdk#1016, which rewrote the `Customer workflow:` header from "copy this
771
+ directory … and `west build`" to "`tan init --from-example
772
+ <category>/<name>` … and `tan build`" (ADR-0020: tan is the whole command
773
+ surface) in the example `board.yaml` files it touched. Six VENDORED
774
+ `board.yaml` files moved here as a result; comment-only. (Upstream #1016
775
+ touched 33 example files — the six is this tree's count, not its.)
776
+
777
+ NOT every example: #1016 left four `Customer workflow:` headers untouched
778
+ upstream, and one of them is vendored here —
779
+ `examples/connectivity/mqtt-telemetry/board.yaml`, whose copy at
780
+ `iot/E1M-AEN801/board.yaml:21-23` still says `west build`, byte-faithful
781
+ to a file still un-rewritten on alp-sdk `origin/dev` and `origin/main`.
782
+ It is the only one of the four that is a Zephyr `west build` flow.
783
+
784
+ It is NOT the only vendored text routing a customer around `tan`: all NINE
785
+ vendored `README.md` files document `west build`/`west flash` and none
786
+ mentions `tan` (measured). The `board.yaml` comment is the smaller half.
787
+ Fixing either is an alp-sdk change plus a re-vendor — a hand-edit here is
788
+ possible via `scaffold_byte_parity.py`'s `DELIBERATE_EDITS`, but it books
789
+ a standing divergence somebody unwinds when upstream lands. This is (c2)
790
+ of tan-cli#821, which closes without fixing it (the PR fixes (a) only);
791
+ filed upstream as alp-sdk#1689 (not yet landed — no
792
+ `DELIBERATE_EDITS` entry here for it, per the same reasoning entry 4 in
793
+ "Deliberate edits on top of the emit" gives for `edge-ai`'s pointers: this
794
+ is the smaller half of a wider `west`-vs-`tan` gap every README already
795
+ shares, not a defect isolated enough to book a standing divergence for
796
+ on its own).
797
+ - Previous: **v0.13.0 (`93ef5726`)** — `minimal` was re-vendored at this commit
798
+ (only its `README.md` doc-version link changed, `v0.11.1` -> `v0.13.0`; the
799
+ scaffold content itself is unchanged since the `a0849e10` vendor point
800
+ below). `sensor` and `edge-ai` were vendored fresh at this same commit --
801
+ `edge-ai` supersedes the `edge-ai-starter` flag this manifest carried
802
+ before v0.13.0 (see "Template-id mapping" below).
803
+ - Prior vendor point: `a0849e10` (`feat(build-plan): --emit scaffold
804
+ derives cores per SKU + adapts scaffold content (#864) (#877)`) —
805
+ re-vendored from this commit (tan-cli#25 had vendored `75ef3b02`, before
806
+ #877 fixed `--emit scaffold` deriving the wrong, non-buildable Alif
807
+ `m55_hp` core for every SKU including `E1M-V2N101`; see "App-core
808
+ disagreement" below).
809
+ - **`diagnostics`/`iot` vendor point:** commit
810
+ `0ed078a6d04f4072ab00d8ce92cb2ac82f7adcbc` — an `alp-sdk` `dev` commit, 31
811
+ commits past `v0.13.0`, pinned as `tan-cli`'s own
812
+ `.github/workflows/parity.yml` `PINNED_SDK_TAG` at vendor time. alp-sdk#903
813
+ (closed) added both catalog entries; vendored at this exact SHA (rather
814
+ than a later `dev` tip) so the checked-in bytes match what `parity.yml`'s
815
+ own `scaffold_byte_parity.py` step already re-verifies on every PR.
816
+ - Command: `PYTHONPATH=$SDK/scripts python3 scripts/alp_project.py --emit
817
+ scaffold --template <id> --sku <SKU>`
818
+
819
+ ### Deliberate edits on top of the emit
820
+
821
+ The rule above ("re-vendored by re-running the emit, not by editing these
822
+ files") has standing exceptions, each because the emit's own output is wrong
823
+ for a customer and the fix lives in alp-sdk, not here. Each is a real diff
824
+ `tests/parity/scaffold_byte_parity.py` would otherwise report, and each
825
+ disappears on its own the moment alp-sdk fixes it and this tree is
826
+ re-vendored — nothing here needs unwinding by hand.
827
+
828
+ The `DELIBERATE_EDITS` table below currently carries **fifty-one** live entries
829
+ (counted from `scaffold_byte_parity.py`'s own `DELIBERATE_EDITS`
830
+ dict, not by hand). The `34c11c9de` re-vendor retired fourteen of the
831
+ forty-one below — every `workflow_pointer` entry — and added twenty-four
832
+ `alp_conf_pregeneration` entries (see the "Current vendor point" bullet
833
+ above), so the itemised walk that follows is the HISTORY of how the table
834
+ reached sixty-two and then forty-one, not today's contents:
835
+ one `multicore-mailbox`/`E1M-AEN801` entry for the
836
+ leading "blocked ahead" caveat (tan-cli#864 Q5, see entry 9 below), the
837
+ `iot` CMakeLists edit (entry 2), two `iot`/`E1M-AEN801` `README.md` entries
838
+ for the `native_sim.conf` link and "copy it in first" comment (tan-cli#1001
839
+ review, see entries 13/14 below), one `edge-ai`/`E1M-AEN801` `board.yaml`
840
+ entry and one `edge-ai`/`E1M-AEN801` `README.md` entry for the DEEPX
841
+ retarget comment/sentence pair (tan-cli#814, re-anchored tan-cli#1001
842
+ review, see entries 5/6 below), two `sensor`/`src/main.c` entries (one per
843
+ SKU) for the header-comment `Contrasts with... i2c-scanner` paragraph
844
+ (tan-cli#924, see entry 11 below), six more for
845
+ `diagnostics`/`E1M-V2N101`'s Alif/AEN801-shaped `src/main.c`/`README.md`
846
+ bytes (tan-cli#932, see entry 8 below), twenty-four more for the
847
+ remaining bare cross-repo referents PR #975's review round scoped out of
848
+ that PR, **re-derived against this same tan-cli#996/#1001 re-vendor**
849
+ (tan-cli#977, see entry 15 below) — two SKUs each across `sensor`'s
850
+ `src/main.c` (five substitutions: the `Hardware:` paragraph, the `#1269`
851
+ historical note, the `BMP581_ADDR_7BIT` doc-comment's two referents, and
852
+ the `bmp581_init` failure doc-comment), `sensor`'s `board.yaml` (five
853
+ substitutions: the Customer-workflow paragraph, the chip-classification
854
+ paragraph's two referents, and the `#1269` historical NOTE's two
855
+ referents), `sensor`'s `prj.conf` (one substitution), and `minimal`'s
856
+ `README.md` (one substitution, the template's first `DELIBERATE_EDITS`
857
+ entry), twenty-one more for the sibling sites entry 15's own sweep
858
+ stopped one template short of (tan-cli#1009, PR #1009's round-two review,
859
+ see entry 16 below) across `minimal`/`diagnostics` `board.yaml` (four),
860
+ `minimal` `prj.conf` (two), `diagnostics` `prj.conf` (two), `iot`
861
+ `board.yaml` (one), `iot` `prj.conf` (two), `iot` `src/main.c` (two),
862
+ `edge-ai` `src/main.c` (four), and `multicore-mailbox` `board.yaml` (one),
863
+ native_sim overlay (one), `prj.conf` (one), and `src/main.c` (one), and
864
+ three more the round-two sweep itself missed (tan-cli#1009, PR #1009's
865
+ round-three review, see entry 17 below): `multicore-mailbox`'s
866
+ `board.yaml` (a second bare referent, three lines from the one entry 16
867
+ already qualified), and two `README.md` sites (a link-label mislabel
868
+ byte-for-byte matching entry 16's `src/main.c` fix, and a `west build`
869
+ command that names a path that does not exist in a scaffolded project).
870
+ That is `1 + 1 + 2 + 2 + 2 + 6 + 24 + 21 + 3 = 62` -- the table as it stood
871
+ at tan-cli#1009, which is what the derivation above walks. tan-cli#1151's
872
+ `ff27f179` re-vendor then retired **seventeen** of those (enumerated under
873
+ "Current vendor point" above), leaving the **forty-five** this section opens
874
+ with. `test_the_manifest_deliberate_edit_count_matches_the_table` pins that
875
+ forty-five -- not the sixty-two derived here -- against
876
+ `len(DELIBERATE_EDITS)` itself, the drift this paragraph shipped with nothing
877
+ catching before tan-cli#932. tan-cli#384's
878
+ seven `README.md` doc-link entries
879
+ are NOT among them — they were RETIRED when alp-sdk cut the real `v0.15.0`
880
+ tag (see "Current vendor point" above); listed here only as history, not as
881
+ a current exception. tan-cli#501's four `sensor`/`diagnostics` CMakeLists
882
+ edits were REVERTED (see entry 3 below) — the theory behind them measured
883
+ false, so those four files carry no deliberate edit at all now.
884
+
885
+ **tan-cli#1275 (the `c81cb5db` re-vendor) retired four more entries**,
886
+ forty-five down to forty-one, taking 0 `un_edit_*` functions with it (each
887
+ retired transform's mechanics-only round-trip proof is kept in
888
+ `scaffold_byte_parity.py`'s `self_check()`, the same treatment
889
+ `un_edit_doc_link_ref` already got when ITS own registration retired).
890
+ Retired: `iot`/`E1M-AEN801`'s `extra_conf_order` (`CMakeLists.txt`, entry 2
891
+ above) and its `README.md` `native_sim_conf_link`/`native_sim_conf_copy_
892
+ comment` pair (entries 13/14 above) — all three qualified or reordered
893
+ `native_sim.conf`-referencing content that alp-sdk#2173 deleted outright
894
+ when it fixed the mbedtls PSA-crypto break at its root, so `mqtt-telemetry`
895
+ no longer ships (or needs) a `native_sim.conf` at all; and `iot`/
896
+ `E1M-AEN801`'s `prj.conf` `fleet_ota_pointer` (the migration-note paragraph
897
+ it qualified was replaced wholesale by alp-sdk#2173's new paragraph). See
898
+ the "Current vendor point" bullet above for the full re-vendor accounting,
899
+ including a KNOWN UPSTREAM GAP flagged there (not fixed by this change):
900
+ alp-sdk's own `metadata/templates/catalog-v1.json` `iot` entry was never
901
+ updated for the `cc3501e_bridge.{c,h}` dependency alp-sdk#2112 added.
902
+
903
+ **tan-cli#1151 (the `ff27f179` re-vendor) retired seventeen entries in one
904
+ change**, sixty-two down to forty-five, taking 11 `un_edit_*` functions and
905
+ 22 constants with them. Unlike the `722320a1` round below, these did not go
906
+ stale from alp-sdk rewording the surrounding prose: alp-sdk#1855 makes the
907
+ EMIT ITSELF do what each of these entries was doing by hand — rewrite a bare
908
+ `docs/*.md` / `examples/<category>/<name>[/<subpath>]` referent in a
909
+ `board.yaml` or `src/*.c` comment into an absolute GitHub URL. Upstream took
910
+ over their job. `multicore-mailbox`'s `peer_build_path` (tan-cli#1009) is
911
+ the clearest case: tan's hand-fix and alp-sdk's produce **byte-identical**
912
+ output (`./peer`), so that file needed no re-vendor at all — only the
913
+ now-redundant entry dropped.
914
+
915
+ Retired: `edge-ai` `model_readme_pointer_1`/`_2` (both SKUs), `iot`
916
+ `cc3501e_bridge_pointer` (`board.yaml` + `src/main.c`) and
917
+ `sensor_template_pointer`, `multicore-mailbox` `peer_build_path` and
918
+ `peer_main_pointer`, `sensor` `historical_note_v2n_pointer` (`board.yaml` +
919
+ `src/main.c`, both SKUs), `init_fail_scanner_pointer` (both SKUs) and
920
+ `pattern_paragraph` (both SKUs) — that last being tan-cli#924's final live
921
+ entry, so tan-cli#924 now has none.
922
+
923
+ **Two entries were flagged by the same self-check and deliberately NOT
924
+ retired**, which is why this round was walked per entry instead of
925
+ batch-dropped. `sensor`'s `historical_note_boards_pointer` (both SKUs) lives
926
+ in the same re-flowed `NOTE (#1269):` paragraph as the retired
927
+ `historical_note_v2n_pointer`, so taking upstream's whole hunk dropped it
928
+ silently — but what it qualifies is a bare `metadata/boards/e1m-evk.yaml` /
929
+ `e1m-x-evk.yaml` mention, and alp-sdk#1855 leaves `metadata/` and `scripts/`
930
+ alone on purpose (its own `_BARE_REPO_PATH_RE` comment says why). Upstream
931
+ did not take over that job, so the edit was RE-ANCHORED and re-applied on top
932
+ of the new emit. Nineteen flagged, seventeen retired, two re-anchored.
933
+
934
+ Re-anchoring was not optional, and the first attempt at it shipped a real
935
+ defect worth recording: the entry's old span ran to `# ... (0x47) on each --`
936
+ and handed the rest of the paragraph to `historical_note_v2n_pointer`, the
937
+ sibling this round retires. Re-applied unchanged, its `--\n` tail landed in
938
+ front of the emit's own ` see\n# https://...` continuation and orphaned a
939
+ bare `see` line with no `# ` prefix — making the vendored `board.yaml`
940
+ **invalid YAML** (`yaml.safe_load` → `ScannerError: could not find expected
941
+ ':'`, line 52). `tests/commands/test_validate_command.py::
942
+ test_offline_accepts_every_board_yaml_the_templates_ship` caught it. The
943
+ entry is now narrowed to just the two `metadata/boards/*.yaml` mentions it
944
+ exists to qualify, ending mid-sentence so the tail stays passthrough.
945
+
946
+ **tan-cli#996/#1001 (the `722320a1` re-vendor) retired nineteen entries in
947
+ one change** — entries 4, 7, 10, 11 and 12 below (`## Model`/`## Tests`
948
+ pointers x6, the eeprom/
949
+ i2c-scanner README bullets x4, the `E1M-V2N101` DEEPX-scope sentence x1,
950
+ `sensor`'s four-more-`i2c-scanner` substitutions x8, and the `Hardware:`
951
+ paragraph x2 — thirty-one minus twelve is nineteen; the dict landed at
952
+ twelve, not eleven, because the tan-cli#1001 review round below re-anchored
953
+ one of the twenty originally-retired entries rather than leaving it retired,
954
+ and then added two more). Each entry's declared `EDITED` anchor stopped
955
+ matching a fresh `--emit scaffold` at `722320a1` verbatim: alp-sdk reworded
956
+ the surrounding prose in every case, most from
957
+ `examples/peripheral-io/i2c-master`'s own alp-sdk#1269 fix (the `sensor`
958
+ template's underlying example swapped its target chip, TMP112 -> BMP581) or
959
+ from real doc-link/units-test-pointer improvements alp-sdk made to
960
+ `examples/ai/cold-chain-monitor` and the `diagnostics`/`sensor` README
961
+ Troubleshooting sections on its own. Per this section's own strict-in-both-
962
+ directions rule below, an anchor that no longer matches is a hard failure,
963
+ not a quiet pass, so each entry was individually re-examined against the new
964
+ bytes rather than blanket-dropped: `pattern_paragraph` (part of entry 11)
965
+ still matched byte-for-byte and was kept, reapplied forward, and re-numbered
966
+ into entry 11's own remaining half; every other entry's anchor was gone
967
+ (the underlying prose reworded past it, or in one case -- entry 5/6's README
968
+ half -- reworded to state the SAME defect in different words, not healed at
969
+ all) and was either retired or, for that one, RE-ANCHORED on the new wording
970
+ rather than hand-reconstructed against a paraphrase. Retiring the other
971
+ nineteen is not asserting every one is fully healed -- some (`hardware_
972
+ paragraph` x2, `init_fail_comment` x2) are anchor-gone, not healed: their
973
+ specific matched string is simply gone, replaced by a new string with the
974
+ identical underlying defect, and reconstructing a correction against it is
975
+ out of scope for a pin-bump alone (see `scaffold_byte_parity.py`'s
976
+ `DELIBERATE_EDITS` block comment, which now names these four explicitly as
977
+ a separate "anchor-gone, not healed" category rather than folding them into
978
+ either/or language — tan-cli#1001 review). The rest (`bringup_instruction`
979
+ x2, `failure_modes_instruction` x2, `i2c_scanner_bullet` x2,
980
+ `eeprom_script_pointer` x2, `model_tests_pointers` x2,
981
+ `model_comment_1`/`model_comment_2` x4, `deepx_v2m102_scope`) genuinely are
982
+ healed: the customer-facing gap the retired edit closed is now closed by
983
+ alp-sdk's own 722320a1 prose, verified per entry against the upstream diff.
984
+ The full per-entry reasoning lives in `scaffold_byte_parity.py`'s
985
+ `DELIBERATE_EDITS` dict, in the block comment directly above the surviving
986
+ entries.
987
+
988
+ **Each is DECLARED to that gate, in `scaffold_byte_parity.py`'s
989
+ `DELIBERATE_EDITS`, and the declaration is strict in both directions.** An
990
+ entry is not a path-level allow-list: it carries an `un_edit` that maps these
991
+ bytes back onto what the emit is expected to say, and the byte-diff runs
992
+ against THAT — so an unrelated change in the same file still fails the gate,
993
+ and a declared edit that finds nothing to undo (this tree re-vendored, or
994
+ alp-sdk fixing its emit) ALSO fails, forcing the entry out instead of leaving
995
+ a dead excuse behind. That is the same `xfail(strict=True)` discipline
996
+ `python/tests/parity/test_scaffold_content_oracle_parity.py` used on the
997
+ port-vs-oracle axis, before tan-cli#269 deleted that axis with the oracle.
998
+ Editing this section without editing that table (or the reverse) is what the
999
+ strictness exists to catch.
1000
+
1001
+ 1. **(RETIRED, history only) Doc-link ref, all seven `README.md` files (tan-cli#384).** The emit
1002
+ renders cross-directory links as `github.com/alplabai/alp-sdk/blob/v<SDK
1003
+ VERSION>/…`, and this tree is vendored from a PRE-RELEASE
1004
+ (`v0.15.0-rc1`) — so it emitted 40 links to `v0.15.0`, a tag alp-sdk has
1005
+ never cut. Every one 404s in a scaffolded project. Rewritten to
1006
+ `v0.15.0-rc1`, the exact ref these bytes come from; all 14 distinct link
1007
+ targets verified present at that ref (`git cat-file -e v0.15.0-rc1:<path>`
1008
+ for each, 14/14 OK).
1009
+ `python/tests/core/test_template_integrity.py` holds both halves: one test
1010
+ keeps the links and the `- Ref:` line above in step, and
1011
+ `test_the_vendored_ref_is_a_tag_alp_sdk_actually_has` asks GitHub whether
1012
+ that ref EXISTS — consistency is not existence, and the two agreed on
1013
+ `v0.15.0` while all 40 links were dead. It queries the EXACT-ref endpoint
1014
+ (`/git/ref/tags/`, singular) and carries a permanent negative control,
1015
+ because the plural `/git/refs/tags/` prefix-matches and answers 200 for
1016
+ `v0.15.0` with `refs/tags/v0.15.0-rc1` — a gate built on it calls every
1017
+ dead link healthy.
1018
+ 2. **`iot`'s `CMakeLists.txt`: `list(PREPEND …)`, not `APPEND` (tan-cli#379).**
1019
+ Zephyr merges `EXTRA_CONF_FILE` in list order, last assignment wins.
1020
+ Appending the generated `alp.conf` put it AFTER a caller's own
1021
+ `-DEXTRA_CONF_FILE=native_sim.conf` (measured: `native_sim.conf;<build-dir>/
1022
+ generated/alp.conf`), so the emitted `CONFIG_MBEDTLS=y` overrode the very
1023
+ overlay that exists to turn it off — the README's documented native_sim
1024
+ build and `testcase.yaml`'s `extra_args` were both no-ops. Prepending keeps
1025
+ `alp.conf` winning over `prj.conf` (the whole list merges after it) and
1026
+ lets an explicit caller overlay win. alp-sdk's own
1027
+ `examples/connectivity/mqtt-telemetry/CMakeLists.txt` still appends;
1028
+ the other two templates without any vendored native_sim conf (`minimal`,
1029
+ `edge-ai`) are left as emitted (neither ships an overlay or documents a
1030
+ `-DEXTRA_CONF_FILE=` build).
1031
+ 3. **(REVERTED, history only) `sensor`/`diagnostics` `CMakeLists.txt`, both
1032
+ SKUs: `list(PREPEND …)` (tan-cli#501), on the theory that it was the same
1033
+ defect class as #2.** It is not: `boards/native_sim_native_64.conf` (the
1034
+ pair these four trees gained in the same change) joins Zephyr's
1035
+ `CONF_FILE`, not `EXTRA_CONF_FILE` -- Zephyr auto-discovers a board's own
1036
+ `boards/<board>.conf` regardless of `EXTRA_CONF_FILE` order, and
1037
+ `CONF_FILE_AS_LIST` merges strictly before `EXTRA_CONF_FILE_AS_LIST`
1038
+ either way. MEASURED with a real CMake configure against Zephyr v4.4.1:
1039
+ PREPEND and APPEND produced the identical merge order and identical
1040
+ `.config` (`CONFIG_EMUL=y`, `CONFIG_I2C_EMUL=y` both ways). Reverted to
1041
+ plain `list(APPEND …)`, which is what `--emit scaffold` produces unedited
1042
+ for these four files, so they carry no deliberate edit anymore and this
1043
+ entry is not live.
1044
+ 4. **(RETIRED at tan-cli#996/#1001, history only) `edge-ai`'s `## Model`/`## Tests` README sections and matching
1045
+ `src/main.c` comments, both SKUs (tan-cli#821(a)).** The emit points a
1046
+ customer at `models/README.md` and `twister ... -T tests/unit/cold_chain`
1047
+ — real paths only in the alp-sdk checkout the text was captured from,
1048
+ never emitted into any scaffolded project (`_vendored_files` in
1049
+ `tan/core/scaffold.py` reads nothing outside `vendored/edge-ai/<sku>/`).
1050
+ Both referents are a bare inline code span / bare twister argument rather
1051
+ than a markdown link, so the emit's own doc-link rewriter (which only
1052
+ rewrites `[text](https://github.com/alplabai/alp-sdk/blob/<ref>/...)`
1053
+ links) never touches them, same as most cross-repo references this tree
1054
+ carries, which ARE real markdown links the rewriter already covers. Not
1055
+ the only bare ones, though: `diagnostics`'s `README.md` names a bare
1056
+ `scripts/program_eeprom.py` and `sensor`'s a bare
1057
+ `examples/peripheral-io/i2c-scanner`, the same defect class, on both
1058
+ SKUs, shipping to every `tan init` of those two templates. Tracked
1059
+ separately as tan-cli#912 and fixed there — see entry 7 below, not here
1060
+ (this entry is (a) of tan-cli#821 only). Rewritten to a
1061
+ real link (`README.md`, pinned at `v0.16.0` per the current vendor point's
1062
+ `- Ref:`) or a named alp-sdk path (`src/main.c`, a C comment — no markdown
1063
+ rewriter applies there either way), each noting the referent is not part
1064
+ of this scaffolded project. The new `README.md` link is version-pinned
1065
+ like every other cross-repo link this tree carries, so `edge-ai` now
1066
+ COUPLES to the tree's own `- Ref:` where before it carried zero
1067
+ version-pinned links at all (see `MANIFEST.md`'s v0.14.0 vendor-point
1068
+ bullet above, now corrected). MEASURED: bumping `- Ref:` alone to
1069
+ `v0.17.0` (leaving the vendored bytes at `v0.16.0`) reds
1070
+ `test_every_alp_sdk_link_pins_the_ref_the_tree_is_vendored_from[edge-ai-starter::E1M-AEN801]`
1071
+ and `[::E1M-V2N101]` in `python/tests/core/test_template_integrity.py`
1072
+ (9 failed, 2 passed, where a clean tree is 11/11) exactly like the seven
1073
+ READMEs that already carried links — so a re-vendor that updates one half
1074
+ and not the other is caught, not silent. This is the same class of
1075
+ standing exception as entry 2 above, not a new mechanism: the emit's own
1076
+ output is wrong for a customer, and the real fix — turning the two bare
1077
+ referents into real alp-sdk markdown links so the rewriter and
1078
+ `test_template_integrity.py`'s ref-consistency check cover them going
1079
+ forward — lives in alp-sdk's `examples/ai/cold-chain-monitor/README.md`
1080
+ and `src/main.c`, not here. Filed upstream as alp-sdk#1688; this entry
1081
+ retires the moment that lands and this tree is re-vendored.
1082
+ 5. **`edge-ai`/`E1M-AEN801`'s `README.md`: the DEEPX DX-M1 retarget sentence
1083
+ (tan-cli#814, re-anchored tan-cli#1001 review after a false retirement).**
1084
+ The original tan-cli#814 emit said "Flip `som.sku` in `board.yaml` to
1085
+ `E1M-V2M101` for the DEEPX DX-M1 path" — correct on the `E1M-V2N101`
1086
+ sibling (V2N101/V2M101 share one PCB, so every other field the flip
1087
+ leaves untouched already matches), but wrong here: `E1M-AEN801`'s
1088
+ `board.yaml` pins `preset: e1m-evk` (which `metadata/boards/e1m-evk.yaml`
1089
+ hosts only `alif-ensemble`, not V2M101's
1090
+ `renesas-rzv2n-deepx`), plus Alif-shaped `cores:`/`pins:`. MEASURED against
1091
+ `v0.16.0-rc1` and re-measured against the GA `v0.16.0` tag (identical
1092
+ result): performing exactly the documented edit and nothing else,
1093
+ `tan validate` refuses on ALP-B007 (board/family mismatch) -- and keeps
1094
+ refusing as each message is fixed forward, through unknown `cores:` ids,
1095
+ a `libraries:` entry scoped to a core the flip left undeclared, a `pins:`
1096
+ route absent from the resolved board, and a pad macro that does not match
1097
+ the resolved pad. No count is pinned here on purpose: an earlier revision
1098
+ of this entry said "three times in a row" and a re-measurement found five,
1099
+ because the cascade depends on how far the customer patches forward. The
1100
+ load-bearing fact is that it does not terminate in a working project, not
1101
+ how many messages it takes.
1102
+
1103
+ **tan-cli#996/#1001's `722320a1` re-vendor retired this entry on the
1104
+ theory that alp-sdk's rewording healed it — that was wrong.** The new
1105
+ emit's sentence, "The DEEPX DX-M1 NPU is populated on `E1M-V2M101`/
1106
+ `E1M-V2M102` ... Pick either via `som.sku` in `board.yaml`", makes no
1107
+ mention of a target SKU at all, but the underlying defect is unchanged:
1108
+ `tan init --template edge-ai-starter --som E1M-AEN801`, flip `som.sku` to
1109
+ `E1M-V2M101`, `tan validate` still refuses with the identical ALP-B007
1110
+ (`board preset 'e1m-evk' hosts SoM families ['alif-ensemble'],
1111
+ but E1M-V2M101 is family 'renesas-rzv2n-deepx'`) -- re-measured at
1112
+ `722320a1` in the tan-cli#1001 review round. Worse, retiring only the
1113
+ README half left `board.yaml`'s own comment (entry 6 below, untouched by
1114
+ the re-vendor) contradicting it in the same scaffold. Re-anchored on the
1115
+ new sentence rather than retired: rewritten, same as before, to tell the
1116
+ customer to re-scaffold (`tan init --template edge-ai-starter --som
1117
+ E1M-V2M101`) instead of flipping `som.sku` in place. Fix belongs
1118
+ upstream, in alp-sdk's `examples/ai/cold-chain-monitor/README.md`;
1119
+ declared here pending that fix and a re-vendor.
1120
+ 6. **`edge-ai`/`E1M-AEN801`'s `board.yaml`: the comment reinforcing the same
1121
+ sentence (tan-cli#814).** "Same source targets the V2N DEEPX path when
1122
+ som.sku is flipped" is the comment-form of entry 5's defect, one file
1123
+ over. Reworded to point at the re-scaffold instead.
1124
+ 7. **(RETIRED at tan-cli#996/#1001, history only) `diagnostics`'s README `program_eeprom.py` bullet and `sensor`'s README
1125
+ `i2c-scanner` bullet, both SKUs (tan-cli#912).** Entry 4 above named these
1126
+ two as the same defect class as `edge-ai`'s bare pointers, tracked
1127
+ separately (same `- Ref:` coupling as entry 4 — no new coupling created;
1128
+ both trees already carried v0.16.0-pinned links): `diagnostics`'s
1129
+ Troubleshooting section names a bare `scripts/program_eeprom.py`, and
1130
+ `sensor`'s names a bare `examples/peripheral-io/i2c-scanner` in one bullet
1131
+ (the SAME README already carries two real markdown links to that identical
1132
+ referent elsewhere — this is the only bare instance in this README, not a
1133
+ novel shape; at the time this entry was written the wider `sensor`
1134
+ scaffold still shipped several more bare `i2c-scanner` referents outside
1135
+ this README — `src/main.c`'s four are now covered by entry 11 below
1136
+ (tan-cli#924); `board.yaml` and `testcase.yaml` carry one bare mention
1137
+ each, descriptive/contrastive prose rather than run-this instructions,
1138
+ and remain untracked here). Both scripts/examples are real in the alp-sdk
1139
+ checkout the text was captured from, never emitted into any scaffolded
1140
+ project (`_vendored_files` in `tan/core/scaffold.py` reaches nothing
1141
+ outside `vendored/<template>/<sku>/`). Each is a bare inline code span, so
1142
+ the emit's own doc-link rewriter never touches it, same as entry 4's
1143
+ `edge-ai` pair. Byte-identical between the two SKUs for `sensor` (only the
1144
+ `west build`/`west flash` block elsewhere in the README differs by SKU);
1145
+ for `diagnostics` the two SKUs also differ in the "Real hardware" heading
1146
+ and the `[selftest] SoM identity` line elsewhere in the README, in
1147
+ addition to the build/flash block — none of that touches the matched
1148
+ region here, so each template's two SKU entries still share one
1149
+ `un_edit`. Rewritten to a real link, pinned at `v0.16.0` per the current
1150
+ vendor point's `- Ref:`, each noting the referent is not part of this
1151
+ scaffolded project; verified the replacement text holds with no alp-sdk
1152
+ checkout present at all (`alplabai/alp-sdk` is public, `isPrivate: false`,
1153
+ and both blob/tree URLs return HTTP 200 at this ref — checked directly,
1154
+ not assumed, after a prior PR in this same series asserted the opposite of
1155
+ a link and was corrected in review). Fix belongs upstream, in alp-sdk's
1156
+ `examples/bringup/board-selftest/README.md` and
1157
+ `examples/peripheral-io/i2c-master/README.md`; filed as alp-sdk#1705, and
1158
+ this entry retires the moment that lands and this tree is re-vendored.
1159
+ (Status as of tan-cli#924: alp-sdk#1705's `i2c-master/README.md` half
1160
+ landed upstream 2026-08-28 via alp-sdk#1792 — this entry stays live only
1161
+ because the vendored tree is still pinned at `v0.16.0`, pre-fix; the next
1162
+ re-vendor retires it. A comment on #1705 also extended it to
1163
+ `i2c-master`'s `src/main.c`/`board.yaml`/`testcase.yaml`, but #1792's
1164
+ `Closes #1705` auto-closed the issue without covering that extension —
1165
+ re-filed as alp-sdk#1795, see entry 11 below for the `src/main.c` half.)
1166
+ 8. **`diagnostics`/`E1M-V2N101`'s `src/main.c` and `README.md`: Alif SoC
1167
+ identity + an AEN801-shaped SoM SKU/serial on a Renesas RZ/V2N scaffold
1168
+ (tan-cli#932).** The emit's per-SKU substitution never reaches
1169
+ `src/main.c` at all — it is byte-identical to `E1M-AEN801`'s, still
1170
+ naming that Alif module in its own "what success looks like" comment —
1171
+ and reaches `README.md` only on the SoM SKU line, leaving the serial
1172
+ beside it and both `SoC identity:` lines at their AEN801/Alif values. A
1173
+ customer running the selftest on real V2N101 hardware sees output that
1174
+ contradicts what the scaffold told them to expect, and the natural
1175
+ reading of that mismatch is "my board failed", not "the README is
1176
+ wrong". Six entries, not one: the SoM SKU fix in `src/main.c` (its own
1177
+ entry per occurrence — the header comment and the sample-output line are
1178
+ independent locations), a placeholder-serial fix shared by both files
1179
+ (one entry per file, since `edit_id` keys on `path`), and a SoC-identity
1180
+ fix shared by both files (`README.md`'s carries two occurrences of the
1181
+ same wrong token, undone by one `.replace()` call same as entry 1's
1182
+ `un_edit_doc_link_ref`). The SoC identity string is not hand-written to
1183
+ match the Alif shape: it is `renesas:rzv2n:n44`,
1184
+ `metadata/socs/renesas/rzv2n/n44.json`'s own `ref` field (what
1185
+ `scripts/gen_soc_caps.py` bakes into `ALP_SOC_REF_STR`) and the same
1186
+ value `metadata/e1m_modules/E1M-V2N101.yaml`'s `silicon:` key names for
1187
+ this SKU — `n44`, not the `n48gbg` this same README's own (correct,
1188
+ untouched) `west build -b alp_e1m_v2n101_m33_sm/r9a09g056n48gbg/cm33`
1189
+ line names two lines below it, because that is only the
1190
+ `zephyr_soc_variant` Zephyr's own Kconfig/DT layer references in place of
1191
+ the SoC json's `ref` (the SoC json documents the n44/n48 delta as
1192
+ GPU/ISP/crypto fusing only, devicetree-identical for the peripherals this
1193
+ SDK targets) — not a contradiction, two different identifiers for the
1194
+ same silicon. The serial is not a plausible-looking invented value in
1195
+ AEN801's shape either (e.g. `V2N0000123`, which would just repeat the
1196
+ same mistake with a different prefix): alp-sdk's own
1197
+ `scripts/program_eeprom.py --serial` help text uses a completely
1198
+ different shape (`2026W19-0001`), proving `AEN0000123` was never a
1199
+ schema-driven format, just this one example's flavour text — so the fix
1200
+ reuses `<factory-serial>`, the README's own angle-bracket placeholder
1201
+ convention already sitting two lines above it (`west flash --host
1202
+ <board-ip>`), obviously a placeholder rather than a serial a customer
1203
+ might mistake for real. Fix belongs upstream, in alp-sdk's
1204
+ `examples/bringup/board-selftest/src/main.c` and `README.md`, whose
1205
+ per-SKU substitution machinery is what needs to reach these tokens;
1206
+ declared here pending that fix and a re-vendor.
1207
+ 9. **`multicore-mailbox`/`E1M-AEN801`'s `README.md`: a leading caveat that
1208
+ the scaffold's own IPC carve-out resolves `blocked` (tan-cli#864 Q5).**
1209
+ Measured: `alp_shmem0`'s `memory_map.base` comes back `TBD` for region
1210
+ `mram_main` on this SKU, so the mailbox roundtrip the scaffold teaches
1211
+ compiles and does nothing — a customer's first run is silent, and the
1212
+ emit's own text says nothing about it. Rewritten with a leading "## Before
1213
+ you run this: the channel is not allocated yet" section explaining the
1214
+ `blocked` status up front rather than letting the customer discover it by
1215
+ running a no-op. `un_edit_mailbox_blocked_caveat` strips that whole
1216
+ section (plus the blank line it opens with, appended after the emit's own
1217
+ trailing newline) to recover the emit's own bytes. This is the one entry
1218
+ in the table with no matching alp-sdk issue filed pending a re-vendor —
1219
+ the carve-out `blocked`/`TBD` state is a hardware fact about this SKU's
1220
+ memory map, not something alp-sdk's own emit is wrong to omit generically;
1221
+ the caveat stays a standing tan-side edit rather than an upstream fix to
1222
+ wait on.
1223
+ 10. **(RETIRED at tan-cli#996/#1001, history only) `edge-ai`/`E1M-V2N101`'s `README.md`: the same DEEPX retarget
1224
+ sentence as entries 5/6, but its OWN, narrower defect (tan-cli#946,
1225
+ review round on #932/#942).** `E1M-V2N101`/`E1M-V2N102`/`E1M-V2M101`/
1226
+ `E1M-V2M102` all render this one tree, and unlike the `E1M-AEN801`
1227
+ tree's problem (a target family the `e1m-x-evk` preset does not host
1228
+ at all), "Flip `som.sku` in `board.yaml` to `E1M-V2M101` for the DEEPX
1229
+ DX-M1 path" IS legal here: `E1M-V2N101` is family `renesas-rzv2n` and
1230
+ `E1M-V2M101` is family `renesas-rzv2n-deepx` -- two DIFFERENT
1231
+ families, not one -- but `e1m-x-evk`'s `hosts_som_families` lists both,
1232
+ so the flip is a legal cross-family swap on THIS preset, correct in
1233
+ shape for a V2N101/V2N102 customer ("intra-family" is not the right
1234
+ word for it -- neither alp-sdk board.yaml nor this repo uses that
1235
+ vocabulary; the preset simply hosts more than one family). It is still
1236
+ wrong for a V2M102 one: `metadata/socs/deepx/dx/m1.json`'s
1237
+ `alp_module_skus` lists BOTH `E1M-V2M101` and `E1M-V2M102` (confirmed:
1238
+ `E1M-V2M102.yaml`'s own `on_module.npu: deepx_dxm1`), so a V2M102
1239
+ customer reading this line is told to abandon their own DEEPX-equipped
1240
+ module for a sibling SKU no more DEEPX-equipped than the one they
1241
+ already have. Discovered widening
1242
+ `test_no_planned_file_names_a_different_skus_exact_token`'s foreign-SKU
1243
+ list off the full SDK catalogue instead of the two vendored trees'
1244
+ representative SKUs alone (tan-cli#946): the widened guard flagged this
1245
+ sentence for every non-source SKU sharing the tree, and unlike the
1246
+ other (deliberate) `edge-ai-starter` cross-references, declared in
1247
+ `test_template_integrity.py`'s `_ALLOWED_CROSS_SKU_MENTIONS`, this one
1248
+ was a real substitution gap one SKU over -- not allowlisted. Rewritten
1249
+ SKU-neutral ("`E1M-V2M101` and `E1M-V2M102` both carry the DEEPX DX-M1
1250
+ NPU; pick either via `som.sku` in `board.yaml` for the DEEPX DX-M1
1251
+ path"), true regardless of which of the four SKUs the customer actually
1252
+ scaffolded -- and naming only the two DEEPX-equipped siblings, not all
1253
+ four, keeps the number of (now legitimate) cross-SKU mentions this one
1254
+ sentence adds to the allowlist as small as the fact it states allows.
1255
+ Fix belongs upstream, in alp-sdk's
1256
+ `examples/ai/cold-chain-monitor/README.md`; filed as alp-sdk#1749, and
1257
+ this entry retires the moment that lands and this tree is re-vendored.
1258
+ 11. **`sensor`'s `src/main.c`: four more bare `i2c-scanner`/
1259
+ `examples/peripheral-io/i2c-scanner` mentions entry 7 above deliberately
1260
+ left uncovered (tan-cli#924, the review-flagged follow-up to
1261
+ tan-cli#912/#918).** Entry 7's scope was the `README.md` bullet only;
1262
+ a `tan init --template sensor-starter --som E1M-AEN801` scaffold's
1263
+ `src/main.c` also named the same referent bare four more times, at what
1264
+ were then lines 10, 18, 103 and 114 of the emitted file: the header
1265
+ comment's "Contrasts with `examples/peripheral-io/i2c-scanner`..."
1266
+ paragraph (line 10, descriptive), "On a brand-new bring-up you may want
1267
+ to run `examples/peripheral-io/i2c-scanner` first..." (line 18, a
1268
+ run-this instruction), the `tmp112_init` doc-comment's "`i2c-scanner`
1269
+ can confirm which devices ACK" (line 103, descriptive), and "Use
1270
+ `i2c-scanner` to enumerate what IS on this bus before chasing a
1271
+ TMP112..." (line 114, a run-this instruction). Same defect class as
1272
+ entry 7: real only in the alp-sdk checkout the text was captured from,
1273
+ never emitted into any scaffolded project, and a C comment rather than
1274
+ a markdown link so the emit's own doc-link rewriter never touches it.
1275
+ Byte-identical between the two SKUs (`sensor`'s `src/main.c` carried no
1276
+ SKU substitution at all), so each of the four substitutions shared one
1277
+ `un_edit` across both SKUs -- eight entries total, originally.
1278
+ **tan-cli#996/#1001 (the `722320a1` re-vendor) retired three of the
1279
+ four substitutions** -- the line-18 bring-up instruction, the line-103
1280
+ `tmp112_init`-doc-comment mention, and the line-114 "Use i2c-scanner to
1281
+ enumerate" instruction. `examples/peripheral-io/i2c-master`'s own
1282
+ alp-sdk#1269 fix swapped the example's target chip (TMP112 -> BMP581)
1283
+ and reworded all three surrounding comments past this entry's declared
1284
+ anchors in the process; see `scaffold_byte_parity.py`'s
1285
+ `DELIBERATE_EDITS` block comment for the retirement rationale, and this
1286
+ file's "Deliberate edits" intro paragraph above for why they were not
1287
+ hand-reconstructed against the new wording. **The line-10 "Contrasts
1288
+ with" paragraph survives, unchanged and still live** (`pattern_paragraph`,
1289
+ the two entries in the table below) -- alp-sdk#1269's rewrite left this
1290
+ specific paragraph (generic, names no chip) byte-identical, so the
1291
+ original tan-cli#924 fix still applies verbatim. Fix for the surviving
1292
+ entry belongs upstream, in alp-sdk's
1293
+ `examples/peripheral-io/i2c-master/src/main.c`; it retires the moment
1294
+ that lands and this tree is re-vendored.
1295
+ `board.yaml`/`testcase.yaml` carry one more bare mention each (line 6
1296
+ and line 10 respectively) but are descriptive/contrastive prose, not
1297
+ run-this instructions, and stay untracked here -- tan-cli#924's own
1298
+ scope note.
1299
+
1300
+ **Re-examined at tan-cli#1009's round-three review**, since entries
1301
+ 15-17 went on to sweep twenty-four-plus purely descriptive bare
1302
+ mentions (no run-this instructions among them) across `sensor`,
1303
+ `minimal`, `diagnostics`, `iot`, `edge-ai` and `multicore-mailbox` --
1304
+ "descriptive, not run-this" no longer distinguishes THIS exception from
1305
+ everything since fixed under the identical ground. The exception still
1306
+ holds, but now on precedent rather than principle: tan-cli#924 itself
1307
+ scoped its OWN fix to the `i2c-scanner` referents it was filed against
1308
+ and explicitly left `board.yaml`/`testcase.yaml` for later (a
1309
+ deliberate, reviewed scope decision, not an oversight), and nothing
1310
+ since has re-opened that specific scope decision to widen it. Left
1311
+ exactly as tan-cli#924 left it; a future sweep that reaches `sensor`'s
1312
+ `board.yaml`/`testcase.yaml` again should fold these in rather than
1313
+ treat the "descriptive" wording above as still doing any distinguishing
1314
+ work on its own.
1315
+ 12. **(RETIRED at tan-cli#996/#1001, history only) `sensor`'s `src/main.c`: the header comment's `Hardware:` paragraph
1316
+ bare `metadata/chips/tmp112.yaml` mention, three lines above entry 11's
1317
+ `pattern_paragraph` substitution in the same comment block (PR #975
1318
+ review round).** A different specific referent than entry 11's (which
1319
+ tracks only the `i2c-scanner` mentions #924 itself named), but the
1320
+ identical defect class: real only in the alp-sdk checkout the text was
1321
+ captured from, never emitted into any scaffolded project, and a C
1322
+ comment so the emit's own doc-link rewriter never touches it. Rewritten
1323
+ to name the real alp-sdk path in prose, noting the referent is not part
1324
+ of this scaffolded project, matching entry 7's/entry 11's phrasing.
1325
+ Byte-identical between the two SKUs (same as entry 11) -- one `un_edit`
1326
+ shared across both, two entries total. `src/main.c:51,53`'s
1327
+ `metadata/chips/tmp112.yaml`/`include/alp/chips/tmp112.h` referents in
1328
+ the separate `TMP112_ADDR_7BIT` doc-comment below (outside this rewritten
1329
+ block), plus `board.yaml:12,34,36`, `prj.conf:4`, and the `minimal`
1330
+ template's `README.md:19` are the same defect class again but were left
1331
+ for a follow-up (tan-cli#977) rather than folded in here -- this entry
1332
+ covers only the in-paragraph sibling entry 11 itself sat three lines
1333
+ above. Fix belongs upstream, in alp-sdk's
1334
+ `examples/peripheral-io/i2c-master/src/main.c`, alongside entry 11's; it
1335
+ retires the same way, the moment alp-sdk#1795 lands and this tree is
1336
+ re-vendored.
1337
+ 13. **`iot`/`E1M-AEN801`'s `README.md`: the `native_sim.conf` link
1338
+ (tan-cli#1001 review).** alp-sdk's own `examples/connectivity/
1339
+ mqtt-telemetry/README.md` links `native_sim.conf` via a self-referential
1340
+ `../mqtt-telemetry/native_sim.conf` detour (its own README pointing back
1341
+ at its own directory), which IS `../`-prefixed, so the doc-link rewriter
1342
+ (the same one entry 4's `- Ref:`-pinned links go through) treats it as
1343
+ cross-directory and renders a `github.com/.../blob/<ref>/...` link --
1344
+ wrong here, since `tan init --template iot-starter` vendors and writes
1345
+ `native_sim.conf` as a sibling of this very `README.md` (tan-cli#379,
1346
+ `NON_ENVELOPE_EXTRAS`; confirmed present in a live scaffolded tree).
1347
+ Rewritten to the plain sibling link, `[native_sim.conf](native_sim.conf)`.
1348
+ Fix belongs upstream, in alp-sdk's
1349
+ `examples/connectivity/mqtt-telemetry/README.md`; declared here pending
1350
+ that fix and a re-vendor.
1351
+ 14. **`iot`/`E1M-AEN801`'s `README.md`: the "copy it in first" build comment
1352
+ (tan-cli#1001 review).** The re-vendor imported a comment reading
1353
+ "native_sim.conf ships only in the alp-sdk tree, not in this scaffold --
1354
+ copy it in first" ahead of the `west build -b native_sim/native/64`
1355
+ command block. True in an alp-sdk checkout, where this text was
1356
+ captured from; false in a tan scaffold, where entry 13's own file
1357
+ already vendors and writes `native_sim.conf` next to this README --
1358
+ confirmed by running `tan init --template iot-starter` and finding the
1359
+ file written. A customer following this instruction would go looking
1360
+ for an alp-sdk checkout to copy from, for a file already sitting in
1361
+ their own project. Own entry, separate from entry 13, per this file's
1362
+ own two-independent-substitutions-get-two-entries discipline (see entry
1363
+ 11's `model_comment_1`/`_2` split under `scaffold_byte_parity.py`).
1364
+ Removed outright -- no replacement text needed once the file is known
1365
+ to already be present. Fix belongs upstream, in alp-sdk's
1366
+ `examples/connectivity/mqtt-telemetry/README.md`; declared here pending
1367
+ that fix and a re-vendor.
1368
+ 15. **The remaining bare cross-repo referents entry 12 scoped out (tan-cli#977,
1369
+ the follow-up filed off PR #975's own review round), re-derived against
1370
+ THIS SAME tan-cli#996/#1001 re-vendor rather than the tree #977 was
1371
+ originally filed against.** alp-sdk#1269 (part of #1001's re-vendor)
1372
+ swapped `sensor`'s chip from TMP112 to BMP581 -- TMP112 lives on
1373
+ `BRD_I2C`, a bus this example never opens, and on the E1M-AEN family
1374
+ `BRD_I2C` is additionally the slave-only Alif LPI2C0 (ADR 0017), so the
1375
+ SoC cannot master it at all -- and #1001 retired entry 12 (and entry
1376
+ 11's `bringup_instruction`/`init_fail_comment`/`failure_modes_
1377
+ instruction` trio, folded into entry 11's own history above) as
1378
+ "anchor-gone" rather than leaving them live, since the specific TMP112
1379
+ bytes those entries matched no longer exist. That retirement was
1380
+ correct for the exact bytes retired -- but the underlying BARE-REFERENT
1381
+ DEFECT recurred almost one-for-one in the rewritten BMP581 paragraphs,
1382
+ which is exactly the trap this manifest's own doctrine two sections up
1383
+ warns against ("retire only when upstream healed the underlying
1384
+ problem, not when the anchor stopped matching"): an anchor going stale
1385
+ is not evidence the customer-facing defect it was tracking is gone.
1386
+ Swept the whole of `sensor`'s `src/main.c` and `board.yaml` rather than
1387
+ only re-deriving the two lines #977 originally named (`src/main.c:51,53`
1388
+ in the pre-re-vendor tree), and found the defect at eleven distinct
1389
+ sites, not the seven #977 was filed against:
1390
+ - `sensor`'s `src/main.c`, the `Hardware:` paragraph -- a bare
1391
+ `metadata/boards/e1m-evk.yaml`/`e1m-x-evk.yaml` mention. New: this
1392
+ paragraph carried no bare referent in the pre-re-vendor tree (its
1393
+ `i2c-scanner` mention was already qualified); the re-vendor's
1394
+ rewrite introduced this one.
1395
+ - `sensor`'s `src/main.c`, the `#1269` historical note -- a bare
1396
+ `examples/v2n/v2n-temp-sensor` mention. New text this re-vendor
1397
+ added outright; no predecessor entry ever covered it.
1398
+ - `sensor`'s `src/main.c`, the `BMP581_ADDR_7BIT` doc-comment -- bare
1399
+ `metadata/boards/e1m-evk.yaml`/`e1m-x-evk.yaml` and
1400
+ `include/alp/chips/bmp581.h` mentions (two substitutions). The direct
1401
+ BMP581-era descendant of #977's original `src/main.c:51,53` sites
1402
+ (then `TMP112_ADDR_7BIT`, then naming `metadata/chips/tmp112.yaml`/
1403
+ `include/alp/chips/tmp112.h`).
1404
+ - `sensor`'s `src/main.c`, the `bmp581_init` failure doc-comment -- a
1405
+ bare `examples/peripheral-io/i2c-scanner` mention. The BMP581-era
1406
+ recurrence of entry 11's retired `init_fail_comment` anchor
1407
+ ("`i2c-scanner` can confirm which devices ACK", now "`examples/
1408
+ peripheral-io/i2c-scanner` can confirm which devices ACK").
1409
+ - `sensor`'s `board.yaml:12` -- the "Customer workflow" paragraph's bare
1410
+ `scripts/alp_project.py` mention, unchanged by the re-vendor. Unlike
1411
+ entries 4/7/11/12's purely descriptive pointers, this one names real
1412
+ build machinery a scaffolded project's own `CMakeLists.txt` genuinely
1413
+ invokes, resolved via `ALP_SDK_ROOT` -- but the physical file still
1414
+ lives only in the alp-sdk checkout `ALP_SDK_ROOT` points at, never in
1415
+ this scaffolded tree, so worded "resolved via `ALP_SDK_ROOT`, not
1416
+ part of this scaffolded project" rather than the plain "not part of
1417
+ this scaffolded project" the purely-descriptive entries use.
1418
+ - `sensor`'s `board.yaml:34,36` -- the chip-classification paragraph's
1419
+ bare `metadata/chips/bmp581.yaml` (the chip-renamed descendant of
1420
+ #977's original `tmp112.yaml` site) and `scripts/check_example_
1421
+ portability.py` (unchanged) mentions, purely descriptive, same defect
1422
+ class as entry 11/12. Two substitutions.
1423
+ - `sensor`'s `board.yaml`, the `#1269` historical NOTE -- bare
1424
+ `metadata/boards/e1m-evk.yaml`/`e1m-x-evk.yaml` `i2c_devices:` and
1425
+ `examples/v2n/v2n-temp-sensor` mentions (two substitutions), the
1426
+ board.yaml-side siblings of `src/main.c`'s equivalents above. New
1427
+ text this re-vendor added, same as its `src/main.c` counterpart.
1428
+ - `sensor`'s `prj.conf:4` -- the same bare `scripts/alp_project.py`
1429
+ mention as `board.yaml`'s Customer-workflow paragraph, unchanged, a
1430
+ different file, same "resolved via `ALP_SDK_ROOT`" wording.
1431
+ - `minimal`'s `README.md:19` -- "before moving on to `gpio-button-led`
1432
+ or `i2c-scanner`", unaffected by the re-vendor (`minimal` was not one
1433
+ of the seven `README.md` files it re-vendored). Entry 7's shape (a
1434
+ markdown file, so turned into real links -- both real alp-sdk
1435
+ directories, pinned at `v0.16.0` per the current vendor point's
1436
+ `- Ref:`) rather than this entry's own prose naming, since no
1437
+ existing `DELIBERATE_EDITS` entry touched the `minimal` template at
1438
+ all before this one.
1439
+
1440
+ Byte-identical between the two SKUs at every substitution above
1441
+ (`sensor`'s `src/main.c`, `board.yaml` at these lines, and `prj.conf`
1442
+ carry no SKU substitution; `minimal`'s `README.md` differs only in the
1443
+ `west build`/`west flash` block further down, untouched here) -- each
1444
+ `un_edit` is shared across both SKUs. Eleven distinct substitutions x 2
1445
+ SKUs = twenty-two entries for `sensor`, plus one substitution x 2 SKUs =
1446
+ two entries for `minimal` -- twenty-four entries total. Fix belongs
1447
+ upstream: the `sensor`-side sites are alp-sdk's `examples/peripheral-io/
1448
+ i2c-master/{src/main.c,board.yaml,prj.conf}` -- distinct tokens from
1449
+ entry 11/12's `i2c-scanner`/`tmp112.yaml`/`tmp112.h` mentions (this
1450
+ entry's own `metadata/boards/*.yaml`, `examples/v2n/v2n-temp-sensor`,
1451
+ `metadata/chips/bmp581.yaml`, `include/alp/chips/bmp581.h`,
1452
+ `scripts/alp_project.py`, `scripts/check_example_portability.py`), so
1453
+ NOT covered by alp-sdk#1795, though several sites share entry 11/12's
1454
+ files; the `minimal` site is alp-sdk's `examples/peripheral-io/
1455
+ hello-world/README.md`, a template no prior entry has touched. Filed
1456
+ upstream as **alp-sdk#1855** (PR #1009 review finding 4 -- unlike
1457
+ entries 4/5/6/7/10/11/12 this entry carried no issue number at all
1458
+ before then); all twenty-four retire the moment alp-sdk#1855 lands and
1459
+ this tree is re-vendored.
1460
+
1461
+ 16. **The sibling sites entry 15's own sweep stopped one template short of
1462
+ (tan-cli#1009, PR #1009's review round): `minimal`/`diagnostics`
1463
+ board.yaml/prj.conf, `iot` board.yaml/prj.conf/src/main.c, `edge-ai`
1464
+ src/main.c, and `multicore-mailbox` board.yaml/native_sim overlay/
1465
+ prj.conf/src/main.c.** PR #1009 (the direct follow-up to #977, closing
1466
+ it) swept only `sensor` and `minimal`'s `README.md` -- the same
1467
+ byte-identical `scripts/alp_project.py` "Customer workflow" paragraph
1468
+ entry 15 fixed in `sensor`'s `board.yaml` survived, unqualified, in
1469
+ `minimal`'s own `board.yaml` (a template that PR had already edited,
1470
+ two files over) and in `diagnostics` (named in tan-cli#977's own
1471
+ title). The review round that found this (PR #1009's own review, not a
1472
+ new issue) also swept every other vendored template for the same
1473
+ defect class rather than stopping at the two sites it happened to name
1474
+ first, per this manifest's own repeated lesson that a sweep bounded to
1475
+ only the sites an issue named leaves siblings unswept indefinitely
1476
+ (entries 11, 12, 15 above all restate variants of this same lesson).
1477
+ Twenty-one entries:
1478
+ - `minimal`/`diagnostics` `board.yaml` (four entries, one shared
1479
+ `un_edit` -- byte-identical substitution, the same "Customer
1480
+ workflow" `scripts/alp_project.py` mention entry 15 fixed in
1481
+ `sensor`'s `board.yaml`).
1482
+ - `minimal`'s `prj.conf` (two entries, one per SKU, byte-identical) --
1483
+ the same bare `scripts/alp_project.py` mention, a different file, plus
1484
+ a trailing "Add app-specific tuning knobs" sentence `diagnostics`'s
1485
+ shorter `prj.conf` does not carry.
1486
+ - `diagnostics`'s `prj.conf` (two entries, one per SKU, byte-identical)
1487
+ -- same mention, four lines total, no trailing sentence.
1488
+ - `iot`'s `board.yaml` (one entry, one SKU only -- `iot` ships
1489
+ `E1M-AEN801` only) -- a bare `docs/cc3501e-bridge.md` mention.
1490
+ - `iot`'s `prj.conf` (two entries, two independent paragraphs) -- a bare
1491
+ `scripts/alp_project.py` mention in the opening comment (one of
1492
+ finding 1's two named "different wording" instances), and a bare
1493
+ `examples/connectivity/iot-fleet-ota/prj.conf` mention in the mbedtls
1494
+ migration note.
1495
+ - `iot`'s `src/main.c` (two entries, two independent doc-comments) -- a
1496
+ bare `docs/cc3501e-bridge.md` mention (the src/main.c-side sibling of
1497
+ `board.yaml`'s), and a bare `examples/peripheral-io/i2c-master`
1498
+ mention in the "sensor reading" paragraph. **Widened in the PR #1009
1499
+ round-three review** to also correct that same paragraph's stale
1500
+ "TMP112 over `<alp/chips/tmp112.h>`" -- alp-sdk#1269 swapped
1501
+ `sensor`'s underlying chip to BMP581 and this template's own
1502
+ `README.md:52` already said BMP581, but this `src/main.c` copy was
1503
+ never updated; kept as one entry since both fixes sit in the same
1504
+ matched paragraph.
1505
+ - `edge-ai`'s `src/main.c` (four entries, two independent doc-comments x
1506
+ two SKUs, byte-identical between SKUs) -- a bare `examples/ai/
1507
+ cold-chain-monitor/models/README.md` mention, twice (the "Honest
1508
+ scope" paragraph and the model-stub comment).
1509
+ - `multicore-mailbox`'s `board.yaml` (one entry, one SKU only --
1510
+ `multicore-mailbox` ships `E1M-AEN801` only) -- a bare
1511
+ `metadata/e1m_modules/E1M-AEN801.yaml` mention.
1512
+ - `multicore-mailbox`'s `boards/native_sim_native_64.overlay` (one
1513
+ entry, a `NON_ENVELOPE_EXTRAS` file diffed against the catalog
1514
+ example's own copy, same declaration mechanism) -- a bare
1515
+ `scripts/alp_orchestrate.py` mention.
1516
+ - `multicore-mailbox`'s `prj.conf` (one entry) -- a bare
1517
+ `scripts/alp_project.py` mention (finding 1's second named "different
1518
+ wording" instance).
1519
+ - `multicore-mailbox`'s `src/main.c` (one entry) -- a different SHAPE
1520
+ than the rest of this entry: the "Peer firmware" doc-comment labelled
1521
+ this template's OWN local `./peer/main.c` (a real file
1522
+ `multicore-mailbox` scaffolds) with alp-sdk's upstream example path
1523
+ instead of the real local one. Corrected to name the actual local
1524
+ path, with the upstream path kept parenthetically for anyone
1525
+ comparing against alp-sdk's own copy of this example.
1526
+
1527
+ Also fixed in the same review round, not a new `DELIBERATE_EDITS`
1528
+ concern: the twelve `E1M-V2N101` entries entry 15 added under the
1529
+ generic reason `"same as ... above"` were re-worded to each name their
1530
+ own substitution and entry, since a shared generic reason string across
1531
+ multiple entries on the same `(template, sku, path)` defeats
1532
+ `self_check`'s own reason-string-filtered non-vacuity proof (the exact
1533
+ class of vacuity `self_check`'s own comment already warned against, but
1534
+ entry 15 shipped without exercising it against these entries -- verified
1535
+ by mutation this round: reverting `addr_header_pointer` alone for
1536
+ `sensor`/`E1M-V2N101` now produces exactly one filtered failure naming
1537
+ that entry, not a sibling's).
1538
+
1539
+ Fix belongs upstream, alongside entry 15's alp-sdk#1855, which this
1540
+ entry's own review round EXTENDED to cover it (verified: #1855's own
1541
+ Scope note previously named `sensor`/`minimal` only, exactly the
1542
+ alp-sdk#1795 orphaning shape it warns against -- fixed in the same
1543
+ review round that found this gap, not left standing) -- `diagnostics`,
1544
+ `iot`, `edge-ai` and `multicore-mailbox` are the same defect class, in
1545
+ alp-sdk's `examples/bringup/board-selftest`, `examples/connectivity/
1546
+ mqtt-telemetry`, `examples/ai/cold-chain-monitor`, and
1547
+ `examples/multicore/mproc-mailbox` catalog entries respectively -- not
1548
+ filed as a separate issue, since #1855 now explicitly covers all four;
1549
+ all twenty-one retire the moment #1855 lands and this tree is
1550
+ re-vendored.
1551
+
1552
+ 17. **The eleventh sibling site entry 16's own sweep missed, plus a broken
1553
+ run-this command (tan-cli#1009, PR #1009's round-three review).** The
1554
+ round-two sweep audited every vendored template but still missed one
1555
+ site in the same file it had already partly fixed, and mislabeled one
1556
+ more the same way `src/main.c`'s `peer_main_pointer` entry already
1557
+ corrected, one file over:
1558
+ - `multicore-mailbox`'s `board.yaml` -- a bare `src/backends/mproc/
1559
+ zephyr_drv.c` mention, three lines above the `e1m_modules_pointer`
1560
+ entry (entry 16) in the same IPC-carve-out comment block. Worse than
1561
+ the other entries in this file: this scaffold has its own `src/` (the
1562
+ `m55_hp` app's), so the unqualified mention reads as a customer path.
1563
+ - `multicore-mailbox`'s `README.md` -- the "peer-side firmware" link
1564
+ labels the scaffold's own local `./peer/main.c` (the href, already
1565
+ correct) with alp-sdk's upstream example path as the link TEXT.
1566
+ Byte-for-byte the same mislabel shape as `src/main.c`'s
1567
+ `peer_main_pointer` entry (entry 16), left in place by the round-two
1568
+ sweep because it sits in a different file.
1569
+ - `multicore-mailbox`'s `README.md`, the HE-side standalone `west
1570
+ build` command four lines below the link above -- names
1571
+ `examples/multicore/mproc-mailbox/peer`, which does not exist in a
1572
+ `tan init` project at all (`m55_he: app: ./peer` is this scaffold's
1573
+ own path). Not a mislabel: a **run-this instruction that fails** when
1574
+ a customer runs it, the strongest form under tan-cli#924's own
1575
+ rationale (descriptive prose vs. run-this instructions is why that
1576
+ entry's exception still stands -- see the note after entry 11 above).
1577
+
1578
+ Three entries. Fix belongs upstream, alongside entries 15/16's
1579
+ alp-sdk#1855 (extended again in this same review round to cover the
1580
+ `zephyr_drv.c` mention and the `west build` command fix); all three
1581
+ retire the moment #1855 lands and this tree is re-vendored.
1582
+
1583
+
1584
+ ## Template x SKU matrix vendored
1585
+
1586
+ | tan `WizardTemplateId` | SDK catalog id | Vendored SKUs | Example dir | Files |
1587
+ |---|---|---|---|---|
1588
+ | `zephyr-app` | `minimal` | `E1M-AEN801`, `E1M-V2N101` | `examples/peripheral-io/hello-world` | 6 |
1589
+ | `sensor-starter` | `sensor` | `E1M-AEN801`, `E1M-V2N101` | `examples/peripheral-io/i2c-master` | 8 |
1590
+ | `edge-ai-starter` | `edge-ai` | `E1M-AEN801`, `E1M-V2N101` | `examples/ai/cold-chain-monitor` | 8 |
1591
+ | `board-diagnostics` | `diagnostics` | `E1M-AEN801`, `E1M-V2N101` | `examples/bringup/board-selftest` | 8 |
1592
+ | `iot-starter` | `iot` | `E1M-AEN801` only (`status: preview`) | `examples/connectivity/mqtt-telemetry` | 7 |
1593
+ | `multicore-mailbox` | `multicore-mailbox` | `E1M-AEN801` only (`status: stable`) | `examples/multicore/mproc-mailbox` | 11 |
1594
+
1595
+ Layout: `vendored/<sdk-template-id>/<sku>/<path>`, e.g.
1596
+ `vendored/minimal/E1M-AEN801/CMakeLists.txt`. Four templates ship past the
1597
+ common six: `edge-ai` adds `src/cold_chain.c` + `src/cold_chain.h` (the
1598
+ cold-chain-metrics core the app links against); `sensor` and `diagnostics`
1599
+ each add `boards/native_sim_native_64.{conf,overlay}` (tan-cli#501 — a
1600
+ board-dir pair Zephyr auto-discovers with no CMakeLists wiring at all); and
1601
+ `multicore-mailbox` adds a whole second build slice — `peer/CMakeLists.txt`,
1602
+ `peer/prj.conf`, `peer/main.c` — plus `peer/testcase.yaml` and
1603
+ `boards/native_sim_native_64.overlay`; the overlay is load-bearing rather than
1604
+ decorative, since both `src/main.c` and `peer/main.c` carry
1605
+ `#define SHMEM_REGION_NAME "alp_shmem0"` and only that overlay declares the
1606
+ `alp-shmem0` alias the README's own `west build -b native_sim/native/64 .`
1607
+ depends on. `peer/testcase.yaml` is the first NESTED
1608
+ `NON_ENVELOPE_EXTRAS` entry (tan-cli#864); it is a literal path rather than a
1609
+ suffix match, and `scaffold_byte_parity.py` says why. And
1610
+ `iot` adds `native_sim.conf` at the project root (tan-cli#379 — NOT
1611
+ auto-discovered the same way: its own README build line passes it explicitly
1612
+ via `-DEXTRA_CONF_FILE=native_sim.conf`; see the non-envelope-extras section
1613
+ at the end).
1614
+
1615
+ `python/tan/core/scaffold.py::_vendored_files` reads these through the packaged
1616
+ `tan.templates.VENDORED_ROOT`. Setuptools includes the tree in a wheel/source
1617
+ install and the PyInstaller build includes it in the frozen distribution. The
1618
+ reader:
1619
+
1620
+ - picks the SKU-family bucket (`E1M-V2N*`/`E1M-V2M*` -> the `E1M-V2N101`
1621
+ tree, everything else -> the `E1M-AEN801` tree, mirroring
1622
+ `app_core_for_sku`'s own family split) — except `iot`, which has only ONE
1623
+ tree (`E1M-AEN801`) and no family split at all (see "iot-starter is
1624
+ AEN-only" below);
1625
+ - retargets `board.yaml`'s `som.sku:` line onto the caller's exact `--som`
1626
+ value when it isn't the tree's own vendored SKU (reusing the existing
1627
+ `retarget_board_yaml_som`, the same mechanism `init --from-example` already
1628
+ uses) — a byte-exact no-op for a tree's own vendored SKU, including a
1629
+ column-aligned trailing comment on the `sku:` line (`iot`'s board.yaml has
1630
+ one; `retarget_board_yaml_som` replaces only the value token, not the rest
1631
+ of the line);
1632
+ - splices `--cores` companions (+ a default RPMsg channel to the first active
1633
+ one) into the vendored `cores:` block via `splice_companion_cores`, mirroring
1634
+ the retired Rust `gen_board_yaml` companion-core loop.
1635
+
1636
+ ## Template-id mapping: resolved vs. deferred (maintainer decision)
1637
+
1638
+ `zephyr-app -> minimal`, `sensor-starter -> sensor`, `edge-ai-starter ->
1639
+ edge-ai`, `board-diagnostics -> diagnostics`, and `iot-starter -> iot` are
1640
+ mapped/vendored — five mappings confirmed clean, each template's existing
1641
+ generator (`gen_zephyr_project_files`) already targeted (or was retired in
1642
+ favor of) a real, west-buildable Zephyr layout structurally matching the
1643
+ SDK's canonical scaffold (`find_package(Zephyr)` + `board.yaml` -> generated
1644
+ Kconfig): `examples/peripheral-io/hello-world` for `minimal`,
1645
+ `examples/peripheral-io/i2c-master` for `sensor`,
1646
+ `examples/ai/cold-chain-monitor` for `edge-ai`,
1647
+ `examples/bringup/board-selftest` for `diagnostics`, and
1648
+ `examples/connectivity/mqtt-telemetry` for `iot`. `zephyr-app` is also the
1649
+ one directly responsible for #864's motivating regression: the retired
1650
+ CMakeLists.txt ran `--emit zephyr-conf` **without** `--core <id>`, which on a
1651
+ heterogeneous (`--cores`) project lets one core's Kconfig leak into another
1652
+ core's build. Every vendored template's CMakeLists.txt threads `--core <id>`
1653
+ explicitly, closing it. `sensor-starter`'s and `board-diagnostics`'s
1654
+ hand-written generators emitted a generic polling/checklist stub,
1655
+ `edge-ai-starter`'s a generic arena-sizing stub, and `iot-starter`'s a
1656
+ Wi-Fi/MQTT/TLS-toggle stub with no real transport; the vendored trees instead
1657
+ emit the SDK's real TMP112 `<alp/chips/tmp112.h>` i2c-master example, BME280
1658
+ cold-chain-monitor app, board self-test (SoM/SoC identity + RUN
1659
+ operating-point + I2C management-bus scan), and CC3501E Wi-Fi6+BLE bridge
1660
+ MQTT/TLS telemetry app, respectively.
1661
+
1662
+ `edge-ai-starter` was flagged (not vendored) through v0.11.1: the SDK's
1663
+ `edge-ai` scaffold's `cores:` topology did **not** change with `--sku` back
1664
+ then (see "Per-SKU substitution" below) — its `E1M-V2N101` render kept
1665
+ `cores:` keyed on `m55_hp`/`a32_cluster`, the Alif-only pair, not the real
1666
+ Renesas `m33_sm`/`a55_cluster`. alp-sdk#877 (the same fix that resolved
1667
+ `zephyr-app`'s per-SKU core bug) fixed this for `edge-ai` too, so it is now
1668
+ vendored at v0.13.0: `E1M-V2N101`'s tree correctly keys the app core on
1669
+ `m33_sm` (companion `a55_cluster`, `os: "off"`). It is also the FIRST
1670
+ HETEROGENEOUS (multi-core) vendored template — its `board.yaml` lists the
1671
+ companion core BEFORE the app core, which is why
1672
+ `python/tan/core/scaffold.py::vendored_app_core_key` reads the core that OWNS
1673
+ an `app:` key rather than trusting
1674
+ positional "first child under `cores:`" (correct for `minimal`/`sensor` only
1675
+ because they are single-core).
1676
+
1677
+ `board-diagnostics` and `iot-starter` were flagged (not vendored) through
1678
+ v0.13.0 because no SDK catalog template covered diagnostics/bring-up or
1679
+ Wi-Fi/MQTT/TLS connectivity at all (`gateway` is Modbus, not IoT
1680
+ connectivity). alp-sdk#903 added both catalog entries; they are vendored at
1681
+ `0ed078a6d04f4072ab00d8ce92cb2ac82f7adcbc` (see "Source" above), retiring
1682
+ `tan`'s hand-written generators for both templates — including
1683
+ `iot-starter`'s `config/iot.env.example` file, which the vendored `iot`
1684
+ scaffold doesn't emit (SSID/broker config lives in `src/main.c`, per the
1685
+ scaffold's own "Customer workflow" comment).
1686
+
1687
+ ### `iot-starter` is AEN-only
1688
+
1689
+ `iot`'s SDK catalog entry covers exactly ONE SoM SKU, `E1M-AEN801`
1690
+ (`status: preview`; `supported.som_skus: ["E1M-AEN801"]`) — the Wi-Fi
1691
+ transport is the CC3501E Wi-Fi6+BLE bridge, silicon-validated only on that
1692
+ SKU; `E1M-V2N101`'s Murata Wi-Fi (`murata_lbee5hy2fy`) is `hil_silicon:
1693
+ untested` with an unmerged Linux data path. Unlike every other mapped
1694
+ template, there is no `_V2N` tree and no `_family_bucket` call for `iot`
1695
+ (`python/tan/core/scaffold.py` uses `IOT_STARTER_SUPPORTED_SKU` directly).
1696
+ `tan init`'s upfront guard (`python/tan/commands/init_cmd.py`) rejects any
1697
+ `--som` other than `E1M-AEN801` for this template with `init.invalid-som`, naming the
1698
+ supported SKU, before a single file is planned — never a silent fall-back
1699
+ onto a hand-written generator, which would keep alive on this one path the
1700
+ exact drift issue #14 retires everywhere else.
1701
+
1702
+ ### `minimal-app` stays hand-generated; tan-cli#309 fixed its CMake AND its `app:`
1703
+
1704
+ **`minimal-app`** is semantically closest to SDK `minimal`, but it is not
1705
+ vendored from the SDK catalog at all — it is tan's OWN generator
1706
+ (`tan/core/scaffold.py`'s `_minimal_app_files`), the ONLY tan wizard template
1707
+ left hand-generated after the vendoring pass above. Folding it onto the same
1708
+ vendored tree as `zephyr-app` would make the two templates byte-identical in
1709
+ the wizard's template picker (a product decision to merge/deprecate one, not
1710
+ something to invent here), and `contract/envelopes/init-preview-minimal-app/
1711
+ expected.json` pins its exact eight-file list/order — path + change-kind
1712
+ only, never file content or `board.yaml`'s `app:` value, so it did not need
1713
+ re-pinning for the fix below.
1714
+
1715
+ Through v0.5.0-rc3 this template had TWO compounding bugs, and an earlier pass
1716
+ at this fix landed only the first — which, alone, turns a silent wrong-binary
1717
+ build into a hard CMake configure error, not a working one (caught by
1718
+ adversarial review before it shipped):
1719
+
1720
+ 1. **Which file `west build` even reads.** `board.yaml`'s `app:` decides this,
1721
+ via the planner's `_zephyr_app_dir` (`tan/planner/orchestrator.py`): it
1722
+ resolves `app:` to a directory and picks that directory ITSELF whenever it
1723
+ holds a `CMakeLists.txt` of its own, falling back to the PARENT only when
1724
+ it does not. This template's `src/` deliberately keeps its own
1725
+ `CMakeLists.txt` (the two-file split below), so `app: ./src` sent `west
1726
+ build` straight at `src/CMakeLists.txt` — the root `CMakeLists.txt`
1727
+ (`project()` + `add_subdirectory(src)`, never `add_executable`) was dead
1728
+ code the entire time, not the file at fault.
1729
+ 2. **What that file said.** `src/CMakeLists.txt` — the file actually
1730
+ configured — called plain `add_executable(alp_app ${ALP_APP_SOURCES})`, no
1731
+ `find_package(Zephyr ...)` anywhere in either CMake file. CMake configures
1732
+ and links that shape fine: measured on a real checkout, `west build -b
1733
+ <board> <project>/src` produced a genuine PE32+ x86-64 `alp_app.exe` built
1734
+ from `CMakeFiles/alp_app.dir/{main,features/app_bootstrap}.obj` —
1735
+ `app_bootstrap.c` WAS compiled and linked, just into a host binary Zephyr's
1736
+ own build machinery never touched — so `tan build` reported success for a
1737
+ project that was never Zephyr at all.
1738
+
1739
+ tan-cli#309 fixed both, in `tan/core/scaffold.py`: `_minimal_app_root_cmake`/
1740
+ `_minimal_app_src_cmake` now emit `find_package(Zephyr REQUIRED HINTS
1741
+ $ENV{ZEPHYR_BASE})` before `project()` in the root file, with `src/
1742
+ CMakeLists.txt` contributing via `target_sources(app PRIVATE ...)`/
1743
+ `target_include_directories(app ...)` against Zephyr's own `app` target
1744
+ instead of a second `add_executable` — the same KIND of CMake every vendored
1745
+ tree above already writes, while keeping its own smaller, hand-generated
1746
+ CONTENT; and `_minimal_app_board_yaml` now emits `app: .` (the project root)
1747
+ instead of `./src`, so `_zephyr_app_dir` resolves straight to the root file
1748
+ without ever consulting `src/`. Measured after both fixes, against a real
1749
+ CMake + Ninja + Zephyr SDK: configure reaches Zephyr's own boilerplate
1750
+ (`Loading Zephyr default modules`, board/toolchain/devicetree resolution) and
1751
+ a full build compiles `src/features/app_bootstrap.c` into `app/libapp.a`
1752
+ alongside `src/main.c`, which Zephyr's own link step pulls in whole
1753
+ (`-Wl,--whole-archive app/libapp.a`) on the way to a real `zephyr.elf`.
1754
+
1755
+ **`crates/tan-core/src/wizard/service/c_project.rs` shipped the pre-#309
1756
+ broken shape (both bugs) and was never fixed** — it was frozen, and tan-cli#269
1757
+ deleted it; its own `wizard/vendored/MANIFEST.md` had
1758
+ already flagged the CMake half of this ("a `board.yaml` declaring `os:
1759
+ zephyr` over a plain-CMake tree is exactly the silent host-binary build that
1760
+ issue [#14] reports") as "deliberately deferred, not a permanent gap" before
1761
+ the freeze, and the freeze is why it never got un-deferred there. `minimal-app`
1762
+ stays **not** the non-interactive `tan init` default — `zephyr-app` is
1763
+ (tan-cli #97) — an independent choice (a vendored, real-catalog scaffold vs.
1764
+ tan's own hand-generated stub) that #309 does not revisit.
1765
+
1766
+ **A correction to tan-cli#309's own "Measured" evidence.** Its table reports
1767
+ `Machine: ARM`, a real `zephyr.elf`, `CMakeFiles/app.dir/src/main.c.obj`
1768
+ present, and no `app_bootstrap*.obj` — but under the pre-#309 shape (`app:
1769
+ ./src` + `src/CMakeLists.txt`'s `add_executable(alp_app ...)`), the object
1770
+ directory is named `alp_app.dir` (the target's own name), never `app.dir`,
1771
+ and paths are relative to `src/` (already the CMake source root), so the real
1772
+ artefact is `alp_app.dir/main.obj`, not `app.dir/src/main.c.obj` — confirmed
1773
+ by reproducing that exact shape locally. `app.dir/src/main.c.obj` and a real
1774
+ ARM `zephyr.elf` are what the SAME `E1M-AEN801` plan's OTHER Zephyr slice
1775
+ (`m55_he`, which builds `${SDK_ROOT}/firmware/alp-stock-shim`, a genuine
1776
+ Zephyr app with its own unrelated `src/main.c`) would produce. The evidence
1777
+ table most likely mixed that slice's build output into the `minimal-app`
1778
+ customer slice's (`m55_hp`) row — the underlying defect (silent wrong-binary
1779
+ build) is real and independently reproduced above, but that one table
1780
+ conflates two slices.
1781
+
1782
+ **`host-tooling-starter`** (a host-tool monorepo scaffold, not a
1783
+ firmware/board.yaml project — categorically out of the scaffold catalog's
1784
+ scope) is **retired entirely** (tan-cli#14): its `WizardTemplateId` variant,
1785
+ generator (`c_project.rs`'s old `gen_host_tooling_files`), and registry entry
1786
+ are gone, not just left unvendored.
1787
+
1788
+ Reverse gap (informational): the SDK catalog also ships `peripheral`,
1789
+ `multicore-rpmsg`, and `gateway`, none of which has a tan wizard counterpart
1790
+ today. `multicore-mailbox` was in that list until tan-cli#864 vendored it.
1791
+
1792
+ `multicore-rpmsg` is NOT merely unvendored -- it cannot be vendored as it
1793
+ stands. Its catalog `files.user_owned` omits a root `CMakeLists.txt` and
1794
+ `prj.conf` while its own emitted `README.md:22` diagrams one, so
1795
+ `_require_complete_tree` refuses the tree (`init.template-unreadable`, exit 5);
1796
+ `linux/CMakeLists.txt:24` builds `src/main.c`, which the envelope never emits;
1797
+ and `linux/CMakeLists.txt:21` points its generated dir at the project's PARENT.
1798
+ Filed upstream as alplabai/alp-sdk#1712; vendoring it is blocked on that.
1799
+
1800
+ ## Per-SKU substitution (alp-sdk#864/#877) — not a two-line patch
1801
+
1802
+ Earlier (`75ef3b02`, tan-cli#25's original vendor point) `--sku` substituted
1803
+ only the rendered `board.yaml`'s `som.sku:`/`preset:` lines, and the
1804
+ `cores:` key stayed the template's own canonical SKU's core for every SKU —
1805
+ for `minimal` that meant `m55_hp` (Alif) even when `--sku E1M-V2N101` was
1806
+ requested, a non-buildable core id on Renesas silicon. alp-sdk#877 fixed
1807
+ `--emit scaffold` to derive the real per-SKU app core (`app_core_for_sku`'s
1808
+ SDK-side equivalent) and adapt every file that names it: `board.yaml`'s
1809
+ `cores:` key, `CMakeLists.txt`'s `--core` flag, and `README.md`'s
1810
+ "On real silicon" board-id line (now the fully-qualified Zephyr 4.4 form,
1811
+ e.g. `alp_e1m_v2n101_m33_sm/r9a09g056n48gbg/cm33`). Confirmed for `minimal`:
1812
+ `E1M-AEN801` and `E1M-V2N101` now differ in `board.yaml`, `CMakeLists.txt`,
1813
+ and `README.md`; `prj.conf`, `src/main.c`, and `testcase.yaml` (not part of
1814
+ the scaffold envelope, see below) stay byte-identical between the two.
1815
+
1816
+ The vendored `minimal`/`sensor`/`edge-ai`/`diagnostics` scaffolds' `cores:` key
1817
+ is `m33_sm` for `E1M-V2N101`/`E1M-V2M101` and `m55_hp` for `E1M-AEN801`. `iot`
1818
+ has no per-SKU variance; its app core is always `m55_hp`. The shipping Python
1819
+ reader derives the `--cores` companion-splice target from each planned
1820
+ `board.yaml` through `vendored_app_core_key`, rather than re-deriving it from
1821
+ `app_core_for_sku`. `plan_template_files` reads and retargets the tree first;
1822
+ `splice_companion_cores` then operates on those actual bytes. Unit tests in
1823
+ `python/tests/core/test_scaffold.py` cover the family mapping, companion splice,
1824
+ and heterogeneous app-owner selection, so a future re-vendor cannot silently
1825
+ move the app core while the CLI keeps using an independent table.
1826
+
1827
+ ### Heterogeneous `cores:` — `vendored_app_core_key` reads the `app:` owner
1828
+
1829
+ `edge-ai`'s `board.yaml` lists its companion core FIRST
1830
+ (`a55_cluster:`/`a32_cluster:`, `os: "off"`, no `app:` key) and the real app
1831
+ core SECOND (`m33_sm`/`m55_hp`, the one with `app: ./src`) — the reverse
1832
+ insertion order every single-core `minimal`/`sensor` tree happens to share
1833
+ with its app core being the only entry. `vendored_app_core_key` therefore
1834
+ scans every child under `cores:`, tracking the current ` <core>:` key, and
1835
+ returns whichever one owns a ` app:` line, instead of positionally
1836
+ trusting the first indented child (which used to be correct only because
1837
+ `minimal`/`sensor` are single-core). Python's
1838
+ `test_vendored_app_core_key_skips_a_pre_declared_companion_listed_first` and
1839
+ `test_splice_adds_a_companion_and_a_default_rpmsg_channel` are the regression
1840
+ guards for the lookup and for where a `--cores` splice lands its IPC endpoint.
1841
+
1842
+ `testcase.yaml` and `iot`'s `native_sim.conf` are vendored alongside the
1843
+ scaffold envelope but are **not** part of `--emit scaffold`'s output — the
1844
+ catalog's `files.user_owned` for every mapped template is
1845
+ `board.yaml`/`prj.conf`/`CMakeLists.txt`/`src/main.c`/`README.md` (plus, for
1846
+ `edge-ai`, `src/cold_chain.c`/`src/cold_chain.h`) only. `native_sim.conf`
1847
+ (tan-cli#379) is the same class and, for `iot`, the same PAIR:
1848
+ `testcase.yaml`'s `extra_args: EXTRA_CONF_FILE=native_sim.conf` is what loads
1849
+ it, so vendoring one without the other shipped a scaffold whose own twister
1850
+ scenario and whose own documented `west build` named a file that did not
1851
+ exist. Both are byte-diffed against the catalog example's own copy
1852
+ (`scaffold_byte_parity.py`'s `NON_ENVELOPE_EXTRAS`), not against the emit.
1853
+ `testcase.yaml` is a byte-exact copy of the canonical example's own
1854
+ `testcase.yaml`
1855
+ (`examples/peripheral-io/hello-world/testcase.yaml` for `minimal`,
1856
+ `examples/peripheral-io/i2c-master/testcase.yaml` for `sensor`,
1857
+ `examples/ai/cold-chain-monitor/testcase.yaml` for `edge-ai`,
1858
+ `examples/bringup/board-selftest/testcase.yaml` for `diagnostics`,
1859
+ `examples/connectivity/mqtt-telemetry/testcase.yaml` for `iot`) — the SDK's
1860
+ twister harness for that example, family-independent like every other
1861
+ non-`board.yaml` file. `tests/parity/scaffold_byte_parity.py` diffs it
1862
+ against that example directory rather than the live scaffold emit.