Skip to content

Commit a2bea90

Browse files
GeiserXclaude
andcommitted
docs: add Alembic stamping reminder to AGENTS.md
Prevent future regressions where new migrations aren't added to the entrypoint.sh stamping logic, causing crash-loops on pre-Alembic DBs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c98a4c5 commit a2bea90

File tree

1 file changed

+39
-250
lines changed

1 file changed

+39
-250
lines changed

AGENTS.md

Lines changed: 39 additions & 250 deletions
Original file line numberDiff line numberDiff line change
@@ -1,184 +1,65 @@
1-
# Telegram-Archive - AI Assistant Configuration
1+
# telegram-archive - AI Assistant Configuration
22

33
## Before Starting Any Coding Task
44

5-
1. Always create a new git worktree for the task
6-
2. Use the naming convention: `git worktree add -b ai/[task-description] ../Telegram-Archive-ai-[task-description]`
5+
1. Check existing worktrees with `git worktree list` and create a new one for this task if needed
6+
2. Use the naming convention: `git worktree add -b ai/<task> .worktrees/<task>`
77
3. Navigate to the worktree directory before making any changes
88
4. Commit changes when the task is finished. Merge to main, and clean the worktree.
99

10-
<!--
11-
This file is synced with LynxPrompt (Blueprint: bp_cmk483at3000001pdq0ohz0t5)
12-
13-
Sync Commands:
14-
15-
# Using LynxPrompt CLI (recommended):
16-
lynxp push # Upload local changes to cloud
17-
lynxp pull # Download cloud changes to local
18-
lynxp diff # Compare local vs cloud versions
19-
20-
# Install CLI: npm install -g lynxprompt
21-
# Login: lynxp login
22-
23-
Docs: https://lynxprompt.com/docs/api
24-
-->
25-
26-
> **Project Context:** This is an open-source project. Consider community guidelines and contribution standards.
27-
2810
## Persona
2911

30-
You assist developers working on Telegram-Archive.
31-
32-
Project description: Own your Telegram history. Automated, incremental backups with a local web viewer that feels just like the real app. Docker-ready and supports public chat sharing
12+
You assist developers working on telegram-archive.
3313

3414
## Tech Stack
3515

36-
- Python 3.11
37-
- Telethon (Telegram MTProto client)
38-
- FastAPI + uvicorn (web viewer)
39-
- SQLAlchemy async (ORM)
40-
- aiosqlite / asyncpg (database drivers)
41-
- APScheduler (cron scheduling)
42-
- Alembic (database migrations)
43-
- Jinja2 (HTML templates)
44-
- PostgreSQL / SQLite
16+
- Python
17+
- FastAPI
18+
- sqlalchemy
4519

4620
> **AI Assistance:** Let AI analyze the codebase and suggest additional technologies and approaches as needed.
4721
4822
## Repository & Infrastructure
4923

