# Kontenery i debug Ten dokument opisuje wdrożony kontrakt kontenerów i jawnie wskazuje pozostałe etapy. Pełne uzasadnienie, przepływ ucznia i kolejność wdrożenia są w `doc/architecture-plan.md`. Stan obecny: - `stemctl` uruchamia trzy profile przez rootless Podman; - repo środowiska zawiera wieloetapowy `Dockerfile` i Compose z dokładnie trzema usługami; gotowych obrazów nie publikujemy; - profil RP2350 zawiera oba toolchainy, Pico SDK, FreeRTOS, OpenOCD i picotool; - `rvctl`, `host` i `rv32i` pozostają aliasami okresu zgodności. ## Profile docelowe | Profil | Alias przejściowy | Service | Target Dockerfile | Targety | | --- | --- | --- | --- | --- | | `native-amd64` | `host` | `native-amd64` | `native-amd64-final` | `native` | | `hazard3-sim` | `rv32i` | `hazard3-sim` | `hazard3-sim-final` | `hazard3-baremetal`; `hazard3-freertos` po BSP timer/IRQ | | `rp2350` | brak | `rp2350` | `rp2350-final` | `rp2350-rv`, `rp2350-arm` | Wszystkie targety dziedziczą stage `dev-ui-base`. Nie jest on osobnym kontenerem roboczym. ## Jednolity kontrakt CLI ```bash stemctl env list stemctl env ensure native-amd64 stemctl env ensure hazard3-sim stemctl env ensure rp2350 stemctl build native-amd64 inf bss 1 stemctl test native-amd64 inf bss 1 stemctl run native-amd64 inf bss 1 stemctl debug native-amd64 inf bss 1 stemctl build hazard3-sim inf bss 1 --target hazard3-baremetal stemctl test hazard3-sim inf bss 1 --target hazard3-baremetal stemctl run hazard3-sim inf bss 1 stemctl debug hazard3-sim inf bss 1 stemctl build rp2350 inf bss 1 --target rp2350-rv stemctl deploy rp2350 inf bss 1 --target rp2350-rv --device /dev/bus/usb/001/006 stemctl debug rp2350 inf bss 1 --target rp2350-rv --device /dev/bus/usb/001/006 ``` Selektory `series card task` są opcjonalne, jeśli ustawiono wartości domyślne. Każda akcja obsługuje: ```text --instance NAME --target NAME --editor nvim|vim --dry-run ``` `debug` dodatkowo może przyjąć `--pane TARGET` i `--device PATH`. `deploy` przyjmuje `--device PATH` oraz `--backend probe|bootsel`. `probe list --json` ma stabilny format maszynowy, a akcje zapisują maszynowy `result.json`. `deploy` jest capability wyłącznie targetów sprzętowych RP2350. W `hazard3-sim` artefakt ładuje `run` albo `debug`; osobne `deploy` zwraca `unsupported`. ## Capabilities zamiast wyjątków w kodzie Launcher nie powinien zawierać warunków typu „jeśli profil rv32i, uruchom ten konkretny skrypt”. Profil publikuje capabilities i adaptery: ```json { "profile": "rp2350", "targets": ["rp2350-rv", "rp2350-arm"], "actions": { "build": {"requires": []}, "test": {"requires": []}, "run": {"requires": ["matching-deploy-record"]}, "debug": {"requires": ["probe"]}, "deploy": {"requires": ["probe"]}, "shell": {"requires": []} }, "debug_backends": ["openocd"], "devices": ["debug-probe", "target-usb-optional"] } ``` Karta deklaruje target, a profil dostarcza implementację. Brak capability jest normalnym, maszynowo czytelnym wynikiem `unsupported`, nie awarią launchera. ## Wspólny interfejs w kontenerze Każdy profil zapewnia kanoniczny dispatcher: ```text /usr/local/bin/stem-entry /usr/local/bin/stem-card ``` Dispatcher przyjmuje `build/test/run/debug/deploy`; manifest może zwrócić `unsupported`, jeżeli akcja nie ma sensu dla targetu. Skrypty korzystają wyłącznie z manifestu karty i zmiennych kontraktowych: ```text STEM_WORKSPACE=/workspace STEM_PROFILE=hazard3-sim STEM_TARGET=hazard3-freertos STEM_CARD=inf/bss STEM_TASK=task1 STEM_INSTANCE=hazard3-sim-inf-bss-t1 STEM_STATE_DIR=/workspace/.stem/instances/ STEM_ARTIFACT_DIR=/workspace/.stem/artifacts/// ``` Stare `RV_*` są czytane wyłącznie jako fallback w okresie migracji. ## Montowania Minimalny zestaw: | Źródło hosta | Cel | Tryb | | --- | --- | --- | | repo karty | `/workspace` | `rw` | | cache profilu | `/cache` | `rw`, osobny named volume | | `$XDG_RUNTIME_DIR/stem` | ta sama krótka ścieżka | `rw` | | konkretne urządzenie probe | urządzenie | tylko profil `rp2350` | Nie montujemy: - `$HOME/.ssh`; - pliku tokenów; - całego `/dev`; - socketu Docker/Podman; - katalogu domowego hosta; - repo innych uczniów. Repo jest własnością użytkownika hosta. Rootless Podman uruchamia kontener z mapowaniem UID/GID (`keep-id` albo równoważnym), żeby artefakty nie powstawały jako root. ## Layout tmuxa i Neovima W każdym profilu po `debug` powstaje ten sam układ: ```text work ├─ pane 0 (góra): jeden Neovim │ ├─ okno lewe: Termdebug/GDB dashboard │ └─ okno prawe: edytor źródeł i nvim-dap └─ pane 1 (dół): bash w /workspace ``` Lewa i prawa część są oknami Neovima, nie osobnymi pane'ami tmuxa. Hazard3 trzyma symulator w ukrytym oknie tmuxa `tb`. Prefix tmuxa to `Ctrl-s`. Neovim ma identyczne mapowania Termdebug i nvim-dap. Opis backendu GDB pochodzi z jednego target descriptor, dzięki czemu oba frontendy nie rozjeżdżają się. ## Debugger według profilu ### `native-amd64` - GDB jest backendem domyślnym; - LLDB pozostaje dostępny ręcznie w shellu; wspólny interfejs Termdebug używa GDB; - przed debugowaniem powstaje build `-g3 -O0`; - `test` uruchamia co najmniej ASan i UBSan. ### `hazard3-sim` - obecny codzienny backend: `fast-rsp`, jawnie opisany jako uproszczony; - docelowy, planowany backend: `fast-dm`, czyli GDB RSP -> APB/DMI -> prawdziwy DM Hazard3; - gate zgodności: OpenOCD + JTAG remote-bitbang; - planowany simulatorowy BSP FreeRTOS ma używać timera 1 kHz, czyli ticka co 150 000 cykli przy zegarze gościa 150 MHz; nie jest jeszcze wdrożony. ### `rp2350` - OpenOCD/probe dla targetów RISC-V i ARM; - picotool dla informacji, resetu i operacji wspieranych przez RP2350; - `deploy` zawsze raportuje probe, target, artefakt i jego SHA-256; - launcher nie wybiera pierwszego przypadkowego probe, gdy widocznych jest kilka urządzeń. Zewnętrzny debug probe i natywne USB płytki są rozdzielone. Standardowy debug/flash używa stabilnego probe, który nie re-enumeruje się przy resecie targetu. BOOTSEL/picotool jest opcjonalną, krótkotrwałą akcją z tego samego profilu: launcher wykrywa bieżący węzeł USB przed każdą fazą i ponownie po re-enumeracji. Nie przekazuje całego `/dev`. Build i test offline nie wymagają żadnego urządzenia. `run rp2350` wymaga zgodnego rekordu ostatniego deploy dla wybranej płytki i artefaktu. Bez niego odmawia wykonania, chyba że użytkownik jawnie zaakceptuje istniejący firmware. Zapobiega to testowaniu starego programu. ## Instancje i sockety MCP Stabilna nazwa logiczna: ```text --- ``` Aktualny container ID jest pobierany z `podman inspect`. Pełne nazwy logiczne są w labels i registry, a sockety używają krótkich kluczy: ```text $XDG_RUNTIME_DIR/stem////t.sock $XDG_RUNTIME_DIR/stem////n.sock $XDG_RUNTIME_DIR/stem///current ``` Klucze wątku i instancji mają 12 znaków, `cid12` jest prefiksem bieżącego ID. Registry przechowuje pełne wartości. Host i kontener używają tej samej ścieżki, a całkowita długość musi być mniejsza lub równa 100 bajtów, pozostawiając zapas względem linuksowego limitu `sun_path`. Wrapper MCP otrzymuje `instance`, sprawdza label kontenera, aktualny ID i typ socketu. Dopiero wtedy łączy się z tmuxem albo Neovimem. Pozwala to Codexowi obsługiwać kilka kontenerów bez przypadkowego wejścia do starej sesji. Publiczne wejścia launchera to `stemctl mcp tmux INSTANCE` oraz `stemctl mcp nvim INSTANCE`. Hostowy wrapper wykonuje wyłącznie resolver i `podman exec`; sam serwer wraz z przypiętymi zależnościami działa wewnątrz zweryfikowanego kontenera. Neovim MCP publikuje również stan i komendy Termdebug/nvim-dap. MCP nie jest czwartym kontenerem. Bridge jest częścią `dev-ui-base`, a hostowy resolver jest częścią `stem-launcher`. ## Cykl życia ```bash stemctl start hazard3-sim inf bss 1 stemctl status hazard3-sim --instance hazard3-sim-inf-bss-t1 stemctl attach hazard3-sim --instance hazard3-sim-inf-bss-t1 stemctl stop hazard3-sim --instance hazard3-sim-inf-bss-t1 stemctl rm hazard3-sim --instance hazard3-sim-inf-bss-t1 ``` `start` jest idempotentne dla zgodnej instancji. Jeżeli zmienił się lokalny ID wyniku builda albo konfiguracja targetu, launcher odtwarza kontener. `stop/start` zachowuje container ID; `rm/start` tworzy nowy ID i nowy katalog socketów. Stan karty pozostaje na hoście. ## Zdalny komputer Na komputer ucznia wchodzimy przez SSH z kluczem i uruchamiamy `stemctl` tam: ```bash ssh uczen-lab cd ~/dev/workspace/stem/tools/stem-launcher ./stemctl status hazard3-sim --instance hazard3-sim-inf-bss-t1 ./stemctl debug hazard3-sim inf bss 1 ``` GDB, UART i MCP nasłuchują na loopback albo Unix socketach. Jeżeli potrzebny jest dostęp z komputera nauczyciela, launcher generuje jawne polecenie tunelu SSH. Nie otwiera portów na `0.0.0.0`. ## Budowanie i cache `dev-ui-base` zawiera przypięte binaria/dependencies UI, lecz nie zawiera często zmienianych `configs/` i entrypointów. Trzy toolchainowe stage'e dziedziczą z niego, instalują ciężkie SDK/kompilatory, a dopiero finalne stage'e kopiują konfigurację UI i skrypty. Kod karty jest montowany, nie kopiowany. Zmiana entrypointu, dashboardu albo mapowania klawisza nie może invalidować warstwy pobierającej toolchain. ## Status implementacji | Element | Status | | --- | --- | | `native-amd64` | wdrożony i sprawdzony z ASan/UBSan | | `hazard3-sim` | wdrożony dla bare metal; `fast-dm` i BSP FreeRTOS są następne | | `rp2350` | wdrożony; FreeRTOS 1 kHz buduje ELF/UF2 dla RV i ARM, hardware gate wymaga ACL probe | | `stemctl` | wdrożony; `rvctl` jest wrapperem zgodności | | pobieranie kart z Git | istnieje w `rvctl` | | `build/test/run/debug/deploy` | wdrożony dispatcher manifestu i `result.json` | | sockety tmux/nvim | wdrożone z `thread12/instance12/container12` i resolverem | | zdalny SSH | kontrakt zaprojektowany, smoke test do dodania |