# Dynamic Task System

## Purpose

Phase 11 activates operator-managed tasks without trusting the Mini App for time, reward, eligibility, or balance decisions. A task start creates an immutable server snapshot; a successful claim creates the task completion and balanced ledger reward in the same MySQL transaction.

## User API

All routes require a verified Telegram session. Unsafe routes also require the session-bound CSRF token/cookie pair and a unique `Idempotency-Key` header containing 16–128 URL-safe characters.

| Method | Path                                      | Success | Purpose                                                         |
| ------ | ----------------------------------------- | ------: | --------------------------------------------------------------- |
| GET    | `/api/v1/tasks`                           |     200 | Paginated available tasks; destination URLs remain private      |
| POST   | `/api/v1/tasks/:taskId/start`             | 200/201 | Replay/create an attempt and reveal its snapshotted destination |
| GET    | `/api/v1/task-attempts/:attemptId/status` |     200 | Server-derived timer, expiry, and verification state            |
| POST   | `/api/v1/task-attempts/:attemptId/claim`  | 200/201 | Replay/create the completion and ledger reward                  |
| POST   | `/api/v1/task-attempts/:attemptId/claim`  |     202 | Manual verification is still pending                            |

`GET /api/v1/tasks` accepts only `category`, `page`, and `page_size`. Listing evaluates user status, earning bans, feature flags, category/task schedules, username/country/language rules, account age, completed-task thresholds, one-time/daily/repeatable rules, cooldowns, and global caps.

Start and claim bodies must be empty JSON objects. The client cannot submit `reward_minor`, currency, timer timestamps, balance, completion status, or eligibility values.

## Attempt lifecycle

```mermaid
stateDiagram-v2
  [*] --> started: Start accepted
  started --> eligible: Server timer elapsed
  started --> verifying: Manual claim
  verifying --> eligible: Admin verifies
  eligible --> completed: Atomic claim
  started --> expired: Server expiry
  verifying --> rejected: Admin rejects
```

Terminal states cannot return to an active state. `active_slot` is a generated database column populated only for `started`, `eligible`, and `verifying`; its unique index permits only one active attempt for a task/user pair even under concurrent requests.

Scheduled tasks become active under the same row lock when their server-side `starts_at` is reached. Expired active attempts are closed before another attempt is considered.

## Immutable attempt snapshot

At start, the server copies these values into `task_attempts`:

- task type and title;
- destination URL and browser mode;
- reward minor units and currency ID/code/decimals;
- daily limit and repeat cooldown;
- `started_at`, `eligible_at`, and `expires_at`.

Editing or pausing the task later cannot change an already-started attempt's destination or promised reward contract. Claim still rechecks safety caps and account restrictions under locks.

## Atomic reward flow

A claim locks the user, task, and attempt, then rechecks expiry, eligibility time, verification, uniqueness, daily/type/cooldown limits, and task-wide capacity. The transaction posts:

```text
SYSTEM task_rewards/reward_pool  ->  USER available
```

Both ledger entries use the snapshotted server reward. The task completion, ledger transaction, attempt completion state, wallet projection rebuild, and activity record commit or roll back together. Claim and ledger layers have separate stable idempotency contracts.

## Verification policy

| Mode                  | Draft | Publish | Claim behavior                                              |
| --------------------- | ----: | ------: | ----------------------------------------------------------- |
| `timer`               |   Yes |     Yes | Server timer is the verification authority                  |
| `manual`              |   Yes |     Yes | Claim waits for an Admin review, then the user claims again |
| `callback`            |   Yes |      No | Fails closed until a signed provider adapter exists         |
| `telegram_membership` |   Yes |      No | Fails closed until server-side Telegram verification exists |

Opening an external link is not proof of engagement. Operators must use only destinations whose terms allow the intended traffic, and must not publish provider-backed tasks without a genuine signed server integration.

## Admin API and permissions

Admin routes require the configured network allowlist, an authenticated Admin session, session-bound CSRF for mutations, mutation rate limiting, validation, and the exact permission.

| Area               | Routes                                    | Permission                         |
| ------------------ | ----------------------------------------- | ---------------------------------- |
| Task list/detail   | `GET /api/v1/admin/tasks*`                | `tasks.view`                       |
| Task create/update | `POST/PATCH /api/v1/admin/tasks*`         | `tasks.create` / `tasks.update`    |
| Publish/pause      | `POST .../:taskId/publish` or `.../pause` | `tasks.publish`                    |
| Archive            | `POST .../:taskId/archive`                | `tasks.archive`                    |
| Categories         | `/api/v1/admin/task-categories*`          | task view/create/update permission |
| Attempt inspection | `GET /api/v1/admin/task-attempts`         | `task_attempts.view`               |
| Manual review      | `POST .../:attemptId/review`              | `task_attempts.review`             |

Task and category mutations use a numeric `version`. A stale version or invalid lifecycle transition returns a conflict instead of silently overwriting another Admin's change. All Admin mutations are captured by the global audit hook.

## Deployment checklist

1. Confirm `MANAGED_URL_ALLOWED_DOMAINS` includes only reviewed task destination domains.
2. Run `npm run migrate:dry-run`, review migration `016`, back up the database, and run `npm run migrate`.
3. Run `npm run verify:database-schema` and `npm run check`.
4. Create tasks as drafts, verify timer/expiry/limit behavior, then publish with the minimum Admin permission.
5. Monitor duplicate-key, rate-limit, verification, ledger reconciliation, and task-limit events.

No live database, Telegram account, or external provider is touched by the automated suite.
