<feed xmlns='http://www.w3.org/2005/Atom'>
<title>tinyusb.git/CLAUDE.md, branch claude/usbh-enum-timeout</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.</subtitle>
<id>http://cgit.235523.xyz/tinyusb.git/atom/CLAUDE.md?h=claude%2Fusbh-enum-timeout</id>
<link rel='self' href='http://cgit.235523.xyz/tinyusb.git/atom/CLAUDE.md?h=claude%2Fusbh-enum-timeout'/>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/'/>
<updated>2026-08-20T10:43:44Z</updated>
<entry>
<title>hil, docs: reference toolchains by their official env vars, not one rig's paths</title>
<updated>2026-08-20T10:43:44Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-20T10:43:44Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=21d7332d5884e0abbf9e3f3762deefa589564783'/>
<id>urn:sha1:21d7332d5884e0abbf9e3f3762deefa589564783</id>
<content type='text'>
~/code/pico/pico-sdk and $HOME/code/esp-idf/export.sh are the ci rig's private layout;
written into instructions they silently stop being true on tusb, a dev PC, or any
future rig. The docs now use the variables the vendors define -- PICO_SDK_PATH for the
Pico SDK and IDF_PATH for ESP-IDF, activated explicitly as `. "$IDF_PATH/export.sh"` --
and leave where the checkouts live to each host's profile.

The variables are only useful if the shells that agents actually get can see them, and
`ssh &lt;rig&gt; 'cmd'` is non-interactive AND non-login: it reads no profile, and Debian's
sshd-sourced ~/.bashrc returns at the interactive guard before most of the file. The ci
rig already keeps its exports in the section ABOVE that early-return; IDF_PATH now sits
there beside PICO_SDK_PATH, and the whole chain is verified from a plain non-interactive
ssh: both variables visible, `. "$IDF_PATH/export.sh"` activates ESP-IDF v5.5.3 with
idf.py on PATH -- no login shell, no alias, no hard-coded path. hil-pool-check documents
that placement so the next rig is set up the same way.
</content>
</entry>
<entry>
<title>skills, CLAUDE.md: correct instruction claims the source refutes</title>
<updated>2026-08-20T09:47:22Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-20T09:47:22Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=126e29f777f07a35b06b327b50e62e0150521b48'/>
<id>urn:sha1:126e29f777f07a35b06b327b50e62e0150521b48</id>
<content type='text'>
Findings from an audit of the .claude instruction surface: 1,387 falsifiable claims
extracted with a quote-gate (zero hallucinated), each verdicted against the code, the
kernel at the rig's running version, or the rig itself. Only claims the current source
actively refutes were touched; hard-earned rig knowledge stands as source of truth.

usbtest told operators to stop the actions runner before touching hardware. Every
other file forbids exactly that since the per-board flock landed - following it stops
CI on a shared rig. Twice in the same file it said hil_test.py serializes usbtest
batteries; hil_lock.py budgets 2 concurrent per host controller, a profiled
throughput trade rather than a safety ceiling - while the recorded hazards stay: an
unbudgeted battery has hard-frozen the rig through a VFIO xHCI PCIe error, and a
marginal DUT port bouncing under concurrent batteries has killed a uPD720201 outright,
which lowering the widths does not fix. It also cited src/usb_descriptors.h and
src/tusb_config.h as if repo-relative (they are the example's own, and the comment sat
above the cd that establishes the base) and presented usbtest_do_ioctl() and
tools/usb/testusb.c as repo files when both are Linux kernel.

usb-kernel-recover called the Renesas ppps "real per-port" in its rig layout while
saying four sections earlier that VBUS stays up. Both describe the same silicon and
only the second was right: owner-confirmed, the cards advertise ppps and do not
implement it, so a root cycle is purely a re-enumeration - both places now say so and
warn against reading uhubctl's flag as power control. The layout listed three cards;
the rig has five (01/03/04/05/06:00.0; AMD 02:00.0 has none), re-derived from
lspci/uhubctl/sysfs and written as a derivation recipe because bus numbers renumber
every boot. The root-cycle rung also gains the board-flock requirement the other
files already demand - it avoids the KERNEL device_lock, which is a different lock,
and the text now names the two apart instead of reading as "no reservation needed".

