Files
2026-07-17 09:22:59 +02:00

343 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
## Źródła projektu
Źródła kart i projektów pobiera wyłącznie `stemctl` na hoście, zawsze pod
`~/dev/workspace` danego konta Unix. Kanoniczny workspace STEM ma postać
`~/dev/workspace/stem`; nie używamy do pracy katalogów kontenera ani
przypadkowych klonów poza `~/dev/workspace`. Pierwszy klon launchera trafia do
`~/dev/workspace/stem/tools/stem-launcher` i od niego zaczyna się cały
bootstrap workspace. Typowy przepływ to:
```bash
stemctl workspace sync
stemctl series cards fetch inf pointers
stemctl debug rp2350 inf pointers 4
```
Katalog karty pozostaje na hoście, na przykład
`~/dev/workspace/stem/series/inf/pointers/`, i jest przekazywany kontenerowi
jako bind-mount `/workspace`. Nie klonujemy repozytoriów źródłowych wewnątrz
kontenera: kontenery są odtwarzalne i mogą zostać usunięte, natomiast host
zachowuje Git, zmiany ucznia, odpowiedzi i historię pracy. W obrazie lub
osobnym cache volume mogą znajdować się wyłącznie narzędzia, zależności i
odtwarzalne cache budowania.
Dla Hazard3 po przygotowaniu przez `stemctl env sources SERIES CARD` ten sam
bind-mount zawiera również rzeczywiste (niebędące symlinkami) katalogi
`vendor/Hazard3` i `vendor/lab-runtime`. Program, testbench oraz GDB korzystają
wyłącznie z nich. Build testbencha i ELF trafia do
`.stem/instances/<instance>/build`, a DWARF zachowuje ścieżki
`/workspace/vendor/Hazard3/...` i `/workspace/src/...`. `/opt` pozostaje
miejscem toolchainów i programów obrazu; nie jest źródłem kodu wyświetlanego w
Neovimie ani GDB.
## 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.
Dla jednego stale działającego klienta hostowego dostępny jest również jawny,
atomowy przełącznik obu socketów:
```bash
stemctl mcp list
stemctl mcp select INSTANCE_OR_CONTAINER_ID
stemctl mcp status
```
`mcp select` sprawdza registry, pełny ID i label kontenera, aktywnie testuje
serwery Neovima i tmuxa, a następnie jednym `rename(2)` przełącza symlink
`$XDG_RUNTIME_DIR/stem/mcp-selected/current`. Jeśli choć jeden serwer nie
odpowiada, poprzedni wybór pozostaje bez zmian.
Katalog socketów jest tworzony przez launcher na hoście i bind-mountowany do
kontenera pod identyczną ścieżką. Dopiero `nvim` i `tmux` działające w
kontenerze tworzą odpowiednio `n.sock` i `t.sock`; hostowy Codex łączy się z
tym samym plikiem przez widok hosta. Sockety nie są przekazywane przez sieć ani
nie dają kontenerowi dostępu do socketu Podmana.
Połączenia MCP mają profil `observe`, `assist` albo `full`. `full` jest
przeznaczony dla nauczyciela: może wykonywać dowolne działania dostępne w
Neovimie, tmuxie i terminalu wybranego kontenera jako konto ucznia. Profile
`observe` i `assist` muszą używać oddzielnych, ograniczonych providerów;
nie wolno udawać ograniczenia przez samą zmienną środowiskową przy providerze
udostępniającym ogólny Vimscript lub polecenia panea.
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 |