1. Cíl a motivace
Problém — SIGKILL incident
Původní architektura sdílela globální Mařin tým plugin (talkide-be/plugin/) přes všechny
generované projekty. Porty a konfigurace se Mara dozvídala z textu v CLAUDE.md — textová
instrukce “Porty 9090 a 5200 jsou rezervovány pro TalkIDE platformu, nepoužívej je”.
Tato instrukce selhala v praxi: při práci na projektu pekarna-u-jelena Mařin devops agent
na příkaz kill-port.sh 9090 sundal samotnou TalkIDE platformu (BE běžící na portu 9090),
ve které právě Mara pracovala. Platforma okamžitě přestala fungovat.
Root cause: agent četl port z textu, ne z deterministického zdroje. Textová instrukce “neuhybej na platformní porty” neměla dostatečnou autoritu vůči shell příkazu.
Vedlejší problém — create-vite ghost directory bug
Při bootstrapu nových projektů frontend dev agent volal npm create vite@latest s absolutní
cestou jako cílovým argumentem. create-vite má zdokumentovaný bug: absolutní cestu
concatenuje na cwd místo aby ji použil jako absolutní. Výsledek:
cwd = /Users/.../output-projects/pekarna-u-lesa
npm create vite@latest /Users/.../frontend → vytvoří ghost adresář:
pekarna-u-lesa/Users/mirek/Work/.../pekarna-u-lesa/ (prázdný strom!)
Bug byl opatřen workaroundem v talkide-frontend-dev.md (relativní cesta + verify krok),
ale ghost dirky v existujícím projektu pekarna-u-lesa jsou relikt starší verze.
Řešení novou architekturou: scaffold celého projektu přebírá TalkIDE BE při create
projektu — frontend kostra je vygenerovaná ze serveru přes templates. npm create vite
agenti volat nebudou vůbec. Viz sekce 7.
Řešení — deterministická konfigurace
Přecházíme na architekturu, kde každý generovaný projekt nese vlastní kopii pluginu
a strukturovaný konfigurační soubor .talkide/project.yml. Plugin skripty čtou porty
výhradně z tohoto YAML souboru přes yq — nikdy z argumentů, nikoli z textu CLAUDE.md.
Tím je kolize s platformními porty strukturálně nemožná (viz sekce 3 — Port invariants).
2. Struktura generovaného projektu
Každý generovaný projekt žije v:
workspace/output-projects/<slug>/
Plná struktura po vytvoření projektu:
<slug>/
├── .talkide/
│ ├── plugin/ # rsync kopie talkide-be/plugin/ — obnovuje se při každém spawn
│ ├── project.yml # sloučený config projektu (meta + porty + URLs)
│ └── team/ # FUTURE: per-project user overrides agentů (zatím .gitkeep)
├── backend/ # Spring Boot kostra — naskaffoldovaná BE při create projektu
├── frontend/ # Vite/Vue kostra — naskaffoldovaná BE při create projektu
├── documentation/ # Dokumentace projektu (dříve `docs` — viz sekce 7)
└── CLAUDE.md # Lidský kontext, briefing týmu — renderuje BE před každým spawn
Popis jednotlivých položek
| Položka | Vlastník | Životní cyklus |
|---|---|---|
.talkide/plugin/ | TalkIDE BE (rsync) | Obnovuje se před každým spawn Claude CLI |
.talkide/project.yml | TalkIDE BE | Vzniká při create, aktualizuje se BE při změně projektu |
.talkide/team/ | Uživatel (FUTURE) | Prázdný adresář (.gitkeep); budoucí per-project overrides |
backend/ | Scaffold + agenti | Vzniká při create; agenti kód přidávají/mění |
frontend/ | Scaffold + agenti | Vzniká při create; agenti kód přidávají/mění |
documentation/ | Agenti (analyst) | Vzniká při create (prázdný); agenti doplňují UC docs |
CLAUDE.md | TalkIDE BE | Renderuje se před každým spawn (viz mara-context.md) |
.talkide/ jako systémový adresář: vedoucí tečka signalizuje “system-managed, needitovat
ručně”. Agenti NESMÍ editovat soubory v .talkide/ (výjimka: team/ v budoucnu pro user
overrides). Skripty v .talkide/plugin/scripts/ jsou read-only vstup z TalkIDE platformy.
3. Schéma .talkide/project.yml
.talkide/project.yml je jediný zdroj pravdy o identitě projektu a přiřazených URL/portech.
Čtou ho výhradně plugin skripty přes yq. TalkIDE BE ho nepotřebuje číst — má tyto informace
v databázi.
Příklad souboru
# .talkide/project.yml — managed by TalkIDE BE. Do NOT edit manually.
id: 7
slug: pekarna-u-jelena
name: Pekárna u Jelena
description: Rezervační systém pro malou rodinnou pekárnu
accent: "#e8a045"
backend:
language: Kotlin
framework: Spring Boot
frontend:
language: TypeScript
framework: Vue.js
urls:
backend: http://localhost:8097
frontend: http://localhost:5207
Popis polí
| Pole | Typ | Povinné | Popis |
|---|---|---|---|
id | integer | ano | Unikátní ID projektu z databáze TalkIDE (≥ 1) |
slug | string | ano | URL-safe identifikátor projektu (a-z, 0-9, pomlčky) |
name | string | ano | Zobrazovaný název projektu |
description | string | ne | Popis projektu (může být prázdný) |
accent | string | ano | Accent barva projektu v hex formátu |
backend.language | string | ano | Programovací jazyk backendu (default: Kotlin) |
backend.framework | string | ano | Framework backendu (default: Spring Boot) |
frontend.language | string | ano | Programovací jazyk frontendu (default: TypeScript) |
frontend.framework | string | ano | Framework frontendu (default: Vue.js) |
urls.backend | string | ano | Plná URL backendu včetně portu |
urls.frontend | string | ano | Plná URL frontendu včetně portu |
Port invariants
Porty jsou deterministicky odvozeny z project.id:
| Položka | Vzorec | Minimum (id=1) |
|---|---|---|
| Backend port | 8090 + id | 8091 |
| Frontend port | 5200 + id | 5201 |
Kolize s platformními porty je strukturálně nemožná: TalkIDE platforma běží na portech
9090 (BE) a 5200 (FE). DB sequence pro project.id startuje na hodnotě 1 (Postgres default
BIGSERIAL / IDENTITY). Protože id ≥ 1, nejnižší projektový BE port je 8091 a nejnižší
FE port je 5201. Port 9090 ani 5200 nikdy nemůže být přiřazen projektu — kolize je
vyloučena na úrovni datového modelu, ne až na úrovni textové instrukce agentovi.
Production URL: urls.backend a urls.frontend jsou plné URL — v produkci obsahují
doménové jméno místo localhost portu (např. https://api.pekarna-u-jelena.talkide.app).
Plugin skripty port extrahují z URL přes sed, ne obráceně — URL je zdrojem pravdy.
Source of truth a regenerace
Soubor .talkide/project.yml generuje BE:
- Eager: při vytvoření projektu (
CreateProjectUseCase), bezprostředně po DB save — projekt už má přidělenéid, takže porty jsou známé. - Lazy guard: před každým spawn Claude CLI se soubor re-renderuje (idempotentní — pokud je obsah byte-identický, write se přeskočí). Pojistka pro případ že soubor chybí (manuální smazání, nový projekt vytvořený starší verzí BE bez eager kroku).
DB je jediný source of truth — project.yml je projekce stavu. Pokud uživatel ručně
upraví soubor, změna se ztratí při příštím spawn.
Privacy & gitignore
.talkide/project.yml je BE-managed sidecar — stejný režim jako bývalý
.project-config.yml (viz project-config.md, nyní deprecated):
- Není git-tracked —
.talkide/je v.gitignoregenerovaného projektu. - Nikdy neopouští TalkIDE infrastrukturu — nečte ho FE, neposílá se do API odpovědí, není součástí prompt kontextu posílaného Anthropicu.
- MAY contain sensitive data (DB credentials, API tokeny v budoucnu) — privacy
boundary z původního
project-config.mdzůstává v platnosti.
4. Plugin sync mechanismus
Kdy a jak probíhá sync
TalkIDE BE provede rsync globálního talkide-be/plugin/ do <project>/.talkide/plugin/
ve dvou momentech:
- Eager — při create projektu:
CreateProjectUseCaseprovede rsync bezprostředně po DB save, ve stejné transakci kde se zapíše.talkide/project.yml. Projekt je tak “kompletně vyklopený” hned po dokončení create operace, ještě než uživatel pošle první zprávu. - Lazy guard — před každým spawn Claude CLI:
ClaudeCliService.startProcessprovede rsync znovu těsně před spawnem (initial i--resume). Pojistka pro případ že eager krok selhal nebo že byl projekt vytvořen starší verzí BE bez eager kroku.
Příkaz:
rsync -a --delete talkide-be/plugin/ <project>/.talkide/plugin/
-a(archive): rekurzivní kopie se zachováním oprávnění a struktury.--delete: soubory smazané z globálního pluginu jsou smazány i z per-project kopie (jinak by zombie soubory přetrvávaly do nekonečna).
Operace je idempotentní — opakované spuštění na identickém stavu pluginu je no-op (rsync porovnává mtime + velikost a přeskakuje beze změny soubory).
Předání pluginu do Claude CLI
BE volá claude s flagem --plugin-dir:
--plugin-dir <project>/.talkide/plugin
Cesta míří na per-project kopii, ne na globální talkide-be/plugin/. Tím je zajištěno
že každý projekt má svou izolovanou plugin instanci (a v budoucnu může mít vlastní
overrides — viz sekce 11).
Cache invalidace
Plugin agenti (agents/talkide-pm.md atd.) jsou součástí prompt cache per-projekt.
Lazy guard rsync před každým spawnem znamená, že opravený plugin se projeví při dalším
spawn — cache projektu se rozbije, protože obsah souboru se změnil. Aktivní konverzace
(právě běžící Claude CLI proces) vidí ještě starý obsah; cache invalidace nastane až
při příštím spawn.
Implikace pro bug fixy: Oprava v globálním pluginu (talkide-be/plugin/) se propaguje
do všech projektů automaticky — bez nutnosti manuálního zásahu. Každý projekt dostane
opravenou verzi nejpozději při příštím spawn.
Bezpečnost & gitignore
- Agenti NESMÍ editovat soubory v
.talkide/plugin/— jsou přepsány při dalším spawn. .talkide/plugin/je v.gitignoregenerovaného projektu (jde o generovaný artifact, který nemá být verzovaný).- Celý
.talkide/adresář je BE-managed — viz sekce 3 “Privacy & gitignore”.
5. Plugin skripty — nový pattern čtení konfigurace
Plugin skripty přestávají přijímat port jako argument příkazové řádky. Port (a celou URL)
čtou výhradně z .talkide/project.yml přes yq.
Nový pattern (deterministický)
#!/usr/bin/env bash
# start-backend.sh — Start Spring Boot backend in background
set -euo pipefail
PROJECT_DIR="${1:?Usage: start-backend.sh <project-dir>}"
BE_URL=$(yq -r '.urls.backend' "$PROJECT_DIR/.talkide/project.yml")
PORT=$(echo "$BE_URL" | sed -E 's|.*:([0-9]+).*|\1|')
HEALTH_URL="$BE_URL/actuator/health"
Starý pattern (deprecated)
# DEPRECATED — port přichází jako argument, default 9090 (kolize s platformou!)
PORT="${2:-9090}"
Pravidla pro plugin skripty
- Každý skript přijímá
<project-dir>jako první argument. - Port se nikdy nepředává jako argument — vždy se extrahuje z
.talkide/project.yml. - Žádné hardcoded default porty.
- Skript MUSÍ selhat s chybovou hláškou, pokud
.talkide/project.ymlneexistuje nebo pokudyqpříkaz selže.
Závislost na yq — žádný fallback
Skripty vyžadují yq (https://github.com/mikefarah/yq) jako system-level tool.
Bez fallbacku — pokud yq chybí, skript okamžitě skončí s chybou:
command -v yq >/dev/null 2>&1 || {
echo "error: yq is required (brew install yq)" >&2
exit 1
}
Důvod: yq je standardní nástroj na práci s YAML v shellu (analogie jq pro JSON).
Implementovat fallback parsování YAML v čistém bashi by přidalo ~50 řádků křehkého
kódu na případ, který v praxi nenastane (TalkIDE provoz vyžaduje development environment
se základními tooly). Explicitní fail s instalační instrukcí je čistší než tichý degradace.
Viz runtime-dependencies.md — kompletní seznam systémových závislostí včetně instalačních instrukcí pro macOS, Debian a Docker.
6. Plugin agenti — úprava
Všechny plugin agent soubory (agents/talkide-*.md) musí reflektovat novou architekturu:
- Dropnout sekci “Port Configuration”: Port není záležitost agenta — je v
project.yml. - Dropnout
<port>argumenty z příkladů: Volání scriptů jsou ve tvarustart-backend.sh $PROJECT_DIR, nestart-backend.sh $PROJECT_DIR 8097. - Přidat odkaz na
.talkide/project.yml: Agenti mají vědět, kde žije source of truth — v.talkide/project.yml, ne v CLAUDE.md.
Pravidlo pro agenty ohledně portů:
Pokud potřebuješ znát port nebo URL projektu, přečti
.talkide/project.ymlpřesyq. Nikdy nepředpokládej port z textu instrukce ani z CLAUDE.md.
7. Scaffold při create projektu
Při vytvoření projektu BE naskaffolduje celou adresářovou strukturu i skeleton soubory. Agenti pak doplňují doménový kód — nemusí řešit boilerplate.
Co BE vytvoří
<slug>/
├── .talkide/
│ ├── project.yml # vyplněný config projektu
│ └── team/.gitkeep # prázdný adresář pro budoucí team overrides
├── backend/ # Kotlin/Spring skeleton
│ ├── gradlew
│ ├── gradlew.bat
│ ├── build.gradle.kts
│ ├── settings.gradle.kts
│ ├── .gitignore
│ └── src/main/kotlin/<package>/
│ ├── Application.kt
│ └── resources/
│ ├── application.yaml # port nastavený z BE_URL
│ └── db/changelog/
│ └── db.changelog-master.yaml
├── frontend/ # TypeScript/Vue skeleton
│ ├── package.json # vue, vite, typescript dependencies
│ ├── vite.config.ts # port nastavený z FE_URL
│ ├── tsconfig.json
│ ├── index.html
│ ├── .gitignore
│ └── src/
│ ├── main.ts
│ └── App.vue
└── documentation/ # prázdný adresář pro UC dokumentaci
└── .gitkeep
Kde žijí templates
talkide-be/src/main/resources/templates/
├── kotlin-spring/
│ ├── build.gradle.kts.mustache
│ ├── settings.gradle.kts.mustache
│ ├── Application.kt.mustache
│ ├── application.yaml.mustache # proměnná: {{port}}
│ └── db.changelog-master.yaml.mustache
└── typescript-vue/
├── package.json.mustache
├── vite.config.ts.mustache # proměnná: {{port}}
├── tsconfig.json.mustache
├── main.ts.mustache
├── App.vue.mustache
└── index.html.mustache
Template proměnné
Scaffolder předává do templates:
| Proměnná | Zdroj | Příklad |
|---|---|---|
{{slug}} | project.slug | pekarna-u-jelena |
{{name}} | project.name | Pekárna u Jelena |
{{packageName}} | slug → Java package | pekarnaujelena |
{{backendPort}} | 8090 + project.id | 8097 |
{{frontendPort}} | 5200 + project.id | 5207 |
{{backendUrl}} | http://localhost:{{backendPort}} | http://localhost:8097 |
{{frontendUrl}} | http://localhost:{{frontendPort}} | http://localhost:5207 |
Gradle wrapper
Soubory gradlew, gradlew.bat, a gradle/wrapper/* se kopírují z talkide-be/gradle/wrapper/
(samotná TalkIDE platforma). Důvod:
- Gradle wrapper má dvě binární komponenty (
gradle-wrapper.jar,gradle-wrapper.properties), které nelze rozumně držet jako Mustache template. - Verze Gradle, kterou používá TalkIDE BE, je ověřená (Kotlin 2.x + Spring Boot 3.x kompatibilní). Nemá smysl ji synchronizovat zvlášť pro generované projekty.
- Každý update Gradle v TalkIDE BE se automaticky propaguje do nově generovaných projektů (existující projekty nezahrnujeme — používají Gradle z doby své create operace).
Scaffold provede cp -r talkide-be/gradle/wrapper/* <project>/backend/gradle/wrapper/
a cp talkide-be/gradlew{,.bat} <project>/backend/. Tyto soubory nejsou template,
nejsou rsynced (jednorázová operace při create).
MVP scope a rozšiřitelnost
MVP: jen default stack — Kotlin/Spring Boot + TypeScript/Vue.js.
Přidání nového stacku (např. Python/FastAPI) vyžaduje:
- Novou sadu templates v
templates/<novy-stack>/ - Nový branch v scaffold use case podle
backend.framework+frontend.framework - Žádná změna ve struktuře
.talkide/ani v plugin sync mechanismu
8. MaraContextRenderer — CLAUDE.md po novém
Konkrétní hodnoty portů a URL z přidané sekce # Infrastruktura v CLAUDE.md
(viz mara-context.md) se dropují. Místo toho se renderuje
krátký pointer odkazující agenta na strukturovaný zdroj:
# Infrastructure
See `.talkide/project.yml` for ports, URLs, and configuration.
Tento pointer:
- Je statický (stejný napříč všemi projekty) — nenarušuje prompt cache.
- Učí Maru, kde hledat — pokud potřebuje vědět port nebo URL, ví že má číst
.talkide/project.ymlpřes Read/Bash, ne hádat z textu. - Neobsahuje žádná projektově-specifická data — žádné porty, žádné URLs, žádné varování typu “neuhybej na 9090”. Strukturální omezení (porty ≥ 8091) tato varování činí zbytečnými.
CLAUDE.md zůstává jako lidský briefing: popis projektu, tým, welcome instrukce. Neobsahuje žádné technické porty ani URLs.
Důvod: CLAUDE.md jde do LLM kontextu (Anthropic infrastructure). Port a URL jsou
operační data — patří do .talkide/project.yml, který Mara čte lokálně přes file system,
ne do promptu přes text.
Sync contract:
MaraContextRenderer.ktmusí přestat renderovat per-project hodnoty v sekci# Infrastructurea místo toho renderovat statický pointer výše. Jednotkový testMaraContextRendererTestCasesmusí být upraven — expected output musí obsahovat pointer namísto konkrétních portů. Viz mara-context.md pro popis sync contractu.
9. Whitelist project exploreru
BE vrací FE seznam souborů projektu přes Files Explorer API. application.yaml obsahuje
whitelist top-level adresářů viditelných na root úrovni ("[/]").
Současný stav (před touto změnou)
whitelist:
"[/]":
directories: [backend, docs, frontend]
Cílový stav (po přejmenování a doplnění)
whitelist:
"[/]":
directories: [backend, documentation, frontend, .talkide]
"[/backend]":
directories: [src]
files: []
"[/frontend]":
directories: [src]
files:
- index.html
- package.json
- vite.config.ts
- tsconfig.json
Změny oproti původnímu stavu:
docs→documentation(přejmenování adresáře)- Přidán
.talkide(uživatel vidí project.yml a plugin strukturu pro debugging)
Poznámka k .talkide/plugin/: plugin adresář je velký (všechny agent soubory).
Pokud by Explorer zobrazoval .talkide jako klikatelný, je vhodné přidat do whitelist
"[/.talkide]" s omezením na files: [project.yml] a directories: [] — uživatel
vidí jen konfigurační soubor, ne interní plugin soubory. Konzultovat s dev při implementaci.
10. Migrace stávajících projektů
Stávající projekty v workspace/output-projects/ se vymažou — zatím je nikdo nepoužívá
v produkci. Žádný migration script.
Postup:
rm -rf workspace/output-projects/*
Nové projekty budou od začátku vytvořeny s novou strukturou (.talkide/, scaffold).
11. Future — .talkide/team/ per-project overrides
Adresář .talkide/team/ je připraven pro budoucí funkci: uživatel může přepsat chování
konkrétního agenta pro svůj projekt bez zásahu do globálního pluginu.
MVP: BE vytváří .talkide/team/.gitkeep při create projektu — adresář existuje jako
prázdný placeholder, nepoužívá se, žádná logika merge. Důvod existence v MVP:
deterministická adresářová struktura napříč všemi projekty (snadnější verzování změn
struktury bez per-projektových rozdílů).
Future use case: Mara pro projekt pekarna-u-jelena má jiná pravidla pro tón
komunikace než globální default. Uživatel (nebo Mara na jeho přání) vytvoří:
.talkide/team/talkide-pm.md # override globálního agents/talkide-pm.md
Merge strategie (TODO při implementaci future feature):
- Per-project soubor má přednost před globálním plugin souborem.
- Globální plugin soubor se stále rsynuje do
.talkide/plugin/agents/. - Claude CLI resoluje agenty: nejprve
.talkide/team/, pak.talkide/plugin/agents/.
Thanks for the feedback.