CLAUDE.md listed src/tusb_config.h among the key files; no such path exists -
tusb_config.h is per-example, src/tusb_option.h is the file that lives there.
</content>
</entry>
<entry>
<title>skill(read-doc): search the Calibre database instead of the filesystem</title>
<updated>2026-08-18T09:58:58Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-18T08:28:58Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=f59c8948729debc6d57c4dfade5486176468edbf'/>
<id>urn:sha1:f59c8948729debc6d57c4dfade5486176468edbf</id>
<content type='text'>
Finding documents by walking the library tree misses anything the filename
does not carry - Calibre stores only a truncated title and the author there, so
the tags, series, publisher and description that hold most part numbers and
errata IDs are invisible to it. A zero-result tree search then reads as "the
document does not exist" rather than as a bad search; that happened here, and
led to a confident claim that a fully populated 14,000-file library was empty.

search.py queries metadata.db, ANDs its keywords across every metadata field
(including the stored filename), and prints the best matches first with the
exact path to read. Matching is NFKC + casefold, so a typed ASCII apostrophe or
mu reaches the titles that store the typographic ones.

Every printed path is checked on disk. Calibre renames &lt;author&gt;/&lt;title&gt; (&lt;id&gt;)
when metadata is edited and leaves the old directory behind, so a miss retries
by the stable book id before reporting MISSING - which distinguishes "the file
is not here right now" from "no such document". The gate tests for metadata.db
rather than the directory, since an unmounted or half-synced mountpoint is
still a directory.

Consumers that prescribed their own tree search - driver-reviewer, port-dev,
the driver-review workflow, and the calibre-library references in CLAUDE.md,
usbtest, etm-trace and target-debug - now point at the skill, which owns the
library's location.
</content>
</entry>
<entry>
<title>docs: hand off follow-up work as per-PR plans</title>
<updated>2026-08-18T05:19:09Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-17T12:20:32Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=c7290c4d3167766055f492de43f1ede83940e23c'/>
<id>urn:sha1:c7290c4d3167766055f492de43f1ede83940e23c</id>
<content type='text'>
Records the convention in CLAUDE.md -- deferred work is a SEPARATE scope that deserves
its own PR, written by another session, so it is handed off as a writing-plans doc in
docs/superpowers/followup/pr&lt;NNN&gt;-&lt;topic&gt;.md rather than accumulated in the PR that
found it.

Five handoffs from #3803: flasher_recover (convoy-safe recovery for J-Link boards, seven
validated on the rig), the blindness reporting gaps, the usbtest recovery reserve, the
IAR re-run spec, and the pci-rebind stranding question. Each carries what is already
established with its citations and measurements, what remains, and why it was split out.

One doc per follow-up, not one per PR: a per-PR file invites unrelated work into the
same document and rots as a unit.
</content>
</entry>
<entry>
<title>skills, docs: rewrite USB recovery from the live incidents</title>
<updated>2026-08-18T05:19:09Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-13T18:08:50Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=f822f69a9871b2115c5213889d70411da66ca1b1'/>
<id>urn:sha1:f822f69a9871b2115c5213889d70411da66ca1b1</id>
<content type='text'>
Two things the rig taught us that the old guidance got wrong.

A usbfs ioctl wedged in D state cannot be freed on a running kernel. It holds
the device lock, so usb_disconnect() blocks behind it; reboot(2) walks
device_shutdown() and takes the same lock, so every userspace reboot stalls too.
Only sysrq b (emergency_restart, which skips device_shutdown) or hypervisor
action clears it -- all cited to the kernel source. The recovery ladder is
generic across rigs now (ci.lan, hifiphile, a bench PC) and ends at hypervisor
escalation only where host access exists. Two claims are corrected outright:
JLinkExe is NOT convoy-safe, and a park-flash cannot free a device-lock owner.

