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-debugCLI - 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/skillsand.claude/skillsviews for CLI providers, with deterministicload_skillsfallback for API providers
The current live status is:
codex-cliis 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-cliandclaude-clion the attachednucleo_l476rg + nrf52840dkboards; exactnrf52833dk, 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-debugshell; post-fix validation reran the Python-change gate, full suite ladder, preloaded skills proof, Branch C attached-board matrix, and representativeb001code-writing repair benchmarks onnucleo_l476rg + nrf52840dkwith 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_idchaining, Anthropic local-memory continuation, prompt render modes, retries/errors, and prompt accounting without requiring unavailable API credits. - the new
pyocd-debugshell 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-cliis no longer globally blocked on this host:- STM32 freeform and
k001benchmark 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 + nrf52840dkpair (b001_wrong_boot_textandb002_wrong_known_valueon both boards) - the official-pair second-provider closure bar is still open
because the attached Nordic board identifies as
0x52840, not the officialnrf52833dk - the
--model sonnetalias path that previously failed withAPI Error: 404 ... model: claude-sonnet-4-20250514should be treated as an alias-specific failure mode, not as proof that the provider path is unusable
- STM32 freeform and
- 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
- shared Stage 1 and benchmark helpers live under
- 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_idcannot 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-summarycompaction 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-cliandclaude-cliare 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_KEYand, 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.
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
nrf52833dksecond-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.
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
- 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, butfirmware.elfis 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.
.python-versionis committed and pins the team interpreter to3.12.- Use
uvas the canonical environment and command surface. - Install
uvfirst on a raw machine; this repo does not assume it already exists. Windows:pip install uvmacOS:brew install uv - Use
uv syncto create.venv/and install the locked dependency set. - Use
uv run ...for repo commands so they always run inside the pinned env. .envis optional, gitignored, and auto-loaded by the MCP server and the Phase A host scripts when present..envcurrently carries:PYOCD_PROBE_UIDPYOCD_TARGET- optional
PYOCD_BOARD_ID/PYOCD_BOARD_CONFIG PYOCD_TURNKEY_PROVIDERfor the turnkey brain backend:openai-api,anthropic-api,codex-cli, orclaude-cliPYOCD_TURNKEY_MODELfor an optional explicit model overridePYOCD_TURNKEY_MEMORY_MODEfor turnkey provider-memory compaction:deterministicormodel-summaryPYOCD_TURNKEY_NATIVE_SYNC_EVERYfor native-session safety-sync cadence (default10;0disables periodic sync injection)PYOCD_TURNKEY_RECENT_TURN_DETAIL_LIMITfor the detailed recent provider memory window (default2)PYOCD_TURNKEY_MID_HISTORY_TURN_LIMITfor the deterministic Tier 2 mid-history provider memory lane (default6)PYOCD_TURNKEY_MID_HISTORY_RENDER_CHAR_LIMITfor the Tier 2 rendered memory cap (default4000)PYOCD_TURNKEY_MEMORY_SUMMARY_MAX_CHARSfor the hard limit on compacted Tier 3 provider memory summaries (default2000)PYOCD_TURNKEY_PRELOAD_COMMON_DETAILSto enable/disable preloading common governed-action details such asconnectandrun_green_check(default enabled)PYOCD_TURNKEY_PRELOADED_SKILLSfor preloaded skills bridge mode:auto(default),off, orrequirePYOCD_TURNKEY_PRELOADED_SKILL_ROOTto override the shipped preloaded skills source rootOPENAI_API_KEYfor the native OpenAI API providerANTHROPIC_API_KEYfor 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>.yamlthrough the shared loader, soconnectneeds no raw target and theget_board_infotool reports the loaded facts. pyocd.local.yamlis an optional per-developer pyOCD override file and is gitignored.pyocd.yamlis 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.
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_validationandrecover_mode - UART expectation metadata
Forbidden examples:
reference_firmware_pathrecovery_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.
Bootstrap the environment:
uv syncOn Windows, the preferred unattended host bootstrap entry point is:
powershell -ExecutionPolicy Bypass -File .\setup_host.ps1 -BoardId nrf52833dkOn macOS, the preferred host bootstrap entry point is:
bash ./setup_host.sh --board-id nrf52833dkWhen 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 -EnsureZephyrBuildEnvbash ./setup_host.sh --board-id nucleo_l476rg --ensure-zephyr-build-envRun host bootstrap for all tracked boards:
uv run python host_bootstrap.pyRun Stage 0 validation for all tracked boards:
uv run python stage0_check.pyWindows PowerShell:
uv run python stage0_check.pyIf 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 nrf52833dkBoard-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.txtFor 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 historyOperator-shell note:
pyocd-debuguses the Rich +prompt_toolkitshell 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 onto 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
/repairstill requires/workspaceand/build-command, and freeform repair context is constrained by workspace containment rather than a hardcodedsrc/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-20250514Turnkey provider rules:
openai-apiusesOPENAI_API_KEYand requires an explicit model from--modelorPYOCD_TURNKEY_MODELanthropic-apiusesANTHROPIC_API_KEYand requires an explicit model from--modelorPYOCD_TURNKEY_MODELcodex-cliuses the locally installedcodexCLI and inherits whatever Codex auth you already configured there, including a ChatGPT/Codex subscription or Codex's own API-key pathclaude-cliuses the locally installedclaudeCLI and inherits whatever Claude Code auth you already configured there, including a Claude subscription orANTHROPIC_API_KEYclaude-cliis 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-apiis 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-primaryvia Responsesprevious_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-primaryvia real--resume <session_id>reuse, with--fork-sessionretries plus strict resume-failure handling - Codex CLI is
remote-primaryvia realcodex exec resume <thread_id>reuse, with same-thread correction retry and strict resume-failure handling - Anthropic Messages API remains
local-primarybecause 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, orclaude-cliestablishes 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
- after
- turnkey memory controls are optional:
--memory-mode deterministic|model-summary--native-sync-every N(10by default;0disables periodic sync)--recent-turn-detail-limit N(2by default)--mid-history-turn-limit N(6by default)--mid-history-render-chars N(4000by default)--memory-summary-max-chars N(2000by default)--no-preload-common-detailsto disable defaultconnectandrun_green_checkdetail preload--preloaded-skills auto|off|requireto control run-local preloaded skills projection for CLI providers--preloaded-skill-root PATHto 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.agentsfolders are written
- Codex CLI receives a run-local
Windows PowerShell:
uv run python host_bootstrap.py --board-id nrf52833dk
uv run python stage0_check.py --board-id nrf52833dkVendor-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_unlockfor Nordic APPROTECT recovery through pyOCD's built-in unlock and mass-erase flowmanual_onlyfor boards that need recover validation but do not yet have an automated Stage 0 implementation
Start the MCP server:
uv run pyocd-debug-mcpRepo-owned firmware rebuilds now go through one cross-platform entrypoint:
uv run pyocd-zephyr-build --ensure-onlyWhat 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/zephyrtagv4.3.0 - reuses
ZEPHYR_SDK_INSTALL_DIRor 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 installextractor 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/nrf52840The 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_64by current Zephyr releases, so Intel Macs must pointZEPHYR_SDK_INSTALL_DIRat a preinstalled older supported SDK or use another supported host
- Setup and bootstrap: init.md
- Bench bring-up operator guide (setup_host, host_bootstrap, stage0_check): stage0_setup.md
- MCP server runtime tools: documented in the tool docstrings in
src/pyocd_debug_mcp/server.py(read by the MCP client over the protocol) - Official Nordic runbook: firmware/nrf52833dk/README.md
- Official STM32 runbook: firmware/nucleo_l476rg/README.md
- Roadmap: markdowns/ROADMAP.md
- Current progress ledger: markdowns/current-progress.md
R12turnkey contract: markdowns/curr/r12_turnkey_spec.md- R12 context/scaffold hardening hard bar: markdowns/curr/r12-context-scaffold-hardening_spec.md
- R12 compact delta indexes hard bar: markdowns/curr/r12-delta-compact-indexes_spec.md
- R12 in-run prompt/memory cost hardening reference: markdowns/curr/r12-in-run-prompt-memory-cost-hardening_reference.md
- R12 Tier 2 memory bridge implementation record: markdowns/curr/r12-tier2-memory-bridge_spec.md
- R12 preloaded skills bridge hard bar: markdowns/curr/r12-preloaded-skills-bridge_spec.md
- R12 preloaded skills invocation proof: markdowns/curr/r12-preloaded-skills-invocation-proof_spec.md
- R12 preloaded skills UX CLI controls gap record: markdowns/curr/r12-preloaded-skills-ux-cli-controls_spec.md
- Wave 2 codebase-map spec: markdowns/curr/wave2-codebase-map_spec.md
- Wave 2 mid-tool checkpoint buffer spec: markdowns/curr/wave2-midtool-checkpoints_spec.md
- Wave 2 provider-visible stream and interrupt spec: markdowns/curr/wave2-provider-stream-interrupt_spec.md
- Archived July 1 current adversarial audit handoff/process ledger: markdowns/tmp/curr-archive-20260701-current-adversarial-audit/wave1-current-adversarial-audit_process.md
- Archived
P0.0layered validation plan: markdowns/tmp/curr-archive-20260628/p0_0_layered_validation_plan.md - Archived
P0.0validation report: markdowns/tmp/curr-archive-20260628/p0_0_validation_report.md - Archived
P0.0doc-sync and superpowers audit spec: markdowns/tmp/curr-archive-20260628/p0-0-doc-sync-superpowers-audit_spec.md - Archived
P0.0static audit fix spec: markdowns/tmp/curr-archive-20260628/p0-0-static-audit-fix_spec.md - Archived Wave 0 reconcile spec: markdowns/tmp/p0-wave0-main-reconcile_spec.md
- Concrete build plan: markdowns/firmware_agent_build_plan_concrete (10).md
- Architecture notes: markdowns/firmware_agent_mcp_architecture.md
Until later roadmap items intentionally revise it, this README.md is the
canonical repo-layout and naming reference for Phase A.
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_l476rgnow 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
R0bootstrap 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-packsandstage0_check.py --board-id nucleo_l476rg --reference-firmware ...both pass, withCOM9+ 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 subprocesspyocd listpenalty 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, UARTboot ok, recover, and shared USB confirmation - the repo now carries full baseline packages for the scoped board pair under
firmware/nrf52833dk/andfirmware/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_l476rgandnrf52833dk - 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, andunlock_recover connect(...)now creates a visiblesession_id, and the runtime session writesruns/<session_id>/logs/events.jsonlplusruns/<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.hexall 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 onlyread_serial, repeated recover failures block onlyunlock_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), andG6(R11) - the tracked Codex benchmark pilot is now live-proven on the scoped pair:
the frozen
pilot_v1suite passed 8/8 cases withfull_success=8,partial_success=0,fail=0, andaverage_score=100.0 - the final minimal
R11hardening expansion is also live-proven:b003_silent_uartandb004_dual_signal_regressionboth reachedFULL_SUCCESSonnucleo_l476rgandnrf52833dk - benchmark session accounting is now hardened for real Codex behavior:
the final structured
session_idis 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
b004bug fixtures preserve the stable Stage 1 symbol-access pattern so Nordic runs cannot “look green� while violating the symbol contract - the first
R12code path now exists in the repo:src/pyocd_debug_mcp/brain/,skills/,pyocd-debug-brain, andtests/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_batchdecisions execute through the brain gate - bounded
wait, UARTwrite_serial, andrun_scriptclient 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
- ordered
- 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, andpyocd-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_b004corpus 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 execbudget 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
nrf52840dkis now live-proven on this Windows host for Zephyr rebuild, Stage 0, Stage 1, and a full six-case alternateR11suite (k001,b001,b002,f001,b003,b004) -
R12is now live-proven through the full frozen 12-case corpus with thecodex-cliprovider onnrf52833dk + nucleo_l476rg:- freeform healthy verification passed on both boards
- the full
pilot_v1_plus_b003_b004turnkey 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 sonnetalias path that previously failed withAPI Error: 404 ... model: claude-sonnet-4-20250514remains a known alias-specific failure mode and should not be treated as the provider's current global status
-
the Pass 1
pyocd-debugshell 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-
NCSportability claim still needs true fresh Windows and macOS host validation that pass