docs: define host workspace and MCP access

This commit is contained in:
user
2026-07-17 08:25:07 +02:00
parent 1e1c124c9f
commit 1d8f16303e
4 changed files with 231 additions and 0 deletions
+46
View File
@@ -23,6 +23,23 @@ 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
@@ -69,6 +86,10 @@ Domyślny układ:
└── 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:
@@ -102,6 +123,30 @@ się w wybranym kontenerze:
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,
@@ -134,6 +179,7 @@ Nowe materiały powinny używać nazw kanonicznych.
- [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)
+89
View File
@@ -17,6 +17,10 @@ mieć wgląd w tę samą sesję, w której pracuje uczeń:
Dzięki temu agent widzi środowisko debugowania, a nie tylko statyczne pliki.
Punkt wejścia `stemctl` jest także projektem hostowym: pierwszy klon znajduje
się w `~/dev/workspace/stem/tools/stem-launcher`. Agent ani użytkownik nie
klonują launchera, kart lub repozytoriów odpowiedzi do kontenera.
## Aktualny fundament
`rv32i-hazard3-student-env` dostarcza źródła wspólnego modelu:
@@ -108,6 +112,91 @@ Aktualny ID uniemożliwia użycie socketu pozostałego po odtworzeniu kontenera.
`stemctl` traktuje te pliki jako szczegóły implementacyjne. Użytkownik i agent
dostają komendy wyższego poziomu.
### Kto tworzy sockety
Hostowy launcher `stem` najpierw tworzy katalog runtime i przekazuje go
Podmanowi jako bind-mount pod **tą samą bezwzględną ścieżką**. Po uruchomieniu
interfejsu debuggera procesy wewnątrz kontenera tworzą sockety:
- `tmux` tworzy `t.sock`,
- `nvim` tworzy `n.sock`.
Nie są one kopiowane ani przekazywane przez sieć: host i kontener widzą ten sam
plik Unix socket w zamontowanym katalogu. ID kontenera jest częścią ścieżki,
więc nowy kontener po `rm/start` dostaje nowy katalog i nie może przypadkiem
obsłużyć socketu poprzednika.
### Dynamiczny wybór kontenera
Stały provider MCP na hoście używa dwóch krótkich ścieżek:
```text
$XDG_RUNTIME_DIR/stem/mcp-selected/current/n.sock
$XDG_RUNTIME_DIR/stem/mcp-selected/current/t.sock
```
Polecenie `stemctl mcp select INSTANCE_OR_CONTAINER_ID` weryfikuje registry,
pełny ID i label kontenera oraz oba żywe serwery. Następnie atomowo przełącza
symlink `current` dla obu socketów. Dzięki temu kolejna operacja MCP trafia do
wybranej sesji tmuxa i Neovima bez restartowania Codexa. `stemctl mcp list`
pokazuje tylko rekordy registry, a `stemctl mcp status` potwierdza bieżący
wybór.
Wybór pozostaje jawny: samo stworzenie kontenera tworzy jego katalog socketów,
ale nie przejmuje automatycznie aktywnego MCP innej sesji.
### Wiele kont użytkowników i pomoc nauczyciela
Sockety muszą pozostać prywatne dla unixowego konta, które uruchamia
kontener, np.:
```text
/run/user/<uid-ucznia>/stem/<thread>/<instance>/<container-id>/
```
Nie montujemy ich do wspólnego `/tmp`, nie zmieniamy grup socketów tmuxa i nie
udostępniamy ich przez port sieciowy. Codex nauczyciela łączy się przez SSH na
konkretne konto ucznia; po drugiej stronie ograniczony wrapper MCP łączy się
lokalnie z jego `n.sock` i `t.sock`. Stdio SSH jest transportem MCP — socket
Unix nie opuszcza komputera ucznia.
Każdy uczeń ma własny tmux i Neovim. Można uruchomić wiele serwerów MCP dla
tej samej sesji (np. ucznia i nauczyciela), ale oba mogą równocześnie edytować
bufor lub wysyłać klawisze, więc nie ma gwarancji arbitrażu zmian.
Dostęp nauczyciela rozdzielamy na dwa klucze SSH:
- zwykły klucz nauczycielski z pełną powłoką, używany wyłącznie do świadomej,
ręcznej interwencji na koncie ucznia;
- osobny klucz automatyzacji Codexa z forced command, bez TTY, forwardingu i
powłoki, ograniczony do `nvim`, `tmux`, `status` i dozwolonych kontenerów.
Pełny klucz nauczyciela nie trafia do kontenera ani do konfiguracji MCP.
### Poziomy uprawnień MCP
Każdy wpis MCP otrzymuje jawny poziom dostępu. Poziom jest własnością klucza
SSH i wrappera po stronie konta ucznia, a nie ustawieniem przekazywanym przez
model lub klienta MCP:
| Poziom | Przeznaczenie | Dozwolone działania |
| --- | --- | --- |
| `observe` | podgląd postępów | stan nvim, lista i capture pane’ów tmuxa, logi i metadane; bez edycji i wysyłania klawiszy |
| `assist` | wspólne rozwiązywanie problemu | działania debuggera i jawnie dozwolona edycja/panele; bez ogólnego terminala oraz bez poleceń powłoki |
| `full` | interwencja nauczyciela | pełne sterowanie nvimem i tmuxem, w tym terminalem w wybranym kontenerze, jako konto ucznia |
`full` jest równoważny interaktywnej pracy na koncie ucznia w granicach
wybranego kontenera. W szczególności arbitralne `nvim-remote-expr`, `nvim-ex`
lub wysyłanie poleceń do panea tmuxa mogą uruchomić kod. Taki wpis tworzymy
wyłącznie dla nauczyciela i zapisujemy w audycie konto, instancję, container ID,
czas oraz użyty poziom.
Nie wystarczy przekazać `MCP_ACCESS_LEVEL=observe` do tego samego pełnego
serwera: niższe poziomy wymagają osobnych providerów z allowlistą narzędzi.
W przeciwnym razie użytkownik nadal mógłby użyć ogólnego Ex/Vimscriptu albo
terminala do obejścia ograniczenia. Klucz `full` może korzystać z obecnego
providera STEM, ponieważ jego możliwości są celowo pełne.
## Role profili
Profil `hazard3-sim`:
+50
View File
@@ -195,6 +195,29 @@ re-enumeracji. Nie przekazuje całego `/dev`. Build i test offline nie wymagają
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.
## Instancje i sockety MCP
Stabilna nazwa logiczna:
@@ -227,6 +250,33 @@ Publiczne wejścia launchera to `stemctl mcp tmux INSTANCE` oraz
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`.
+46
View File
@@ -0,0 +1,46 @@
# Wytyczne dokumentowania debugowania
## Cel
Zrzut debuggera jest dowodem pomiaru, nie dekoracją. Ma pozwolić uczniowi
połączyć jedną operację w C z instrukcjami RISC-V, rejestrami, ramką stosu i
konkretnymi bajtami w RAM.
## Kadr dowodowy
- Używaj rzeczywistej sesji `stemctl debug` z karty, symulatora albo płytki.
- Zatrzymaj program w jednym nazwanym punkcie: breakpoint, checkpoint albo
instrukcja bezpośrednio przed obserwowaną operacją.
- Zachowaj wspólny układ: dashboard Termdebug/GDB po lewej, źródło C po
prawej, listing `.lst` pod źródłem. Listing musi być zsynchronizowany z PC.
- Pokaż tylko dane potrzebne do tezy: wywołanie w C, odpowiadającą instrukcję,
argument w rejestrze, fragment stosu albo pamięć w RAM.
- Dołącz podpis z platformą, taskiem i stanem pomiaru, na przykład „przed
pierwszym przydziałem pamięci”.
## Oznaczenia
- Stosuj najwyżej cztery krótkie oznaczenia numeryczne na jednym kadrze.
- Każdy numer ma odpowiadać jednemu zdaniu w podpisie lub legendzie.
- Bieżący kolor oznaczeń to czerwony: oznacza „zatrzymaj się i sprawdź”.
Paleta może później ulec zmianie, ale numeracja i podpis muszą pozostać
zrozumiałe bez koloru.
- Nie zasłaniaj kodu ani nie zmieniaj jego treści. Zachowaj surowy zrzut jako
źródło, a adnotowany obraz zapisz jako osobny plik.
## Wstawienie do karty
- Zasoby zapisuj w `doc/assets/`, np.
`task04-first-allocation-annotated.png`.
- W `doc/main.tex` umieść obraz blisko instrukcji, której dotyczy, oraz dodaj
zwięzły podpis wyjaśniający numery.
- Po zmianie zbuduj PDF skryptem `scripts/render_pdf.sh` i sprawdź stronę
wynikową w rozmiarze A4.
- W materiałach dla ucznia używaj słowa **RAM**, nie skrótu „SRAM”.
## Przykład referencyjny
Karta `inf/pointers`, Task04: `alloc_local(5)` przed pierwszym przydziałem.
Kadr pokazuje wywołanie C, `li a0,5` i `jal alloc_local`, wartość `a0=5` oraz
pusty `allocbuf`. Taki obraz dokumentuje związek źródła, ABI i pamięci bez
zastępowania go opisem narracyjnym.