The hil skill's banner list is what an operator agent matches a report against,
so it enumerates the banners that actually exist, including the D-state note --
which is explicitly NOT a wedge, since a healthy in-flight testusb is
uninterruptible for most of every case and a concurrent CI battery would
otherwise turn a clean run red.
</content>
</entry>
<entry>
<title>test/hil, ci: contain a wedged USB stack instead of stranding the runner</title>
<updated>2026-08-18T05:19:09Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-13T18:08:40Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=3963a1b70a572132aced1c1a0033e1c8249a0c7e'/>
<id>urn:sha1:3963a1b70a572132aced1c1a0033e1c8249a0c7e</id>
<content type='text'>
A wedged USB device used to take the whole HIL run with it. Every worker that
touched the poisoned node blocked uninterruptibly, the pool could not be joined,
map_async discarded every board's result, and the job ran to the GitHub ceiling
with no report at all -- while the self-hosted runner's single job slot stayed
occupied and every queued job waited behind it.

Bound the calls a worker makes itself. read_sysfs, bounded_open and run_cmd all
answer within a wall clock; read_sysfs distinguishes "absent" from "unknown",
because a blocked read is not evidence of absence, and caps stranded readers at
four (each costs a thread and an fd for the life of the process) after which the
worker declares itself blind. mtype, the gio unmount, the libmtp session and the
arecord/iperf reaps go through those bounds; the MTP session runs in a disposable
subprocess, since libmtp's ctypes calls block unkillably in D state.

Bound the run. A pool guard (HIL_POOL_TIMEOUT, 60 min) fires before any job
ceiling and still writes a report. When the pool will not shut down, the sweep
kills what the workers spawned -- descendants, not just direct children, since
flashers run in their own session -- confirms each kill actually landed, and
exits early so the runner is freed. Whatever survived is named in the report.

Deliberately shallow past that point. We do not re-scan process groups, prove
pid ownership, or escalate through sudo: a root-owned survivor is reported, not
force-killed, because signalling a pid we cannot prove is ours is the worse
failure, and the job ceiling backstops whatever this misses. A D-state holder
was never killable anyway.

Recover instead of reporting a wedge. A HUNG usbtest case reflashes its own DUT
through its roster flasher, but only where the flasher can reach its probe past
a poisoned node -- openocd pinned to a validated vid_pid, or esptool. Where it
cannot, the run says so rather than reserving budget for a path that cannot fire.

Raise the CI ceilings above the pool guard so the guard fires first and still
writes its report, and pin --retry 1 on every HIL leg: the guard is a flat
constant and does not scale with max_retry, so argparse's default of 3 would
triple the serialized usbtest tail against an unchanged guard.

Split the module: execution in hil_test/hil_flash/usbtest, infrastructure in
helper/ (locking, health, selection, shared bounded IO), and the two matrix
generators into .github/scripts/ -- ci_set_matrix.py sat in workflows/, where
GitHub treats every file as a workflow definition. 193 tests cover the bounded
paths, the kill ladder, the guard and the selector against synthetic /proc trees
and PATH-injected fakes; a real wedge cannot be manufactured on demand.
</content>
</entry>
<entry>
<title>docs: add the unified OpenOCD plan, note worktree dep symlinks</title>
<updated>2026-07-28T07:43:32Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-07-28T07:43:32Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=3fdd294b95143f909a6c991943a2e55e638b177a'/>
<id>urn:sha1:3fdd294b95143f909a6c991943a2e55e638b177a</id>
<content type='text'>
The plan doc records why the fork exists and how each vendor source was
ported; the interim handoff it superseded is dropped.

