<feed xmlns='http://www.w3.org/2005/Atom'>
<title>tinyusb.git/.claude/workflows, branch docs/worktree-reuse-workflow</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.</subtitle>
<id>http://cgit.235523.xyz/tinyusb.git/atom/.claude/workflows?h=docs%2Fworktree-reuse-workflow</id>
<link rel='self' href='http://cgit.235523.xyz/tinyusb.git/atom/.claude/workflows?h=docs%2Fworktree-reuse-workflow'/>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/'/>
<updated>2026-08-28T07:10:33Z</updated>
<entry>
<title>Merge pull request #3863 from hathach/claude/validate-loop</title>
<updated>2026-08-28T07:10:33Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T07:10:33Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=4c7bb3ced98cb7262dd8eefd6c9f7df6e42ffcee'/>
<id>urn:sha1:4c7bb3ced98cb7262dd8eefd6c9f7df6e42ffcee</id>
<content type='text'>
validate workflow: loop validate -&gt; fix cycles until green

Turn the single-pass validate gate into a loop: run unit + builds +
size + PVS + claude/codex reviews in parallel; on a red verdict one
fix agent repairs the gate-failing evidence (CONFIRMED findings,
codex P0/P1, failed stages - PLAUSIBLE/quality stay report-only),
commits, and the affected stages re-run, up to maxCycles (default 5).

