Skip to content

Repository files navigation

Firmware CLI

Firmware CLI is a local embedded-debug and firmware-agent repo built around pyOCD, Stage 0 validation tooling, and an MCP server surface in src/pyocd_debug_mcp/.

This README.md is the canonical repo-level source for layout and naming rules. Detailed bootstrap steps live in init.md. The bench/setup scripts are operated through the single guide stage0_setup.md (run a script with --help for its full flag list). The MCP server's runtime tools are described in the tool docstrings the MCP client reads over the protocol (src/pyocd_debug_mcp/server.py); there is no sidecar doc for them. Today the Stage 0 flow is shell-first, but the intended product direction is shared board-validation logic that is callable from stage0_check.py, future MCP tools, and local programmer flows; only raw host bootstrap remains pre-server.

The portability contract for this repo is post-bootstrap, not universal: supported hosts plus supported boards, after a short documented developer bootstrap equivalent to the setup needed for normal manual board debugging. That bootstrap may still include vendor-driver or vendor-tool installation.

The scoped pair is green through the current R11 benchmark layer: nrf52833dk + nucleo_l476rg have passed the safety/runtime validation, the shared Stage 1 smoke harness, the full MCP surface proof, and the frozen 12-case Codex benchmark corpus. R12 is now implemented in the repo as a turnkey product layer with live Codex/Claude CLI proof on the currently attached STM32/Nordic boards, an exact official-pair proof gap for nrf52833dk, and a new additive Pass 1 operator shell over the same brain/runtime:

  • native Python brain package
  • hybrid-session multi-provider decision backends
  • stable headless turnkey CLI
  • operator-facing pyocd-debug CLI
  • board-aware skills tree
  • sibling turnkey benchmark runner
  • compact MCP tool-index prompting sourced from the local server metadata plus a curated response-semantics overlay
  • preloaded skills projection into run-local .codex/skills and .claude/skills views for CLI providers, with deterministic load_skills fallback for API providers

The current live status is:

  • codex-cli is now live-proven on the scoped pair through the full frozen 12-case turnkey suite: full_success=12, partial_success=0, fail=0, average_score=100.0
  • the normal turnkey path worked from connect(board_id=...) with no hard-coded probe UID or serial-port override
  • for the current prototype boundary, this Codex-backed path is the required proven baseline
  • the current Wave 1 A/B/C merge-back candidate has also passed live provider/hardware Branch C harness checks and explicit two-turn CLI smokes with both codex-cli and claude-cli on the attached nucleo_l476rg + nrf52840dk boards; exact nrf52833dk, live API-provider parity, and fresh-machine proof remain deferred
  • the final current Wave 1 adversarial audit on this Windows host is green after fixing PowerShell BOM-prefixed slash-command handling in the non-interactive pyocd-debug shell; post-fix validation reran the Python-change gate, full suite ladder, preloaded skills proof, Branch C attached-board matrix, and representative b001 code-writing repair benchmarks on nucleo_l476rg + nrf52840dk with both CLI providers
  • credentials-free API-provider simulation is now green through the real OpenAI/Anthropic provider factory and turnkey-loop paths. It covers request/response handling, OpenAI previous_response_id chaining, Anthropic local-memory continuation, prompt render modes, retries/errors, and prompt accounting without requiring unavailable API credits.
  • the new pyocd-debug shell is now implemented on top of the same shared turnkey loop with:
    • structured brain events
    • live tool/progress rendering
    • evidence summaries
    • history/show/rerun flows
    • raw-provider-output visibility after completed turns
  • Wave 2 Module E mid-tool checkpoint buffers are now specified in markdowns/curr/wave2-midtool-checkpoints_spec.md; they are not implemented yet. The target is a generic brain-mediated checkpoint observation buffer for UART/build/client-action streams so providers can inspect null reads, bad reads, stalls, partial output, and early errors before final timeout.
  • Wave 2 Module D/H provider-visible stream and user-interrupt behavior is now specified in markdowns/curr/wave2-provider-stream-interrupt_spec.md; it is not implemented yet. The target is live provider-visible status text or brain-owned heartbeats before final provider output, no hidden chain-of-thought contract, structured decisions as the only authoritative brain input, cancellation before partial output becomes an action, and cleanup/partial-work review.
  • claude-cli is no longer globally blocked on this host:
    • STM32 freeform and k001 benchmark proof now exist through the local Claude CLI path
    • after the 2026-06-29 usage refresh, Claude CLI also passed real code-writing repair prompts on the attached nucleo_l476rg + nrf52840dk pair (b001_wrong_boot_text and b002_wrong_known_value on both boards)
    • the official-pair second-provider closure bar is still open because the attached Nordic board identifies as 0x52840, not the official nrf52833dk
    • the --model sonnet alias path that previously failed with API Error: 404 ... model: claude-sonnet-4-20250514 should be treated as an alias-specific failure mode, not as proof that the provider path is unusable
  • the turnkey/runtime surface is now product-owned rather than test-owned:
    • shared Stage 1 and benchmark helpers live under src/pyocd_debug_mcp/
    • packaged installs bundle the benchmark cases, skill manifests, and turnkey playbooks the runtime depends on
    • provider/runtime failures now persist inspectable turnkey run artifacts even when no board session is ever created
  • turnkey provider continuity inside one top-level run is brain-owned and memory-first:
    • canonical compact local memory is persisted for every provider
    • Codex CLI, Claude CLI, and OpenAI Responses use native continuation as the primary path and periodically inject the compact local memory as a safety sync; the default cadence is every 10 provider turns, and users can change it with --native-sync-every / PYOCD_TURNKEY_NATIVE_SYNC_EVERY
    • OpenAI uses native continuation when healthy; if a stored previous_response_id cannot resume, headless runs fail closed unless an explicit recovery path starts a newly labeled session from saved memory
    • Claude CLI now uses real remote session resume plus fork retry on top of the same local-memory model; resume failure is not silently replaced by a fresh session
    • across separate user prompt runs, the product does not depend on a persistent provider/model session; each new prompt run starts a fresh provider session and reconstructs continuity from the injected updated long memory payload, memory index, 10 extra more-detailed recent turns, 30 more-detailed mid-history turns, and compact skill/turn index
    • Codex CLI now uses real remote thread resume plus same-thread retry / explicit recovery labeling on top of the same local-memory model
    • Anthropic remains local-primary because the current Messages API is stateless and does not expose a resumable native conversation handle
    • deterministic compaction is the default; model-summary compaction is available as an explicit option
    • coarse provider progress checkpoints are now emitted as first-class brain events for runtime/UX inspection
  • current provider integration direction:
    • codex-cli and claude-cli are the current subscription-backed local CLI bridges
    • future Codex hardening should use SDK/app-server style thread and turn APIs behind the same brain-owned provider adapter
    • Claude subscription use should remain a BYO local Claude Code CLI adapter unless Anthropic approves SDK/subscription use for this product
    • Claude API-key use should go through ANTHROPIC_API_KEY and, later, a Claude Agent SDK adapter; the current Anthropic Messages path depends on the brain-owned memory layer rather than a Claude Code-style session

