Files
archive-clients/docs/telegram-ux.md
T

269 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Telegram UX
Archive Control adds a separate handler/module to the existing bot. Existing
commands and conversations retain their current behavior.
## Authorization and shared state
The handler accepts private-chat users through the existing `@username`
allowlist for compatibility. A configured numeric Telegram user ID may pin an
entry; when pinned, both the ID and current allowlist rule must match. This
mitigates username reassignment while permitting a staged migration.
Jobs, placements, events, queues, and routes are shared administrative state and
store no initiating-user identity. UI session state records only what is needed
to resume the conversation/view and expires after 24 hours by default.
## Entry point and navigation
The existing “Archive/Unarchive Resources” entry opens one message:
```text
What do you want to do?
[ Archive ] [ Unarchive ]
[ Evict Cache ] [ Job Status ]
```
All subsequent pages edit this message. Leaving a resource/tree selection
returns to the Archive Control operation menu without closing the shared
conversation. Confirmation `Cancel` returns to its immediate prior selection;
the main menu alone can close Archive Control.
Callback payloads contain opaque session/action IDs, not resource names or
paths, and are validated against persisted session revision and expiry.
## Archive and unarchive flow
### 1. Select source resource
Control queries relevant online nodes on demand. It groups identical resource
and selection replicas into one display choice while showing distinct
selections separately. The list is stable-sorted and paged:
```text
Which cached resource do you want to archive? (page 1/3)
Send a message to filter resources by name.
Active filters: `documentary`, `2025`
Send another message to narrow the results further.
1. `10.0 GB` Resource A — cache-1
2. `840 MB` Resource B — cache-2
[ 1 ] [ 2 ]
[ Prev ] [ Next ]
[ Remove filters ] [ Cancel ]
```
Size is selected-complete logical content, not total torrent size. Every
filterable page explicitly says that a new message applies a filter. New
messages add literal, case-insensitive filters and narrow the current results;
Remove filters resets the chain. The one-entry tree is filterable by path or
display name in the same way. If no item remains, the message explains whether
no source is online, no selected-complete data exists, or filters eliminated
all results.
### 2. Select source replica and target
If multiple identical-selection replicas exist, control may choose a healthy
source automatically using deterministic availability/route/free-space rules;
otherwise it lists the source replicas. One eligible target is selected
automatically; multiple targets are paged. Offline targets are not offered.
A resource already on a target is still eligible when the requested selection
has an unverified delta. The UI says that it will extend an existing placement.
### 3. Confirm or choose one entry
Before showing confirmation, control obtains fresh full trees from source and
target and calculates the transfer delta.
```text
Archive Resource A from cache-1 to archive-2?
Requested: 7 files, 10.0 GB
Already verified there: 2 files, 3.1 GB
Will transfer: 5 files, 6.9 GB
[ Confirm ]
[ Choose one entry… ] [ Cancel ]
```
Choose one entry opens a paged/filterable tree. Only source-selected-complete
files/directories are enabled. Selecting a directory expands it to descendant
file indices, returns to a freshly recomputed confirmation, and clearly states
the chosen subtree. The protocol supports multiple entries from v1, but the
initial UI exposes one.
Confirmation uses a persisted, single-use nonce bound to session revision,
operation, resource, source, target, selection, fingerprints, and placement
generation. Double taps or stale callbacks cannot create a second job.
## Evict Cache flow
The first page lists current cache placements. Selecting one triggers fresh
queries to the cache and relevant online archive nodes. Each row/confirmation
shows coverage:
- enabled when the union of verified archives covers every selected cache file;
- disabled with missing file count/size and unavailable archive explanation
otherwise.
There is no override button. Confirmation creates a durable three-step
eviction job; it never clears an archive placement or a transfer history row.
## Job Status
Status pages show live and retained jobs with operation, resource, endpoints,
state, current-step and overall percentages, bytes/s where meaningful, and
failure reason.
Routine edits are coalesced to one per five seconds by default. Step changes,
stall transitions, failures, commit, cleanup-required, and terminal states edit
immediately subject to Telegram rate-limit backoff.
State symbols:
| Symbol | Meaning |
| --- | --- |
| 🟡 | queued/not started |
| 🔵 | active, waiting, or stalled |
| 🟠 | committed but cleanup required |
| 🟢 | succeeded |
| 🔴 | failed or cancelled |
Transfer jobs display five numbered steps: Source Stage/HardLink, Syncthing,
Target Merge/Copy, QB Verification, and Staging Cleanup. Route setup and
preflight are named, unnumbered pre-steps. Eviction displays Verify Archive
Coverage, QB Remove Entry, and Safe File Unlink.
A stalled job keeps its last overall percentage:
```text
🔵 Archive: Resource A — cache-1 → archive-2
[2/5 Syncthing: 63.42%, overall 32.68%, stalled, last progress 38m ago]
Waiting indefinitely; retry occurs when progress/service connectivity returns.
```
Failure messages include stable phase and clear reason, while hiding secrets
and host-only paths.
## Clear and remove behavior
`Clear Finished` hides only green successful jobs and leaves a 30-day default
tombstone. Orange Cleanup Required is not clearable as finished.
`Remove One` pauses status auto-refresh, retains the current pages numbered
job rows, and replaces the buttons with current-page job numbers and Cancel.
A successful green row hides immediately. Other confirmation pages identify
the resource, operation, source/target or cache node, state, overall progress,
and only then the internal job ID. A queued job confirmation deletes only its
queue record/reservation. An active, failed-with-residue, or cancelling job
requires a second confirmation and begins/continues compensation; its row
remains until safe cleanup is known. A committed job can only retry staging
cleanup. No history action deletes a cache placement; that is exclusively
Evict Cache.
Archive Control messages use Telegram HTML formatting with all dynamic resource
names, paths, node IDs, filter strings, progress details, and failure reasons
escaped before rendering.
## Restart and rate-limit behavior
Sessions, pages, filters, chosen entries, confirmation bindings, and live status
views are persisted. On bot restart, active status messages resume updates when
the stored chat/message remains editable; otherwise users can reopen Job Status
without losing job state. “message is not modified” is treated as success.
Telegram retry-after values pause/coalesce edits without affecting orchestration.
## Interactive test entry
An opt-in `Archive Control (Test)` entry reuses this same handler with an
independent callback namespace and SQLite database. Its scripted backend
provides comprehensive, empty, and slow-route scenarios without contacting
control/client daemons, qBittorrent, or Syncthing. The production entry,
callback namespace, database, jobs, placements, and other bot features are
unchanged.
The test entry exposes administrator-only `/actest` controls to reset a
scenario, move a synthetic job through displayed states, fail the next selected
backend call, complete a slow route, inspect recent trace events, or export the
complete JSONL trace. The trace contains callback and filter inputs, session
state transitions, exact message/keyboard renders, backend results/errors, and
job transitions, so a UI report can be reproduced without live storage nodes.
### `/actest` operator guide
Run `/actest` only in a private chat as an authorized bot manager. The test UI
must be enabled, and all commands affect only the isolated dummy database.
Sending `/actest` without arguments is equivalent to `/actest status`.
| Command | Effect |
| --- | --- |
| `/actest status` | Show the current scenario, route flags, synthetic job count, trace count, and test database path. |
| `/actest reset [scenario]` | Clear dummy sessions, jobs, faults, and traces, then load `comprehensive`, `empty`, or `route_slow`. The configured scenario is used when omitted. |
| `/actest scenario [scenario]` | Alias of `reset`; it does not preserve the previous dummy jobs or sessions. |
| `/actest advance <job-id> <state>` | Move one synthetic job directly to a display state and persist the transition. |
| `/actest fail-next <method> [message]` | Make exactly the next matching dummy backend call fail with the supplied message, then disarm the fault. |
| `/actest route-ready` | Complete the pending dummy route used by `route_slow`. |
| `/actest trace [limit]` | Show the most recent trace events in chronological order; the limit defaults to 10 and is bounded to 1500. |
| `/actest export` | Send the complete append-only trace as `archive-control-telegram-trace.jsonl`. |
Valid `advance` states are:
```text
queued preparing running waiting stalled cancelling
cleanup_required succeeded failed cancelled
```
The internal job ID is shown on the final line of a Remove One confirmation.
Press Cancel after copying it if the purpose is only to drive the job with
`advance`. It is also present in `trace` backend results and the JSONL export.
Valid `fail-next` methods are:
```text
list_operation_resources
list_clients
get_resource_tree
preview_transfer
preview_eviction
create_job
list_jobs
clear_finished_jobs
hide_job
cancel_job
```
For example:
```text
/actest reset comprehensive
/actest fail-next preview_transfer simulated planning failure
```
The next archive or unarchive preview fails visibly with that reason; a later
preview proceeds normally. To exercise slow route provisioning:
```text
/actest reset route_slow
# Start an archive flow and choose a source/target.
# The preview reports that route provisioning has started.
/actest route-ready
# Reopen/retry the archive flow; the preview can now complete.
```
A useful review sequence is:
1. Run `/actest reset comprehensive`, then reopen `Archive Control (Test)`;
reset intentionally makes pre-reset inline buttons stale.
2. Walk through Archive, Unarchive, Evict Cache, filtering, selective entry,
pagination, Job Status, Remove One, and cancellation without confirming
destructive-looking choices unless that behavior is under review.
3. Copy a job ID from Remove One and use `/actest advance` to inspect waiting,
stalled, failed, cleanup-required, and terminal rendering.
4. Arm one `fail-next` point and repeat its matching UI action.
5. Run `/actest trace 50` for a quick diagnosis or `/actest export` when
reporting a reproducible UI problem.
Scenario state, synthetic jobs, and traces survive bot restarts. Only `reset`
clears them. None of these commands starts the production control daemon,
contacts storage nodes, or mutates production jobs or placements.