CLAUDE.md: a new worktree should symlink the dependency dirs to the
primary checkout rather than re-fetching them, replacing a single
symlink only when the branch needs a different dep revision. Also allow
'linke' in codespell - WCH-LinkE is a product name.
</content>
</entry>
<entry>
<title>docs(skills): rename debug skills, drop the PC-host/TinyUSB-device assumption</title>
<updated>2026-07-24T07:55:59Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-07-23T03:18:11Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=8918c4fec4a99b174d480c043000fd4405678282'/>
<id>urn:sha1:8918c4fec4a99b174d480c043000fd4405678282</id>
<content type='text'>
Rename usb-target-debug -&gt; target-debug, usb-debug -&gt; usb-kernel-debug,
usb-recover -&gt; usb-kernel-recover (script filenames unchanged), and make all
debug skills/agents decide tool applicability by which end of the link runs
Linux: TinyUSB may run the device or host stack, and its peer may be a Linux
PC, another TinyUSB board, or a Linux gadget (e.g. Raspberry Pi UDC).

- usbmon: exists only when a Linux PC is the link's host
- usb-kernel-debug: either Linux end; allowlist gains dwc3/libcomposite/udc_core
  for the gadget side of a Linux peer
- usb-sniffer: the only full-visibility capture when TinyUSB is the host
- target-debug: covers dcd_* and hcd_*/tuh_ debugging; channel choice by topology
- update target-debugger/hil-operator agents, pre-pr, hil-validate.js, and the
  USB_RECOVER path constant in test/hil/usbtest.py
- CLAUDE.md: fold the dcd/hcd datasheet cross-check rule into the read-doc line
</content>
</entry>
<entry>
<title>docs: add read-doc skill, tighten CLAUDE.md and skill docs (#3778)</title>
<updated>2026-07-21T11:06:01Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-07-21T11:06:01Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=1b5c26b76e7194d05b82b0cccf76684716d3374d'/>
<id>urn:sha1:1b5c26b76e7194d05b82b0cccf76684716d3374d</id>
<content type='text'>
* docs: add read-doc skill for on-demand datasheet lookup

Search and read MCU datasheets, reference manuals, errata, and the USB
spec from a local Calibre library ($HOME/Documents/calibre-library)
instead of answering register/bitfield/pinout questions from model
memory. Gated on the library's existence, so it no-ops for contributors
who don't have it.

* docs: reference read-doc skill and tighten CLAUDE.md

Point the datasheet/reference entry at the new read-doc skill, and trim
sections that only duplicate a skill already owning the detail:
PVS-Studio and Code Size collapse to pointers; GDB/Build/Flash command
blocks condensed to essentials. 213 -&gt; 129 lines; behavioral guidelines
and the validation checklist unchanged.

* docs: tighten skill redundancy; rename AGENTS.md refs to CLAUDE.md

code-size: fold the step list into a sentence and drop invocation
examples the argument tables already cover. hil: merge the duplicated
self-lock bullets and compress the hifiphile note. usbmon: compress the
group-membership setup paragraph. All commands, flags, lock rules, and
report paths preserved. usb-target-debug and the pvs script only get
stale AGENTS.md references renamed to CLAUDE.md (now the real file);
run_pvs.sh no longer cites a --dump-files mention that CLAUDE.md dropped.

* docs: fix review findings — restore Espressif cd step, ELF placeholder, code-size comment

Codex/Copilot/Claude review of #3778: the condensed Espressif bullet
lost its cd (idf.py resolves the project from CWD, so the command failed
from repo root); the GDB example now uses the build/your_app.elf
placeholder that docs/troubleshooting.rst established; the code-size
invocation comment no longer references --combined, which the shown
command doesn't use.</content>
</entry>
<entry>
<title>docs: make CLAUDE.md the real agent-instructions file (#3769)</title>
<updated>2026-07-21T05:35:00Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-07-21T05:35:00Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=c6a1c66f8a64446ad1160b8e5b2da88ad1f3eac5'/>
<id>urn:sha1:c6a1c66f8a64446ad1160b8e5b2da88ad1f3eac5</id>
<content type='text'>
* docs: make CLAUDE.md the real agent-instructions file</content>
</entry>
</feed>
