Two small fixes after running the test inside the prod container for
the first time:
- The prod image lays out the backend at /app/app/, not /app/backend/app/
(the Dockerfile.standalone COPYs only the inner package). Use /app
as the sys.path root so `from app.pro.services import ...` resolves.
- The CHIP=0x60 and BLOCK= prints race against the socket drain. The
test was treating "saw CHIP= but BLOCK= not in buffer yet" as a
hard failure and exiting before the second I2C read finished.
Gate the success path on both markers present and keep polling
otherwise.
Verified end-to-end in the prod container:
[proto] >>> ['I2C', '1', '76', 'RR', 'd0', '1']
[proto] <<< I2C_DATA 1 76 60
[proto] >>> ['I2C', '1', '76', 'RR', 'f7', '8']
[proto] <<< I2C_DATA 1 76 530280155e607b50
[test] OK — guest read chip ID = 0x60
[test] OK — block read BLOCK=530280155e607b50
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the public extension points the velxio-prod overlay uses to bind
real canvas-side I2C/SPI/UART models (BME280, future MCP23017, etc.)
to a running Pi guest's protocol shims:
- qemu_manager: set_pi_slave_handler(fn) / get_pi_slave_handler() for
pi_attach_slave + pi_detach_slave WebSocket messages. OSS image
leaves the hook unset so the messages are silently dropped.
- simulation route: parses the two new WS message types and forwards
them to the registered handler when present.
- RaspberryPi3Bridge: attachSlave(spec) / detachSlave(spec) frontend
side of the protocol.
- piSlaveScanner: at simulation start walks components + wires,
identifies I2C/SPI/UART peers wired to Pi protocol pins (40-pin
header physical-pin numbering), and emits one attach per
bus/address pair (deduped across SDA+SCL wires).
- RaspberryPiWorkspace: invokes the scanner once the bridge is open,
with retries to ride out the WS-still-connecting race.
- integration test: pi3_bme280_attach.py boots the Pi, pre-attaches a
BME280 via the slave handler, runs a host-side proto loop, runs
guest python smbus2.read_byte_data(0x76, 0xD0) and asserts the
console reads back CHIP=0x60.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>