Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion desktop/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2144,6 +2144,9 @@ fn cobs_encode(input: &[u8]) -> Vec<u8> {
out
}
fn cobs_decode(input: &[u8]) -> Option<Vec<u8>> {
if input.is_empty() {
return None;
}
let mut out = Vec::new();
let mut at = 0;
while at < input.len() {
Expand All @@ -2153,7 +2156,11 @@ fn cobs_decode(input: &[u8]) -> Option<Vec<u8>> {
}
at += 1;
for _ in 1..code {
out.push(*input.get(at)?);
let value = *input.get(at)?;
if value == 0 {
return None;
}
out.push(value);
at += 1;
}
if code != 0xff && at < input.len() {
Expand Down Expand Up @@ -6498,6 +6505,19 @@ mod tests {
assert_eq!(payload, vec![80, 2, 1]);
}
#[test]
fn shared_cobs_vectors() {
let vectors: serde_json::Value = serde_json::from_str(include_str!("../../tests/fixtures/cobs.json")).unwrap();
let bytes = |v: &serde_json::Value| v.as_array().unwrap().iter().map(|v| u8::try_from(v.as_u64().unwrap()).unwrap()).collect::<Vec<_>>();
for value in vectors["valid"].as_array().unwrap() {
let encoded=bytes(&value["encoded"]);let decoded=bytes(&value["decoded"]);
assert_eq!(cobs_decode(&encoded),Some(decoded.clone()));
let mut actual=cobs_encode(&decoded);assert_eq!(actual.pop(),Some(0));
assert_eq!(actual,encoded);
}
for value in vectors["invalid"].as_array().unwrap() { assert!(cobs_decode(&bytes(value)).is_none()); }
assert_eq!(crc8(&bytes(&vectors["crc8_atm"]["input"])),vectors["crc8_atm"]["checksum"].as_u64().unwrap() as u8);
}
#[test]
fn streaming_packet_bounds() {
let p = rainbow_packet(240, 240, true, 20);
assert_eq!(p.len(), 730);
Expand Down
39 changes: 22 additions & 17 deletions docs/FILE-COMPARISON.md

Large diffs are not rendered by default.

76 changes: 76 additions & 0 deletions docs/REUSE-PREFLIGHT-RECEIPT.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,81 @@
# Donor-first preflight receipt

## Controller transport convergence first slice, 2026-10-30 (#41 / PC#702)

Read current repository instructions, transport and merge-readiness documents;
consult avr-convergence and exact donor-audit Project/ProtocolCodec.h mappings.
Read complete PC/Pakar C++ CRC/COBS primitives and current Go COBS, plus concrete
UART/header, UDP packet, Rust/Dart USB and Pealayer decoder slices. The latter
are slice review, not whole-file donor certification. Added a transport-convergence
index route so previously unindexed host UDP/framing sources remain visible.
Both root epics track actual unrelated-history/file-by-file merger requirements
and reference the existing MCU/firmware owners, not competing architecture epics.

The pre-fix Go conformance test accepts0200 as valid COBS. C++/Go/Rust/Dart
decoders now reject embedded zeroes without changing valid encodings. PC UDP
already reuses the Go primitive and now rejects short nonfinal fragments in its
encoder, matching the existing decoder. Pealayer already rejects embedded zeroes;
its runtime is left unchanged. Shared JSON fixtures are byte-identical in both
repos; no third protocol, socket owner, allocator or dispatcher is introduced.
PC's read-only MCU list exposes existing Mega/native lanes and explicit adapter
metadata; it does not claim an STM32/ESP hardware port or change328P programming.

Initial verification hit the existing hash gate after protocol_test.cpp changed;
no bypass was used. Existing proofs are rebound only after reviewing the bounded
diff and retaining anchors. A Go command initially requested a UDP package with
no tests; real UDP codec tests were added, not a test-selector exception. The
first UDP fixture compared nil and empty payload slices as different; byte-wise
payload comparison now retains every actual wire-field assertion. Source/CI,
installed apps, LAN loss/timing and hardware acceptance remain separate.

PC checkpoint f2b1b3ff is pushed in PR703: stable-path Go native/UDP tests pass,
canonical CMake firmware_protocol_codec_unit passes, and both MCU inventory
tests pass without a toolchain invocation. Pakar's canonical Rust test passes
shared_cobs_vectors; canonical mobile analysis reports no issues and all13 USB
transport tests pass. The shared fixture SHA256 is81683deafa749b231e483d2b75e4c45cae2a3262cab014ad0a20c74a82b31694.
The larger protocol wrapper passed its framing/renderer/storage/programming
fixtures but stopped at a later stale reference after a formatting-only source
edit; it was not labeled a complete passing suite. Final comparison checks and
CI remain exact-head gates. No source/profile tests are physical timing proof.

Repository reconciliation is not complete: the initial refresh found ten PC
PRs (before this new PR), including failing host/source checks on699, an unstable
692, blocked688 and several active drafts; Pealayer has seven, including unstable
drafts216/212. Owner worktrees are clean and preserved, along with22 PC and three
Pealayer stashes. They are not discarded or blindly merged. Pakar40's stale route
gate is repaired; its final full CI is still independent from this transport pass.

The full protocol wrapper's later isolated LLDP build then failed to find
GameRole.h via S3 BoardPins.h. Read the actual test-lldp/test-ethernet wrappers
and headers, consult ethernet/build-storage, and reuse the sibling wrapper's
BoardCore include directory. The unchanged LLDP codec/ownership/TCPIP fixture now
compiles and passes with SDK doubles. No production header, assertion or gate
was weakened; the larger wrapper is not retroactively labeled fully passed.
PC's new guide links initially failed its privacy policy; a document-and-repo
specific exception plus negative tests preserves unrelated/private-path rejection.
The13 policy tests and repository checker pass, and the corrected PC source
checkpoint is f122ccdf; its exact-head AVR/source CI passes while other jobs
remain separate gates. Existing unrelated PR failures/drafts are still open.

PR42's full CI then detected the independent original contract-manifest binding
to WireCodec.h still held the old target hash. The exhaustive comparison was
already updated; reread the23-entry source-mapping guard and focused record, then
refresh only that reviewed target binding and retained-behavior note. The original
donor revision/hash, every other mapping and the strict guard stay unchanged.
The mapping test now agrees with the authoritative comparison. Under the user's
renewed clean-worktree request, the one known generated pyc was removed normally
with native Remove-Item; this supersedes its old cleanup blocker. No alternative
shell, ignore entry, stash deletion or source/history loss was used.

After that binding repair the complete canonical `node tools/ci-checks.mjs quality`
runner exited0. The still-running runner was observed by its exact PID/command
and its retained process handle reported the exit code; it was not rerun merely
because earlier console output had scrolled away. Full paginated Pakar records
were refreshed with the existing auditor, and PR40/42 bodies and empty review/
thread sets were read. PR40 merged normally as d5008bd after all exact-head checks
passed; PR42 was retargeted to main. PC703's complete conversation is also empty
and all checks now pass. Separate final metadata/head CI remains required.

## Web correction preview connection/foreground ownership, 2026-10-30 (#3)

Reread the complete actual PCController mobile/lib/unified/pwm_control.dart
Expand Down
112 changes: 112 additions & 0 deletions docs/TRANSPORT-CONVERGENCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Controller transport convergence

Tracking: [PakarController#41](https://github.com/atomicdeploy/PakarController/issues/41)
and [PCController#702](https://github.com/atomicdeploy/PCController/issues/702).
This is an implementation inventory and staged decision, not a claim of complete
transport parity or wire interoperability. Existing donor decisions remain in
file-comparison-evidence.json; this document does not replace that ledger.

## Decision: shared semantics, appropriate carriers

Keep one command/event/service contract, portable framing primitives and tested
carrier adapters. Do not call COBS a USB transport or put a desktop-sized envelope
in every AVR message. USB-UART, hardware CDC and TinyUSB CDC are physical choices
for the same application protocol. UDP datagrams and serial streams have different
delivery properties; bootloader protocols are external recovery contracts.

Do not remove the current native-service boundary: outer opcode0x11 is an audio
cue in Pakar but PWM in PCController. Both use A5/COBS/CRC-8/ATM and48-byte payloads,
yet reserved-byte admission, HELLO/status/settings, timestamped ACK/events and
EEPROM layouts differ. Sharing codec mathematics does not make these semantics
interchangeable. See PC-CONTROLLER-MERGE-READINESS.md for the preserved comparison.

## Actual implementation comparison

| Path / current owner | Useful attributes | Gaps / decision |
| --- | --- | --- |
| Pakar BoardCore Protocol/WireCodec, ESP UsbApiService, Rust usb_api and Flutter usb_transport | Fixed buffers and48-byte frames; explicit identity/boot fencing, demand leases; USB API chunks reuse selected services without Wi-Fi | Not a universal file/API/maintenance parity claim. Audit every advertised operation and client surface. Keep offline recovery and on-demand streams. |
| PCController Project/ProtocolCodec + UartProtocol, Go internal/native | Allocation-free C++ primitives; separate RX/TX and synchronous borrowed frame lifetime; timestamped ACK/events; Go shares framing with host UDP | Preserve nested replies and AVR stack budget. Wire IDs collide with Pakar; do not reinterpret old commands or replace zero-copy views blindly. |
| PCController transport/udp and Pealayer server/{udp_codec,binary_udp,udp_client} | Already share DGLU typed-value RPC, UUID target/session,64-bit correlation, bounded fragmentation/reassembly, leases, replay ledger and event sequencing | Not encrypted/authenticated by CRC. Local access/authority policy remains required. Flutter/MCU parity and real LAN/loss/soak acceptance are not established by codec tests. |
| Pealayer four_d/protocol command framing | Existing compact cinema commands use CRC-8/Maxim; its native controller envelope and host UDP use CRC-8/ATM | These are distinct device contracts, not interchangeable because they both use COBS. Keep established wire bytes and verify the selected peer/service before dispatch; do not globally replace one CRC polynomial. |
| Pakar PKAR UDP pixel frames | Small bounded RGB chunks and staged frame commit; useful for low-latency presentation | Not a general command protocol: no authentication or peer/boot targeting in this packet. Keep trusted-LAN scope; never promote to Internet maintenance/control. Retain throughput while designing an owned streaming service. |
| Pakar HTTP/WebSocket/MQTT, BLE and USB API | Existing typed commands, shared capability/peer policy, correlated receipts, maintenance and presentation services | Enabled adapters must advertise actual commands/limits; a selected-route bridge is not complete API parity. WS-first applies where a named command exists, not an invented opcode. |
| PCController IPC/HTTP/WS, native stdio/TCP/C ABI | Shared production dispatch and protected host ownership rather than another firmware implementation | Reconcile its typed operation registry and push domains with Pakar; do not start a second serial owner or turn discovery into authority. |
| Urboot/urclock/ISP and ESP ROM/OTA | Standard programming/recovery, backup/integrity and target-specific electrical policies | Preserve these adapters. They are not competing business protocols and cannot be replaced by a generic packet decoder. |
| Telnet/SSH shell, SCP/SFTP/FTP | Human/standard-client access and bounded file services | Keep shell negotiation and access policy distinct; terminal text must not interleave framed USB. Not an excuse to duplicate command dispatch. |

Sources inspected include the complete current C++ framing primitives, Go COBS,
native and UDP packet codec slices, Rust/Dart USB codec slices, USB request/route
and demand contracts, Pealayer's actual COBS decoder, and the existing PC/Pakar
merge-readiness and host-UDP specifications. This is not a complete read of every
service/dispatcher/client. Per-file pending work stays pending.

## First implementation checkpoint

- Same malformed COBS input was accepted by PCController C++/Go and Pakar C++/
Rust/Dart decoders: zero inside a data block. Reject it before dispatch, even
if a surrounding envelope has a matching CRC. Valid wire bytes do not change.
- PCController UDP encoding now applies the same full-nonfinal-fragment rule as
its decoder. Previously it could emit a packet its own receiver rejects.
- tests/fixtures/cobs.json is a byte-identical cross-repository fixture, not a
new transport. Go and Rust consume it; C++ and Dart have targeted malformed
envelope regressions. Keep CRC-8/ATM's123456789→F4 check and encoder bytes.
- Pealayer already rejects embedded zeroes; do not change its working runtime
merely to claim an import. Its empty-decoded-buffer convention is not evidence
that an empty application frame is accepted.
- PCController exposes its actual extra-MCU build lanes through the read-only
`node Tools/Firmware/targets.mjs list`. Explicit adapter metadata prevents a
future STM32 registration accidentally executing the AVR-specific build path.

No board update, network scan/actuation or production process replacement is
part of this checkpoint. Source/fixture checks, compiled targets, installed apps,
wire timing and physical output have separate acceptance.

## Next pass: living shared contract and adapters

1. Select a canonical portable core owner and generated operation registry. Keep
hardware profile, business role and feature budget independent. Until the
repository merger, pin shared sources/fixtures with reviewed hashes; do not
invent a third dispatcher or silently fork the source owner.
2. Define shared identities, boot/session admission, request IDs, deadline/cancel,
typed errors, receipts, event revision/time, leases and resynchronization.
Preserve stable operation IDs and richer timing fields. Choose explicit
service namespaces rather than treating the reserved byte as a version.
3. Accept unknown optional fields; reject unknown required semantics safely.
Advertise enabled services and actual size/queue/fragment limits. Slim builds
omit implementations with flags and return Unsupported, not success/no-op.
Back/forward compatibility means a negotiated common capability subset; it
cannot guarantee arbitrary future semantics or ambiguous existing opcode use.
4. Use reliable control/maintenance and owned lossy presentation separately.
Bound queues, fragment lifetimes and memory; CRC is not authentication.
Outcome-unknown mutations never automatically replay after reconnect/retry.
5. Exercise the same operations/events, files/firmware, subscriptions and errors
through every enabled adapter and client. Each missing cell in that matrix is
an issue, not an advertised completed transport. Do not remove useful recovery
or high-rate paths merely because another carrier lacks their capability.

## Following pass: MCU HAL and history-preserving merger

PCController already builds the production sketch and source aggregators for
ATmega328P, explicit Mega2560 profiles and native conformance targets. Pakar's
separate AVR lifecycle/dispatch is not yet the same source tree. Reconcile it
under existing PC issues103/227/18/125/375 and Pakar issues3/6/16.

Extract GPIO, serial/USB, monotonic clocks/timers, storage, interrupts, buses,
watchdog/reset and LED-driver seams. Add actual STM32/ESP adapters only after chip,
pins, toolchain, peripherals and memory budgets are selected. A name or build flag
alone is not MCU support; no fabricated working target is added here. Protect
current ISR timing, RF input, tone scheduling, relay safety and storage commits.

The future repository destination remains undecided. Before merging, preserve
immutable refs and manifests, worktree/stash/private-state inventories and both
Git graphs. Stage a reviewed `git merge --allow-unrelated-histories`, with path
moves retaining provenance. Compare every unique/overlapping file: source hash,
owner, licenses, useful behavior, tests, conflicts, semantic result and omissions.
No copy-flattening, blanket ours/theirs choice or deletion of uncertain history.
Code must have a named canonical owner before duplicate implementations retire.

Raw EEPROM/settings images remain rollback artifacts, not cross-product imports.
Use guarded semantic import/readback. Require slim/full profile flash/RAM/stack,
interoperability, loss/reorder/reconnect/backpressure, real-device/soak and recovery
evidence before calling the consolidated system finished. Resolve all applicable
PR reviews and checks without merging unrelated active owner drafts blindly.
Loading
Loading