For the current prototype branch plan, second-provider parity and true fresh-machine Windows/macOS portability are explicit deferred risks, not the current Wave 0 gate.

What The Repo Currently Delivers

The repo still inherits the Phase A layout and naming decisions, but the working baseline is now broader than Phase A alone. It includes the stable board-control substrate, the runtime/safety layer, and the first live benchmark proof on the scoped pair.

Today that means:

  • one canonical source tree under src/pyocd_debug_mcp/
  • tracked board definitions in boards/
  • repo-owned test-firmware scaffolding in firmware/
  • untracked runtime-output space in runs/
  • test and harness scaffolding in tests/
  • a local MCP server entrypoint plus host and Stage 0 validation scripts
  • a shared Stage 1 smoke harness
  • a tracked Codex benchmark corpus and benchmark runner
  • a Codex-proven turnkey brain and turnkey benchmark path over the same corpus
  • an implemented Pass 1 operator-facing UX shell over the same brain
  • an open official-pair second-provider validation gap; STM32 Claude CLI proof exists, but full scoped-pair closure is not complete
  • deferred second-provider and fresh-machine portability proof work
  • current attached-board deployment proof has now been run with both local CLI providers on nucleo_l476rg + nrf52840dk, including preloaded skills usage, Branch C harness checks, multi-turn governed decisions, and representative code-writing repair benchmarks
  • remaining deployment proof gaps are external to the current attached-board surface: exact official nrf52833dk second-provider closure, live API-provider parity, and fresh-machine Windows/macOS portability

The official scoped board pair for the real Phase A / Phase B bench path is nrf52833dk plus nucleo_l476rg. The repo also carries nrf52840dk as a retained alternate Nordic profile for future support work, but it is no longer the board that blocks the current Phase A / Phase B gates.

Canonical Tree

