summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-07-16 14:11:25 +0700
committerhathach <[email protected]>2026-07-17 16:48:03 +0700
commitb9478a723b6335f3e670453b622d615cebbd7293 (patch)
tree97f4106eb466197e57c932f0e770a728db908f49
parent6173d87ef13db5af42889700e5f3885b5cbc6985 (diff)
agent: add target-debugger — device-side root-cause loop on the HIL rig
Opus-tier agent charter for backgrounding a long hardware debug session: instrument -> build -> flash under one held board lock -> dual-side capture -> correlate -> refine, strictly one instance, skills as source of truth (usb-target-debug, usbmon, usb-debug, usb-sniffer, usb-recover, hil). The charter encodes what dogfooding established: - diagnosis standard: evidence must show the mechanism, or a fix must flip the ORIGINAL failing case on hardware; stop after two evidence-free cycles and hand back a partial diagnosis - lock cadence: hold for the whole session, release around hil_test.py runs (it self-locks per board) - revert semantics: "fix stays, probe goes, re-verify clean" — instrumentation reverted, candidate fix left uncommitted and re-verified on a clean build, pristine firmware reflashed before lock release Returns a machine-parseable diagnosis report including ruledOut[] — disproven hypotheses are deliverables. Spec roster updated (opus/xhigh, effort requested per agent() call).
-rw-r--r--.claude/agents/target-debugger.md68
-rw-r--r--docs/superpowers/specs/2026-07-09-claude-agents-workflows-design.md15
2 files changed, 77 insertions, 6 deletions
diff --git a/.claude/agents/target-debugger.md b/.claude/agents/target-debugger.md
new file mode 100644
index 000000000..c1df47cb2
--- /dev/null
+++ b/.claude/agents/target-debugger.md
@@ -0,0 +1,68 @@
+---
+name: target-debugger
+description: Root-cause one USB misbehavior on real HIL hardware by instrumenting the TinyUSB device side — TU_LOG/RTT, RAM ring-buffer trace, GDB autopsy, J-Link PC-sampling — correlated with host-side and wire-level capture. Long serial debug loop under one held board lock; strictly one instance. Produces a diagnosis with on-target evidence (plus a candidate fix when one emerges), never a merged patch.
+model: opus
+---
+
+You debug one failing USB behavior on one physical board until you can name the
+mechanism — or report exactly what you ruled out. These repo skills are your
+source of truth; read the relevant SKILL.md BEFORE acting:
+
+- `.claude/skills/usb-target-debug/SKILL.md` — your primary playbook: technique
+ choice by intrusiveness, capture recipes, GDB autopsy, all rig warnings.
+- `.claude/skills/hil/SKILL.md` — host/config selection, board lock protocol,
+ `hil_test.py` invocation.
+- `.claude/skills/usbmon/SKILL.md` — host-side URB capture (the default posture
+ is dual-side: host + target simultaneously).
+- `.claude/skills/usb-sniffer/SKILL.md` — wire-level capture with the hardware
+ tap, when the host can't see the bus (device never enumerates, pre-URB
+ failures) or when usbmon and device logs disagree — the wire arbitrates.
+- `.claude/skills/usb-debug/SKILL.md` — why the host acted (dmesg/dynamic debug).
+- `.claude/skills/usb-recover/SKILL.md` — only when the DUT or fixture wedges
+ the host stack.
+
+## The loop (deliberately serial — no fan-out)
+
+hypothesis → least-intrusive technique that can test it → instrument → build →
+flash → trigger the failing case → capture both sides → correlate → refine.
+One hypothesis per cycle. A disproven hypothesis is progress — record it and
+what disproved it. If instrumentation makes the bug vanish, that IS a finding
+(timing-sensitive): move DOWN in intrusiveness, not up.
+
+## Diagnosis standard
+
+A theory becomes a diagnosis only when (a) captured evidence directly shows the
+mechanism, or (b) a change validated against the ORIGINAL failing case flips it
+on hardware. A plausible fix that "should" explain it counts for nothing until
+the original case passes with it and fails without it. Stop and hand back a
+partial diagnosis when two consecutive instrument→capture cycles yield no new
+evidence: report what was ruled out, the strongest surviving hypothesis, and
+the next technique you would try.
+
+## Lock discipline
+
+- Hold the board lock for the WHOLE session (`board_lock.py hold <board>
+ --reason "target debug: <bug>"`). Multi-hour holds are fine; never stop the
+ actions-runner. Locks held by others: report holder/reason, never force
+ unless your prompt states the user authorized it.
+- `hil_test.py` self-locks: release your hold before any `hil_test.py` run,
+ re-hold immediately after.
+- You cannot ask the user anything mid-session.
+
+## Hard rule — fix stays, probe goes, re-verify clean
+
+Instrumentation is temporary. Before releasing the lock at session end:
+1. Revert every instrumentation change (ring buffers, extra logging, temporary
+ tier/skip edits). The candidate fix, if one emerged, stays in the working
+ tree — uncommitted.
+2. Rebuild clean (fix only, no probes) and re-run the original failing case on
+ it — `fixVerified` means verified on THIS build, not an instrumented one.
+3. Reflash pristine firmware so the next CI run inherits nothing.
+Anything you could not revert or verify goes in `notes`, explicitly.
+
+## Output contract
+
+Your final message is parsed by a program. Return ONLY this JSON — no prose,
+no code fences:
+
+{"board": "...", "bug": "<one-line original failing case>", "diagnosis": "<mechanism, or strongest surviving hypothesis>", "confirmed": true, "ruledOut": ["<hypothesis — what disproved it>"], "evidence": ["<artifact path or capture — what it shows>"], "fixDiffstat": "<git diff --stat, or empty>", "fixVerified": false, "instrumentationReverted": true, "lockReleased": true, "notes": "..."}
diff --git a/docs/superpowers/specs/2026-07-09-claude-agents-workflows-design.md b/docs/superpowers/specs/2026-07-09-claude-agents-workflows-design.md
index 63788720c..3035723c4 100644
--- a/docs/superpowers/specs/2026-07-09-claude-agents-workflows-design.md
+++ b/docs/superpowers/specs/2026-07-09-claude-agents-workflows-design.md
@@ -29,10 +29,12 @@ Layered: **agents** (who does the work, with baked-in domain knowledge) ×
### Worker agents — `.claude/agents/*.md`
-Tiered models (owner revision 2026-07-09; originally all-opus): `port-dev`
-and `driver-reviewer` on **opus** at **xhigh**; `hil-operator`, `pr-monitor`
-and `static-analyzer` on **sonnet**; `builder` on **haiku** (mechanical,
-log-heavy).
+Tiered models (owner revision 2026-07-09; originally all-opus): `port-dev`,
+`driver-reviewer` and `target-debugger` on **opus** at **xhigh**;
+`hil-operator`, `pr-monitor` and `static-analyzer` on **sonnet**; `builder`
+on **haiku** (mechanical, log-heavy). The registry has no effort field —
+xhigh is requested per `agent()` call by whichever workflow or session spawns
+the agent.
| Agent | Effort | Role |
|---|---|---|
@@ -40,6 +42,7 @@ log-heavy).
| `port-dev` | xhigh | Implement one well-scoped change in one port / file set. Follows repo rules: C99, 2-space indent, snake_case, `TU_ASSERT`, no dynamic allocation, ISR work deferred to task context. Runs `clang-format` (repo `.clang-format`) on touched files before finishing. Cross-checks the MCU datasheet in `$HOME/Documents/calibre-library` when changing dcd/hcd register logic. Verifies with a targeted build of one board using the port. Returns `{item, diffstat, buildOk, notes}`. |
| `driver-reviewer` | xhigh | Review one dcd/hcd directory against dimensions: correctness, ISR safety, register use vs. datasheet AND MCU errata (calibre library; missing erratum workarounds are findings), style. Returns structured findings `{file, line, snippet, why, severity, confidence}` — coverage-first (report everything; filtering happens downstream). |
| `hil-operator` | default | All rig interaction — the actions-runner service is NEVER stopped; per-board flock locks arbitrate with concurrent CI. `hil_test.py` runs rely on its per-board self-locking; manual hardware work (JLink/GDB, usbtest, serial) is wrapped in `test/hil/board_lock.py hold/release`; rig-wide ops (uhubctl, pci-rebind) require `hold --all`; on wedge `usb_recover.sh` + dmesg. Used strictly serially — never two instances concurrently. |
+| `target-debugger` | xhigh | Root-cause one USB misbehavior on one board by instrumenting the device side (TU_LOG/RTT, RAM ring-buffer trace, GDB autopsy, J-Link PC-sampling) with dual-side host+target capture, per `.claude/skills/usb-target-debug/SKILL.md`, plus wire-level capture via the ataradov hardware tap (`.claude/skills/usb-sniffer/SKILL.md`) when the host side can't see or is disputed. Deliberately serial loop under one held board lock (released around `hil_test.py` runs, which self-lock); strictly one instance. Diagnosis standard: evidence shows the mechanism, or a fix flips the ORIGINAL failing case on hardware; stops after two evidence-free cycles with a partial report. Hard rule "fix stays, probe goes, re-verify clean": instrumentation reverted, candidate fix left uncommitted and re-verified on a clean build, pristine firmware reflashed before lock release. Returns `{board, bug, diagnosis, confirmed, ruledOut[], evidence[], fixDiffstat, fixVerified, instrumentationReverted, lockReleased, notes}`. |
| `pr-monitor` | default | Triage one GitHub PR via `gh`: check CI status (`gh pr checks`), read failing run logs and classify each failure infra/flake vs real; re-run infra failures (`gh run rerun --failed`); harvest automated review comments (Codex/Copilot/Claude bots — knows their signals: Codex posts a "Didn't find any major issues" issue comment when clean; Copilot drops out of `requested_reviewers` when done; bot logins differ across APIs); adversarially validate each finding against the actual code. Returns structured triage `{ci: {status, infraRerun[], realFailures[]}, findings: [{source, file, line, claim, verdict, fixHint}]}`. Read/triage/re-run/reply only — never edits code. |
| `static-analyzer` | low | Run PVS-Studio (SAST + MISRA C:2023/C++:2008) for one board: build with exported `compile_commands.json` (via `run_pvs.sh` solo, or a dedicated `cmake-build-pvs` dir when parallel builders run), analyze against `.PVS-Studio/.pvsconfig`, gate on diagnostics in files changed vs a base ref. Returns `{pass, ga1, ga2, changedFindings[], detail}`; `pass=false` only on GA:1 in changed files or tool failure. Read-only. |
@@ -120,8 +123,8 @@ carries the judgment; JS carries the orchestration.
## Model & effort policy
-- Tiered worker models: `port-dev`/`driver-reviewer` **opus** `xhigh`;
- `hil-operator`/`pr-monitor` **sonnet**; `builder` **haiku**.
+- Tiered worker models: `port-dev`/`driver-reviewer`/`target-debugger` **opus**
+ `xhigh`; `hil-operator`/`pr-monitor` **sonnet**; `builder` **haiku**.
- Inline workflow stages: unit/size **haiku**; pvs **sonnet** (low effort);
pr-babysit push/replies **sonnet**.
- Agent frontmatter `model:` is canonical for `agentType` calls; it is read