Files
stem-launcher/doc/containers.md
T

284 lines
9.9 KiB
Markdown

# 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/<instance>
STEM_ARTIFACT_DIR=/workspace/.stem/artifacts/<profile>/<target>/<task>
```
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
<profile>-<series>-<card>-<task>
```
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/<thread-key>/<instance-key>/<cid12>/t.sock
$XDG_RUNTIME_DIR/stem/<thread-key>/<instance-key>/<cid12>/n.sock
$XDG_RUNTIME_DIR/stem/<thread-key>/<instance-key>/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 |