Internal Documentation internal
TalkIDE internal documentation

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žkaVlastníkŽivotní cyklus
.talkide/plugin/TalkIDE BE (rsync)Obnovuje se před každým spawn Claude CLI
.talkide/project.ymlTalkIDE BEVzniká 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 + agentiVzniká při create; agenti kód přidávají/mění
frontend/Scaffold + agentiVzniká 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.mdTalkIDE BERenderuje 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í

PoleTypPovinnéPopis
idintegeranoUnikátní ID projektu z databáze TalkIDE (≥ 1)
slugstringanoURL-safe identifikátor projektu (a-z, 0-9, pomlčky)
namestringanoZobrazovaný název projektu
descriptionstringnePopis projektu (může být prázdný)
accentstringanoAccent barva projektu v hex formátu
backend.languagestringanoProgramovací jazyk backendu (default: Kotlin)
backend.frameworkstringanoFramework backendu (default: Spring Boot)
frontend.languagestringanoProgramovací jazyk frontendu (default: TypeScript)
frontend.frameworkstringanoFramework frontendu (default: Vue.js)
urls.backendstringanoPlná URL backendu včetně portu
urls.frontendstringanoPlná URL frontendu včetně portu

Port invariants

Porty jsou deterministicky odvozeny z project.id:

PoložkaVzorecMinimum (id=1)
Backend port8090 + id8091
Frontend port5200 + id5201

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:

  1. 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é.
  2. 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 truthproject.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 .gitignore generované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.md zů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:

  1. Eager — při create projektu: CreateProjectUseCase provede 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.
  2. Lazy guard — před každým spawn Claude CLI: ClaudeCliService.startProcess provede 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 .gitignore generované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.yml neexistuje nebo pokud yq pří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 tvaru start-backend.sh $PROJECT_DIR, ne start-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.yml přes yq. 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áZdrojPříklad
{{slug}}project.slugpekarna-u-jelena
{{name}}project.namePekárna u Jelena
{{packageName}}slug → Java packagepekarnaujelena
{{backendPort}}8090 + project.id8097
{{frontendPort}}5200 + project.id5207
{{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:

  1. Novou sadu templates v templates/<novy-stack>/
  2. Nový branch v scaffold use case podle backend.framework + frontend.framework
  3. Žá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.yml př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.kt musí přestat renderovat per-project hodnoty v sekci # Infrastructure a místo toho renderovat statický pointer výše. Jednotkový test MaraContextRendererTestCases musí 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:

  • docsdocumentation (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/.

Was this page helpful?

Thanks for the feedback.