# AGENTS.md — Instructions for AI Agents

> Any AI agent (Claude, GPT, Copilot, …) working on this project follows this file. It is the
> entry point; the rules live in `RULEFOLDER/`.

## Project

TikTok Manager: a Dockerized Flask app that manages TikTok accounts through the official APIs
(Login Kit, Display API, Content Posting API) — publishing (direct/draft, file/URL),
**app-side scheduling** (ADR-005: TikTok has no scheduled-publish API, so a dedicated scheduler
container publishes due posts), video stats and insights — with a web GUI and a REST API
(`/api/v1`). Sibling project of `YoutubeManager` and `FacebookManager`; the governance system is
ported from there.

## Before writing any code, read in this order

1. `RULEFOLDER/README.md` — the map and the eight non-negotiables
2. `RULEFOLDER/ARCHITECTURE.md` — modules, flows (incl. the scheduler), layering
3. `RULEFOLDER/FEATURE-REGISTRY.md` — what exists, GUI ↔ API mapping, known gaps
4. `RULEFOLDER/CODING-CONVENTIONS.md` — how code is written (incl. the naive-UTC timestamp rule)
5. `RULEFOLDER/SECURITY.md` — what code must never do, and the open findings (TT-S-*)
6. `RULEFOLDER/API-PARITY.md` — GUI change ⇒ API change
7. `RULEFOLDER/CONTRIBUTING.md` — the workflow you will follow
8. `RULEFOLDER/CHANGELOG.md` — recent history (source: `RULEFOLDER/changes/`)

## The workflow, compressed

1. **Create the fragment first**:
   `python RULEFOLDER/tools/new_change.py "title" --type <type> --scope <scopes...>`
   Use the developer code of the human you work for (`--author XX` or `DEV_CODE`), never `AI`
   unless you are unattended automation.
2. Write `## What` / `## Why` before code.
3. Logic in `app/services/` (ADR-002); routes are thin adapters; the facade
   (`tiktok_api.py`) stays Flask-free and DB-free and raises `TikTokAPIError` with safe messages.
4. GUI route ⇒ matching API endpoint ⇒ `DOC/API.md` section ⇒ `FEATURE-REGISTRY.md` row.
5. Tests: service unit test with a fake facade + endpoint test through the Flask test client
   (401, happy path, one validation error, explicit-account case for mutations) + CSRF test for
   GUI mutations.
6. Fill `## API impact`, `## Security review` (answer every checklist line), `## Verification`.
   Set `status: done`, `api_doc_updated: yes` when endpoints changed.
7. `python RULEFOLDER/tools/build_changelog.py` then `python RULEFOLDER/tools/check_rules.py`.
   Both must be clean before you report the task done.
8. Commit message: `CHG-YYYYMMDD-DEV-NN: title`. Do not commit unless asked; if asked, the hooks
   must pass without `--no-verify`.

## Hard rules

- Never edit `RULEFOLDER/CHANGELOG.md` by hand. Never renumber or reuse a change ID.
- Never hand-write a change ID or create a fragment file by hand: always `new_change.py`.
- Never invent a design-pattern name for ordinary code. Name a pattern only where it is
  deliberately applied and explain it in `## How`.
- Never return raw dicts from API routes; use `api_success` / `api_error`.
- Never put a secret, token, or exception text in a response, a log line, a URL or a fragment —
  including the scheduler's recorded `error` strings (client-safe messages only).
- Never make a GET change state. Never trust a client file name or path.
- Never call the facade with a stored token — always `auth.ensure_fresh_token(account)`
  (24-hour access tokens; TikTok rotates the refresh token on every refresh).
- Never add a dependency without pinning it and noting the `pip-audit` result.
- Never widen scope silently: a related bug you notice becomes its own fragment, or a note in
  `## What` under "Out of scope", not an unrecorded fix.
- Never claim verification you did not run. `## Verification` lists real commands and results.
- Do not add code comments unless asked; do write a one-line docstring on every service function.
- Mutating API endpoints require an explicit account (`account_id` or `X-Account-Id`); only
  reads may fall back to the first connected account.
- The GUI guard is session-only (`@login_required`); there is no first-account fallback.
- Timestamps (`expires_at`, `scheduled_at`) are **naive UTC**; parse with
  `parse_scheduled_time` and strip tzinfo at the storage boundary. Never mix naive and aware.
- Never run two scheduler containers against one database (ADR-005).

## Where things are

| Path | Purpose |
|------|---------|
| `app/__init__.py` | App factory |
| `app/config.py` | Env config + startup guards |
| `app/models.py` | `Account` + `ScheduledPost` models (Fernet-encrypted tokens) |
| `app/auth.py` | TikTok Login + `ensure_fresh_token` + `@login_required` |
| `app/tiktok_api.py` | Facade over the three TikTok APIs |
| `app/services/` | Use cases shared by GUI, API and the scheduler (ADR-002) |
| `app/scheduler.py` | The scheduler loop entry point (ADR-005) |
| `app/routes/api_routes.py` | REST API adapters (17 endpoints) |
| `app/routes/*_routes.py` | GUI adapters |
| `DOC/API.md` | API reference |
| `RULEFOLDER/changes/` | One file per change (source of the changelog) |
| `RULEFOLDER/tools/` | `new_change.py`, `build_changelog.py`, `check_rules.py` |
| `.githooks/` | pre-commit and commit-msg enforcement |

## TikTok API notes (the honest caveats)

- **Unaudited apps post private videos only** (`privacy_level=SELF_ONLY`); drafts are
  unaffected. The UI defaults to SELF_ONLY; TikTok's refusal surfaces as `403 FORBIDDEN`.
- **No scheduled publish in the API** — scheduling is app-side (ADR-005).
- **Daily posting caps and rate limits** exist; a 429 surfaces as `RATE_LIMITED` and the
  scheduler records it as a retryable failure.
- Publishing is **async**: poll `fetch_publish_status` (`PUBLISH_COMPLETE`, `FAILED`, or still
  `PROCESSING_*`); `publish_now` waits ~90 s for a verdict before responding.
- The Comment API is not implemented (needs an audited app) — recorded as a gap in the registry.
- Redirect URIs must be **HTTPS** (mkcert locally, see README).

## Build & test

```bash
docker compose build --no-cache && docker compose up -d     # web on https://localhost:9506 + scheduler
python RULEFOLDER/tools/check_rules.py
pytest
```
