# STEM Launcher `stemctl` jest hostowym wejściem do kart pracy, Git i trzech źródłowo budowanych kontenerów STEM. Zdalne repo nosi kanoniczną nazwę `edu-tools/stem-launcher`; Gitea przekierowuje historyczny URL `rv-launcher`, a `rvctl` pozostaje cichym wrapperem zgodności. Launcher nie pobiera i nie publikuje gotowych obrazów środowiska. Klonuje repo narzędzi zawierające `Dockerfile` i `docker-compose.yml`, a brakujący profil buduje lokalnie rootless Podmanem. Tag `localhost/stem/...:local` jest tylko lokalnym wpisem cache runtime. ## Model | Profil | Targety | Zastosowanie | | --- | --- | --- | | `native-amd64` | `native` | C/C++, golden tests, ASan/UBSan, GDB | | `hazard3-sim` | `hazard3-baremetal`; `hazard3-freertos` po BSP timer/IRQ | RTL Hazard3/Verilator bez płytki | | `rp2350` | `rp2350-rv`, `rp2350-arm` | Pico 2/2 W, FreeRTOS, OpenOCD/picotool | W każdym profilu interfejs pozostaje taki sam: tmux z prefixem `Ctrl-s`, Neovim, Termdebug, nvim-dap oraz MCP tmuxa i Neovima. Kod karty, wyniki i artefakty są bind-mountem na hoście. Kontenery nie dostają kluczy SSH, tokenów, socketu Podmana/Dockera ani całego `/dev`. ## Pierwsza instalacja Pierwszy klon launchera również należy do workspace. Nie uruchamiamy `stemctl` z przypadkowego katalogu domowego ani z kontenera: ```bash mkdir -p ~/dev/workspace/stem/tools git clone http://77.90.8.171:3001/edu-tools/stem-launcher.git \ ~/dev/workspace/stem/tools/stem-launcher cd ~/dev/workspace/stem/tools/stem-launcher ./stemctl workspace sync ``` Od tego momentu wszystkie komendy `stemctl`, karty i repozytoria odpowiedzi pozostają pod `~/dev/workspace/stem`. Kontener dostaje wybraną kartę jako `/workspace`, ale nie jest miejscem przechowywania źródeł. ## Szybki start ```bash ./stemctl workspace sync ./stemctl series list ./stemctl series cards fetch inf bss ./stemctl test native-amd64 inf bss 1 ./stemctl test hazard3-sim inf bss 1 ./stemctl debug hazard3-sim inf bss 1 ./stemctl probe list ./stemctl deploy rp2350 inf bss 1 \ --target rp2350-rv --device /dev/bus/usb/001/006 ``` Pierwsze wywołanie danego profilu może potrwać, ponieważ buduje go ze źródłowego Dockerfile. Kolejne korzystają z lokalnych warstw cache. Wymuszenie samego przygotowania środowiska: ```bash ./stemctl env sync ./stemctl env build native-amd64 ./stemctl env build hazard3-sim ./stemctl env build rp2350 ``` `env sync` klonuje lub aktualizuje `edu-tools/rv32i-hazard3-student-env`. Repo środowiska zawiera źródłowy Dockerfile i pliki Compose. ## Workspace Domyślny układ: ```text ~/dev/workspace/stem/ ├── meta/workspace-info/ ├── series/// ├── tools/ │ ├── stem-launcher/ │ └── rv32i-hazard3-student-env/ └── tokens/tokens.json ``` Pierwszym repozytorium w `tools/` jest `stem-launcher`; to ono pobiera workspace-info, źródła kart oraz repo środowiska. Pozostałe repozytoria są zarządzane przez `stemctl`, a nie klonowane wewnątrz kontenera. Jeżeli istnieje tylko starszy `~/dev/workspace/rv`, launcher wykrywa go bez niszczenia danych. Jawna migracja: ```bash ./stemctl workspace migrate ./stemctl workspace doctor ``` ## Akcje i tożsamość instancji Jedna logiczna instancja zachowuje nazwę między `build`, `test`, `run`, `debug` i `attach`. Bieżący ID kontenera wchodzi natomiast do krótkiej ścieżki socketu: ```text $XDG_RUNTIME_DIR/stem////t.sock $XDG_RUNTIME_DIR/stem////n.sock ``` Resolver wykonuje `podman inspect` przed połączeniem i odrzuca osierocony socket. Pozwala to jednemu Codexowi obsługiwać kilka kontenerów bez kolizji. Stabilne wejścia MCP wymagają jawnej instancji i uruchamiają serwer znajdujący się w wybranym kontenerze: ```bash ./stemctl mcp tmux hazard3-sim-inf-bss-t1 ./stemctl mcp nvim hazard3-sim-inf-bss-t1 ``` Oddzielne wpisy klienta MCP mogą wskazywać inne nazwy instancji, więc jeden Codex obsługuje kilka kontenerów bez globalnego „ostatniego socketu”. Klient MCP działający stale na hoście może także przełączać oba narzędzia atomowo przez wspólny wskaźnik `current`: ```bash ./stemctl mcp list ./stemctl mcp select rp2350-pointers-final ./stemctl mcp status ./stemctl mcp select 3aca2c1c4c7a ``` Selektor przyjmuje nazwę instancji, nazwę kontenera albo co najmniej 12 znaków ID. Weryfikuje ID i label przez `podman inspect`, a także aktywnie sprawdza oba serwery. Dopiero wtedy atomowo przełącza: ```text $XDG_RUNTIME_DIR/stem/mcp-selected/current/n.sock $XDG_RUNTIME_DIR/stem/mcp-selected/current/t.sock ``` Wskaźniki prowadzą do katalogu zawierającego aktualny `container12`, więc odtworzony kontener nie może przejąć socketów poprzednika. Neovim i tmux otwierają nowe połączenie przy każdym wywołaniu narzędzia MCP, dlatego zmiana działa bez restartowania klienta. ## Komputer zdalny Na komputer ucznia wchodzimy wyłącznie SSH z parą kluczy. Launcher, Git, rootless Podman i sockety działają na tym komputerze: ```bash ssh uczen-lab cd ~/dev/workspace/stem/tools/stem-launcher ./stemctl status hazard3-sim --instance lekcja-1 ./stemctl debug hazard3-sim inf bss 1 --instance lekcja-1 ``` Nie kopiujemy prywatnego klucza do kontenera. Jeżeli potrzebny jest zdalny GDB/MCP, używamy jawnego tunelu SSH albo wykonujemy klienta po stronie zdalnej. ## Zgodność Aliasy nadal działają: ```text host -> native-amd64 rv32i -> hazard3-sim rvctl -> stemctl RV_* -> fallback dla STEM_* ``` Nowe materiały powinny używać nazw kanonicznych. ## Dokumentacja - [plan architektury](doc/architecture-plan.md) - [kontenery i interfejs](doc/containers.md) - [wytyczne dokumentowania debugowania](doc/debug-documentation-guidelines.md) - [migracja nazw i workspace](doc/migration-stem-launcher.md) - [serie i karty](doc/series.md) - [tokeny Gitea](doc/tokens.md) - [przegląd Claude](doc/review-claude-2026-07-14.md)