Firmware-CLI/
|-- .python-version
|-- pyproject.toml
|-- uv.lock
|-- README.md
|-- init.md
|-- setup_host.ps1
|-- setup_host.sh
|-- host_bootstrap.py
|-- stage0_check.py
|-- stage0_setup.md
|-- packs/
|   |-- manifest.yaml
|   |-- README.md
|   `-- live_index_repair.md
|-- boards/
|   |-- nrf52833dk.yaml
|   |-- nrf52840dk.yaml
|   `-- nucleo_l476rg.yaml
|-- firmware/
|   |-- README.md
|   |-- nrf52833dk/
|   |   |-- reference/src/
|   |   |-- reference/build/
|   |   |-- recovery/
|   |   `-- bugs/
|   |-- nrf52840dk/
|   |   |-- reference/src/
|   |   |-- reference/build/
|   |   |-- recovery/
|   |   `-- bugs/
|   `-- nucleo_l476rg/
|       |-- reference/src/
|       |-- reference/build/
|       |-- recovery/
|       `-- bugs/
|-- runs/
|   `-- README.md
|-- skills/
|   |-- README.md
|   |-- common/
|   |-- mcu_families/
|   |-- client_owned_fallback_workflow_skills/
|   `-- preloaded_skills/
|-- playbooks/
|   `-- turnkey/
|-- tests/
|   |-- README.md
|   |-- fixtures/
|   |-- cases/
|   |   |-- README.md
|   |   |-- suites.yaml
|   |   `-- r11_result_schema.json
|   `-- harness/
|       |-- stage1_smoke.py
|       |-- r11_benchmark.py
|       |-- r12_turnkey_benchmark.py
|       `-- preloaded_skills_usage.py
|-- src/
|   `-- pyocd_debug_mcp/
|       |-- adapters/
|       |   |-- swd_interface.py
|       |   |-- swd_pyocd.py
|       |   |-- uart_interface.py
|       |   `-- uart_pyserial.py
|       |-- guardrails/
|       |   |-- flash_gate.py
|       |   `-- recover_gate.py
|       |-- brain/
|       |   |-- actions.py
|       |   |-- app.py
|       |   |-- benchmark.py
|       |   |-- cli.py
|       |   |-- config.py
|       |   |-- evidence.py
|       |   |-- events.py
|       |   |-- loop.py
|       |   |-- mcp_client.py
|       |   |-- playbooks.py
|       |   |-- provider_anthropic.py
|       |   |-- provider_claude_cli.py
|       |   |-- provider_codex_cli.py
|       |   |-- provider_factory.py
|       |   |-- provider_openai.py
|       |   |-- provider_parsing.py
|       |   |-- provider_types.py
|       |   |-- skills.py
|       |   |-- state.py
|       |   |-- tool_schemas.py
|       |   `-- workspace.py
|       |-- ux/
|       |   |-- artifacts.py
|       |   |-- cli.py
|       |   |-- commands.py
|       |   |-- history.py
|       |   |-- renderer.py
|       |   `-- shell.py
|       |-- services/
|       |   |-- convergence_watcher.py
|       |   |-- session_runtime.py
|       |   |-- symbols.py
|       |   |-- target_control.py
|       |   `-- uart_capture.py
|       |-- __init__.py
|       |-- board_config.py
|       |-- board_config_cli.py
|       |-- benchmark_support.py
|       |-- local_env.py
|       |-- pack_index_repair.py
|       |-- probe_inventory.py
|       |-- reference_artifacts.py
|       |-- reference_smoke.py
|       |-- runtime_resources.py
|       |-- serial_resolver.py
|       |-- server.py
|       |-- timeouts.py
|       |-- zephyr_build.py
|       `-- target_errors.py
|-- scratch/
|   `-- README.md
`-- markdowns/
    |-- ROADMAP.md
    |-- firmware_agent_build_plan_concrete (10).md
    |-- firmware_agent_mcp_architecture.md
    |-- current-progress.md
    |-- repo_file_index.md
    |-- curr/                 # step-scoped docs for the current/active step (graduate to tmp/ when done)
    |   |-- README.md
    |   |-- r12-context-scaffold-hardening_spec.md
    |   |-- r12-delta-compact-indexes_spec.md
    |   |-- r12_turnkey_spec.md
    |   |-- r12-in-run-prompt-memory-cost-hardening_reference.md
    |   |-- r12-tier2-memory-bridge_spec.md
    |   |-- r12-preloaded-skills-bridge_spec.md
    |   |-- r12-preloaded-skills-invocation-proof_spec.md
    |   |-- wave2-codebase-map_spec.md
    |   `-- wave2-midtool-checkpoints_spec.md
    `-- tmp/                  # step-scoped / throwaway docs no longer needed after their step

Naming Rules

  • Board IDs are lowercase stable identifiers and key all per-board paths.
  • Product code lives only under src/pyocd_debug_mcp/.
  • Repo-owned reference firmware lives under firmware/<board>/reference/.
  • The canonical symbol-bearing baseline artifact name is firmware/<board>/reference/build/firmware.elf.
  • Additional flash artifacts may sit beside firmware.elf, but firmware.elf is the stable symbol-resolution artifact name.
  • Recovery assets live under firmware/<board>/recovery/.
  • Bug variants live under firmware/<board>/bugs/<id>__<slug>/.
  • Runtime outputs live under runs/<session_id>/....
  • User firmware never lives in this repo and is always supplied later as runtime input.

Environment Standard

  • .python-version is committed and pins the team interpreter to 3.12.
  • Use uv as the canonical environment and command surface.
  • Install uv first on a raw machine; this repo does not assume it already exists. Windows: pip install uv macOS: brew install uv
  • Use uv sync to create .venv/ and install the locked dependency set.
  • Use uv run ... for repo commands so they always run inside the pinned env.
  • .env is optional, gitignored, and auto-loaded by the MCP server and the Phase A host scripts when present.
  • .env currently carries:
    • PYOCD_PROBE_UID
    • PYOCD_TARGET
    • optional PYOCD_BOARD_ID / PYOCD_BOARD_CONFIG
    • PYOCD_TURNKEY_PROVIDER for the turnkey brain backend: openai-api, anthropic-api, codex-cli, or claude-cli
    • PYOCD_TURNKEY_MODEL for an optional explicit model override
    • PYOCD_TURNKEY_MEMORY_MODE for turnkey provider-memory compaction: deterministic or model-summary
    • PYOCD_TURNKEY_NATIVE_SYNC_EVERY for native-session safety-sync cadence (default 10; 0 disables periodic sync injection)
    • PYOCD_TURNKEY_RECENT_TURN_DETAIL_LIMIT for the detailed recent provider memory window (default 2)
    • PYOCD_TURNKEY_MID_HISTORY_TURN_LIMIT for the deterministic Tier 2 mid-history provider memory lane (default 6)
    • PYOCD_TURNKEY_MID_HISTORY_RENDER_CHAR_LIMIT for the Tier 2 rendered memory cap (default 4000)
    • PYOCD_TURNKEY_MEMORY_SUMMARY_MAX_CHARS for the hard limit on compacted Tier 3 provider memory summaries (default 2000)
    • PYOCD_TURNKEY_PRELOAD_COMMON_DETAILS to enable/disable preloading common governed-action details such as connect and run_green_check (default enabled)
    • PYOCD_TURNKEY_PRELOADED_SKILLS for preloaded skills bridge mode: auto (default), off, or require
    • PYOCD_TURNKEY_PRELOADED_SKILL_ROOT to override the shipped preloaded skills source root
    • OPENAI_API_KEY for the native OpenAI API provider
    • ANTHROPIC_API_KEY for the native Anthropic API provider
  • With a board id set, the MCP server resolves that board's facts (target, recover policy, silicon id, baud) from boards/<board>.yaml through the shared loader, so connect needs no raw target and the get_board_info tool reports the loaded facts.
  • pyocd.local.yaml is an optional per-developer pyOCD override file and is gitignored.
  • pyocd.yaml is optional and only belongs in the repo once the team has a real shared pyOCD option to commit.
  • Host and Stage 0 validation default to all non-example tracked boards.
  • For first-time bring-up on one physical bench, use --board-id <board> to scope the run to the board you actually have attached.

Board YAML Policy

Tracked board YAML is hardware-focused.

Allowed examples:

  • board identity and display name
  • MCU family
  • probe family
  • pyocd_target
  • baudrate
  • probe and serial hint terms
  • recover policy such as requires_recover_validation and recover_mode
  • UART expectation metadata

Forbidden examples:

  • reference_firmware_path
  • recovery_image_path
  • project paths
  • build commands
  • artifact output paths
  • any user- or session-scoped path

Canonical artifact locations are part of the repo tree and naming standard, not tracked board-config fields in this Phase A layout.

Main Workflows

Bootstrap the environment:

uv sync

On Windows, the preferred unattended host bootstrap entry point is:

powershell -ExecutionPolicy Bypass -File .\setup_host.ps1 -BoardId nrf52833dk

On macOS, the preferred host bootstrap entry point is:

bash ./setup_host.sh --board-id nrf52833dk

When the machine must also rebuild the repo-owned Zephyr firmware locally, opt into the managed Zephyr bootstrap during host setup:

powershell -ExecutionPolicy Bypass -File .\setup_host.ps1 -BoardId nucleo_l476rg -EnsureZephyrBuildEnv
bash ./setup_host.sh --board-id nucleo_l476rg --ensure-zephyr-build-env

Run host bootstrap for all tracked boards:

uv run python host_bootstrap.py

Run Stage 0 validation for all tracked boards:

uv run python stage0_check.py

Windows PowerShell:

uv run python stage0_check.py

If Stage 0 cannot auto-resolve the UART endpoint, it prompts in an interactive terminal. In non-interactive runs, rerun with --port BOARD_ID=PORT.

Run host bootstrap and Stage 0 for one board on your bench:

uv run python host_bootstrap.py --board-id nrf52833dk
uv run python stage0_check.py --board-id nrf52833dk

Board-scoped host_bootstrap.py --board-id ... behavior:

  • it now requires a unique matching probe for each selected board
  • it now attempts board-specific serial resolution for each selected board
  • on success it prints the matched probe UID/description and the matched serial port
  • if serial matching is still ambiguous, it warns with rerun guidance using --port BOARD_ID=PORT

Run the turnkey CLI in freeform verify/diagnose mode:

uv run pyocd-debug-brain run --board-id nrf52833dk --task "Verify this reference firmware is healthy and explain why."
uv run pyocd-debug-brain run --provider codex-cli --board-id nrf52833dk --task "Verify this reference firmware is healthy and explain why."
uv run pyocd-debug-brain run --provider claude-cli --board-id nrf52833dk --task "Verify this reference firmware is healthy and explain why."
uv run pyocd-debug-brain run --board-id nrf52833dk --task "Diagnose this board interaction." --memory-mode model-summary --native-sync-every 0
uv run pyocd-debug-brain run --provider codex-cli --board-id nrf52833dk --task-file prompt.txt

For long prompts, prompts with JSON examples, or prompts containing many shell quotes, prefer --task-file prompt.txt or --task-stdin over inline --task. That keeps the task text portable across Windows PowerShell, macOS shells, and CI before the turnkey brain receives it.

Register run-local Branch B client actions with repeatable --client-action NAME=PATH. The action is loaded into the current brain run, listed in the provider prompt with its content hash, and executed by run_script(name, inputs) through the brain gate rather than as a general host executor:

uv run pyocd-debug-brain run --provider codex-cli --board-id nucleo_l476rg --client-action uart_write=tests/fixtures/client_actions/uart_write.py --task "Use the registered uart_write action, then verify UART boot text."

Run the operator-facing CLI over the same turnkey brain:

uv run pyocd-debug
uv run pyocd-debug run --board-id nrf52833dk --task "Verify this reference firmware is healthy and explain why."
uv run pyocd-debug run --board-id nrf52833dk --task-file prompt.txt
uv run pyocd-debug benchmark --case-id nrf52833dk__k001_reference_green
uv run pyocd-debug history

Operator-shell note:

  • pyocd-debug uses the Rich + prompt_toolkit shell in a real TTY and falls back to plain non-live printing when stdout is not interactive
  • the REPL now defaults to summary-first output; use /raw on to show full completed provider turns live
  • generic freeform run --task "fix ..." is now diagnose-first and no longer pre-refuses based only on wording
  • explicit /repair still requires /workspace and /build-command, and freeform repair context is constrained by workspace containment rather than a hardcoded src/ root
  • the REPL now supports persistent repair/artifact context and guided commands:
    • /workspace, /build-command, /flash-artifact, /elf
    • /verify, /diagnose, /repair
    • /prompt, /diff, /serial, /score, /events
    • /memory-mode, /native-sync-every
    • /recent-turn-detail-limit, /mid-history-turn-limit
    • /mid-history-render-chars, /memory-summary-max-chars

Run the turnkey benchmark against one case or the full frozen 12-case suite:

uv run pyocd-debug-brain benchmark --case-id nrf52833dk__k001_reference_green
uv run pyocd-debug-brain benchmark --suite pilot_v1_plus_b003_b004
uv run pyocd-debug-brain benchmark --provider anthropic-api --case-id nrf52833dk__k001_reference_green --model claude-sonnet-4-20250514

Turnkey provider rules:

  • openai-api uses OPENAI_API_KEY and requires an explicit model from --model or PYOCD_TURNKEY_MODEL
  • anthropic-api uses ANTHROPIC_API_KEY and requires an explicit model from --model or PYOCD_TURNKEY_MODEL
  • codex-cli uses the locally installed codex CLI and inherits whatever Codex auth you already configured there, including a ChatGPT/Codex subscription or Codex's own API-key path
  • claude-cli uses the locally installed claude CLI and inherits whatever Claude Code auth you already configured there, including a Claude subscription or ANTHROPIC_API_KEY
  • claude-cli is a local user-controlled provider dependency, not bundled Claude access. The user authenticates Anthropic's official CLI; this app does not collect, proxy, or repackage Claude subscription credentials.
  • anthropic-api is the API-key-backed Claude path. It is stateless at the Messages API layer and uses the brain's compact memory/compaction layer rather than Claude Code-style session resume.
  • provider continuity inside one top-level run is unified across all backends:
    • the brain always persists compact local turn-fact memory
    • ordinary later turns render compact canonical state and focused loaded details rather than repeating the full bootstrap task body, full decision schema, and full provider memory
    • local memory is tiered: Tier 1 keeps the detailed recent window, Tier 2 keeps deterministic mid-history compact facts, and Tier 3 keeps the bounded rolling summary; Tier 2 is compacted from Tier 1 without model calls, while provider/model summary mode is only eligible when Tier 2 overflows into Tier 3
    • selected turnkey skill facts may render in full for in-run bootstrap/sync paths when explicitly loaded or visible, while new top-level prompt runs use the compact skill/turn index by default and do not inject full skill bodies or full tool schemas unless details are selected and loaded
    • remote-primary providers periodically receive that compact memory as a safety sync; the default is every 10 provider turns, configurable with --native-sync-every / PYOCD_TURNKEY_NATIVE_SYNC_EVERY
    • OpenAI is remote-primary via Responses previous_response_id; failed resume is a typed provider-resume failure unless the operator explicitly starts a new labeled session from saved memory
    • Claude CLI is remote-primary via real --resume <session_id> reuse, with --fork-session retries plus strict resume-failure handling
    • Codex CLI is remote-primary via real codex exec resume <thread_id> reuse, with same-thread correction retry and strict resume-failure handling
    • Anthropic Messages API remains local-primary because the current Messages API surface is stateless and does not expose a resumable conversation handle equivalent to OpenAI response chaining, Claude session resume, or Codex thread resume
  • deployment policy should treat real provider session handles strictly:
    • after openai-api, codex-cli, or claude-cli establishes a real provider handle, a later resume failure must stop the run or ask the operator before starting a new provider session
    • silent fresh-session fallback is a recovery mode, not normal continuation
    • headless runs should fail closed by default; interactive shell runs should offer retry resume, start new session from saved local memory, or abort
  • turnkey memory controls are optional:
    • --memory-mode deterministic|model-summary
    • --native-sync-every N (10 by default; 0 disables periodic sync)
    • --recent-turn-detail-limit N (2 by default)
    • --mid-history-turn-limit N (6 by default)
    • --mid-history-render-chars N (4000 by default)
    • --memory-summary-max-chars N (2000 by default)
    • --no-preload-common-details to disable default connect and run_green_check detail preload
    • --preloaded-skills auto|off|require to control run-local preloaded skills projection for CLI providers
    • --preloaded-skill-root PATH to override shipped preloaded skills packages
    • .env: PYOCD_TURNKEY_MEMORY_MODE, PYOCD_TURNKEY_NATIVE_SYNC_EVERY, PYOCD_TURNKEY_RECENT_TURN_DETAIL_LIMIT, PYOCD_TURNKEY_MID_HISTORY_TURN_LIMIT, PYOCD_TURNKEY_MID_HISTORY_RENDER_CHAR_LIMIT, PYOCD_TURNKEY_MEMORY_SUMMARY_MAX_CHARS, PYOCD_TURNKEY_PRELOAD_COMMON_DETAILS, PYOCD_TURNKEY_PRELOADED_SKILLS, PYOCD_TURNKEY_PRELOADED_SKILL_ROOT
  • preloaded skills projection is CLI-provider-only:
    • Codex CLI receives a run-local .codex/skills/<skill_id> view and is prompted to prefer $<skill_id> or explicit preloaded skills requests
    • Claude CLI receives a run-local .claude/skills/<skill_id> view and the provider command includes --allowedTools Skill(<skill-id>)
    • OpenAI API and Anthropic API stay fallback-only and use load_skills(skill_ids=[...])
    • no global user .codex, .claude, or .agents folders are written

Windows PowerShell:

uv run python host_bootstrap.py --board-id nrf52833dk
uv run python stage0_check.py --board-id nrf52833dk

Vendor-assisted serial auto-detect is used when the helper CLI is available:

  • Nordic + J-Link boards: nrfjprog --com
  • ST-LINK boards: STM32_Programmer_CLI -l

These CLIs improve auto-detect but are not required for every board. If they are missing or a board remains ambiguous, Stage 0 falls back to prompting or an explicit --port override.

Tracked board YAML now uses a typed recover_mode selector rather than a free-form recover command string. Today stage0_check.py supports:

  • nrf_pyocd_unlock for Nordic APPROTECT recovery through pyOCD's built-in unlock and mass-erase flow
  • manual_only for boards that need recover validation but do not yet have an automated Stage 0 implementation

Start the MCP server:

uv run pyocd-debug-mcp

Zephyr Rebuilds

Repo-owned firmware rebuilds now go through one cross-platform entrypoint:

uv run pyocd-zephyr-build --ensure-only

What it does:

  • reuses an existing Zephyr workspace when ZEPHYR_WORKSPACE_DIR, ZEPHYR_BASE, ~/zephyrproject, or a detected NCS workspace is already present
  • otherwise bootstraps a managed upstream Zephyr workspace under the local cache and pins it to zephyrproject-rtos/zephyr tag v4.3.0
  • reuses ZEPHYR_SDK_INSTALL_DIR or an existing SDK when one is already on the machine
  • otherwise detects common global NCS toolchain installs and, if none are usable, downloads and installs the Zephyr SDK toolchain component it needs into the local cache with the repo helper's own managed archive/setup path rather than requiring a preinstalled west sdk install extractor workflow

Direct board builds are then:

uv run pyocd-zephyr-build --app-dir firmware/nucleo_l476rg/reference/src --build-dir firmware/nucleo_l476rg/reference/build --board nucleo_l476rg
uv run pyocd-zephyr-build --app-dir firmware/nrf52833dk/reference/src --build-dir firmware/nrf52833dk/reference/build --board nrf52833dk/nrf52833
uv run pyocd-zephyr-build --app-dir firmware/nrf52840dk/reference/src --build-dir firmware/nrf52840dk/reference/build --board nrf52840dk/nrf52840

The helper preserves the live Zephyr build tree and defaults to incremental west build -p auto behavior so repeated agent rebuilds stay fast. Pass --pristine always when a full clean reconfigure is actually required.

For R11 benchmark cases, the nested Codex prompt is intentionally self-contained so the benchmark agent spends its time on the board task rather than re-reading repo workflow docs. That benchmark-only rule does not change the real product path: non-benchmark deployment runs should still load the repo workflow docs and skills before they edit, rebuild, flash, or diagnose.

The per-board build_reference.sh / build_bug.sh wrappers now delegate to that same helper. NCS is optional: if it is already installed, the helper reuses it; if not, the helper can provision a managed upstream-Zephyr build path itself.

Current limitation:

  • managed Zephyr SDK install is not supported on macOS x86_64 by current Zephyr releases, so Intel Macs must point ZEPHYR_SDK_INSTALL_DIR at a preinstalled older supported SDK or use another supported host

Docs

Until later roadmap items intentionally revise it, this README.md is the canonical repo-layout and naming reference for Phase A.

Verification Status

Verified:

  • non-hardware verification: this document's tree, command surface, and doc links match the current root-level scripts and docs
  • latest clean-slate rerun on the macOS mixed-board host re-proved:
    • both scoped-board probes visible together
    • strict host_bootstrap.py --board-id ... success for both scoped boards, including matched probe UID + matched serial-port reporting
    • STM32 Stage 0 + Stage 1 smoke
    • Nordic Stage 0 + recover + Stage 1 smoke
  • hardware-backed STM32 proof on this Mac host: nucleo_l476rg now passes Stage 0 connect, flash, and UART through the shared target-control services
  • the STM32 bench truth is fully closed in repo status, including the confirmed shared USB correlation between the visible ST-Link probe and /dev/cu.usbmodem144403
  • the canonical Windows R0 bootstrap path has been verified on a real Windows host
  • the STM32 Windows Stage 0 path is now also bench-proven on a real Windows host: host_bootstrap.py --board-id nucleo_l476rg --install-packs and stage0_check.py --board-id nucleo_l476rg --reference-firmware ... both pass, with COM9 + ST-Link visibility confirmed through the shared path
  • the Windows MCP stdio connect(board_id="nucleo_l476rg") regression is also closed on real hardware: shared probe discovery now uses the pyOCD Python API first instead of paying the subprocess pyocd list penalty inside the live server process
  • the scoped Nordic board is now nrf52833dk, and its Stage 0 path is bench proven on this Mac host for probe visibility, exact silicon identity, flash, UART boot ok, recover, and shared USB confirmation
  • the repo now carries full baseline packages for the scoped board pair under firmware/nrf52833dk/ and firmware/nucleo_l476rg/
  • the tracked Stage 1 proof surface is now tests/harness/stage1_smoke.py; the old scratch API harness is retired
  • the tracked Stage 1 smoke harness now passes on both scoped boards: nucleo_l476rg and nrf52833dk
  • the full current MCP tool surface is now live-validated on both scoped boards through server.py: connect, disconnect, get_board_info, get_state, halt, resume, step, reset, read_core_register, write_core_register, read_memory, read_memory_block, write_memory, set_breakpoint, remove_breakpoint, flash_firmware, read_serial, and unlock_recover
  • connect(...) now creates a visible session_id, and the runtime session writes runs/<session_id>/logs/events.jsonl plus runs/<session_id>/run-metadata/session.json
  • flash guardrails are live-proven on the scoped pair: default tracked baseline flash, explicit valid .elf, and explicit valid .hex all succeed; missing-path and invalid-suffix inputs refuse deterministically
  • recover guardrails are live-proven on the scoped pair: Nordic recover succeeds with confirm=true, while STM32 recover refuses deterministically because no supported recover mode is tracked
  • mutation watchers are live-proven on the scoped pair: repeated flash failures block only flash_firmware, repeated UART misses block only read_serial, repeated recover failures block only unlock_recover, and disconnect/reconnect clears block state
  • the current scoped milestones are now green through product #1: G1 (R2 + R3), G3 (R6 + R7 + R8), G4 (R9), G5 (R10), and G6 (R11)
  • the tracked Codex benchmark pilot is now live-proven on the scoped pair: the frozen pilot_v1 suite passed 8/8 cases with full_success=8, partial_success=0, fail=0, and average_score=100.0
  • the final minimal R11 hardening expansion is also live-proven: b003_silent_uart and b004_dual_signal_regression both reached FULL_SUCCESS on nucleo_l476rg and nrf52833dk
  • benchmark session accounting is now hardened for real Codex behavior: the final structured session_id is the canonical case root, and extra MCP sessions are recorded as runner warnings rather than automatic failures
  • benchmark scoring now treats the runner-owned final verification as authoritative, and the b004 bug fixtures preserve the stable Stage 1 symbol-access pattern so Nordic runs cannot “look greenâ€Â� while violating the symbol contract
  • the first R12 code path now exists in the repo: src/pyocd_debug_mcp/brain/, skills/, pyocd-debug-brain, and tests/harness/r12_turnkey_benchmark.py
  • the current Branch A provider/prompt layer is also in place:
    • provider capabilities and loop-owned provider session state are explicit
    • all providers share one canonical compact local-memory model
    • that model now has deterministic Tier 2 mid-history memory between the recent detailed window and the bounded Tier 3 rolling summary
    • OpenAI uses native continuation as an accelerator, with strict resume-failure handling and optional periodic safety sync
    • Claude CLI and Codex CLI now add real remote continuation on top of that same canonical compact memory model
    • Anthropic remains local-primary because the current Messages API does not expose a resumable native conversation handle
  • the current Branch B action layer is also in place:
    • ordered action_batch decisions execute through the brain gate
    • bounded wait, UART write_serial, and run_script client actions are model-visible action choices
    • session-scoped client actions are audited in run artifacts and server-native calls from those actions route back through the same governed brain path
    • model-facing server-tool docs now come from live MCP metadata through brain/tool_schemas.py
  • the first Pass 1 UX-layer code path now also exists in the repo: src/pyocd_debug_mcp/ux/, src/pyocd_debug_mcp/brain/events.py, and pyocd-debug
  • the turnkey layer no longer depends on Codex CLI or MCP registration: it launches the local MCP server as a subprocess and talks to it directly
  • the turnkey benchmark path reuses the frozen pilot_v1_plus_b003_b004 corpus and the existing case manifests instead of inventing a second benchmark taxonomy

Latest turnkey verification:

  • benchmark bug-repair cases now allow a longer default codex exec budget so diagnose -> patch/build -> flash/verify runs are not cut off by a blanket sub-60-second cap while they are still making progress

  • the retained alternate Nordic profile nrf52840dk is now live-proven on this Windows host for Zephyr rebuild, Stage 0, Stage 1, and a full six-case alternate R11 suite (k001, b001, b002, f001, b003, b004)

  • R12 is now live-proven through the full frozen 12-case corpus with the codex-cli provider on nrf52833dk + nucleo_l476rg:

    • freeform healthy verification passed on both boards
    • the full pilot_v1_plus_b003_b004 turnkey suite passed: full_success=12, partial_success=0, fail=0, average_score=100.0
    • the normal path used connect(board_id=...) without hard-coded UID or serial-port tuning
    • no forbidden recover usage occurred on non-recover cases
    • no case watcher-blocked due to turnkey thrash
  • deferred prototype risk that is not yet re-proved:

    • full official-pair second-provider closure
    • true fresh-machine Windows/macOS portability proof
    • multi-prompt user-computer deployment proof, including provider-session isolation per prompt and real bug-repair code-writing validation
    • strict provider-session resume-failure proof: bad/missing resume handles must not silently create a fresh provider session
  • current official-pair second-provider gap:

    • the STM32 Claude CLI path is proven on this host, but the official-pair freeform + suite closure bar is still open
    • the --model sonnet alias path that previously failed with API Error: 404 ... model: claude-sonnet-4-20250514 remains a known alias-specific failure mode and should not be treated as the provider's current global status
  • the Pass 1 pyocd-debug shell is implemented and green under the local non-hardware test/lint/typecheck ladder, but provider-visible streaming before final provider output and user-interruptible provider turns are still intentionally deferred to the next UX follow-up

  • the broader self-contained no-NCS portability claim still needs true fresh Windows and macOS host validation that pass

About

automated code-writing, self-testing & debugging firmware agent that runs in CLI

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages