The public record of where this project stands, and the file the issue templates point at. Keep it current: if a statement here stops being true, change it here first.
Agents:
agents/STATUS.mdis the single source of truth for state - the measured numbers, the open list with an owner per row, each agent's Current, and what only a human can do. This file is the long-form public write-up of the same thing; where the two disagree, the one whose number names the command that printed it wins, and the other is corrected in the same commit.
-
Last updated: 2026-09-13 (round 6, AGENT-1 — the CH32H417 per-peripheral mapping audit: one measured truth table on six axes, and every document that states a number about it made to agree with the tool)
-
New 2026-09-13: CH32H417 has a per-peripheral truth table, and building it found generated C that could not have run.
docs/COVERAGE.md's ledger asks whether every fact the DATASHEET states is accounted for; it said 0 open and still says so. It does not ask how far the APP models each peripheral, and nothing did, so four documents quoted four different versions of that. Now measured, peripheral by peripheral, on six axes — pins, settings, params, clock, interrupt, codegen reach — plus whether a fixture exercises it and whether it compiles:tests/evidence/round6/2026-09-13-h417-peripheral-map.md.The finding: I2S2 and I2S3 had no clock-enable bit at all. Switching either on emitted
I2S_Init(SPI2, &s)under the comment "I2S2 has no clock enable bit in codegen.periph_clock — none is written". It compiled, and on silicon it would have configured an I2S whose clock was never turned on — the same shape as theUSBFS→RCC_HBPeriph_OTG_FSmiss this file already records, one peripheral over. The I2S is a MODE of the SPI block (RM ch.23 is "SPI/I2S") and shares its gate, which is what the EVT does one line above its ownI2S_Init(data/sources/H417/Evt/EXAM/DFSDM/DFSDM_I2S_Audio/Common/hardware.c:103). Modelled.Of the six unexplained
clockcells, three more are genuine absences and are now declared with afile:line: DBGMCU is a core CSR (R32_DBGMCU_CR, "Offset address: 0x7C0(CSR)",CH32H417RM.md:66907), HSEM and IPC are core-private at0xE000C000and0xE000D000(:6652,:6332; the RM names them as private peripherals in words at:4313). The RCC gates HB/HB1/HB2 and none of the three sits on one. The sixth, RTC, is left OPEN on purpose: its gate is TWO bits —RCC_HB1Periph_PWR | RCC_HB1Periph_BKP(.../RTC/RTC_Calendar/Common/hardware.c:94) — andcodegen.periph_clockmaps one peripheral to one bit, so writing BKP alone would be a half-enable that reads exactly like a whole one. TASKS.md carries it, owner AGENT-2 for the schema half.Cells: 41 → 36. What remains is 35
params(23 of them routing pins) and that oneclock, every one with an owner and a live TASKS.md line. Nothing was closed by lowering anything: the collision ceiling is untouched at QFN68 4 / QFN88 0 / QFN128 0,SOFT_CELLSis still exactly{params, clock}, and the ledger still reads 0 open on all six parts. -
Verified 2026-09-13:
python tools/validate_mcu.py→ 0 errors, 99 warnings ·python tools/verify_sdk_names.py→ 0 errors, 0 warnings ·python tools/coverage.py --gate→ 6 of 6 ·python tools/ledger.py --write→ no change ·python build.py→ OK ·node tests/run.js→ ALL GREEN, 780 tests, 0 skipped (389 s). Nothing has been flashed; every green above is a compile or a check against a document. -
Superseded (round 5, AGENT-3 cycle 2): CI made publish-ready, the case-sensitivity defects that preparing it exposed, and one reading order for the data sources: markdown first, PDF last, in
tools/source_docs.pyandtests/source_order.test.js -
Branch:
main, remoteorigin https://github.com/arwidcool/WCH_Cube.git. One shared working tree, three agents, one working directory (agents/); rounds 1–4 are archived underagents/history/. -
New 2026-09-12 (human-directed): the coverage ledger, and the numbers it found. Every gate in this repository proved an MCU file was consistent; nothing proved it was complete against the sources, which is how the committed CH32H417 file carried 207 signals routed to pins that no setting could claim, CH32L103's ADC routed ten channels with no way to select one, and USBFS/USBHS/USBSS/TKEY said "holds no pin on any package" while the datasheet gives every one of them pads.
tools/coverage.pynow reads the datasheet's pin table (both readings, where the DS has two), the RM's chapter list and the SPL header's instances on every run and joins them to the file: a row is modelled, absent with afile:lineindata/coverage/<PART>.yaml, or open, andtests/coverage.test.jsholds every part to its declared status.validate_mcu.pygained the reverse-direction check (a routed signal no choice claims is an ERROR) and thepins: { none | open }declaration a routing-less peripheral must make. Coverage, quoted frompython tools/coverage.py --quietat 2026-09-12T22:38Z — the numbers move as rows close, so read the tool rather than this line. On CH32H417 this figure went 113 → 104 → 58 → 50 → 0 in a single afternoon, and CH32L103 and CH32V003 followed. All six parts now readstatus: complete:CH32H417 OPEN 0 (modelled 1256, absent 23, disagreements 45) CH32L103 OPEN 0 (modelled 272, absent 11, disagreements 1) CH32V003 OPEN 0 (modelled 116, absent 10, disagreements 0) CH32V005 OPEN 0 (modelled 214, absent 14, disagreements 0) CH32V006 OPEN 0 (modelled 218, absent 11, disagreements 0) CH32X035 OPEN 0 (modelled 278, absent 11, disagreements 0)All six read
status: completeas of 2026-09-12T21:05Z, re-measured 2026-09-13; this line previously still said CH32V003 and CH32L103 werein_extraction, which stopped being true the cycle deliverable F closed. No part is owned, and no count may rise — never reported as done while the tool prints an open row.CH32H417 reaching 0 open rows is not the same as CH32H417 being finished, and the difference is the point of this round. The ledger asks whether every fact the DATASHEET states has been accounted for. It now has been — including 45
disagreements:entries, each citing BOTH readings with afile:linerather than averaging them (the SerDes pairs are the clearest: DS Table 2-1-1 line 3791 putsSERDES_TXPon PE3 and Table 2-2-20 line 7341 putsSERDES_RXPthere; both are recorded, neither is picked). What the ledger does NOT ask is what the APP does with those facts, and asking that found a defect the same day the count reached zero:tests/h417_packages.test.jssweeps every mode choice on each package (428 then, 450 when re-measured 2026-09-13, the menu having grown) and found 26 / 20 / 17 choices that put two signals on one pad although the signal had another bonded pad free — reached by doing nothing but switching a peripheral on, and invisible to the conflict engine because both claims share an owner. Now 4 / 0 / 0, ratcheted, owner AGENT-1. A part can pass every gate and still be half-built if no gate reads the datasheet; it can also pass the gate that reads the datasheet and still hand the user a configuration the silicon cannot honour. The tool found two things on the "clean" parts too: CH32V003's USART1 clock pin (declared open, not absent) and a handful of conversion typos (X0forXO,C1NOforC1N0, a tornACK4) now recorded as corrections with the line they came from. Planted breaks: 21/21 caught.docs/COVERAGE.mdis the process;CLAUDE.mdputs it first for every session. -
Verified this pass:
python build.py→ OK ·node tests/run.js→ ALL GREEN, 0 skipped ·python tools/validate_mcu.py→ 0 errors, 5 warnings ·python tools/verify_sdk_names.py→ 0 errors ·node tools/wchcube_cli.js --project <fixture> --strict→ exit 0 on all five fixtures, in both the default format and--format c·git archive→ 1.7 MB with no vendor material in it. -
New this pass: the repository is ready to publish, and getting it there found two real defects — one of them the class this repo exists to catch. The workflows had never been read by anything, because at that date there was no remote. (Corrected 2026-09-12: a remote arrived, the workflows ran 31 times, failed every one and nobody read them; §1 has the green run.)
ci.ymldid not install PlatformIO anywhere, socodegen_compile,generated_projectandfirmware_nativeall skipped — and a skipped gate reads as a green job, which is how "the generated C compiles" stood unproven for a whole round once before. Itsdesktopjob asked forlibappindicator3-dev, which does not exist on a current Ubuntu runner. Both fixed, plus arelease.ymlthat publishes on av*tag, andtests/release.test.jsnow checks the plumbing the way everything else here is checked — including that the workflow which installs PlatformIO is the same one that runs the compile gate, and that a commented-outpip install platformiodoes not satisfy it, which the first version of that check wrongly accepted. -
Also new this pass, and this is the interesting one:
data/sources/V003/had landed with the wrong capitalisation, and every gate in the repo passed anyway.evt/anddatasheets/where the other two parts,data/sources/README.mdandtools/verify_sdk_names.pyall sayEvt/andDatasheets/. On Windows — this box — the lookup resolves case-insensitively and 23 headers were checked. On the Linux runner the same lookup would have found nothing, reported CH32V003 NOT CHECKED, and exited 0: a green tick over a part that was never checked. Renamed (not deleted) with the dangling citations inCH32V003.yamlandCH32V003.notes.mdrepaired in the same write, andtests/source_paths.test.jsnow resolves everydata/sources/...path any tracked file cites against the case the filesystem actually has, with both a wrong-case path and a wrong-casecodegen.sdk.evtplanted to prove it bites. It is the same defect ascodegen.header: ch32v00x.hone field over, and it was found by preparing CI rather than by running it.- New this pass: the data sources have one reading order, and it is markdown first, PDF last. Every drop arrives as a PDF with a converted.mdbeside it, and the conversion is now the documented working source: it greps, it diffs, it can be cited by line, and a second pass can check it. The original PDF is the last resort — opened only when the conversion is missing, unreadable or demonstrably incomplete, and then by a script that prints aPDF FALLBACK:line saying why, recovers by word position with a check against numbers the datasheet states elsewhere, and writes the recovered cells back into the repo so the page is opened once. It is implemented intools/source_docs.py(both recovery scripts and the L103 markdown parser now resolve through it), stated indata/sources/README.md, carried intoagents/README.md,agents/STATUS.md,docs/ADDING-A-PART.md,docs/HOW-IT-WORKS.md,data/FORMAT.mdand the PR template, and enforced bytests/source_order.test.js: a PDF with no conversion beside it fails the build, and so does a script that opens a PDF without saying why. Two stale claims went with it, and they are why the rule needed writing down:data/mcus/CH32L103.notes.mdsaid "This drop has no PDF" and asked a human for a second source whileCH32L103DS0.PDFsat in the folder, anddata/sources/README.mdstill said the EVT folders were empty and listed two parts where there are now five.- Also new this pass: the issue chooser's threecontact_linkspointed athttps://github.com/OWNER/REPO/...— three buttons that would 404 the moment the repository was published. Removed, with the guidance moved into the templates as relative markdown (which is correct in a fork and after a rename), and a check that fails if that placeholder ever comes back. -
Verified this pass (previous cycle):
node tests/run.js→ 510 green, 0 skipped. (Superseded: the same command at 2026-09-13 is ALL GREEN, 780 tests, 0 skipped.) -
Also new (previous cycle): a FOURTH part arrived mid-cycle, and every gate it touched was already wide enough to catch it.
data/mcus/CH32V003.yamllanded (DATA, whose brief did not include it).tests/strict.test.js's coverage check failed immediately, which is what it is for — a shipped part with no.wchprojbehind the--strictgate has unproven generated C — and so didtests/completeness.test.js, onCH32V003 OPA1: nvic. Both were closed rather than excused: a fifth fixture (CH32V003_TSSOP20_full.wchproj, USART1 + I2C1 + TIM1 PWM + AIN2 + five GPIOs, remap 0 throughout), a fourth firmware environment, and aWCH_HAL_SERIES_CH32V003branch indata/firmware/lib/wch_halbecause CH32V003's SPL series isch32v00x— small x — and its die has no GPIOB at all (ch32v00x.hdefines GPIOA, GPIOC and GPIOD only, andch32v00x_rcc.h:87-89the matching clock bits). The OPA has no interrupt vector on this part where CH32V006 hasOPCM_IRQn = 40, so that cell is declared ABSENT with its citation rather than left blank.pio run -e CH32V003F4P6builds the generated C. -
New this pass: every choice the app offers is now one the silicon can honour — and the two halves that make that true were written against two different schemas.
data/mcus/CH32X035.yamlcarries seven citedconstraints:entries at the document root in the shapedata/FORMAT.mddocuments;app/engine/constraints.jsreadsgpio.constraintswithfield:/deny:/allow:/on_pins:/off_pins:. Because the placement fails first the consumer sees an EMPTY list, sogpioConstraintProblems()reported nothing — not an error, not a warning — while the GPIO table went on offering Pull-down on PC0 and an output mode on every shorted pair. QA-FAIL on the board at 22:11Z, corrected and narrowed at 22:25Z, and closed in the same cycle when the consumer moved to the documented shape. That is the round's defect class arriving in the round's own mechanism, and it was invisible to every gate in the repo. -
New this pass: one Generate button, one archive, and no invented silicon. Three changes, all on the delivery edge rather than in the engine:
- GENERATE CODE and GENERATE PROJECT became ONE action. The split was
never real: the init code without
platformio.ini,src/main.cand a README around it is a handful of files in Downloads that nobody can flash. There is now one file list, one button (two, doing literally the same thing: the breadcrumb and the Project Manager), and one delivery:<Name>.zipin a browser,<folder>/<Name>/on the desktop or through the File System Access API. The pin table and clock summary moved underdocs/inside the archive. - The page no longer carries its own ZIP writer.
app/template.htmlhad a second, hand-rolled copy of the format next toapp/engine/zip.js— ~60 lines nothing tested, beside oneapp/tests/zip.test.jsproves against a real unzipper. The page is now a thinzipOf()over the engine'szipFiles; one implementation, one set of evidence. WCH-DUMMY32-C8is out of the shipped data. It is not silicon, and it was in the part selector and in the built bundle (726 KB → 675 KB). It moved totests/fixtures/mcus/, kept rather than deleted because it is still the only fixture that reaches LQFP100/LQFP144, that has three GPIO speeds, that uses a different NVIC priority scheme, and that spells the HSE pinsOSC_IN/OSC_OUT.tests/data.test.jsnow fails if a synthetic part reappears indata/mcus/, andtests/clock_ui.test.jscovers the "no HSE, no PLL" clock shape on CH32X035 instead.
- GENERATE CODE and GENERATE PROJECT became ONE action. The split was
never real: the init code without
-
Not yet true, and the words matter: nothing here has been flashed. Every green result in this repository is a compile. See §6 and
agents/STATUS.md§6 item 8. -
New this pass: the CH32X035 DMA request map that the RM's markdown conversion had destroyed (RM Table 9-2 — rows kept, columns lost) is recovered from the original PDF by word position, cross-checked against the surviving row order and nine EVT-example anchors, all three agreeing on all 27 requests:
agents/proposals/CH32X035_dma_requests.yaml. The original RM (V1.9) and DS (V2.2) PDFs now sit beside their conversions. -
New this pass, and the point of the round: a SECOND FAMILY compiles. CH32X035's generated C builds for
CH32X035G8U6through the same gate as the three CH32V00x fixtures, and the fixture that produces it contains no part-specific code — the data decides that the clock enable isRCC_APB2PeriphClockCmdrather thanRCC_PB2PeriphClockCmdand that the one GPIO speed is 50 MHz rather than 30.tests/no_part_names.test.jsholds that line:app/engine/andapp/template.htmlname no part outside comments. -
Not yet true, and the words matter: nothing here has been flashed. Every green result in this repository is a compile. See §6 and
agents/STATUS.md§6 item 8.
Related documents, none of which this file duplicates:
| Read this | For |
|---|---|
TASKS.md |
the backlog, claimed and unclaimed |
agents/README.md |
the working agreement: ownership, cycle, gates, rules, this box |
agents/STATUS.md |
the single source of truth — the measured state, the round's deliverables and their evidence, the three agents' standing briefs and Currents, the acceptance script, the human-only items, the backlog |
agents/BOARD.md |
the current message board — decisions, handoffs, QA results |
agents/history/INDEX.md |
rounds 1–5, what each produced, and why they are archived |
data/FORMAT.md |
the MCU YAML schema — the DATA↔ENGINE contract |
data/firmware/ARCHITECTURE.md |
firmware layering, seams and ownership |
data/sources/README.md |
where hardware facts come from, and the EVT rule |
Two halves, at very different maturities.
The configurator — an STM32CubeMX-style pinout, clock, peripheral and code
generator for WCH RISC-V parts, written as plain JS/SVG/YAML and built by
build.py into one offline dist/index.html. It loads, it is tested by 780
automated tests across two harnesses (node tests/run.js at 2026-09-13,
ALL GREEN, 0 skipped), and it carries six real parts — CH32V003, CH32V005,
CH32V006, CH32X035, CH32L103 and CH32H417 — with the synthetic part moved out of
data/mcus/ entirely into tests/fixtures/mcus/. Every choice it offers is now
supposed to be one the silicon can honour, which is round 5's rule and the
constraint mechanism's job.
Six parts is not six equal parts, and the difference is measured rather than
asserted. The five small parts are complete on every axis. CH32H417 is a 78-peripheral
part whose ledger reads 0 open — every fact the datasheet states is accounted for — while
39 of its 78 peripherals have a params: block and 39 do not; 35 of those 39 are cells
somebody still owes (the other four are declared ABSENT) and 23 of the 35 route pins, so
their pads can be claimed and nothing about them configured. The per-peripheral table, on
six axes, is tests/evidence/round6/2026-09-13-h417-peripheral-map.md.
The firmware — data/firmware/ is a PlatformIO project that compiles for real
CH32V003 / CH32V005 / CH32V006 / CH32X035 / CH32L103 / CH32H417 silicon with the WCH EVT
NoneOS SDK. It exists to
turn the configurator's output into an ELF, and to make "the generated C compiles"
a checkable claim rather than a hope. It builds today, for four environments.
The honest one-line summary: the tool works, two families generate and compile, and the round-5 mechanism that stops it offering impossible choices is real but was found disconnected from its own data on the day it landed. Nothing has ever been flashed.
CI IS GREEN, and this is the first time all three jobs have passed in this repository's
history. Run https://github.com/arwidcool/WCH_Cube/actions/runs/34723740365
on b5a654f: build + tests (ubuntu), firmware + generated project (windows, the whole
suite WITH PlatformIO, so the compile gates execute instead of skipping) and tauri shell (linux) all success. The test job's Coverage ledger step prints 6 of 6 on the
runner, which nothing had ever seen off this one Windows box.
Re-read 2026-09-13T20:15Z (AGENT-3, §5.1 dry-run): it did not stay unbroken. Run 66
(448b628) failed build + tests at its dist/index.html is up to date step — a stale
committed build, the exact recurring class this section's history already names — which
skipped the rest of that job's steps in consequence; firmware + generated project and
tauri shell (linux) still passed on that same run. The next run, 67
(593a82e, https://github.com/arwidcool/WCH_Cube/actions/runs/34779187164), is green on
all three jobs again, every step, including the Coverage ledger step's 6 of 6 and a real
PlatformIO compile (not a skip) in firmware + generated project. Not three consecutive
commits seen green: most commits between the two land in the same git push as a later
one and GitHub runs CI once per push, on the push's final commit — so the run history is
sparser than the commit history, and reading "green" on one run as covering every commit
folded into it would be exactly the thing this section exists to stop doing.
How it got there is worth keeping, because none of it was the code being wrong: the pack
said CI had never run and it had run 31 times and failed every one, unread; the failure
annotations were written but never executed, because GitHub runs run: blocks under
-eo pipefail and the failing pipeline aborted the step first; and 15 of 20 runs were
cancelled by the next agent's push, so only one commit in five was ever verified.
Evidence: tests/evidence/round6/2026-09-12-ci-30-failures.md.
- Engine split into 12 ESM modules under
app/engine/with zero DOM access;build.pyinlines them sodist/index.htmlstays a single offline file, and Node imports the same modules for tests. - Conflict engine: shorted pin pairs treated as one physical pin, exposed pads, per-package reset pins, pre-emptive warnings in the picker, conflict banner.
- Clock engine with
clockCalc, HSE coupling, out-of-spec detection. mcu.inherits:with map merge / list replace /mcu.remove:— CH32V005 is defined as a delta on CH32V006.- Project save/load (
.wchprojYAML), undo/redo, exports (pin table md + CSV, clock summary md), C code generation (app/engine/codegen.js). tools/wchcube_cli.js— the whole engine without a browser.- CH32V006 fully extracted from DS v2.0 + RM v1.4: 7 packages, remap tables
re-derived by an independent parser with 0 differences over 232 pin
assignments, EXTI map, DMA1 request map, 18 peripherals, 29 NVIC vectors,
23 DMA requests with defaults,
params:for USART1/2, SPI1, I2C1, TIM1, TIM2, ADC1, and acodegen:register-encoding block. - CH32V005 via
inherits:, pinout verified against DS Table 2-2 (0 diffs). WCH-DUMMY32-C8synthetic part covering QFN12 → LQFP144 for layout testing — moved totests/fixtures/mcus/(2026-09-12): a fixture is not something the app should offer in its part selector.tools/validate_mcu.py— schema, geometry, pin existence, remap parity, I/O counts, EXTI legality, HSE coupling, codegen names, DMA and NVIC checks. 14 planted breaks, 14 caught.
- CubeMX-shaped frame: menu bar, breadcrumb, four tabs, three-column pinout view.
- Chip SVG with rotation, mirroring, SVG export, search; pin picker; peripheral tree with status glyphs; GPIO settings table; user labels; keyboard shortcuts with an overlay; dark theme; system block-diagram view.
- Parameter Settings tab, driven by the MCU file's
params:. - Round-2 P0a: the clock tab. HSE is selectable everywhere, selecting it
auto-enables RCC's crystal and claims XI/XO through the normal conflict engine,
the crystal frequency moves SYSCLK, out-of-DS-max goes red. QA-verified in a
real browser across three parts (
tests/clock_ui.test.js, 14 checks).
- ~293 tests over
app/tests/(engine) andtests/(QA), one runner. tests/codegen_compile.test.js— the compile gate. Generates C from a checked-in.wchprojthat assigns pins, drops it intodata/firmwareand runspio runfor three part/package/environment combinations. Planted-break tested: three breaks, three caught. Builds in its own.pio/gatetree so a concurrentpio runcannot make it lie in either direction.- Skips are first class.
tests/lib/harness.jshasskip(reason)and the runner counts skips, prints the reason, and lists every one above the verdict on a green run. A check that did not run is never reported as a pass. tests/smoke.jsdrives every MCU × every package with a silent-console assertion;tests/layout.test.jschecks fit at 1280×720 and 1920×1080.- Real-browser evidence with screenshots in
tests/evidence/round2/. - Engine unit tests reach 100 % of
app/engine/*exports.
data/firmware/: a complete, building PlatformIO project. Four environments, SDK-style layering, components as PlatformIO libraries.data/firmware/ARCHITECTURE.md— layers, seams, the two-clock-owners hazard, the EVT authority rule, and the concurrency/ownership rules.data/sources/README.md— the EVT note: EVT sources are provided per MCU underdata/sources/<PART>/Evt/and are authoritative. They have landed for V006 and X035 — section 5.- Two defects in the generated C found by compiling it, and both now closed — section 6.
agents/STATUS.md§6 item 5 settled — section 6.tests/codegen_compile.test.js, the compile gate — section 7. Three configurations that assign pins, generated and built for their own environments.src-tauri/compiled for the first time — and it had never compiled; section 6.- Round 3 put under git.
data/firmware/, this file, the round-3 pack (nowagents/history/round3/) and the whole reorganiseddata/sources/tree were all untracked.
Round 6 — "a gate nobody has watched run is a guess" is open. The brief, the six
deliverables with an owner per row, the acceptance script and each agent's Current are all in
agents/STATUS.md. That file is the single source of truth and this
section does not duplicate it — the reason it does not is that four documents once carried
three different versions of the same params: count, and one of them was this one.
The round exists because round 5 closed with four things that were green and not true: the
coverage ledger at zero on the day 63 default pin collisions were found; three Python gates
green over a file js-yaml refused to parse; header checks silently continue-ing past three
of six parts; and a CI that the pack recorded as "never run" and which had in fact run 31 times
and failed every one, unread. None of those was a wrong gate. Each was a gate that had never
been seen to fail on the thing it was supposed to catch. So: mutate, watch it go red,
restore, and keep the red output — tests/evidence/round6/.
Where the six deliverables stand, in one line each (measured, agents/STATUS.md §1 and §2):
| Deliverable | Owner | State | |
|---|---|---|---|
| A | CI executes and is read green | QA | done; worktrees ON deliberately withheld until three consecutive commits from different agents are green |
| B | every gate proven able to fail | QA | done for tests/** — the list of gates that cannot go red is empty; app/tests/** not yet swept |
| C | CH32H417's Parameter Settings stop being empty | DATA | open — 35 cells owed, 23 of them routing pins |
| D | the clock schema holds a second PLL and a per-peripheral mux | APP schema, DATA data | schema and validator landed. The CH32H417 data is proven and not shipped: it computes USBFS 48 MHz, but the clock tab cannot draw a second PLL at 1280x720 @125% (1095px into a 1024px viewport), so shipping it would put main red. Parked in agents/proposals/ |
| E | the per-instance init struct | APP mechanism, DATA data | mechanism landed; TIM PWM, the LTDC layers and CH32L103's comparators all emit |
| F | CH32L103 and CH32V003 at 0 open rows | DATA | complete — the ledger reads zero on all six parts |
The per-line list with its owner is agents/STATUS.md §2 and TASKS.md. This is the short
version, ordered by what blocks the most.
- CH32H417's
params:— 34 cells, 22 of which route pins (DATA). UHSIF landed 2026-09-13; next worst-first by how many pads a missing block strands: SERDES, FMC, QSPI1/2, SDMMC, SAI, PIOC, then CAN1–3, DAC, LPTIM1/2, GPHA, RTC. Five of them — FMC, ETH, ECDC, FMC_NAND, FMC_SDRAM — are blocked behind one APP change: a struct whose member is a pointer to a second struct that no SDK function takes alone. - CH32H417's clock data (DATA, blocked on APP). The USBHS_PLL + USBFS block is written,
measured and NOT shipped: it computes 48 MHz and the number moves with both axes of the mux,
but the clock tab cannot draw a second PLL at 1280x720 @125% zoom - 1095px painted into a
1024px viewport - so committing it puts
mainred. It is parked inagents/proposals/CH32H417_usbhs_pll.yamlwith the measurements. Three more PLLs and seven muxes are behind it, and behind two further engine changes, not facts: a fixed PLL output that is conditional on its input (USBHSPLL_REFSEL[1:0]makes 480 MHz true only when the reference selection matches the real input - so USBHS_PLL ships with its one input whose frequency the file fixes, and the other three are declared), and a mux entry that carries its own divider (LTDC's choice is "SERDES_PLL clock divided by 2", not a bare source). - The four default pin collisions left on QFN68 (DATA). They have waited three rounds.
COLLISION_CEILINGis a ratchet: it may only go down. - The planted-break sweep over
app/tests/**(QA). 17 files with almost none.tests/**is done and its list of gates-that-cannot-go-red is empty;app/has not had the same pass. - CH32X035's remaining
params:(DATA) — OPA, CMP1/2/3, USBFS/USBPD, TKEY (no EVT header at all, so every write is a raw register), the ADC internal Vrefint channel on both families, and USART LIN / SmartCard / IrDA. Still anIN_EXTRACTIONentry intests/completeness.test.jswith a liveTASKS.mdline, which means those cells print with an owner rather than failing a shared gate — and become hard failures the day the line is ticked. src-taurirelinked and seen in a window.cargo buildis green and the Rust unit tests run in the suite, but the round-4 changes towrite_projecthave never been relinked into a running exe — the old one was open at the time. Do not claim the desktop path writes a project until somebody has watched it.agents/STATUS.md§6 item 7.- The acceptance script run end to end,
agents/STATUS.md§5, with the QA-PASS line posted saying what failed. - More parts. CH32V203 and CH32V307 have no DS/RM in
data/sources/yet (agents/STATUS.md§6 item 4). CH32V003 is extracted and reads 0 open. Adding a part is meant to be a data job; every part that needs an engine change is a finding worth recording. - A human flashes one generated project.
agents/STATUS.md§6 item 8. It is the highest-value open item in the repository and the only one nobody here can close.
| Part | Sources present | YAML | PlatformIO env | Notes |
|---|---|---|---|---|
| CH32V006 | DS v2.0 + RM v1.4, data/sources/V006/Datasheets/ |
complete | CH32V006F8P6, CH32V006K8U6 |
the reference part. 7 packages, 18 peripherals, 29 vectors, 23 DMA requests |
| CH32V005 | same RM (family) | inherits: CH32V006 |
CH32V005F6P6 |
drops TKEY, TIM3, QFN32 |
| CH32X035 | DS v2.2 + RM v1.9, data/sources/X035/Datasheets/ + EVT, 2 239 files |
landed; validate_mcu.py 0 errors, verify_sdk_names.py 0 errors |
CH32X035G8U6 |
the second family. 7 packages, 8 variants, all packages draw with 0 conflicts, generated C compiles, --strict exits 0. Five peripherals are still partly modelled (OPA, CMP1/2/3, USBFS/USBPD params, TKEY) and tests/completeness.test.js prints those cells every run. Seven cited constraints: entries, and the consumer that reads them |
| WCH-DUMMY32-C8 | n/a — synthetic | tests/fixtures/mcus/ |
n/a | layout/scale fixture, QFN12 → LQFP144. Not shipped, not offered in the part selector |
| CH32V003 | DS + RM, data/sources/V003/datasheets/ + EVT |
landed mid-round-5 | CH32V003F4P6 |
the smallest part the app ships: RV32EC, 16 KB flash, 2 KB SRAM, 18 I/O on TSSOP20, no GPIOB, no OPA interrupt vector. 4 packages, 4 variants. --strict exits 0; tests/completeness.test.js and tests/smoke.js cover it; params: for OPA1 is the one open cell |
| CH32V203, CH32V307 | none | none | none | waiting on sources (agents/STATUS.md §6 item 4) |
Both EVT packages have landed — data/sources/V006/Evt/ (988 files) and
data/sources/X035/Evt/ (2 239 files). Every "until the EVT package arrives"
sentence in this repository is stale; the ones in PROGRESS.md,
data/firmware/ARCHITECTURE.md and data/firmware/README.md are corrected,
and data/sources/README.md is AGENT-1's.
EVT packages: BOTH HAVE LANDED. data/sources/V006/Evt/ and
data/sources/X035/Evt/ are populated — 84 MB, ~3 200 files, EXAM/ + PUB/
- the part list files. They are now under git. Per
data/sources/README.mdthey are the highest authority for every name the software uses, above the RM. All 17ch32v00X_*.hare indata/sources/V006/Evt/EXAM/SRC/Peripheral/inc/.
They are not the same as the copy PlatformIO installs, and the drop is newer
— StdPeriph sub-version 0x05 against the package's 0x04. All 17 headers were
diffed against framework-wch-noneos-sdk/Peripheral/ch32v00Xx/inc/: 14
identical, 3 differ, and two of those are value changes rather than version
banners:
| Macro | PlatformIO package | EVT drop (authoritative) | Where |
|---|---|---|---|
GPIO_Remap_LSI_CAL |
0x00200080 |
0x001A3000 |
ch32v00X_gpio.h:117 |
FLASH_FLAG_OPTERR |
0x00000001 |
0x80000001 |
ch32v00X_flash.h:104 |
The third, ch32v00X.h, differs only in which part its #if !defined(...)
fallback selects — harmless here because the build defines the part explicitly
(verified: CH32V006F8P6 compiles with -DCH32V006). GPIO_Remap_LSI_CAL is
the TIM1 CH1-from-LSI remap, so anything derived from the older copy is wrong.
Handed to AGENT-1 and AGENT-2 on the board.
The PlatformIO package (installed at
~/.platformio/packages/framework-wch-noneos-sdk) remains what the build links
against. Series mapping, which is not guessable from the part number: CH32V005/CH32V006 → series ch32v00Xx, header
ch32v00X.h (capital X); ch32v00x.h is a different part, CH32V003.
CH32X035 → series ch32x035, header ch32x035.h.
Silicon facts worth having in one place, all confirmed against the SDK this pass:
- CH32V006 GPIO has one output speed.
GPIOx_CFGLR.MODEyis a single bit (RM v1.4 §7.3.1.1) andGPIOSpeed_TypeDefhas one member,GPIO_Speed_30MHz. CH32X035 likewise has one,GPIO_Speed_50MHz. - CH32V006 has GPIO ports A, B, C, D and uses
RCC_PB2PeriphClockCmd/RCC_PB2Periph_GPIOx. CH32X035 has A, B, C only and usesRCC_APB2PeriphClockCmd/RCC_APB2Periph_GPIOx. RCC_ClocksTypeDefcarriesADCCLK_Frequencyon CH32V00Xx but not on CH32X035.- No hardware has been flashed or run. Every firmware claim here is a build-time claim.
data/mcus/CH32V006.yaml had header: ch32v00x.h. The CH32V006 SPL header is
ch32v00X.h, capital X; ch32v00x.h is the CH32V003 header, a different
part with a different register map. It compiled on Windows only because NTFS is
case-insensitive.
Fixed by AGENT-1 (header: ch32v00X.h, cited to
data/sources/V006/Evt/EXAM/SRC/Peripheral/inc/ch32v00X.h line 2) and held shut
by tests/codegen_compile.test.js, which compares the generated #include
case-sensitively against the files that actually exist in the part's SPL
directory — reading the EVT drop first and the PlatformIO package second.
The lesson is in how it was caught. Planted back in, the compile still passed: NTFS resolved the wrong-case name and the build went green. Only the name check caught it. On this class of bug compiling is necessary and not sufficient, which is why the two checks sit side by side rather than one replacing the other.
codegen.speeds said { Low: GPIO_Speed_2MHz, Medium: GPIO_Speed_10MHz, High: GPIO_Speed_50MHz } — the CH32V10x/20x/30x spellings. On CH32V006 the only member
of GPIOSpeed_TypeDef is GPIO_Speed_30MHz, because GPIOx_CFGLR.MODEy is a
single bit (RM v1.4 §7.3.1.1).
Fixed by AGENT-1: a gpio.speeds: list with one entry, GPIO_Speed_30MHz,
cited to ch32v00X_gpio.h lines 23–26, with the legacy Low/Medium/High keys
aliased to the same macro so nothing breaks mid-flight. Generated C now emits
GPIO_Speed_30MHz and the three fixtures compile.
Held shut two ways, both planted-break tested: the compiler ("'GPIO_Speed_50MHz' undeclared … did you mean 'GPIO_Speed_30MHz'?") and a check that every
GPIO_Speed_* in the generated C is a member of the enum as read out of that
part's header.
Still open — the UI half. The point was never only the macro name: the GPIO
table offers the user a Low/Medium/High choice this silicon does not have.
FORMAT.md's rule is that a one-entry speeds: list means the control is not
shown and the fixed value is displayed as text. ENGINE and UI own that, and it
is in flight (app/tests/project.test.js currently has a red line named
"setGpioField refuses a speed a multi-speed part does not offer").
"Verify the ch32v00x EVT SDK spelling of the port clock enable." Settled, and
AGENT-1's spelling is correct. framework-wch-noneos-sdk/Peripheral/ch32v00Xx/inc/ch32v00X_rcc.h
declares void RCC_PB2PeriphClockCmd(uint32_t, FunctionalState) (line 157) and
RCC_PB2Periph_GPIOA..GPIOD (lines 89–92) and RCC_PB2Periph_AFIO (line 88).
AFIO->PCFR1 is also confirmed (AFIO_TypeDef, line 197). No YAML change needed;
the assumption note in CH32V006.notes.md can be marked confirmed.
CLOSED. The two open-drain entries were deleted, sogpio.modesclaims two modes this silicon does not have.gpio.modesnow has exactly the threeGPIOMode_TypeDefmembers the header declares plus the implicitInput, and every one carries aclass:.verify_sdk_names.pyexits 0.CLOSED. It readscodegen.periph_handle.USBFS: USBFS_DEVICEdoes not exist.USBFSDnow, with the host/device split recorded as an open question inCH32X035.notes.mdrather than guessed at (the handle depends on the mode the user picked, andUSBFSHis the other half).The GPIO table's mode list is hardcoded in the UI.CLOSED.app/template.htmlno longer contains a literal mode list or the words "Output Open Drain"; the table rendersgpioFieldOptions(pin, field), which is the data reduced by the constraints on that row.CLOSED, and this is round 5's whole point. It is now constraintGPIO_Mode_IPDis per-pin on this part.pull-down-only-on-pa0-pa15-pc16-pc17, and the same mechanism carries the shorted-pair rules, thepackages:scoping that keeps Note 4 from over-applying to QFN20/QFN12, and the USBFS-conditional pair.- Five peripherals are partly modelled (OPA, CMP1/2/3, USBFS/USBPD params, TKEY) and
the ADC internal Vrefint channel is missing on CH32V006 too. Tracked open in
tests/completeness.test.js; §4 item 1. - Three variants have no
pio_boardand so cannot produce a generated project:CH32V006F4U6,CH32V006D8U7,CH32X035D8U6. The refusal is correct — F4U6 is 16 KB of flash against F8U6's 62 KB, so the nearest board would lie.
mcu.removeis applied after the parent merge (app/engine/inherit.js), so a child that removes and redefines a path loses its own version. It cost CH32V005 its entire DMA request map. Data-side worked around; engine fix open.setSetting()on acheckboxessetting replaces theSetwith aStringand the nextcompute()throwsv.has is not a function, blanking the app. Not reachable from today's UI; will be the moment peripheral tabs are generic.- 8 text-legibility findings (P0b), all measured, all open.
- Two selectors show stale state:
#mcuselreads the wrong part at boot,#pkgselkeeps the old package after a project load. - RM Table 6-1 has no TIM3 vector although CH32V006 has a TIM3. Recorded, not
invented. The EVT package has now landed (§5), so this is answerable today
from
data/sources/V006/Evt/rather than parked — first in AGENT-1's queue, along with the TouchKey channel map and the OPA polling set. - Two macro values in the PlatformIO SDK copy are superseded by the EVT drop
—
GPIO_Remap_LSI_CALandFLASH_FLAG_OPTERR, §5. Anything derived from the older copy needs re-checking;GPIO_Remap_LSI_CALis the TIM1 CH1-from-LSI remap the MCU file models. - Two red test lines, both in work in flight in
app/as of 14:36Z:app/tests/project.test.js(the GPIO-speed control, above) andtests/build.test.js"dist is not stale" (four agents rebuilding onedist/index.htmlwhile the suite runs). Neither is intests/ordata/firmware.
- No git remote. CI has never run.
agents/STATUS.md§6 item 1. The workflow side is ready as of 2026-09-12 and is now checked bytests/release.test.jsrather than only by reading it:ci.ymlhas three jobs (testwith no PlatformIO;firmware, which installs PlatformIO, builds all five environments and then generates every fixture's project and builds it; anddesktopfor the Tauri shell), andrelease.ymlpublishes on av*tag. Two real defects were found by writing that file and preparing this:ci.ymlnever installed PlatformIO at all, so the compile suites skipped, and thedesktopjob asked forlibappindicator3-dev, which does not exist on a current Ubuntu runner (Tauri 2 needslibayatana-appindicator3-devpluslibxdo-dev). Both are fixed. What remained unproven at that date was that the workflows run. (Closed 2026-09-12: they ran — 31 times, red and unread — and are green on all three jobs now; §1 has the run URL.) - Case-sensitive filesystems, found by preparing the above.
data/sources/V003/had landed asevt/anddatasheets/while the other two parts and the tools sayEvt/andDatasheets/. Everything passed here, because this box is case-insensitive; on the Linux runner the CH32V003 SDK lookup would have resolved nothing, degraded to "NOT CHECKED", and exited 0 — a green tick over a part nobody checked, which is precisely the defect class this repo keeps finding. Renamed (not deleted) and the citations inCH32V003.yamlandCH32V003.notes.mdrepaired with it;tests/source_paths.test.jsnow resolves everydata/sources/...path a tracked file cites against the case the filesystem actually has, and plants both a wrong-case path and a wrong-casecodegen.sdk.evtto prove it bites. - The repo is on a Google Drive mount.
npm installfails with EBADF; test deps live in%LOCALAPPDATA%\wchcube-deps.agents/STATUS.md§6 item 3. agents/README.md"Environment facts" is stale. It says there is nocargoand no C compiler. Both now exist:cargo 1.98.1is on PATH, and PlatformIO has the WCH RISC-V GCC 12.2.0 at~/.platformio/packages/toolchain-riscv.agents/STATUS.md§6 item 2 is done.- RESOLVED — the floating
src-taurichange. Theopen_projectfix was not optional: the command returnsResult<Option<String>, String>and the body returnedResult<String, String>. Reverting just that line and runningcargo checkgiveserror[E0308]: mismatched types … could not compile wch-cubemx. Sosrc-taurihad never compiled, and every claim about the desktop shell before this pass was about code that does not build. Fixed,cargo buildclean, fix andCargo.lockcommitted. - RESOLVED — round 3 is under git.
data/firmware/,PROGRESS.md, theagents/history/round3/pack and the whole reorganiseddata/sources/tree were all untracked, with the two ORIGINAL datasheets showing as deleted and their moved replacements untracked —git checkout .would have taken the CH32V006 datasheet and reference manual with it. All committed. - DECISION —
data/Pio Source/is gitignored. It is a 45 MB reference copy of the installed PlatformIO platformch32v, and the round brief forbids vendoring the SDK into this repo. It stays on disk to read; cite the package. data/sources/was reorganised into<PART>/{Datasheets,Evt}/. Paths indata/FORMAT.md,data/mcus/CH32V006.notes.md,tools/extract_remaps.pyand the agent packs still name the old flat locations.- The repo is 52 MB of history plus an 84 MB EVT drop. Not a problem today;
worth a thought the day a remote appears (
agents/STATUS.md§6 item 1). - The human's
Taskfile.ymlis still a stub (echo "Hello, world!"), untracked. Not half-adopted; seeTASKS.mdhousekeeping.
data/firmware/ — open that folder in VS Code (PlatformIO needs
platformio.ini at the workspace root), or open
data/firmware/wchcube-firmware.code-workspace for a two-root workspace with the
repo alongside it.
data/firmware/
├── platformio.ini 4 environments; every per-part fact comes from board JSON
├── README.md build, flash, debug, and how to feed it generated code
├── ARCHITECTURE.md layers, seams, ownership, the EVT authority rule
├── include/app_config.h build-time switches — the sdkconfig role
├── src/main.c application layer: no registers, no pins
├── lib/
│ ├── board/ what is physically wired. No source, no line.
│ ├── wch_hal/ thin wrappers over the WCH EVT SPL
│ ├── util/ hardware-free, host-buildable (ring buffer)
│ └── wchcube_generated/ drop zone for configurator output, machine-owned
└── test/ empty, and says so
pio run, 2026-09-11, PlatformIO Core 6.2.0, platform ch32v 1.1.0, toolchain
riscv-wch-elf-gcc 12.2.0:
| Environment | Board | Result | Flash | RAM |
|---|---|---|---|---|
CH32V006F8P6 (default) |
genericCH32V006F8P6 |
SUCCESS | 7 796 / 63 488 B (12.3 %) | 712 / 8 192 B (8.7 %) |
CH32V006K8U6 |
genericCH32V006K8U6 |
SUCCESS | — | — |
CH32V005F6P6 |
genericCH32V005F6P6 |
SUCCESS | — | — |
CH32X035G8U6 |
genericCH32X035G8U6 |
SUCCESS | 6 724 / 63 488 B (10.6 %) | 2 224 / 20 480 B (10.9 %) |
pio check -e CH32V006F8P6 (cppcheck): no defects found.
Everything the build needs was already installed locally, so this works offline.
tests/codegen_compile.test.js, run by node tests/run.js. For each fixture it
runs the real CLI over a checked-in .wchproj, puts the header in include/ and
the source in src/, and builds:
| Fixture | Part / package | Environment | Result |
|---|---|---|---|
CH32V006_TSSOP20_full.wchproj |
CH32V006 TSSOP20 | CH32V006F8P6 |
compiles and links |
CH32V006_QFN32_full.wchproj |
CH32V006 QFN32 | CH32V006K8U6 |
compiles and links |
CH32V005_TSSOP20_full.wchproj |
CH32V005 TSSOP20 | CH32V005F6P6 |
compiles and links |
CH32X035_QFN28_full.wchproj |
CH32X035 QFN28 | CH32X035G8U6 |
compiles and links |
CH32V003_TSSOP20_full.wchproj |
CH32V003 TSSOP20 | CH32V003F4P6 |
compiles and links |
CH32H417_QFN128_full.wchproj |
CH32H417 QFN128 | CH32H417QEU6 |
compiles and links |
CH32H417_QFN68_pkg.wchproj |
CH32H417 QFN68 | CH32H417WEU6 |
compiles and links |
CH32L103_QFN32_full.wchproj |
CH32L103 QFN32 | CH32L103K8U6 |
compiles and links |
The last two rows of the CH32H417 pair are one check, not two. CH32H417 is the first
part here where the PACKAGE decides which pins a peripheral can reach — 301 of its signals
have a different set of bonded options on QFN68 than on QFN128 — and until 2026-09-12 one
QFN128 fixture stood for all three packages, so that path had never been compiled. The two
fixtures make the same four engine calls and land on different pads: USART1_RX emits
GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF14) on QFN68 and (GPIOB, GPIO_PinSource15, GPIO_AF4) on QFN128 — a different port, pin and AF code, all three wrong
together if the generator ignored the package, and all three compiling either way. Evidence,
with both listings: tests/evidence/round5/2026-09-12-h417-package-pads.md.
These fixtures assign pins. Between them: ports A/C/D and A/B/C/D, non-default
USART1 and SPI1 remaps, an HSE crystal, a PWM output, an ADC channel, a labelled
GPIO, and params: on USART1/SPI1/TIM1/TIM2/I2C1/ADC1.
tests/fixtures/make_fixtures.js builds them by driving the real engine and
refuses to write one that has a pin conflict or fewer than eight assigned pins.
Flash for CH32V006F8P6 went from 7 796 B on the old default configuration to
7 972 B, so WCHCube_GPIO_Init() is demonstrably not empty this time.
What this now supports saying, and what it does not.
Does: for these three configurations, the generated GPIO, AFIO remap and RCC
code compiles and links against the real WCH SPL for the right part — verified
with -DCH32V006 actually reaching the compiler, because ch32v00X.h picks the
part in a #if !defined(...) block whose first branch is CH32V002, and
everything would otherwise still compile, as a different chip.
Does: the same for CH32X035, a second family, through the same gate and the
same fixture generator, with no part-specific code anywhere in app/. That is
the round-4 thesis — adding a part is a data job — as a green line rather than an
argument.
Does not: nothing has been flashed or run. The gate is a build-time claim.
It covers GPIO, AFIO, RCC, params: → *_InitTypeDef, DMA and NVIC at the level of
"it compiles", and --strict now exits 0 on every fixture in both the default format
and --format c. It has only ever run on Windows: the wrong-case header defect is
caught here by a name check rather than by a compiler, and nothing in this repo has
ever been built on Linux.
Skips are first class, and they are now the thing to watch. The runner counts them and prints every reason above the verdict, a part the SDK-name checker cannot resolve becomes a counted SKIP rather than a green tick, and the compile gate skips loudly when another agent is writing the drop zone mid-build. A green run of 510 with 0 skipped is the only shape that proves what it says.
The previous version of this section reported a pass on a default TSSOP20
configuration, where no GPIO is assigned and WCHCube_GPIO_Init() is empty, and
said so in the same paragraph. That caveat is now discharged rather than
repeated — but the habit it came from is why the paragraph above exists.
The drop zone is .gitignored on purpose: machine output, regenerated per
configuration, and not something two agents should be resolving a diff over. The
gate builds into data/firmware/.pio/gate rather than .pio/build, so a
concurrent pio run by a human or another agent cannot make it lie in either
direction — it produced one spurious red before that was isolated.
What is genuinely implemented: board bring-up (clock readback, delay timer, printf over the WCH-Link SDI channel, which claims no pin), the generated configuration applied if present, a clock-tree report, and a tick loop.
What is deliberately not implemented, and is marked as such in the code rather than stubbed:
- No peripheral drivers. No UART, SPI, I2C, ADC or timer component exists.
- No board pin map. The targets are bare chips, not named dev boards with
published schematics, so
lib/boarddeclares no LED, no button and no UART pins.board_has_led()returns 0 andboard_led_toggle()is a no-op until someone declares a real LED viabuild_flags— seelib/board/include/board.h. - No tests.
test/is empty and explains why. - Nothing has been flashed or run on silicon.
Decisions made earlier and still in force (from TASKS.md and the board):
| Decision | Why |
|---|---|
Single-file offline dist/index.html, plain JS + SVG, no framework, no bundler |
Runs from a USB stick; trivially embeddable in Tauri |
| Pin alternate functions derived from peripheral remap tables, never listed per pin | One place to edit; conflicts computed rather than maintained |
| YAML for MCU data, js-yaml vendored | Human-editable, diffable, parseable in the browser |
| Engine is ESM with zero DOM access | Same modules run in the browser, in Node tests and in the CLI |
Every hardware fact cites a DS/RM table in <PART>.notes.md |
A fact without a citation is a guess |
| Codegen emits an explicit TODO rather than plausible-looking register code when the MCU file lacks an encoding | Wrong bits are worse than absent bits |
| Four agents, strict file ownership, board-only communication | One shared tree with no merge conflicts |
Added this round:
| Decision | Why |
|---|---|
The firmware is a PlatformIO project under data/firmware/, not a vendored SDK tree |
PlatformIO already resolves the WCH toolchain, the NoneOS SDK, the linker script template and the upload tools. Vendoring any of that would be a second copy to keep in sync with WCH. |
Components are PlatformIO libraries under lib/, one concern each |
The ESP-IDF components/ shape. library.json dependencies makes the dependency graph real and build-enforced, and the folder boundary doubles as the agent ownership boundary. |
| Structure borrowed from the NONOS/ESP-IDF ecosystem; APIs not borrowed | esp_err_t, system_os_task and an event loop belong to a different chip and SDK. Importing the names would be inventing an API this silicon does not have. |
lib/util may not include an SDK header |
It is the one layer that compiles for the host, which is what makes off-target unit tests possible. Enforced by its library.json declaring no framework and no platform. |
Generated code lives in its own component and is .gitignored |
Machine-owned. Hand-editing it is a bug, and two agents generating from two .wchproj files must not produce a diff to resolve. |
printf goes to the WCH-Link SDI channel, not USART1 |
SDI claims no pin. The SDK's default (DEBUG_UART1_NoRemap) silently occupies PD5 and would fight whatever the configurator assigned there. |
lib/board states no pin it cannot cite |
The targets are generic chips. "Probably PD4" is the class of invention that produced defects 1 and 2. |
Every component is named in lib_deps |
The LDF only compiles what something includes, and does not evaluate __has_include. Without this, util would never be built and wchcube_generated would never be found. |
The build-time clock (SystemInit) and the generated clock (WCHCube_RCC_Init) both exist, generated wins |
The framework sets SYSCLK from the board file before main(). Rather than fight it, main() applies the generated tree afterwards and re-runs SystemCoreClockUpdate(). Documented in ARCHITECTURE.md as "Two clock owners". |
| EVT sources are the top authority when they arrive | Above the RM for anything that has a name: the RM describes silicon, EVT describes what the SDK will compile. |
Everything the previous version of this list recommended is done — the EVT drop was
worked, the GPIO control stopped being offered where the part has one value, the DMA and
NVIC tabs landed, params: reach the C, and the compile gate covers all four fixtures.
They are not repeated here. What is left, in order:
- DATA: the CH32X035 peripheral gaps, and the ADC internal channel on both
families. §4 item 1. This is the last thing between the round and a clean
--stricton a peripheral nobody has modelled yet. - QA: E5, the breadth pass. Seven packages in
layout.test.jsandlegibility.test.js; LQFP64M at 60 I/O is the first genuine stress either has had. - QA: run
agents/STATUS.md§5 end to end and post where it fails. It is written to be runnable by someone who did not write it, and it has not been run this round. - QA: the stale-documentation greps in
agents/STATUS.md§4, AGENT-3's P2. This file's live pointers atAgents Rounds 4/are fixed in the same commit as this rewrite; the remaining hits are history or a comment about history. - AGENT-3: re-audit or explicitly supersede the round-1 section of
agents/history/round6-pack/DONE.md. It still carries a "15 of 24" count from round 3 and most of what it calls open has since closed. A status nobody has checked is worse than no status. - A human flashes one generated project.
agents/STATUS.md§6 item 8, the highest-value open item in the repo, and the only one that turns "compiles" into "works". Until then every DONE line about the generated code says builds, not flashed. - Build something on Linux. Every compile claim in this file is a Windows claim, and
the one defect a compiler could not catch here (wrong-case header) is exactly the one
Linux would have caught instantly. This wants the git remote —
agents/STATUS.md§6 item 1.