en un clic
dev-guide-gotchas
Nested lingtai-dev-guide reference for known implementation footguns: Bubble Tea v2 paste, textarea theming, dev-mode rebuilds, editable installs, migrations, localization, authorization gates, and config conventions.
Menu
Nested lingtai-dev-guide reference for known implementation footguns: Bubble Tea v2 paste, textarea theming, dev-mode rebuilds, editable installs, migrations, localization, authorization gates, and config conventions.
Router for contributing to the LingTai project. Use this when you are about to change LingTai code or docs, set up a dev environment, navigate the Go TUI/portal repo or Python kernel, develop MCP addons, prepare a release, troubleshoot a running network, audit security, govern avatars, prepare a publication-bound release workflow, run a runtime self-check, get a PR review-ready, or steward a new skill. This is for developers and contributors; for end-user lessons, use tutorial-guide.
Compact lingtai-dev-guide release overview: when you are doing a release, the maintainer-authorization boundary, the version scheme, and a pointer to release-workflow for the full TUI/Portal + kernel publishing checklist, GitHub/PyPI/Homebrew steps, the required HTML release log, and the website release blog.
Nested lingtai-dev-guide reference for consequential LingTai releases: paired TUI/Portal + kernel release planning, clean worktrees, validation gates, GitHub/PyPI/Homebrew publishing boundaries, the required self-contained HTML release log, website release-log/blog drafting, and the reusable release blog template.
Nested lingtai-dev-guide reference for getting a PR review-ready: PR readiness gates, multi-model/daemon/Claude read-only review passes, self-contained local HTML explainers, PR body hygiene and gh pr edit troubleshooting, source-labeled deliverables with syntax/validation checks, and maintainer authorization boundaries for opening, editing, and merging PRs.
Nested lingtai-dev-guide reference for developer/operator runtime self-checks after a refresh, checkout, or preset/MCP change: probe which lingtai code is actually running, confirm the editable source and git HEAD, verify the active TUI/portal binary and dev-mode symlinks, rebuild the TUI from a clean release worktree, inspect MCP/addon module sources and tool surface, and report evidence safely with secrets redacted.
Nested lingtai-dev-guide reference for turning one-off experience into durable skills: when to write a skill vs leave it as notes, the router-vs-nested- reference pattern, distilling experience (归一) and session journals into triggered/executable SKILL.md files, de-privatizing and parameterizing local paths and human-specific details, a lightweight pre-publish benchmark/checklist, grooming shared-library candidates, and PR-ready skill cleanup.
| name | dev-guide-gotchas |
| description | Nested lingtai-dev-guide reference for known implementation footguns: Bubble Tea v2 paste, textarea theming, dev-mode rebuilds, editable installs, migrations, localization, authorization gates, and config conventions. |
| version | 1.0.0 |
Nested lingtai-dev-guide reference. Read this after the top-level router sends you here. Collective wisdom from production incidents. Each entry has caused at least one regression.
Bubble Tea v2 splits keys (tea.KeyPressMsg) from clipboard pastes (tea.PasteMsg). Any Update dispatcher that handles case tea.KeyPressMsg: must also forward tea.PasteMsg to whichever text widget is focused. Otherwise paste silently drops.
For embedded sub-models (e.g. PresetEditorModel inside FirstRunModel), the host's outer default: branch must forward paste msgs into the sub-model. If the host's dispatcher enumerates focused widgets per-step but misses the step that hosts an embedded model, paste dies before reaching the sub-model — and adding a fall-through inside the sub-model's own Update is useless because the msg never arrives.
Symptom: typing into an input works, pasting does nothing.
Fix: Trace top-down (tea.Program → host's outer switch → sub-model's outer switch → widget) and ensure every layer either handles or forwards tea.PasteMsg.
textarea vs textinputtextinput is single-line and drops characters on multi-byte / clipboard pastes. textarea handles paste cleanly. For any paste-friendly field (API keys, base URLs, anything the user might paste from a browser), use textarea even when the content is conceptually one line:
ta := textarea.New()
ta.CharLimit = 512
ta.SetWidth(50)
ta.SetHeight(1)
ta.ShowLineNumbers = false
ta.Prompt = ""
ta.KeyMap.InsertNewline.SetKeys() // single-line: no newline insertion
ta.SetStyles(themedTextareaStyles())
themedTextareaStyles() is in the tui package — always apply it. A bare textarea.New() ships dark default cursor/focus colors that render as a black smear against the warm LingTai theme.
When running symlinked dev binaries, a stale TUI or portal binary against a freshly-migrated project fails with:
data version N is newer than this binary supports (M); upgrade lingtai-tui
data version N is newer than this binary supports (M); upgrade lingtai-portal
Root cause: The TUI and portal share .lingtai/meta.json. Any binary whose
compiled migration CurrentVersion is lower than the project's meta.json
version must refuse to open the project; otherwise it might misread or downgrade
newer state. This can happen even when the feature PR you wanted is already on
main: another local branch or newer dev binary may have already written a
higher data version to the target project.
Preflight before replacing a local dev binary for an existing project:
PROJECT=/path/to/project
CHECKOUT=/path/to/lingtai-checkout
printf 'project meta version: '
python3 - <<PY
import json, pathlib
meta = pathlib.Path('$PROJECT/.lingtai/meta.json')
print(json.loads(meta.read_text()).get('version') if meta.exists() else '<none>')
PY
printf 'tui CurrentVersion: '
grep -R 'const CurrentVersion' "$CHECKOUT/tui/internal/migrate/migrate.go"
printf 'portal CurrentVersion: '
grep -R 'const CurrentVersion' "$CHECKOUT/portal/internal/migrate/migrate.go"
If the target project's version is higher than the checkout's CurrentVersion,
do not install that binary over the user's active lingtai-tui. Either build
from a checkout/branch that includes the matching migration, or stop and explain
that the requested main rebuild cannot safely open that project yet. Do not
"fix" this by editing meta.json downward.
Fix after any migration bump: rebuild both binaries from the same checkout:
cd ~/Documents/GitHub/lingtai/tui && make build
cd ~/Documents/GitHub/lingtai/portal && make build
The brew-installed pair normally avoids this because the released TUI and portal ship together at the same version. Local dev binaries and one-off test overlays are the dangerous case; always do the preflight above before overwriting an active binary for a real project.
The TUI's auto-upgrader (config.CheckUpgrade) compares lingtai.__version__ to PyPI's latest. If your local source's version is lower, it replaces the editable install with the PyPI wheel.
Symptom in the launch banner:
Upgrading lingtai 0.8.0 → 0.8.2...
- lingtai==0.8.0 (from file:///.../lingtai-kernel) ← editable, gone
+ lingtai==0.8.2 ← PyPI wheel
Prevention: Keep lingtai-kernel/pyproject.toml version >= PyPI's latest.
Recovery:
~/.local/bin/uv pip install -e ~/Documents/GitHub/lingtai-kernel \
-p ~/.lingtai-tui/runtime/venv
Use uv, not pip — the venv is uv-managed and has no pip symlink.
A successful PR merge does not prove running agents are executing the merged
code. The TUI-created runtime venv may have lingtai installed editable from a
checkout such as ~/Documents/GitHub/lingtai-kernel, and that checkout may be
behind origin/main even after the PR merged. Agents can also be launched from
older detached worktrees or legacy addon checkouts.
Symptom: a feature that is present on GitHub is missing at runtime; imports
such as lingtai.mcp_servers fail; an MCP/addon path still points at an old
standalone repo or a detached worktree.
Fix: probe the exact Python interpreter named by the agent/runtime, print
module.__file__ for lingtai, lingtai_kernel, and relevant MCP/addon
modules, inspect the git root/HEAD behind those paths, fast-forward the checkout
if it is the intended editable source, then call system(action="refresh") and
rerun the probe. See reference/setup/SKILL.md → “Verify the runtime checkout a
running agent actually uses” for the full command recipe.
TUI and portal share meta.json but have separate migration registries. When adding a TUI migration, you MUST also bump CurrentVersion in portal/internal/migrate/migrate.go.
Adding an i18n key means updating all three of en.json, zh.json, wen.json in both tui/i18n/ and (where applicable) portal/i18n/. Missing translations show as the raw key on screen — they don't fall back.
If a key is genuinely English-only (procedural notes, error stacks), document why. Otherwise translate.
The TUI binary is lingtai-tui, never lingtai. lingtai is the Python agent CLI (installed by the kernel into the TUI's runtime venv at ~/.lingtai-tui/runtime/venv/bin/lingtai). Build the TUI to tui/bin/lingtai-tui; never to tui/bin/lingtai.
Presets are split into two directories:
presets/templates/ — TUI-owned. Rewritten on every Bootstrap from embedded data. Never hand-edit.presets/saved/ — User-owned. Bootstrap never touches it.The directory IS the answer to "is this a template?" — there's no in-band marker. Each loaded Preset carries a Source field (SourceTemplate / SourceSaved). Prefer IsTemplate(p) over the legacy IsBuiltin(p.Name).
When writing manifest.preset.* paths from Go code, always use preset.RefFor(p) to pick the right subdirectory based on Source.
manifest.preset.allowed is the explicit list of preset paths the agent may swap to at runtime. The kernel refuses any swap not in allowed. default and active MUST both appear in allowed; init_schema.validate_init enforces this.
The Go module names are github.com/anthropics/lingtai-tui and github.com/anthropics/lingtai-portal. This is historical naming — they are NOT moving to a Lingtai-AI/ import path even though the GitHub org renamed.
At most ONE system(action="notification") pair lives in the wire history at any time. When the kernel detects a fingerprint change, it strips the prior pair and reinjects a fresh one. Agents observe the current notification state, not a history of arrivals.
uv vs pipThe TUI's runtime venv is uv-managed. There is no pip symlink — only pip3. Always use uv pip for package operations in this venv.