50-
- **Host:** github
51-
- **License:** gpl-3.0
52-
- **Architecture:** Dual-image Docker (shared codebase, separate entrypoints for backup and viewer)
24+
- **License:** mit
25+
- **CI/CD:**
5326
- **Commits:** Follow [Conventional Commits](https://conventionalcommits.org) format
5427
- **Versioning:** Follow [Semantic Versioning](https://semver.org) (semver)
55-
- **CI/CD:** GitHub Actions
56-
- **Deployment:** Docker
57-
- **Docker Images:**
58-
- `drumsergio/telegram-archive` — Backup scheduler (requires Telegram credentials)
59-
- `drumsergio/telegram-archive-viewer` — Web viewer only (no Telegram client)
60-
- **Example Repo:** https://github.com/GeiserX/LynxPrompt (use as reference for style/structure)
61-
62-
## Deployment Environments
63-
64-
| Environment | Image Tag | Purpose |
65-
|-------------|-----------|---------|
66-
| **Production** | `vX.Y.Z` (semver) | Stable releases only |
67-
| **Development** | `:dev` | PR builds, pre-release testing |
68-
69-
- **PRs build `:dev` tag** via `docker-publish-dev.yml` workflow
70-
- **Tags build semver** via `docker-publish.yml` workflow
71-
- Always test on dev environment before releasing to prod
72-
- See gitea docker compose for environment assignments
73-
74-
## Release Guidelines
75-
76-
### Creating Releases
77-
78-
**Always use the release script** to ensure changelog is updated:
79-
80-
```bash
81-
./scripts/release.sh v4.1.5
82-
```
83-
84-
The script:
85-
1. Validates version format (vX.Y.Z)
86-
2. **Checks that CHANGELOG.md has an entry** for this version (fails if missing!)
87-
3. Creates and pushes the git tag
88-
4. GitHub Actions creates the release with changelog notes
89-
90-
### Manual Process (if needed)
91-
92-
1. **Update `docs/CHANGELOG.md`** FIRST:
93-
- Add new section: `## [X.Y.Z] - YYYY-MM-DD`
94-
- Document all changes: Added, Fixed, Changed, Removed
95-
- Mark breaking changes with `### ⚠️ Breaking Change`
96-
- Include migration steps if needed
97-
98-
2. Commit the changelog update
99-
100-
3. Tag: `git tag vX.Y.Z -m "Release vX.Y.Z"`
101-
102-
4. Push: `git push origin vX.Y.Z`
103-
104-
### Breaking Changes
105-
106-
When introducing breaking changes:
107-
- Bump **MAJOR** version (e.g., v4.0.0 → v5.0.0)
108-
- Document in CHANGELOG with migration steps
109-
- Update README upgrade section if significant
110-
- Consider providing migration scripts in `scripts/`
111-
112-
### Chat ID Format (CRITICAL)
113-
114-
All chat IDs must use Telegram's **marked format**:
115-
- Users: positive (e.g., `123456789`)
116-
- Basic groups: negative (e.g., `-123456789`)
117-
- Supergroups/Channels: -100 prefix (e.g., `-1002240913478`)
118-
119-
When documenting or configuring chat IDs, always use marked format!
28+
- **CI/CD:**
12029

12130
## AI Behavior Rules
12231

123-
- **Always enter Plan Mode** before making any changes - think through the approach first
32+
- Optimize code for LLM reasoning: prefer flat/explicit patterns, minimal abstractions, structured logging, and linear control flow
33+
- When you learn new project patterns or conventions, suggest updates to this configuration file
34+
- Always verify your work before returning: run tests, check builds, confirm changes work as expected
35+
- Reuse existing terminals when possible. Close terminals you no longer need
36+
- Always check documentation (via MCP or project docs) before assuming knowledge about APIs or libraries
37+
- **Use Plan Mode** for complex tasks, multi-step changes, or risky modifications
38+
- When stuck, **attempt creative workarounds** before asking for help
12439

12540
## Git Workflow
12641

127-
- **Workflow:** Direct commits to master are acceptable for small fixes and documentation
128-
- For larger features or breaking changes, create a feature branch and open a PR
129-
- Create descriptive branch names when needed (e.g., `feat/add-login`, `fix/button-styling`)
130-
131-
### Git Commit Identity
132-
133-
**IMPORTANT:** Always use the correct GitHub identity for commits:
134-
135-
```bash
136-
git config user.name "GeiserX"
137-
git config user.email "9169332+GeiserX@users.noreply.github.com"
138-
```
139-
140-
- **GitHub User ID:** 9169332
141-
- **Username:** GeiserX
142-
- **No-reply email:** `9169332+GeiserX@users.noreply.github.com`
143-
144-
⚠️ Using the wrong ID in the email (e.g., `57840286+...`) will link commits to a different GitHub account!
145-
146-
## Important Files to Read
147-
148-
Always read these files first to understand the project context:
149-
150-
- `README.md` — Features, configuration, deployment
151-
- `src/config.py` — All environment variables and their handling
152-
- `src/telegram_backup.py` — Core backup logic
153-
- `.env.example` — Configuration reference
154-
- `docker-compose.yml` — Deployment patterns
155-
156-
## Self-Improving Blueprint
157-
158-
> **Auto-update enabled:** As you work on this project, track patterns and update this configuration file to better reflect the project's conventions and preferences.
42+
- **Workflow:** Create feature branches and submit pull requests
43+
- Create a descriptive branch name (e.g., `feat/add-login`, `fix/button-styling`)
44+
- Open a PR for review before merging
45+
- Do NOT commit directly to main/master branch
15946

16047
## Boundaries
16148

16249
### ✅ Always (do without asking)
16350

164-
- Create new files
165-
- Rename/move files
166-
- Rewrite large sections
167-
- Change dependencies
168-
- Touch CI pipelines
169-
- Modify Docker config
170-
- Change environment vars
171-
- Update docs automatically
172-
- Edit README
173-
- Handle secrets/credentials
174-
- Modify auth logic
51+
- Read any file in the project
52+
- Modify files in src/ or lib/
53+
- Run build, test, and lint commands
54+
- Create test files
55+
- Fix linting errors automatically
17556

17657
### ⚠️ Ask First
17758

178-
- Delete files
179-
- Modify database schema
180-
- Update API contracts
181-
- Skip tests temporarily
59+
- Add new dependencies to package.json
60+
- Modify configuration files at root level
61+
- Create new modules or directories
62+
- Refactor code structure significantly
18263

18364
### 🚫 Never
18465

@@ -190,7 +71,7 @@ Always read these files first to understand the project context:
19071
## Code Style
19172

19273
- **Naming:** follow idiomatic conventions for the primary language
193-
- **Logging:** Python logging with `logger = logging.getLogger(__name__)`
74+
- **Logging:** [90m⏭ Skip[39m
19475

19576
Follow these conventions:
19677

@@ -201,114 +82,19 @@ Follow these conventions:
20182
- Add comments for complex logic only
20283
- Keep functions focused and testable
20384

204-
## ⚠️ Data Consistency Rules (CRITICAL)
205-
206-
These rules exist because of bugs that reached production. **Always verify these when modifying DB code.**
207-
208-
### Chat ID Format (Marked IDs)
209-
210-
Telegram uses "marked" IDs that differ from raw entity IDs:
211-
212-
| Entity Type | Format | Example |
213-
|-------------|--------|---------|
214-
| Users | Positive | `123456789` |
215-
| Basic groups | Negative | `-123456789` |
216-
| Supergroups/Channels | -1000000000000 - id | `-1001234567890` |
217-
218-
**Rules:**
219-
- Always use `telethon.utils.get_peer_id(entity)` to get the marked ID
220-
- Never use `entity.id` directly for database operations
221-
- The `_get_marked_id()` method in `telegram_backup.py` wraps this
222-
- User config (`GROUPS_INCLUDE_CHAT_IDS`, etc.) uses marked format
223-
224-
### DateTime Timezone Handling
225-
226-
Telethon returns timezone-aware datetimes, but PostgreSQL uses `TIMESTAMP WITHOUT TIME ZONE`.
227-
228-
**Rules:**
229-
- Always strip timezone before DB insert/update using `_strip_tz(dt)` in `adapter.py`
230-
- Apply to ALL datetime fields: `date`, `edit_date`, `created_at`, etc.
231-
- Check both INSERT and UPDATE operations (v4.0.6 bug: insert used `_strip_tz`, update didn't)
232-
233-
### Consistency Checklist
234-
235-
When modifying database code, verify:
236-
- [ ] All chat_id values use marked format (via `_get_marked_id()`)
237-
- [ ] All datetime values pass through `_strip_tz()` before DB operations
238-
- [ ] INSERT and UPDATE operations handle the same fields identically
239-
- [ ] Tests exist in `tests/test_db_adapter.py` for data type handling
240-
241-
## Alembic Migrations (CRITICAL)
242-
243-
### Architecture
244-
245-
- Migrations live in `alembic/versions/` with format `YYYYMMDD_REV_slug.py`
246-
- Sequential integer revisions: `001`, `002`, ..., `006`, etc.
247-
- `alembic/env.py` runs migrations via async SQLAlchemy (`asyncpg` for PG, `aiosqlite` for SQLite)
248-
- `scripts/entrypoint.sh` calls `alembic upgrade head` on container start (backup container only, not viewer)
249-
- The entrypoint also handles **pre-Alembic stamping** for databases that existed before migrations were added
250-
251-
### Writing a New Migration
252-
253-
1. Create file: `alembic/versions/YYYYMMDD_NNN_slug.py`
254-
2. Set `revision = "NNN"` and `down_revision = "NNN-1"`
255-
3. Use `op.add_column()`, `op.create_table()`, `op.create_index()`, etc.
256-
4. Both SQLite and PostgreSQL must be supported -- check `conn.dialect.name` when behavior differs
257-
5. Update the pre-Alembic stamping logic in `entrypoint.sh` if the new migration adds detectable schema (table, index, column) so existing databases get stamped correctly
258-
259-
### Advisory Lock Rule (v6.2.14 bugfix)
260-
261-
**NEVER execute SQL on the Alembic connection before `context.configure()`.**
262-
263-
Any `connection.execute()` before `configure()` triggers SQLAlchemy's autobegin. Alembic then detects `_in_external_transaction=True` and returns `nullcontext()` from `begin_transaction()`, skipping its own commit. DDL runs but is silently rolled back when the connection closes.
264-
265-
The correct pattern in `env.py`:
266-
267-
```python
268-
def do_run_migrations(connection):
269-
context.configure(connection=connection, ...) # FIRST — no SQL before this
270-
271-
with context.begin_transaction():
272-
# Advisory lock INSIDE the transaction, using xact variant (auto-releases on commit)
273-
if connection.dialect.name == "postgresql":
274-
connection.execute(text("SELECT pg_advisory_xact_lock(7483920165)"))
275-
context.run_migrations()
276-
```
277-
278-
### Entrypoint Stamping
279-
280-
`entrypoint.sh` detects pre-Alembic databases and stamps them at the correct version by checking for schema artifacts (tables, columns, indexes). When adding migration `NNN`, add a detection check for it in the stamping logic so fresh installs and upgrades from any version work correctly.
281-
28285
## Testing Strategy
28386

28487
### Test Levels
28588

286-
- **Smoke:** Quick sanity checks for critical paths
28789
- **Unit:** Unit tests for individual functions and components
28890
- **Integration:** Integration tests for component interactions
289-
- **E2e:** End-to-end tests for full user flows
29091

29192
### Frameworks
29293

29394
Use: pytest
29495

29596
### Coverage Target: 80%
29697

297-
### CI Requirements
298-
299-
**All PRs MUST pass tests before merge.** The `Tests` workflow runs on every PR:
300-
- `tests/test_db_adapter.py` — Data type consistency (timezone, chat IDs)
301-
- `tests/test_config.py` — Environment variable parsing
302-
- `tests/test_telegram_backup.py` — Core backup logic
303-
304-
### When to Add Tests
305-
306-
Add tests when:
307-
1. Fixing a bug — write a test that would have caught it
308-
2. Adding DB operations — test data type handling
309-
3. Modifying config parsing — test edge cases (empty strings, etc.)
310-
4. Adding new features — test the happy path and error cases
311-
31298
## 🔐 Security Configuration
31399

314100
### Secrets Management
@@ -320,10 +106,6 @@ Add tests when:
320106
- Dependabot (dependency updates)
321107
- Renovate (dependency updates)
322108

323-
### Authentication
324-
325-
- Basic Authentication
326-
327109
### Data Handling & Compliance
328110

329111
- Encryption at Rest
@@ -337,6 +119,13 @@ Add tests when:
337119
338120
**🔍 Security Audit Recommendation:** When making changes that involve authentication, data handling, API endpoints, or dependencies, proactively offer to perform a security review of the affected code.
339121

122+
## Alembic Migrations — Critical Reminders
123+
124+
- **`Base.metadata.create_all(checkfirst=True)`** creates ALL tables from SQLAlchemy models at once, including tables that should be created by future Alembic migrations. This means pre-Alembic databases can have schema objects from migrations that haven't "run" yet.
125+
- **`scripts/entrypoint.sh`** stamps pre-Alembic databases by detecting which schema objects exist. **Every time you add a new migration, you MUST update the stamping logic in entrypoint.sh** — both the PostgreSQL block and the SQLite block — to detect the new migration's artifacts (tables, columns, indexes). If you forget, existing databases that were created via `create_all()` will be stamped at a lower version, and Alembic will try to re-create objects that already exist, causing crash-loops.
126+
- Check highest migration first, then descend (009 → 008 → 007 → ...).
127+
- Test with both PostgreSQL and SQLite paths.
128+
340129
---
341130

342-
*Generated by [LynxPrompt](https://lynxprompt.com) CLI*
131+
*Generated by [LynxPrompt](https://lynxprompt.com) CLI*Always use Python type hints for public APIs.

0 commit comments

Comments
 (0)