Hardened per review: fix-commit paths verified from git rather than
self-report, restartRequired when a fix edits the workflow itself,
per-stage evidence budgeting so the fixer prompt JSON never truncates
mid-document, dirty-tree and moving-base-ref guards, dead stage
agents retried instead of ending the loop, and only pure-docs fixes
skip a full stage re-run.</content>
</entry>
<entry>
<title>Merge pull request #3858 from hathach/claude/validator-done-signals</title>
<updated>2026-08-28T04:41:43Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T04:41:43Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=d2b4d8994eb2a1b8994601069f5180a9c979ee8c'/>
<id>urn:sha1:d2b4d8994eb2a1b8994601069f5180a9c979ee8c</id>
<content type='text'>
pr-review-validator: done waits for every auto-reviewer to settle</content>
</entry>
<entry>
<title>pr-review-validator: done waits for every auto-reviewer to settle on the head SHA</title>
<updated>2026-08-28T04:28:15Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-28T04:28:15Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=e413f05ee6eb6062a8b82c48ed685f0414234497'/>
<id>urn:sha1:e413f05ee6eb6062a8b82c48ed685f0414234497</id>
<content type='text'>
A cycle running before the bots posted saw zero findings and reported done;
with a fast-green CI the babysit loop could exit unreviewed. done now needs
every reviewer settled for the current head: Copilot's verdict review
(commit_id), Codex's verdict comment (Reviewed-commit line), its thumbs-up
reaction on the PR body, or the named claude-review check run — with quota/
error notices and the reaction freshness-gated on push time (check-suite
creation, not committer date) and every lookup paginated. pr-babysit re-arms
with backoff on a pending reviewer instead of exiting unactionable, skipping
the pointless final-cycle wait.
</content>
</entry>
<entry>
<title>pr-babysit: never edit HIL rig configs without user approval</title>
<updated>2026-08-27T17:16:26Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-27T17:14:28Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=09164de529cdde8aa4f454361a8dcd413bf43b32'/>
<id>urn:sha1:09164de529cdde8aa4f454361a8dcd413bf43b32</id>
<content type='text'>
The workflow's fix lane once skipped two host tests in test/hil/tinyusb.json
to green a check whose root cause was a failing fixture drive (reverted in
4b11d59a4). Rig rosters describe physical hardware: papering over a fixture
fault hides it from the user who has to swap the part. Now fixAndVerify
strips test/hil/*.json from every fix scope (a group left with no other
files is withheld and logged), the code-writer prompt carries the
constraint, and ok=false keeps such cycles from pushing. HIL stays red
when the fix is a hardware swap - that red is the signal.
</content>
</entry>
<entry>
<title>validate: add claude + codex diff-review stages (opus/high, sol/high)</title>
<updated>2026-08-27T05:21:04Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-27T05:21:04Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=92b38fc3b5e9080c96294079e6117d74429f3448'/>
<id>urn:sha1:92b38fc3b5e9080c96294079e6117d74429f3448</id>
<content type='text'>
The claude stage reviews the diff directly (the code-review skill is a CLI
built-in, unavailable to subagents); the gate is enforced in-script from
structured findings, failing only on confirmed correctness/safety bugs.
</content>
</entry>
<entry>
<title>pr-babysit: overlap a fast review lane with the CI watch</title>
<updated>2026-08-27T05:21:04Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-27T05:21:04Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=208f82efe8b9dc7ca3eb210dcbf455a14c97cf3c'/>
<id>urn:sha1:208f82efe8b9dc7ca3eb210dcbf455a14c97cf3c</id>
<content type='text'>
Review findings are validated, fixed, and pushed without waiting on CI;
checkoutDir decouples the PR checkout from the session cwd. File-less CI
failures are scoped by a dedicated agent, paths canonicalized and
existence-checked via git ls-files, overlapping groups merged. Per-id
reply/resolve accounting retries failures and holds the green exit until
all outward work is drained.
</content>
</entry>
<entry>
<title>agents: split pr-monitor into pr-ci-watcher + pr-review-validator; rename port-dev/driver-reviewer to code-writer/code-verifier; pin model+effort on every agent</title>
<updated>2026-08-27T05:21:04Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-27T05:21:04Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=2b9ddd8b00eb57225fd9caeecd0da1a7954aee06'/>
<id>urn:sha1:2b9ddd8b00eb57225fd9caeecd0da1a7954aee06</id>
<content type='text'>
</content>
</entry>
<entry>
<title>hil: make hil_report.md a rendering of hil_report.json (#3840)</title>
<updated>2026-08-25T08:04:42Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-25T08:04:42Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=375e20090a0a60fd2d47ba6553d189e05482dfea'/>
<id>urn:sha1:375e20090a0a60fd2d47ba6553d189e05482dfea</id>
<content type='text'>
hil_report.json and hil_report.md were written independently. Four writers
produced the markdown and three wrote no JSON at all -- and those three are the
paths where a run died, so a JSON consumer saw nothing exactly when it mattered:
the per-board verdicts an agent hands back reported the whole fleet as "no
report row" while a human read the real story from the markdown.

Every writer now goes through render_report(), so a table can never contain
something the JSON does not. The document gains `scope` (a three-board PR run
and a full run that lost 24 boards were indistinguishable) and `caveat` (how the
run ended). `banner` carries rig health across an --accumulate retry; `caveat`
records how a run ended and must not -- conflating them made a clean retry
publish an abandonment that never happened.

helper/hil_report.py owns the document end to end, dissolving the import cycle
that forced write_timeout_report to compose its own markdown and removing a
duplicate cell classifier kept in sync by hand. hil_summary.py is deleted; its
CLI moves there. hil_ci.sh uploads the sidecar so a remote --accumulate has a
merge bas</content>
</entry>
<entry>
<title>hil: address Copilot review — loud extraction markers, exit-visible variant warnings</title>
<updated>2026-08-20T11:30:45Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-20T11:30:45Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=0fa0ece024fecae0847459b5949c66f40fcc6e11'/>
<id>urn:sha1:0fa0ece024fecae0847459b5949c66f40fcc6e11</id>
<content type='text'>
The workflow-logic harness slices hil-validate.js between marker strings (the body is not
a module; the runtime wraps it, so markers are the only handle). A renamed marker used to
produce a garbage slice and a confusing ReferenceError; it now fails naming the missing
marker, proven by mutating the marker and watching the message.

The variant-warning loop in hil_ci.sh read variant_names through a process substitution --
the exact exit-status blindness the comment in resolve_build_dirs warns about, two
functions earlier in the same file. A plain command-substitution assignment is visible to
set -e, so a malformed roster now aborts instead of silently skipping the warnings.
</content>
</entry>
<entry>
<title>hil: run every board in one hil_test.py and hand results across as JSON</title>
<updated>2026-08-20T09:43:49Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-20T09:43:49Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=6905639b07c69fec68e9ebc77f7d27ac2775ee41'/>
<id>urn:sha1:6905639b07c69fec68e9ebc77f7d27ac2775ee41</id>
<content type='text'>
hil-validate ran one hil-operator per board. That parallelizes at the wrong layer:
hil_test.py already schedules boards across host controllers and budgets concurrent
flashes and usbtest batteries per controller (FLASH_PARALLEL/USBTEST_PARALLEL), and
those permits live in one process - N parallel runs multiply the budget onto the same
uPD720201 cards for no wall-clock gain over one run that already parallelizes. The
workflow now spawns ONE operator with every board as repeated -b.

The operator no longer retypes the report table. Four consecutive max-effort review
rounds found ~15 defects in this file and every one was in reconstructing board
identity from transcribed prose: report rows are named per VARIANT (nanoch32v203 only
ever produces -fsdev/-usbfs rows), a variant need not start with its board's name,
lock contention is a `board-locked` cell rather than a phrase, and each fix introduced
the next round's bug - including a fake-green test that asserted an invariant with the
one input shape that could not break it. The new helper test/hil/helper/hil_summary.py
does the join where the roster lives and emits one machine verdict per board
({board, ran, pass, locked, detail}); the operator returns that JSON verbatim plus
`wedged`, the only field it authors, and the workflow reads fields, never parses a
string. Its cell classifier mirrors hil_test.py's own tally exactly: failures are
always marked ('fail' or a ❌ prefix, TestFail's contract), everything unmarked is a
pass - a passing test may return a plain metric cell like '13443 KB/s', and the
mirrored rule is what keeps a green table from becoming a red verdict.

hil_ci.sh kept only the LAST -b, so multi-board remote runs staged one board's
binaries and every other board died on the rig after its lock and flash slot were
spent. It now parses every -b spelling argparse accepts (with the -bt arms ordered
first, longest-match, so the &lt;config&gt;.failed retry form is never read as a board named
"t..."), pre-flights roster membership and build dirs for ALL boards before anything
is wiped or staged, warns per declared variant with no build dir (which hil_test.py
would silently green-skip), forwards HIL_* knobs as export lines in one %q word the
remote evals ('; '-joined so it round-trips under dash - an authorized
HIL_NO_BOARD_LOCK force must not silently no-op), keeps HIL_REPORT_DIR local because
the copy-backs look in REMOTE_DIR, and copies hil_report.json and the .failed re-run
spec back beside the markdown, deleting stale local copies first so a green run cannot
leave last run's spec looking current.

Retries preserve the fleet: the documented path is the &lt;config&gt;.failed spec, which
already begins with --accumulate; a fresh scoped re-run would unlink the report and
collapse the whole-fleet table to the retried boards alone.

The risky logic is executable, not argued about: .claude/workflows/test-hil-validate.mjs
pins the lookup/verdict helpers and runs in pre-commit (hil-validate-logic); nine
staging tests drive hil_ci.sh through an ssh stub that models the real thing (argv
joined into one string the remote re-splits, heredoc on stdin - the naive echo-stub
passed while the feature was broken); and deliberate mutations of the verdict logic
are all caught.

Validated on the rig: a 2-board run (usbtest 30/30 on both; the pre-fix classifier,
replayed against that run's real report, fails the fully-green stm32f723disco on its
two passing '13443 KB/s' cells), the .failed --accumulate retry (merged report kept
every earlier row), and a 10-run soak over random subsets of a 22-board pool - 43
board-slots, every failure signature matched pre-existing CI state or known flake,
zero tooling failures, no locks left behind.
</content>
</entry>
</feed>
