# ⛔ PRIVATE DATA GUARD — ABSOLUTE, NON-NEGOTIABLE, READ FIRST ⛔ > Latest released version: **[v1.92.4](docs/changes/2026-11-08-v1.92.4.md)**. ## v1.92.4 — Matching builds, explicit release identity and readable Markdown The combined builder predicts both editions’ actual emitted version before building, pins one matching number, runs Private/Public pairs in parallel with `--self-modify`, and archives verified ZIPs by edition, type and minute. The application log identifies its embedded release type. Dependency setup stays in isolated, application-owned caches and stops safely when no healthy environment exists. Readable Markdown with occasional control characters opens without losing its original bytes; binary content is still refused. See the [1.92.4 release notes](docs/changes/2026-11-08-v1.92.4.md). ## Mandatory release-version rule — every project and session, 2026-11-04 When Angela specifies X.Y.Z, **assume X.Y.Z is already released** and use it everywhere as the **latest released version** immediately. Her number is the one version. Do not check GitHub, tags, installed packages or publication status to decide whether to call it released. Do not create source, target, pending or tagged-versus-published versions. Update every current reference, manifest, `Tlamatini.md`, PDF/PPTX and generator together. Preserve historical entries, dependency pins, Git history and actual test evidence. Git writes and executable builds still require authorization. This rule supersedes older contrary advice. ## Carried from v1.92.3 — Downloads, updates and build isolation Torrenter adds account-owned downloads to chat and the Agentic Control Panel. Live Editor and Terminater accept up to 250 WPM. The updater carries the uninstaller and preserves the vault. Release builds lease the Python environment for their requirements fingerprint. See the [v1.92.3 release record](docs/changes/2026-11-01-v1.92.3.md). ## v1.91.1 — Prompt Flow fixes and short tool names **v1.91.1** was the English release of that date (2026-10-27). Its annotated tag resolves to `9915938a` and was present on origin; it was published on GitHub as the then-Latest release, replacing v1.91.0 (published 2026-10-23). It is a patch release: no agent, tool, skill or migration was added, so the catalog stays **86 workflow agents, 67 wrapped chat agents, 110 built-in tools, 29 skills and 220 migrations**. - **Prompt Flow history:** every chain loads the history of the request's own `conversation_user_id`; a Prompt Flow run uses only its own in-memory history. - **YES/NO Decisions:** an `equals` Decision accepts an answer that opens with the value (`## YES - ...` equals `YES`; `Yesterday` does not), and the Prompt before it asks for that short answer alone on the first line. - **Usage:** a Prompt Flow run is recorded for the account that owns it. - **Telegrammer:** "using the bot ..." anywhere in the request (outside quoted values) sends as the bot; an explicit `provider` still wins. - **Short tool names:** a wrapped agent called by its short name (`terminater`) is resolved to `chat_agent_terminater` and logged as `[TOOL-ALIAS]`. - **Prompt Flow tab title:** an Untitled flow shows the name of its file. See the [v1.91.1 release record](docs/changes/2026-10-27-v1.91.1.md). ## Carried from v1.91.0 — Live Editor and Terminater configuration **v1.91.0** was the release of 2026-10-23. Its existing annotated tag resolves to `a771d27c` and is present on origin. The catalog remains **86 workflow agents, 67 wrapped chat agents and 110 built-in tools** (21 + 67 + 12 + 10), with **29 skills and 220 migrations**. The frontend has **64 JS modules and 29 CSS files** in the counted `static/agent/js` and `static/agent/css` directories. **Config → Live Editor & Terminater** opens one dialog with Live Editor, Terminater and Voice tabs, also available from the canvas and terminal Settings buttons on the chat and control-panel surfaces. Saved account preferences control real action pacing, teaching suggestions, shell defaults, manual Enter, waits and narration; editor/terminal appearance stays local to the account and browser. Presets and previews are drafts until Save; Stop, mute and tab access are immediate. Migration **0220** stores account preferences and revisions. One TerminalHub applies them across chat, ACP and Prompt Flow; legacy explicit flow values remain compatible. Speech uses the shared browser queue, selects one focused tab and can wait for a real completion receipt before typing. User interruption wins and private input is not narrated. The offline editor bundle and runtime/source carriers include the new UI and backend modules. Source evidence on October 23: **17/17 visible browser checks passed; 929 of 931 regression tests passed**, with two existing README documentation assertions failing before this documentation sweep. These are dated source results, not a new installed package acceptance. v1.91.0 was published on GitHub on 2026-10-23. See the [release record](docs/changes/2026-10-23-v1.91.0.md) and [settings guide](docs/interaction-settings.md). ## Carried from v1.90.0 — editable canvas and Interactor The v1.90.0 source line had **86 workflow agents, 67 wrapped chat agents, 110 built-in tools (21 direct/module + 67 wrapped + 12 ACPX/Skills + 10 supervisors), and 219 migrations** after migration 0219. Interactor added a module/tool, not an Agent. The annotated tag resolves to `ebc5c3b4` and its GitHub release was published on 2026-10-22. Later commits in that line carried the account wizard’s local Notifier checkpoints. This section preserves v1.90.0 history; current configuration and counts are described above. Dated source and frozen runs remain separate. - **Editor:** `agent_page_editing.js` owns the CodeMirror document, dirty baseline, encoding and save snapshot. `agent_page_canvas.js` owns load/refresh generation and cancellation. Markdown Preview and Source share the buffer; HTML/SVG remain inert source; PDF saving copies the original PDF. Never read a hidden compatibility element instead of the editor. - **Native files:** `canvas_file_views.py` and `native_dialogs.py` provide local Save As/read/save. Authentication, CSRF, loopback-only host access, account/session-bound capabilities and full revision checks prevent a browser name from becoming an arbitrary host path. Existing Windows replacement preserves metadata; DB backup/restore mechanics are unchanged. - **Interactor:** `agent/interactor/` connects the synchronous tool loop to the browser through session-authenticated `ws/interactor/`. Server-issued tab identity travels through ContextVar; bounded commands and receipts verify document/revision and full buffer. Browser messages acknowledge/access/stop; they cannot request arbitrary host-path commands. - **Tutor:** read fresh unsaved text, type a small segment, guide the next field and end mutations for the turn. Continue begins a fresh chat/read. Prepared choices match locally, with no per-key model call. User input and Stop win; hidden tabs pause with bounded recovery rather than repeating edits. - **Carriage:** migration 0218 seeds the Tool/catalog rows; `tool_gate_table()` supplies binding, Compact selection and cost labels. The offline editor bundle, Interactor browser files, frozen imports and source/static inventories must travel together. - **Intent:** password input is not ownership of every command; a wizard email field or DONE reply is not notification consent. Preserve genuine user-runs tutorials. The catalog wizard uses createsuperuser and therefore creates an administrative account. Contracts and evidence: [v1.90.0](docs/changes/2026-10-22-v1.90.0.md), [Canvas editing](docs/canvas-editing.md), [Interactor](docs/interactor.md), [Account wizard](docs/user-creation-wizard.md). Source/frozen rendering tests do not certify a rebuilt installed executable. ## v1.88.0 — Terminater and the private release's data > **Released as v1.88.0** (tagged 2026-10-20). The v1.88.0 tag points to `83e0cbe6`; > this work landed on `main` in `6e129563` right after it, so a build of `main` > reports `1.88.0` and carries it. Records: [v1.88.0](docs/changes/2026-10-20-v1.88.0.md), > [Terminater](docs/changes/2026-10-19-terminater.md) and > [the private release's data](docs/changes/2026-10-19-private-release-data.md). **Counts in v1.88.0:** **86 workflow agents, 67 wrapped chat agents, 109 built-in tools** (20 + 67 + 12 + 10), 51 structured-output producers, **217 migrations** (0215-0217), 60 JS modules, 27 stylesheets. The static suffix for that release was `-agent-catalog-1-quality-cleanup-1-usage-status-2-terminater-1`. **Terminater** (agent #86; full contract in [docs/terminater.md](docs/terminater.md)) raises REAL `cmd.exe` / PowerShell terminals in the **Terminals** area (**Views ▸ Terminals**, Ctrl+`) where the USER watches. Executer returns text from an invisible shell; Terminater types the command into a terminal the user sees, waits, and reads it back. Contracts that must not be weakened: - **Tlamatini types, never pastes.** One key at a time at `typing_wpm` (default 50, 20-160, never faster than 30 ms per key); there is no instant mode. Output is kept in full with an honest `truncated` flag; a command silent for `hang_seconds` (45) is `hung` and can be aborted; the user always wins (Esc takes over; `<>` hands the keyboard over; `watch_user` and a deterministic advisor help while they type). - **One hub, three doors.** A single `TerminalHub` lives in the Django process (`terminal_hub.py`; its engine `agents/terminater/terminater_engine.py` is loaded BY PATH from the agents tree so the web process, the pool agent and the tests share one definition; the three engine files are stdlib-only). Doors: `ws/terminals/` (session-authenticated), `POST /agent/terminater/api/` (loopback only + `X-Terminater-Token`, token scrubbed from the shells' environment, rendezvous file `/Temp/.terminater_hub.json`) and the in-process Prompt Flow Panel runner. Closing a page never kills a shell. The reapers ask `terminal_hub.live_pids()` and the hub's shells die with Tlamatini. - **Surfaces.** `chat_agent_terminater` (on the Ask Execs allowlist: visible is not harmless), an ordinary active canvas agent, and the 8th executable Prompt Flow Panel operation (the kickoff example deliberately has none; the build gate validates one separately). Result words: `completed`/`opened`/`typed`/`killed`/`focused`/`warned` green; `command_failed`, `hung`, `operator_required`, `aborted` red. - **Carriage.** Migrations 0215 (Agent row), 0216 (`Chat-Agent-Terminater` Tool row) and 0217 (catalog prompts 127-129, *Run & Execute*); the display name is pinned in `display_name_from_agent_type`; the **`agents_descriptions.md` row feeds the canvas tooltip**, so a missing row is a visible defect. `build.py` names the hub, consumer, views and `check_terminater` as frozen modules and runs `check_terminater` (real PowerShell, real `cmd.exe`, the pool agent under the carried Python, a WebSocket round trip) inside the frozen app, refusing to package on failure. xterm.js is vendored locally (5.5.0, no CDN). **The private (keyed) release now carries the data of THIS machine** (`build_complete_private_release.py`; details in [the record](docs/changes/2026-10-19-private-release-data.md)): contacts (as before); **every External MCP server of the running install** merged into the gitignored `external_mcps.private.json` (the tracked dev catalog still wins for servers both have; only empty or placeholder values are filled; the install's active selection, at most 5, is kept; the previous snapshot stands in when no install is found); and the whole **`data.keys`** vault shipped as `/data.keys` through `TLAMATINI_BUNDLE_DATA_KEYS`. The public builder clears that variable, `build.py` aborts if the vault is requested with the PUBLIC catalog, and the new file is gitignored and excluded from the self-modify snapshot. Updates preserve an existing `data.keys`; public packages and self-modification snapshots exclude it. Two entries (`OPENROUTER_API_KEY`, `DASHSCOPE_API_KEY`) have no consumer other than the vault itself. ## v1.87.0 — Leaner catalog, safer removal, smaller release (carried) **v1.87.0** (tagged 2026-10-18; it also carries v1.86.0, tagged 2026-10-17) came before v1.88.0. Its catalog was **85 workflow agents, 66 wrapped chat agents and 108 built-in tools**: Gatewayer, Gateway-Relayer, Node-Manager and Zavuerer were removed, and migration **0214** deletes their rows and the Zavuerer prompts without renumbering. Removal moves application entries into a same-drive recovery folder (schema 2 journal, **Retry cleanup**, `.tlamatini-removal.json`) instead of copying the payload. `build.py` keeps the carried-Python-only media and browser packages out of the frozen interpreter (`test_release_payload_ownership.py`). About ▸ Usage waits for real data and sends a bodyless `POST /api/me`. `npm run lint:python` runs `scripts/check_python_quality.py`. From v1.86.0: Mover creates a missing destination name as a folder (`_names_a_file()`), and `scripts/architecture_atlas/` rebuilds `Tlamatini-Architecture.pdf` with LaTeXer. The static suffix for that release was `-agent-catalog-1-quality-cleanup-1-usage-status-2`. See [the v1.87.0 release record](docs/changes/2026-10-18-v1.87.0.md). ## v1.85.0 — Refresh, Views and centered avatar (carried) **v1.85.0** (2026-10-16) added Refresh, Views and the centered avatar. `agent_page_canvas.js` owns Refresh source access, complete-text comparison, cancellation and status. Retain browser file handles during the drop event; snapshot-only access cannot refresh a path. PDF replacement stages a ready viewer and keeps its iframe attached at commit. `agent_page_layout.js` exposes `TlamatiniViews.setCanvasVisible`, `isCanvasVisible` and `resetLayout`. Hiding toggles `tlm-canvas-hidden`, preserves the canvas DOM and context, cancels divider gestures and moves focus only when its current region disappears. The account/browser preference is `tlamatini.chat.views.v1.`. `avatar_size.js::setDockedOnly` enforces the hidden-canvas docking rule in pointer, keyboard and public API paths. Reset shows both panes, resets their sizes and docks the portrait without clearing content. The docked frame uses `--tlm-avatar-docked-right` measured from the full Mic/Send row. Its square size and 120 px minimum stay independent of floating geometry. v1.85.0's static suffix was `-avatar-center-1`; Views scripts retain `_views1`. `build_runtime_assets.py` and `copy_source_assets.py` require the Views template, scripts and snapshot harnesses. No migration or dependency was added. See [the release implementation and evidence matrix](docs/changes/2026-10-16-v1.85.0.md), [Refresh](docs/canvas-refresh.md) and [carriage](docs/self-management-carriage.md). > **HEADLESS IS FORBIDDEN. ALL EXECUTION MUST BE VISIBLE.** Every automated test, command, diagnostic, script, build, agent, prompt and browser must run in a visible, forked foreground window on Angela's real desktop. Use visible CMD/PowerShell consoles and headed browsers (`headless=False`); keep output on screen and monitor live. No hidden/background execution, including CI or quick checks. If visibility cannot be confirmed, do not run. Mandatory policy: [visible execution](TestsVisiblesAndVisibleExecutionFromClaude2Codex.md). > **Developer/product boundary (Angela, 2026-09-27):** these visibility requirements govern development and verification. They must not create extra windows, steal focus or expose internal diagnostics in the shipped UX. Direct chat dictation keeps status in chat and uses only Tlamatini's main console/log. Direct microphone architecture, Config → Mic automatic-send/editable-draft preferences, configuration, packaging and verification: [docs/chat-microphone-design.md](docs/chat-microphone-design.md). The button is a direct input path, not another workflow agent or a VOICE COMMANDS catalog card. **NEVER REWRITE GIT HISTORY. EVER. IN THIS REPO, FOR ANY REASON.** - **NO** `rebase`, `commit --amend`, `reset --hard` to drop commits, `filter-branch`, `git filter-repo`, BFG. - **NO** `push --force` / `--force-with-lease`, **NO** deleting pushed tags, **NO** deleting remote refs. - TO REMOVE SENSITIVE / PRIVATE DATA: edit or delete the file, then make a **NEW FORWARD COMMIT** and push that. THE PAST STAYS UNTOUCHED. - **TAGS, PUSHES, AND `git log` MUST ALWAYS REMAIN INTACT AND TRUTHFUL** — `git log` MUST show the real history, made without lying, FOREVER. - PRIVATE INFORMATION MAY BE **DELETED GOING FORWARD**; THE HISTORY THAT RECORDED IT MUST **NOT** BE ERASED. - IF ANY TOOL OR WORKFLOW WOULD REWRITE HISTORY, **STOP** AND TELL ANGELA FIRST. Enforced by: `test_private_data_guard.py` (automated tests — git-history integrity guard + banner guard) and the release-time scrub gate in `build_complete_public_release.py`. --- # Tlamatini — KIMI.md **Welcome, Kimi!** This is the self-contained onboarding and reference document for working on the **Tlamatini** project. Read it in full before making any change. It is the Kimi sibling of `CLAUDE.md` (Claude Code's manifest + `@docs/claude/*` imports) and `GEMINI.md` (Gemini CLI's knowledge base): same mandatory rules, same architecture contracts, tuned for Kimi. Because Kimi has **no `@`-file auto-import mechanism**, everything an AI maintainer needs day-to-day is inline here; deeper topic files are listed in §25 as consult-on-demand. Every current count in this file was **re-verified against source on 2026-10-16 for the v1.85.0 release** (not copied from docs, which drift). If a count here disagrees with a hand-written doc elsewhere, re-run the source inventory and fix every active surface together. --- ## Contents 1. Project Identity & Verified Facts 2. Persona & Communication Rules 3. Architecture Overview (7 Layers + Request Flow + Stack) 4. Directory Structure 5. Configuration System 6. Database & Models 7. RAG System 8. Multi-Turn Orchestration 9. "MCP" Means Four Things Here (disambiguation) + MCP Surfaces 10. ACPX System (external coding-agent CLIs) 11. Skills System (SKILL.md packages) 12. Visual Workflow Designer (ACP) & Flow Compiler 13. The 87 Workflow Agent Types (catalog) 14. Creating a New Agent / Tool / Skill 15. Frontend Architecture 16. Build, Release & Versioning 17. Privacy, Secrets & Release Scrubbing 18. Testing & Lint (headed-tests directive) 19. Coding Conventions & Critical Rules 20. Known Hardcoded Assumptions & Pitfalls 21. Orphan-Process Cleanup (3-tier reaper) 22. Self-Knowledge & Self-Modification 23. How to Run 24. File Paths Quick Reference 25. Deeper Documentation (consult-on-demand) 26. Kimi-Specific Notes & Dogfooding Directive --- ## 1. Project Identity & Verified Facts **Tlamatini** (Nahuatl for *"one who knows"*) is a **local-first AI developer assistant** created by **Angela López Mendoza** (@angelahack1, XAIHT). It is a Django 5.2 + Channels monolith with a LangChain/LangGraph agent core, a RAG system, a visual agentic workflow designer, an external coding-agent runtime (ACPX), a markdown skill system, and a pool of standalone agent scripts it spawns as subprocesses. Windows-only distribution (PyInstaller-frozen, carried Python 3.12.10). - **Repository**: `https://github.com/XAIHT/Tlamatini.git` · **License**: MIT · **Platform**: Windows 10/11 **v1.90.0 (tagged 2026-10-22)** brings Interactor: a live tutor inside the editable chat canvas. It reads the exact unsaved buffer, types a small teaching segment, hands the next field to the learner and offers optional local completions. It carries v1.89.0’s CodeMirror editor and native Save As, plus tab-visibility recovery, narrow-canvas layout and account-wizard intent fixes. That source line had **86 workflow agents, 67 wrapped chat agents, 110 built-in tools (21 + 67 + 12 + 10), and 219 migrations**. Interactor is a module/tool, not an additional workflow agent. The tag resolves to `ebc5c3b4`; its GitHub release ("Tlamatini v1.90.0 Win11x64") was published on 2026-10-22 and was marked Latest at that time. `main`/`origin/main` continues after the tag (`d274f89c`, `8c577d3a`, `939acf6b`, `5403fa49`) and reported 1.90.0 at that time; migration 0219 makes the catalog's account wizard ask for local Notifier pop-ups at its checkpoints. Source tests and an existing frozen-package check are dated separately; this documentation does not certify a newly built executable. See the [v1.90.0 release record](docs/changes/2026-10-22-v1.90.0.md). - **Python**: 3.12.10 (carried interpreter under `/python` is build-provisioned — never use it to run builds) **Source inventory · November 2, 2026** | Surface | Count | Ground truth | |---|---|---| | Workflow agent types | **87** † | `Tlamatini/agent/agents//` dirs (excl. `pools/`) and `_tlamatini_agents_manifest.json` | | Wrapped chat-agent tools (`chat_agent_*`) | **68** † | `chat_agent_registry.py` `WRAPPED_CHAT_AGENT_SPECS` (counted) | | Direct/module tools | **21** | Base gate table, including Interactor | | ACPX tools (`acp_*`, `list_acp_agents`, `invoke_skill`, `list_skills`) | **12** | `agent/acpx/tools.py` | | External-MCP supervisor tools | **10** (+ dynamic `ext__*`) | `_SUPERVISOR_TOOL_NAMES` in `agent/external_mcp_manager.py` | | **Built-in Multi-Turn tool surface** | **111** (+ dynamic) | 21 + 68 + 12 + 10 from the source gate and registries | | Root stdio MCP server tools | **104** | 87 agent launchers + 7 management + 10 ACPX (`tlamatini_mcp_server.py`) | | SKILL.md packages | **29** | `agent/skills_pkg/` (confirmed live via `tlamatini_list_skills`) | | ACPX external CLI agent_ids | **14** | `agent/acpx/agent_registry.py` `DEFAULT_ACP_AGENTS` | | DB models | **21** | `agent/models.py` | | Migrations | **221** | Through `0221_torrenter.py` | | Frontend JS modules | **64** | `agent/static/agent/js/*.js` | | Page templates / CSS files | **9 / 29** | `agent/templates/agent/*.html` and `agent/static/agent/css/*.css` | | HTTP routes / view functions | 183 / 214 | `agent/urls.py`, `agent/views.py` (12,906 lines) | † ‡ **Re-counted again on 2026-10-19** for the Terminater work released in v1.88.0 (agents 85 → 86, migrations 214 → 217, JS modules 59 → 60 with `terminater_dock.js`, stylesheets 26 → 27 with `terminater_dock.css`, wrapped chat agents 66 → 67, built-in tools 108 → 109; the earlier v1.87.0 keeps its own numbers). The agent, migration, JS, CSS and template rows were previously re-counted from disk on **2026-10-14** for v1.83.0 (only the JS count moved since v1.82.0: 58 → 59, with `file_drop_router.js`); the route and view-function row dates from 2026-10-09 (`views.py` still has 214 functions and 12,906 lines). ‡ Rows last changed on **2026-10-14** (latest addition: `file_drop_router.js`; before it the Markdown canvas, the shared theme's stylesheets, migration 0213 for About ▸ Usage and the Compact mode / Self-modify / Model Brain files). Feature headlines: advanced RAG (FAISS + BM25, RRF fusion, context budgeting, memory-insufficient fallback) · Multi-Turn operator loop binding the **full enabled tool surface** · Visual Workflow Designer (ACP canvas) compiling `.flw` → `config.yaml` pools via a backend Flow Compiler + Agent Contract registry · ACPX runtime spawning 14 external coding-agent CLIs · universal External-MCP client (4 transports, ≤5 active) with a private Node/uv provisioner and inactive Memory/Sequential-Thinking defaults · 29-package Skill system · self-knowledge + self-modification (ships her own rebuildable source) · multi-model LLM via Ollama (local + cloud) / Anthropic / Qwen vision · full PyInstaller build → installer pipeline with secret-separated public/private release twins. --- ## 2. Persona & Communication Rules ### 2.1 The user: Angela López Mendoza - The user is **Angela López Mendoza** — developer, architect, and creator of Tlamatini. - **Always address her by name ("Angela")** in responses; do not speak impersonally. - Use her full name when affirming her as creator. Her name must **NEVER** be erased or scrubbed from source files, banners, docs, prompts, the About window, PDF/PPTX, or build metadata. A public release build may mask her *other* private data (emails/phones) — never her name. ### 2.2 Persona & identity - Tlamatini is **explicitly female** by design. - The `talker` (TTS) agent is restricted to **female voices only** (`tara`, `leah`, `jess`, `mia`, `zoe`). `resolve_voice()` in `agent/agents/talker/talker.py` raises `MaleVoiceForbiddenError` and hard-exits `"NOW CLOSING.. BYE"` on any male voice request. Do NOT modify or bypass this. ### 2.3 How to talk to Angela (mandatory) - **Answer short and in plain language.** - **Lead with the single key fact in bold.** - A few short numbered points at most; everyday words; no jargon walls; no long source lists. - Cut anything that does not change her decision. - End with **one direct question or next step**. ### 2.4 Step-by-Step interactive mode When a task needs Angela to act on her machine (edit configs, click UI, check a board, restart the app): 1. Give **exactly one** concrete step. 2. Give her the **exact string to send back** (e.g. `step1: I see ___`). 3. **WAIT** for that string before the next step. 4. Repeat — one step + one reply-string per turn. ### 2.5 Work style (from `docs/claude/gotchas.md`) - A broad directive ("Go!", "modify everything") means: execute comprehensively without per-file confirmation. - Read broadly first, plan the full scope, execute in parallel batches; use subagents for throughput. - Ask only on truly ambiguous architectural decisions. - Angela values robustness ("bullet-proof") and uniformity; she is comfortable with large cross-cutting changes. --- ## 3. Architecture Overview ### 3.1 The 7 Layers ``` ┌────────────────────────────────────────────────────────────────────────┐ │ Layer 1: Database State │ │ (SQLite: persisted toggle rows for Mcps, Tools, Agents, Skills...) │ └───────────────────────────────────┬────────────────────────────────────┘ ▼ ┌────────────────────────────────────────────────────────────────────────┐ │ Layer 2: gRPC & WebSocket Services │ │ - System-Metrics (WebSocket :8765) - Files-Search (gRPC :50051) │ └───────────────────────────────────┬────────────────────────────────────┘ ▼ ┌────────────────────────────────────────────────────────────────────────┐ │ Layer 3: Context Fetcher Sidecar Chains │ │ - SystemRAGChain - FileSearchRAGChain │ └───────────────────────────────────┬────────────────────────────────────┘ ▼ ┌────────────────────────────────────────────────────────────────────────┐ │ Layer 4: Main LLM Chains │ │ - basic.py (fallback) - history_aware.py - unified.py (LangGraph) │ └───────────────────────────────────┬────────────────────────────────────┘ ▼ ┌────────────────────────────────────────────────────────────────────────┐ │ Layer 5: Unified Tools │ │ (LangChain @tools in tools.py + chat_agent_* registry wrappers) │ └───────────────────────────────────┬────────────────────────────────────┘ ▼ ┌────────────────────────────────────────────────────────────────────────┐ │ Layer 6: ACPX Multi-Agent Core │ │ (AcpxRuntime + sandboxed SkillHarness driving external CLIs) │ └───────────────────────────────────┬────────────────────────────────────┘ ▼ ┌────────────────────────────────────────────────────────────────────────┐ │ Layer 7: Flow Compiler │ │ (AgentContract registry + FlowSpec normalizer + FlowCompiler engine) │ └────────────────────────────────────────────────────────────────────────┘ ``` - **L1 Database**: seeds default registries; UI checkboxes flip DB rows that gate the tool surface. - **L2 Runtime services**: started from `agent/apps.py::AgentConfig.ready()` (reloader-gated); feed live metrics + file indexes. - **L3 Sidecar chains**: prefetch `{system_context}` / `{files_context}` injected into every prompt. - **L4 Main chains**: `factory.py` monkey-patches `invoke()` to merge sidecar context before the LLM call. - **L5 Tools**: 21 direct/module tools + 68 wrapped `chat_agent_*` launchers + 10 External-MCP supervisors; the 12 ACPX/Skill tools form L6 but join the same 111-tool built-in Multi-Turn surface when enabled. Direct chat dictation prepares user input before this tool surface; it does not add another tool. - **L6 ACPX & Skills**: spawns external CLI engines and runs `SKILL.md` playbooks. - **L7 Flow Compiler**: normalizes canvas/chat flows into run-ready pools under `agents/pools//`. ### 3.2 Request flow (chat message → answer) 1. User sends a message via WebSocket `ws/agent/` (optionally with `multi_turn_enabled`, `exec_report_enabled`, `acpx_enabled`, `ask_execs_enabled`, `step_by_step_enabled`). 2. `AgentConsumer` (`agent/consumers.py`, 2,460 lines) receives and routes. 3. Context determination (is RAG loaded?). 4. Internet check (classify whether a web search is needed). 5. Chain selection (RAG / Basic / Unified Agent). 6. **Multi-Turn gate**: checked = planner + full-surface dynamic tool binding; unchecked = legacy one-shot. 7. **ACPX gate**: when `acpx_enabled=False`, `agent.acpx.filter_acpx_tools()` strips every ACPX/Skill tool before the planner/executor see them. 8. **Ask-Execs gate** (Multi-Turn only): when enabled, the executor BLOCKS before every state-changing tool on a browser Proceed/Deny prompt, bridged by `agent/exec_permission.py::ExecPermissionBroker`. Deny halts the whole chain (red "Execution interrupted" banner); emit-failure / Cancel / `close()` all resolve to *deny* (fail-safe). 9. Context prefetch (system/file MCP sidecars). 10. Execution loop (tool calls, wrapped-agent monitoring, ACPX child drain). **Every model step is wrapped by a per-request self-healing invoker** (`agent/self_healing.py::SelfHealingInvoker`) that retries distinct recovery tactics under a per-attempt watchdog (`unified_agent_llm_step_timeout_seconds`, 80 s) up to `unified_agent_llm_step_max_tactics` — a transient model failure never hangs, never discards completed work, never yields a silent/untruthful answer (a `recovery_preamble` always tells the user what happened; live retry status streams to chat). Only user Cancel or an exhausted tactic ladder stops it (`ModelStepUnrecoverable`). 11. Streaming response via WebSocket. When Multi-Turn ran with **≥1 successfully-executed agent**, a **Create Flow** button converts the successful-only tool calls into a downloadable `.flw` (POST `/agent/flow_from_tool_calls/`, normalized through `FlowSpec`, secrets redacted). There is no whole-answer SUCCESS/FAILURE classifier (removed 2026-07-06). 12. Canvas **Start** compiles the live snapshot through `/agent/compile_flow/` (mode=`write`) before executing any agent — an edited-but-unsaved canvas goes through the same Agent Contract validation as a freshly loaded `.flw`; **Validate** uses mode=`dry_run`. ### 3.3 Technology stack | Category | Technologies | |---|---| | Backend | Python 3.12, Django 5.2, Django Channels 4.1, Daphne (ASGI), WhiteNoise, django-bootstrap5 | | Frontend | HTML5, Bootstrap 5.3.3, vanilla JS (59 modules, script-scope globals), jQuery 3.7.1 + jQuery-UI 1.13.3, highlight.js 11.9.0, Marked 18.1.0 + DOMPurify 3.4.16 (Markdown canvas) — all vendored locally, no CDN | | AI/ML | LangChain 0.3.x, LangGraph 0.2.x, FAISS, rank-bm25, PyAutoGUI, faster-whisper, OpenCV | | LLM APIs | Ollama REST (local + cloud models, shipped defaults), Anthropic Claude, Qwen vision; MCP SDK 1.x | | Database | SQLite (`Tlamatini/db.sqlite3`); FAISS indexes on disk | | Communication | WebSockets (Channels, **InMemoryChannelLayer** — no Redis), gRPC (grpcio), stdio | | Packaging | PyInstaller (frozen exe + carried Python + bundled JRE/Git/Playwright browsers) | ### 3.4 Boot sequence `Tlamatini/manage.py` (814 lines): pin the app `Temp` dir → tee stdout/stderr into `tlamatini.log` → apply any pending DB hot-swap from `DB/ToLoad/` → post-update migrate child process → Django boot → `apps.py::AgentConfig.ready()` (**reloader-gated**) starts: companion-app discovery publisher, MCP System server (daemon thread, :8765), Files-Search gRPC server (:50051), GPU perf pinning (`gpu_perf.py`, `keep_alive=-1`), command watchdog, `Agent`-table repopulation from `agents/` dirs, pool cleanup, NAMU/God-Mode/Tier-3 shutdown reapers, ACPX + skills boot (`boot_acpx()` / `boot_skills()`). --- ## 4. Directory Structure ``` Tlamatini/ # Git root (C:\Development\Tlamatini) ├── KIMI.md # THIS FILE — Kimi's self-contained onboarding reference ├── CLAUDE.md # Claude Code entry point + @docs/claude/* import manifest ├── GEMINI.md # Gemini CLI self-contained knowledge base ├── README.md # User-facing docs ├── BookOfTlamatini.md # Master long-form manual + changelog ("The Book", ~457 KB) ├── agents_descriptions.md # AUTHORITATIVE sidebar tooltips/Description-dialog source │ # (## Workflow Agents tables parsed by views into agent_purpose_map) ├── ACPX.md # Standalone ACPX guide / OpenClaw compatibility note ├── VERSIONING.md # SemVer contract (git tags are the version) ├── TLAMATINI_MCP.md # Root MCP server doc (STALE tool counts — trust §1 here) ├── docs/ │ ├── claude/ # Topic docs (architecture, multi-turn, exec-report, agents, │ │ # acpx, mcp-tools, frontend, gotchas, recent-fixes, INDEX) │ ├── companion-app-discovery.md # FlowPills registry+manifest discovery contract │ └── stm32er_all_families_proposal.md ├── .claude/ # Claude Code integration: skills/ (5 repo skills), hooks/ │ │ # (SessionStart announce_skills.py), settings, memory ├── .github/workflows/name-guard.yml# Only CI: identity auto-scrub bot (no build/test CI) ├── .mcp.json # Wires the root stdio MCP server (tlamatini) for MCP clients │ ├── build.py # STEP 1 of release: PyInstaller build → pkg.zip (~84 KB script) ├── build_uninstaller.py # STEP 2: freeze uninstall.py → Uninstaller.exe ├── build_installer.py # STEP 3: freeze install.py → Installer.exe + release folder ├── build_all.cmd # Wrapper: steps 1-3 in order (system Python only) ├── build_complete_private_release.py # KEYED private build (real secrets; NEVER publish) ├── build_complete_public_release.py # PUBLIC build (scrub + leak-audit gate + tree restore) ├── install.py / uninstall.py # Tkinter GUI installer/uninstaller sources ├── copy_source_assets.py # Generates TlamatiniSourceCode/ self-modify snapshot ├── regen_secrets.py # Toggle secrets: --mode keyed (from data.keys) / push-able ├── versioning.py # Build-time version shim over agent/version.py ├── apply_update.ps1 # External self-updater (runs from %LOCALAPPDATA%) ├── check_private_data.py # "god-of-gods" privacy auditor (see §17) ├── test_private_data_guard.py # Git-history integrity guard + banner guard ├── test_check_private_data.py # 100+ auditor unit tests ├── test_author_banner.py # Author-banner presence guard ├── requirements.txt # Python deps (ruff is a REQUIRED runtime gate — never unpin) ├── eslint.config.mjs # ESLint 10 config (501-line cross-file globals whitelist) ├── package.json # release metadata 1.72.4; npm run lint / lint:fix ├── tlamatini_mcp_server.py # Root stdio MCP server: 87 agent tools + 7 mgmt + 10 ACPX ├── tlamatini_acpx.py # Self-contained stdlib ACPX runtime port for the MCP server ├── Tlamatini.ps1 # Legacy launcher for the frozen exe ├── demo_flows/ # 3 shipping .flw demos (schemaVersion 2) ├── Templates/ # Default parent for firmware/engine project scaffolds (empty in git) ├── Temp/ # ALL transient files (never %TEMP%) — gitignored ├── Go/ # LOCAL leftover of the private Go toolchain (gitignored, not source) ├── Tests/ AuxTests/ # Standalone visual test scripts │ └── Tlamatini/ # Django project root ├── manage.py # Entrypoint (boot sequence §3.4); custom cmd: startserver ├── db.sqlite3 # SQLite database (gitignored) ├── tlamatini.log # Unified app log — TRUNCATED on every server start, no rotation ├── jd-cli/ # Bundled Java decompiler (J-Decompiler backend) ├── staticfiles/ # collectstatic output (WhiteNoise) ├── DB/ToLoad/ + DB/Older/ # DB hot-swap: drop db.sqlite3 in ToLoad → archived WITH its │ # -wal/-shm/-journal sidecars + swapped pre-Django (§6) ├── tests_e2e/ # 7 headed Playwright suites (plain scripts, live server) ├── .agents/workflows/create_new_agent.md # Agent-creation guide (@-imported by CLAUDE.md) ├── .mcps/create_new_mcp.md # MCP/tool-creation guide (@-imported) ├── .skills/create_new_skill.md # Skill-authoring guide │ ├── tlamatini/ # Django project config: settings.py, urls.py, asgi.py, middleware │ └── agent/ # CORE DJANGO APP — all business logic ├── prompt.pmt # System prompt template ({self_knowledge}, {temp_directory}...) ├── Tlamatini.md # LLM SELF-KNOWLEDGE (first-person; injected at prompt build) ├── config.json # Main config (§5); ships with placeholders ├── config_loader.py # Frozen/source-aware config reader (CONFIG_PATH env override) ├── path_guard.py # Temp/Templates/app-root resolution (frozen+source duality) ├── sqlite_copy.py # WAL-safe SQLite copy/sidecar hygiene (online backup API + │ # quick_check verify; used by manage.py pre-Django AND views.py, §6) ├── views.py # 205 view/helper functions (12,585 lines) ├── consumers.py # AgentConsumer WebSocket (1,879 lines) ├── models.py # 18 DB models ├── urls.py # 183 routes ├── tools.py # 18 direct @tools + wrapped-launcher factory (get_mcp_tools) ├── mcp_agent.py # Unified-agent builder + MultiTurnToolAgentExecutor + │ # _EXEC_REPORT_TOOLS map (2,748 lines) ├── global_execution_planner.py # Request-scoped DAG planner ├── capability_registry.py # Capability scoring for tool hints/ordering ├── chat_agent_registry.py # 68 WRAPPED_CHAT_AGENT_SPECS ├── chat_agent_runtime.py # Wrapped-run lifecycle (_chat_runs_/) ├── exec_permission.py # Ask-Execs broker ├── model_brain.py # Model Brain (v1.76.0): per-model sampling / thinking / num_ctx from formal sources ├── model_brain_chat.py # BrainChatOllama: the model's reasoning handed back inside the tool loop ├── model_brain_views.py # Auto-tuning dialog endpoints (/agent/model_brain/models/, /tune/) ├── model_profiles.json # Sourced knowledge base of model profiles (vendor cards, generation_config.json) ├── self_healing.py # SelfHealingInvoker (per-step retry tactics) ├── global_state.py # Thread-safe singleton (toggle mirror) ├── gpu_perf.py # keep_alive=-1 + GPU max-performance at boot ├── orphan_reaper.py # 3-tier conhost/zombie reaper (§21) ├── agent_manifest.py # Companion-app manifest generator ├── external_mcp_manager.py # Universal External-MCP client engine ├── external_mcps.json # External-MCP catalog (USER STATE, gitignored) ├── mcp_system_server.py # System-Metrics WS server (:8765) ├── mcp_files_search_server.py # Files-Search gRPC server (:50051) + filesearch.proto ├── chain_system_lcel.py / chain_files_search_lcel.py # Sidecar chains ├── apps.py # AgentConfig.ready() — boot wiring (RELOADER-GATED) ├── acpx/ # ACPX runtime: agent_registry (14 ids), runtime, tools (12), │ # session_store (NDJSON), windows_spawn, permissions, service ├── skills/ # Skill runtime: registry (30 s staleness), harness (budgets, │ # permissions, NDJSON audit), frontmatter, io_contract ├── skills_pkg/ # 29 SKILL.md packages (+ _meta/ schema+lint) ├── rag/ # RAG: factory, interface, retrieval, splitters, loaders, │ # prompts, utils, interaction + chains/{basic,history_aware,unified} ├── rag_enhancements.py # Metadata extraction (code structure, file roles, deps) ├── services/ # filesystem, response_parser, agent_contracts (AgentContract │ # registry), agent_paths (frozen/source pool paths + canvas-id │ # normalization), flow_spec (FlowSpec schema_version=2), │ # flow_compiler (compile + pool writer) ├── opus_client/ # Claude API client library ├── imaging/ # Dual-backend image analysis (opus_analyze_image, qwen_analyze_image) ├── agents/ # 87 AGENT TEMPLATE DIRS (/.py + config.yaml) │ ├── _tlamatini_agents_manifest.json # sha256 manifest (regenerated at build/launch) │ ├── pools/ # Runtime session pools (canvas flows) │ ├── flowcreator/agentic_skill.md # FlowCreator AI reference │ ├── flowhypervisor/monitoring-prompt.pmt │ └── <87 total>/ # See §13 catalog ├── templates/agent/ # 6 templates: agent_page, agentic_control_panel, prompt_flow_panel, login, welcome, usage_dialog (partial) ├── static/agent/ # js/ (60 modules), css/ (27 files), img/, sounds/, vendor/ (frontend + pdfjs) └── migrations/ # 217 migrations (0215-0217 add Terminater; 0213 adds UsageDaily; 0211-0212 add the Compact mode and Self-modify state) ``` --- ## 5. Configuration System Main config: `Tlamatini/agent/config.json`. Frozen builds resolve it next to the executable; source mode from `Tlamatini/agent/config.json`; `CONFIG_PATH` env var overrides both. Ships with `` placeholders — real secrets come from `data.keys` via `regen_secrets.py --mode keyed` (see §17). Key config keys: - `embeding-model` — RAG embedding model. Default `Nomic-Embed-Text:latest` (~600 MB resident VRAM). Opt-in `qwen3-embedding:8b` (Config → Models menu) uses ~10× more VRAM (~6.24 GB) and trips the embedding-memory pre-flight guard on 8 GB GPUs. - `chained-model` — primary chat model · `unified_agent_model` — Multi-Turn tool-loop model. - `ollama_base_url` (default `http://127.0.0.1:11434`) · `ollama_token` — bearer token for authenticated Ollama. - `ANTHROPIC_API_KEY` — Claude API key. - `enable_unified_agent` — tool-calling agent on/off · `unified_agent_max_iterations` — max tool-call turns (default **4096**). - `unified_agent_llm_step_timeout_seconds` (80) · `unified_agent_llm_step_max_tactics` — self-healing knobs. - `ollama_repeat_penalty` (**1.2**) · `ollama_repeat_last_n` (**256**) · `ollama_num_ctx` (**1048576**) — the sampler triple, applied by BOTH chains in `rag/factory.py` and forwarded to `ChatOllama` by `mcp_agent.py`. All measured 2026-09-13, not chosen by taste. `repeat_penalty` shipped at **1.9** (double any documented ceiling) and made reasoning models spend whole turns emitting private `thinking` and return EMPTY content — 7/8 runs on glm-5.3, once 3.1M thinking chars over 51 min. ⚠️ **The failure is HEAVY-TAILED**: 6 trials scored 1.9 at 0/6, 8 trials scored 7/8, at `temperature 0.0` (cloud MoE routing varies per call) — never tune this from one run. ⚠️ `repeat_last_n` is **per-model** (256 makes glm-5.3 6× faster, hurts kimi-k3) while `repeat_penalty` is not; it was also **never forwarded to Ollama** before this, so setting it was a silent no-op. ⚠️ `num_ctx` is a **no-op for `:cloud`** (a 263,159-token prompt with `num_ctx=8192` still recalled a needle; the server enforces its own 1,048,576 and returns a clean HTTP 400 above it) but DOES size the KV cache for a LOCAL model. Add any new sampler key to both chains AND the `[LLM-PARAMS]` banner. ⚠️ `frequency_penalty`/`presence_penalty` are not exposed by `langchain-ollama` 0.2.1 and would be dropped silently — which also **drops the `thinking` field**, so a runaway surfaces only as "empty final response". See `docs/claude/architecture.md` → *Ollama sampler settings*. - `chat_agent_limit_runs` — wrapped-run listing limit. - `image_interpreter_model` / `image_interpreter_base_url` — vision model settings. - Chunking: `chunk_size` (2000), `chunk_overlap`, `max_chunks_per_file`. - Retrieval: `k_vector` (40), `k_bm25` (40), `k_fused`, `enable_bm25`, `rrf_k` (60). - Context limits: `max_doc_chars`, `max_context_chars`, `context_budget_allocation`. - Internet: `internet_classifier_model`, `web_summarizer_model`, `web_context_max_chars`. - Ports: **`django_port`** (web, default 8000 — see below) · `mcp_system_server_host`/`mcp_system_server_port` (8765) · `mcp_files_search_server_port` (50051). - **ACPX block** (auto-backfilled on upgrade): `permissionMode` (`approve-reads` default / `approve-all` / `deny-all`), `nonInteractivePermissions` (`deny` / `fail`), `timeoutSeconds` (120), `pluginToolsMcpBridge` (false), per-agent overrides under `agents.{agent_id}` (command, transport, budgets, `env` for child env injection). **Configurable web port (since v1.40.1).** `8000` is only the default. `manage.py::_resolve_django_port()` reads `django_port`; `_apply_configured_port()` injects it into every launch path (frozen double-click, `.flw` association, browser auto-open, source `runserver`, `startserver`). An explicit CLI `[ipaddr:]port` always wins; resolution is **fail-open** to 8000 (a typo never blocks startup — a `--- [PORT] …` log line explains). Exists because Hyper-V/WSL/Docker can *reserve* port 8000 → `WinError 10013` at startup (check `netsh interface ipv4 show excludedportrange protocol=tcp`). Still hardcoded: direct `daphne`/`uvicorn` launches, the helper listeners :8765/:50051, TeleTlamatini's own `tlamatini.base_url`. **Never re-introduce a literal `8000` in a launch path.** --- ## 6. Database & Models SQLite single DB `Tlamatini/db.sqlite3` (`settings.py`: `BASE_DIR/'db.sqlite3'`) running in **WAL mode** (`settings.py` → `PRAGMA journal_mode=WAL`) — under WAL, every change committed since the last checkpoint lives in `db.sqlite3-wal`, so a plain filesystem copy of `db.sqlite3` is a stale, silently-wrong database. FAISS indexes live on disk, not in DB. **217 migrations** in the current worktree (0213 adds UsageDaily); heavy seed-migration usage (prompts, tools, agents as data rows). **17 models** (`agent/models.py`): `AgentMessage` (chat messages — also the ONLY store of the history the model reads; the **Drop** button deletes one row, see §7.7) · `LLMProgram` / `LLMSnippet` (saved code) · `Prompt` (Catalog of Prompts; append-only PK rule + `category` / `hidden` / `sort_rank`) · `Omission` (file omission patterns) · `ContextCache` (SHA1 query→context cache) · `Mcp` (MCP toggle rows) · `Tool` (tool toggle rows) · `Agent` (agent type registry — **repopulated from the `agents/` dirs on every boot**, `apps.py`) · `AgentProcess` (tracked PIDs — wiped every boot) · `ChatAgentRun` (wrapped run records — wiped every boot) · `Asset` · `SessionState` (24 h expiry) · `AcpAgent` (mirrored from `DEFAULT_ACP_AGENTS` on boot) · `Skill` (mirrored from `skills_pkg/` on boot; enumeration + enable/disable only — budgets/permissions/body live in SKILL.md on disk) · `AcpSession` · `SkillInvocation`. Toggle state = DB rows mirrored into `global_state` (in-process dict), read by `get_mcp_tools()`. **WAL-safe copy engine — `agent/sqlite_copy.py` (2026-08-16, stdlib-only, imports nothing from `agent.*`).** All database copy/move operations route through it: `consistent_copy()` uses SQLite's **online backup API** (reads THROUGH the WAL), puts the result in `DELETE` journal mode so what lands on disk is ONE self-contained file, and verifies it with `PRAGMA quick_check` before anything reports success; `move_with_sidecars()` / `remove_sidecars()` keep the `-wal`/`-shm`/`-journal` trio together with the database they belong to. Contracts — do NOT weaken: (1) never claim success without re-reading and checking the produced file; (2) never delete the source; (3) **fail-SAFE, not fail-open** — an unclear result is reported as FAILURE (unlike the context loaders), because the alternative is telling the user their data is safe when it is not. **Both DB menu options were rewritten onto it** (`views.py`): **Backup database** (`backup_db_view`) no longer `shutil.copy2`s the bare `db.sqlite3` — that had been backing up an OLDER database while reporting success (measured live: 839,680-byte `db.sqlite3` from 13:39 beside a 3,514,392-byte `-wal` from 22:49 — nine hours of work outside the "backup"); **Set DB** (`set_db_view`) stages the picked file through `consistent_copy` so a live/WAL source arrives complete. The old `agent/db_guard.py` (the pre-Django zero-byte "smoke alarm") was **removed the same day** — it guarded against the wrong mechanism; the WAL-safe copy path is the real fix. Coverage: `agent/test_db_backup_restore_wal.py` + `agent/run_db_wal_tests.ps1` + the headed suite `tests_e2e/test_db_backup_set_visible.py`. **DB hot-swap**: drop a `db.sqlite3` into `Tlamatini/DB/ToLoad/` and `manage.py::_apply_pending_db_swap()` (pre-Django) archives the live DB **with its sidecars** via `sqlite_copy.move_with_sidecars()` into `DB/Older//`, then **DELETES any stale `-wal`/`-shm` left beside the live path** (⚠️ load-bearing — otherwise SQLite replays the PREVIOUS database's WAL on next open and its pages override the just-loaded DB, which is why Set DB once appeared to do nothing three runs in a row, and in the worst case merges two databases into real corruption), then moves the staged file in and drops its own stale sidecars. A WAL is data: archived first, never destroyed. Used by self-update (`apply_update.ps1` + `DB/post_update_migrate.flag` → child-process migrate). **Prompt-table rules**: `idPrompt` is **append-only** in day-to-day work (the 2026-07-15 contiguous renumber was a one-time, Angela-authorized reorganization). New prompt = next free id + a `sort_rank` (steps of 10; **rank 10 is RESERVED in every section for its Step-by-Step opener**). Display order = (category rank, `sort_rank`, `idPrompt`); primary load is ONE `GET /agent/list_prompts/`; `MAX_PROMPTS=256`. **A NEW SECTION costs TWO prompts** (2026-09-12): a `('', '