This file provides comprehensive guidance to Codex (Codex.ai/code) when working with code in this repository.
tldw_chatbook - TUI application built with Textual for LLM interactions. Features: conversation management, character chat, notes with file sync, media ingestion, RAG capabilities.
Tech Stack: Python ≥3.12, Textual 8.x (≥8.0.0,<9), SQLite with FTS5, AGPLv3+ Key Dependencies: httpx, loguru, rich, pydantic, toml, keyring, aiofiles, jinja2
# Setup
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]" # Or specific: .[embeddings_rag,websearch,local_vllm,ebook,pdf]
# Run
python3 -m tldw_chatbook.app
# Test
pytest # All tests
pytest Tests/Chat/ # Specific module
pytest --cov=tldw_chatbook # With coverage
# Note: The 'timeout' command is not available in this environmentapp.py- Main entry,TldwCliclass, tab management, global stateconfig.py- TOML config at~/.config/tldw_cli/config.toml, env var fallbacksConstants.py- Tab IDs (TAB_CHAT, TAB_CODING, etc.), UI dimensions, provider mappings
Main Windows (all extend Screen):
Chat_Window_Enhanced.py- Streaming chat, images, RAG, tool callingConv_Char_Window.py- Conversation/character CRUDNotes_Window.py- Notes with templates and syncSearchRAGWindow.py- RAG search interfaceEvals_Window_v3.py- LLM benchmarkingMediaWindow.py- Media management hubScreens/chat_screen.py- The Console: live agent work, approvals, tools, and RAGIngestTldwApiWindow.py- Media ingestion forms
Settings: UI/Screens/settings_screen.py (F9 Settings destination) is the canonical settings surface. The legacy UI/Tools_Settings_Window.py and Widgets/enhanced_settings_sidebar.py (legacy Chat window only) are deprecated parallels — do not add new settings there.
Key Widgets:
chat_message_enhanced.py- Rich messages with actionstool_message_widgets.py- Tool calling UI (ToolCallMessage, ToolResultMessage)IngestTldwApi*Window.py- Media-specific ingestion formsform_components.py- Standardized form builders
Chat/-Chat_Functions.py(conversation CRUD),document_generator.py(export formats)Character_Chat/-Character_Chat_Lib.py,ccv3_parser.py(card formats)Notes/-sync_engine.py(bidirectional sync), template systemRAG_Search/-simplified/for streamlined implementation,chunking_service.pyTools/-tool_executor.py, built-in: DateTimeTool, CalculatorToolEvals/-eval_orchestrator.py,eval_runner.py, task-specific runnersLLM_Calls/- Provider integrations, unifiedchat_with_provider()interfaceImage_Generation//Video_Generation/- Media-generation packages: adapter registry, config with secrets precedence, single validation choke point (worker.run_generation). Video mirrors image per ADR-044;Video_Generation/video_store.pyis the ephemeral message-keyed video file store (task-3401.4); video backends land in task-3401.3/.6/.7
ChaChaNotes_DB.py- Main DB (conversations, messages, characters, notes), schema v37Client_Media_DB_v2.py- Media storage with chunkingRAG_Indexing_DB.py- Vector storage (when enabled)- Other DBs: Evals, Prompts, Subscriptions
- Patterns: soft deletion, optimistic locking, FTS5 triggers, parameterized queries only
Event flow: Widget → post_message() → @on() handler → workers → reactive updates → UI refresh
Key events: ChatEvent, StreamingChunk, RAGSearchEvent, SyncEvent, EvalEvent, TabEvent
Database Operations:
with db.transaction() as cursor:
cursor.execute(query, params) # Always parameterizedReactive UI:
class MyWidget(Widget):
data = reactive([], recompose=True) # Rebuilds UI
status = reactive("idle") # Refresh onlyBackground Work:
self.run_worker(self._heavy_task, exclusive=True)
@work(thread=True)
def _heavy_task(self):
result = process()
self.call_from_thread(self.update_ui, result)New LLM Provider:
- Add to
LLM_Calls/withchat_with_provider()method - Register in main caller
- Add config section
New Tab:
- Create Screen in
UI/ - Add TAB_X constant
- Register in app.py compose()
- Add event handlers
New Tool:
- Extend Tool class in
Tools/ - Implement: get_name(), get_description(), get_parameters(), execute()
- Register in AVAILABLE_TOOLS
- Validate all inputs via
input_validation.py - Use
path_validation.pyfor file paths - SQL identifiers through
sql_validation.py - API keys from env/config only, never logged
- Sanitize HTML/Markdown content
- Workers for operations >100ms
- Stream LLM responses
- Chunk large files
- Paginate DB results
- Clear caches on context switch
Priority: env vars → config.toml → defaults
Key sections:
[API]- Provider keys[splash_screen]- Animation settings[embeddings]- RAG config- Provider-specific sections
- Ask before full sweeps: before running the full test suite, ask whether a full sweep is wanted. Unless the user opts in, verify with targeted runs only — the tests touching the modified files/functionality
- Run the full suite only when the user asks for it (e.g. pre-PR/pre-merge verification they explicitly request)
- Use real SQLite in-memory for DB tests
- Property-based testing with Hypothesis
- Markers: unit, integration, optional, asyncio
- Schema v7 adds tool messages;
tool_executor.pyruns the builtin tools Agents/tool_catalog.pyis the provider seam: builtin/local/skill/MCP providers register with oneToolCatalogRegistry- Local fs_* tools (fs_list/fs_read/fs_write/fs_edit/fs_glob/fs_grep) in
Tools/local_tool_impls.py, exposed viaAgents/local_tool_provider.py - Approvals flow through the MCP permission store; local tools sit under the
local:__local__hub - Console file authority: every live Chat gets private temporary scratch. Named Workspaces may add explicit folder bindings; local
fs_*/Git uses scratch unless project instructions explicitly select one binding.[console] workspace_rootis compatibility-only outside this Console path and never grants a Console Chat access. Workspace folder bindings may carry per-binding exclusions (exact user-marked paths, fully invisible to agent tools — see ADR-174). - SSH workspace bindings reuse the Console file authority model:
ssh-filesystemroots arefs_*-only (family B read_file/write_file/list_directory/edit_file stays local-only), status/availability per binding comes from the in-memory cache, and each call runs a transient stdlib worker over ssh (nothing installed on the server) — see ADR-181.
- User-configured external commands at six lifecycle events (UserPromptSubmit, PreToolUse, PostToolUse, ApprovalRequested, Stop, SubagentStop); config:
[hooks]in config.toml (ADR-148). - Deny-only: hooks can refuse tool calls but never bypass the permission store; PreToolUse fails closed, UserPromptSubmit fails open; argv-list commands only, process-group timeout kill.
AGENTS.override.md/AGENTS.mdstartup and lazy nested guidance is untrusted, ephemeral user context bounded by one selected local-filesystem binding; it never grants tool permission.- Registry ownership and path targets feed one shared activation ledger before normal tool review; read-only bindings do not advertise mutating tools.
- UI, persistence, and logs keep automatic bodies out of metadata surfaces; the explicit disposable Context Next Send preview is the only automatic UI body view.
- Governance:
backlog/decisions/069-console-project-instruction-local-state-and-preflight.mdandDocs/superpowers/specs/2026-08-20-agents-md-support-design.md.
- AES-256 with PBKDF2
Utils/config_encryption.py- Password dialogs in widgets
- 20+ animations in
splash_animations.py - Config:
[splash_screen]section - Custom cards in
examples/custom_splash_cards/
- Bidirectional file ↔ DB
- Last-write-wins conflict resolution
- Background monitoring
auto_review.pyfor Codex integration- Reviews diffs with LLM
- Exit 0 = pass, 2 = fail
- Startup background refresh of cloud-provider model lists (OpenAI, Anthropic, MistralAI, Moonshot, OpenRouter, ZAI) via
LLM_Provider_Catalog/ - Disk TTL cache:
model_catalog_cache.jsonin the user data dir (IDs + timestamps only) - Capped merge (50) into model selectors; full catalog searchable in the Alt+M popover
- Config:
[model_catalog]in config.toml; per-provider opt-in write-through appends new models to[providers] - Governance: ADR-020 (amends ADR-002), spec/plan in Docs/superpowers/{specs,plans}/2026-07-17-model-catalog-auto-refresh*
- Explicit workspaces carry reference-backed
assistant_defaults(persona + permission profile); Default/global stay unset. - Persona policy rules narrow only (deny-by-default advertising, ask floors, per-run call caps); profiles inherit unset keys from
default; all existing gates/floors apply first. - Governance:
backlog/decisions/079-workspace-assistant-defaults.mdandDocs/superpowers/specs/2026-08-29-workspace-assistant-defaults-design.md.
- No localStorage in artifacts - use React state or JS variables
- Tailwind limitations - Only core utility classes, no compilation
- Schema migrations - Always increment version, add to migrations/
- Optional deps - Check with
optional_deps.pybefore importing - Thread safety - Use transaction() context manager
- Tab constants - Must match IDs in compose()
- Streaming - Always offer non-streaming fallback
- FTS5 - Triggers auto-update on text columns
- Workers - Mark exclusive=True to prevent duplicates
- Reactive - recompose=True rebuilds, default just refreshes
- Keybindings - Screens must not bind terminal-convention keys (ctrl+c/v/x/s/d/z/a/r/w) or shadow the globals ctrl+p/ctrl+q/f1/f6; screen actions use single-letter htop-style bindings; footer hints may only advertise implemented actions — see
backlog/decisions/031-tui-keybinding-and-footer-hint-conventions.md
Critical files for common tasks:
- Entry:
app.py,config.py,Constants.py - Chat:
Chat_Functions.py,chat_message_enhanced.py - DB:
base_db.py,ChaChaNotes_DB.py - LLM:
LLM_API_Calls.py,model_capabilities.py - Security:
path_validation.py,input_validation.py - UI:
form_components.py, reactive patterns in any widget
All UI work is governed by a design-token system. Read
backlog/docs/design-language.md and backlog/docs/component-patterns.md
(ADR-161) before creating or modifying any screen, widget, or stylesheet.
Hard rules:
- Every consistent visual value is a
$ds-*token intldw_chatbook/css/core/_variables.tcss— spacing ($ds-space-*), sizing ($ds-control-*), motion ($ds-duration-*), opacity, typography emphasis, colors, status, and component states. Do not invent literals or ad-hoc tokens in feature sheets; the governance test (Tests/UI/test_design_token_governance.py) fails on undefined$ds-*references and on new hex literals outside the token file. - Describe new UI by composing tokens, component patterns, layout laws, and interaction rules from the constitution — never "make it look modern".
- Adding a value the language can't express? Add the token to
_variables.tcssfirst, then use it. - Never edit
tldw_chatbook/css/tldw_cli_modular.tcssdirectly; edit source modules and rebuild withpython tldw_chatbook/css/build_css.py. - Python code assigns token-backed CSS classes instead of ad-hoc
styles.*literals for token-covered values. - Changing an existing token's value is a visual-breaking change; verify
live per
backlog/docs/lessons-live-verification.md.
- Type hints for public APIs
- Docstrings: Google style with Args/Returns/Raises
- Imports: stdlib → third-party → local
- PascalCase classes, snake_case functions
- Pydantic for validation
- Early returns to reduce nesting
- Constants for magic values
- Context managers for resources
- Descriptive test names
- Profile before optimizing
- Validate at boundaries
- Log errors with context
- Tasks live under
backlog/tasks/(drafts underbacklog/drafts/). - Every implementation decision starts with reading the corresponding Markdown task file.
- Project documentation is in
backlog/docs/. - Project decisions are in
backlog/decisions/. - Hard-won working knowledge is in
backlog/docs/lessons-*.md-- traps that have actually cost time in this repo, each recorded with the incident that produced it. Read the one covering your area before starting; they are short, and they exist because these mistakes recur:lessons-testing-evidence.md-- what actually counts as evidence a change workslessons-live-verification.md-- running the app and talking to a real serverlessons-backlog-hygiene.md-- task IDs, CLI quirks, git plumbing
- Canonical Architecture Decision Records (ADRs) live in
backlog/decisions/. Historical ADR-like material elsewhere is reference-only unless a canonical ADR imports or supersedes it. - Before implementation planning, read relevant ADRs and decide whether the task requires a new ADR.
Use ADRs to record significant architectural decisions: what was decided, why, and what alternatives were considered.
An ADR is required for decisions involving storage/schema/migrations, sync/conflict policy, data ownership, provider or runtime boundaries, service contracts, cross-module interfaces, security/privacy/encryption/authentication, dependency/tooling/runtime choices, long-lived UX/application structure, or rejection of a plausible alternative future contributors may ask about again.
An ADR is not required for routine bug fixes, small UI polish, copy-only changes, mechanical refactors that preserve existing boundaries, test-only changes, or direct implementation of an existing ADR.
Every implementation plan must include:
ADR required: yes/no
ADR path: backlog/decisions/NNN-short-title.md or N/A
Reason: brief explanation
If an ADR is required, create it before implementation begins and link it from the Backlog task, Superpowers plan, and final implementation notes. If an existing ADR applies, link it instead of creating a duplicate.
Ask questions to the user if something is not clear or ambiguous. Break down the task into smaller, manageable parts if it is too large or complex.
Use a clear brief title that summarizes the task.
Provide a concise summary of the task purpose and its goal. Do not add implementation details here. It should explain the purpose and context of the task. Code snippets should be avoided.
List specific, measurable outcomes that define what means to reach the goal from the description. Use checkboxes (
- [ ]) for tracking.
When defining ## Acceptance Criteria for a task, focus on outcomes, behaviors, and verifiable requirements rather
than step-by-step implementation details.
Acceptance Criteria (AC) define what conditions must be met for the task to be considered complete.
They should be testable and confirm that the core purpose of the task is achieved.
Key Principles for Good ACs:
-
Outcome-Oriented: Focus on the result, not the method.
-
Testable/Verifiable: Each criterion should be something that can be objectively tested or verified.
-
Clear and Concise: Unambiguous language.
-
Complete: Collectively, ACs should cover the scope of the task.
-
User-Focused (where applicable): Frame ACs from the perspective of the end-user or the system's external behavior.
- Good Example: "- [ ] User can successfully log in with valid credentials."
- Good Example: "- [ ] System processes 1000 requests per second without errors."
- Bad Example (Implementation Step): "- [ ] Add a new function
handleLogin()inauth.ts."
Once a task is created it will be stored in backlog/tasks/ directory as a Markdown file with the format
task-<id> - <title>.md (e.g. task-42 - Add GraphQL resolver.md).
When breaking down features:
- Identify the foundational components first
- Create tasks in dependency order (foundations before features)
- Ensure each task delivers value independently
- Avoid creating tasks that block each other
-
Tasks must be atomic and testable. If a task is too large, break it down into smaller subtasks. Each task should represent a single unit of work that can be completed in a single PR.
-
Never reference tasks that are to be done in the future or that are not yet created. You can only reference previous tasks (id < current task id).
-
When creating multiple tasks, ensure they are independent and they do not depend on future tasks.
Example of wrong tasks splitting: task 1: "Add API endpoint for user data", task 2: "Define the user model and DB schema".
Example of correct tasks splitting: task 1: "Add system for handling API requests", task 2: "Add user model and DB schema", task 3: "Add API endpoint for user data".
# task‑42 - Add GraphQL resolver
## Description (the why)
Short, imperative explanation of the goal of the task and why it is needed.
## Acceptance Criteria (the what)
- [ ] Resolver returns correct data for happy path
- [ ] Error response matches REST
- [ ] P95 latency ≤ 50 ms under 100 RPS
## Implementation Plan (the how) (added after putting the task in progress but before implementing any code change)
1. Research existing GraphQL resolver patterns
2. Implement basic resolver with error handling
3. Add performance monitoring
4. Write unit and integration tests
5. Benchmark performance under load
## Implementation Notes (imagine this is the PR description) (only added after finishing the code implementation of a task)
- Approach taken
- Features implemented or modified
- Technical decisions and trade-offs
- Modified or added filesMandatory sections for every task:
- Implementation Plan: (The "how")
Outline the steps to achieve the task. Because the implementation details may change after the task is created, the implementation plan must be added only after putting the task in progress and before starting working on the task. - Implementation Notes: (Imagine this is a PR note)
Start with a brief summary of what has been implemented. Document your approach, decisions, challenges, and any deviations from the plan. This section is added after you are done working on the task. It should summarize what you did and why you did it. Keep it concise but informative. Make it brief, explain ONLY the core changes and assume that others will read the code to understand the details.
IMPORTANT: Do not implement anything else that deviates from the Acceptance Criteria. If you need to implement something that is not in the AC, update the AC first and then implement it or create a new task for it.
# 1 Identify work
backlog task list -s "To Do" --plain
# 2 Read details & documentation
backlog task 42 --plain
# Read relevant documentation files in `backlog/docs/`.
# Read relevant ADRs in `backlog/decisions/`; create/link one if the task makes a significant architectural decision.
# 3 Start work: assign yourself & move column
backlog task edit 42 -a @{yourself} -s "In Progress"
# 4 Add implementation plan before starting
backlog task edit 42 --plan "1. Analyze current implementation\n2. Identify bottlenecks\n3. Refactor in phases"
# 5 Break work down if needed by creating subtasks or additional tasks
backlog task create "Refactor DB layer" -p 42 -a @{yourself} -d "Description" --ac "Tests pass,Performance improved"
# 6 Complete and mark Done
backlog task edit 42 -s Done --notes "Implemented GraphQL resolver with error handling and performance monitoring"Always ensure you have:
- ✅ Marked all acceptance criteria as completed (change
- [ ]to- [x]) - ✅ Added an
## Implementation Notessection documenting your approach - ✅ Run all tests and linting checks
- ✅ Completed the ADR check: linked an existing ADR, created a new ADR, or documented why no ADR was required
- ✅ Updated relevant documentation
A task is Done only when ALL of the following are complete:
- Acceptance criteria checklist in the task file is fully checked (all
- [ ]changed to- [x]). - Implementation plan was followed or deviations were documented in Implementation Notes.
- Automated tests (unit + integration) cover new logic.
- Static analysis: linter & formatter succeed.
- Documentation:
- All relevant docs updated (any relevant README file, backlog/docs, backlog/decisions, etc.).
- Task file MUST have an
## Implementation Notessection added summarising:- Approach taken
- Features implemented or modified
- Technical decisions and trade-offs
- Modified or added files
- Review: self review code.
- Task hygiene: status set to Done via CLI (
backlog task edit <id> -s Done). - No regressions: performance, security and licence checks green.
- Lessons learned: if the task surfaced knowledge that generalises beyond it — a
trap, a wrong assumption that cost time, a verification that only worked one way —
add or update an entry in
backlog/docs/lessons-*.md. State the incident, not just the rule: a lesson without the evidence that produced it decays into folklore and gets ignored. Most tasks produce nothing here, and that is fine; do not invent one to fill the slot. - ADR hygiene: ADR check completed; any new or superseded ADRs are linked from the task Implementation Plan and Implementation Notes.
| Action | Example |
|---|---|
| Create task | backlog task create "Add OAuth System" |
| Create with description | backlog task create "Feature" -d "Add authentication system" |
| Create with assignee | backlog task create "Feature" -a @sara |
| Create with status | backlog task create "Feature" -s "In Progress" |
| Create with labels | backlog task create "Feature" -l auth,backend |
| Create with priority | backlog task create "Feature" --priority high |
| Create with plan | backlog task create "Feature" --plan "1. Research\n2. Implement" |
| Create with AC | backlog task create "Feature" --ac "Must work,Must be tested" |
| Create with notes | backlog task create "Feature" --notes "Started initial research" |
| Create with deps | backlog task create "Feature" --dep task-1,task-2 |
| Create sub task | backlog task create -p 14 "Add Login with Google" |
| Create (all options) | backlog task create "Feature" -d "Description" -a @sara -s "To Do" -l auth --priority high --ac "Must work" --notes "Initial setup done" --dep task-1 -p 14 |
| List tasks | backlog task list [-s <status>] [-a <assignee>] [-p <parent>] |
| List by parent | backlog task list --parent 42 or backlog task list -p task-42 |
| View detail | backlog task 7 (interactive UI, press 'E' to edit in editor) |
| View (AI mode) | backlog task 7 --plain |
| Edit | backlog task edit 7 -a @sara -l auth,backend |
| Add plan | backlog task edit 7 --plan "Implementation approach" |
| Add AC | backlog task edit 7 --ac "New criterion,Another one" |
| Add notes | backlog task edit 7 --notes "Completed X, working on Y" |
| Add deps | backlog task edit 7 --dep task-1 --dep task-2 |
| Archive | backlog task archive 7 |
| Create draft | backlog task create "Feature" --draft |
| Draft flow | backlog draft create "Spike GraphQL" → backlog draft promote 3.1 |
| Demote to draft | backlog task demote <id> |
Full help: backlog --help
- Always use
--plainflag when listing or viewing tasks for AI-friendly text output instead of using Backlog.md interactive UI. - When users mention to create a task, they mean to create a task using Backlog.md CLI tool.