Skip to content

Commit 9b36fa7

Browse files
committed
test: cover recent thread commands across channels
1 parent 97f71ce commit 9b36fa7

7 files changed

Lines changed: 874 additions & 421 deletions

File tree

docs/agents/repository-contracts.md

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,43 @@ reinterpreted in feature code.
4646

4747
- Mobile recent-thread lists read the gateway SQLite `recent_threads`
4848
projection only.
49+
- A task backing thread durably carries `thread_kind="task"` from creation
50+
through task overlay deletion. `recent_threads.thread_type` and
51+
`thread_meta.thread_type` derive from that canonical kind and therefore
52+
remain `"task"` after the task itself is deleted. Do not infer task identity
53+
from a live task projection, a title, or a title prefix.
4954
- Keep `recent_threads` current by writing it from the thread-store write path;
5055
do not make `GET /api/recent-threads` rescan router/thread files.
56+
- Bot `/threads` and `/bindthread` reads go through the injected
57+
`RecentThreadPageReader`, backed by the same SQLite projection. A missing or
58+
failed reader is an explicit temporary-unavailable result; never add a
59+
thread-store scan fallback.
5160
- Provider bridge run persistence must use the same thread store as the
5261
gateway/router (the `SqliteThreadStore`) so active run state derives into
5362
`recent_threads` inside the same write transaction.
5463
- Do not repair stale `active_run_id` or `run_state` in read routes.
55-
- The only startup recovery is `clear_stale_active_runs`: one SQL pass that
56-
settles running rows orphaned by the previous process (the bridge run
57-
index is empty at boot). There is no other startup reconciliation.
64+
- The only recurring startup recovery is `clear_stale_active_runs`: one SQL
65+
pass that settles running rows orphaned by the previous process (the bridge
66+
run index is empty at boot). Versioned, transactional one-shot cutovers such
67+
as `recent_task_thread_kind_v1` and `endpoint_holder_dedup_v1` run after the
68+
boot import and record a durable marker; they are not read-time or recurring
69+
reconciliation.
70+
71+
## Endpoint Binding Ownership
72+
73+
- Each endpoint key has at most one holder in canonical `thread_records`.
74+
`endpoint_holder_dedup_v1` established this invariant for legacy data; every
75+
later bind or detach must preserve it through the serialized
76+
`EndpointBindingMutator` service.
77+
- Runtime bind/detach paths point-read the previous owner from
78+
`thread_channel_endpoints`, validate known target records, and point-write
79+
only the known previous/target records. HTTP bind/detach, `/newthread`, and
80+
`/bindthread` must share this service. Do not reintroduce `list_keys` or
81+
record-body scans to find endpoint holders.
82+
- When an endpoint has no projected owner, construct its first binding from
83+
inbound or known-endpoint metadata. Persist binding-related delivery context
84+
using the already-known record ids; absence of an owner is not permission to
85+
scan.
5886

5987
## Transcript Rendering
6088

docs/architecture/command-list-design.md

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ command list.
88
Garyx currently has exactly two interaction command kinds:
99

1010
- `channel_native`: built-in channel-only management commands, such as
11-
`/threads`, `/newthread`, `/threadprev`, and `/threadnext`.
11+
`/newthread`, `/threads [page|next|prev]`, and `/bindthread <n>`.
1212
- `shortcut`: global prompt shortcuts configured by the user, such as
1313
`/summary -> "Summarize the active thread"`.
1414

@@ -40,16 +40,17 @@ Response shape:
4040
"revision": "v1:...",
4141
"commands": [
4242
{
43-
"id": "builtin.router.newthread",
44-
"name": "newthread",
45-
"slash": "/newthread",
43+
"id": "builtin.router.threads",
44+
"name": "threads",
45+
"slash": "/threads",
4646
"aliases": [],
47-
"description": "Start a new thread",
47+
"description": "Browse recent threads",
4848
"category": "Thread",
4949
"kind": "channel_native",
5050
"source": "builtin",
5151
"surfaces": ["plugin", "telegram"],
52-
"dispatch": { "type": "router_native", "key": "router.native.newthread" },
52+
"dispatch": { "type": "router_native", "key": "router.native.threads" },
53+
"args_hint": "[page|next|prev]",
5354
"visibility": "visible",
5455
"warnings": []
5556
}
@@ -68,6 +69,27 @@ Response shape:
6869
`channel_native` and `shortcut` commands.
6970
- Shortcuts are globally visible wherever shortcut execution is supported.
7071

72+
The visible thread-management menu is `newthread`, `threads`, and
73+
`bindthread`. `threadprev` and `threadnext` remain reserved and recognizable
74+
for one compatibility release, but are hidden from normal command lists and
75+
Telegram menus. They return migration guidance instead of switching threads:
76+
use `/threads prev|next`, then `/bindthread <n>`. Callers requesting
77+
`include_hidden=true` may inspect those compatibility entries.
78+
79+
## Thread Command Semantics
80+
81+
- `/threads`, `/threads <page>`, `/threads next`, and `/threads prev` browse
82+
the global recent non-task thread projection in pages of 10. Addressed forms
83+
such as `/threads@bot_name 2` preserve the same arguments.
84+
- `/bindthread <n>` switches to an absolute row number from pages successfully
85+
shown to that endpoint. A canonical `thread::<id>` may also be supplied for
86+
compatibility; either form is revalidated before binding.
87+
- `/newthread` creates and binds a fresh thread, then clears the endpoint's
88+
accumulated recent-list snapshot.
89+
90+
`args_hint` is `[page|next|prev]` for `threads` and `<n>` for `bindthread`.
91+
Invalid arguments are handled locally and never become model prompts.
92+
7193
## Plugin Contract
7294

7395
Channel plugins fetch the command list through host RPC:
@@ -117,4 +139,6 @@ Gateway core does not expose a manual Telegram command-sync endpoint.
117139
- Payload uses `revision`.
118140
- Plugin RPC uses `commands/list` and returns the command-list shape.
119141
- Telegram sync includes both `channel_native` and `shortcut` commands.
142+
- Telegram's visible native menu contains `newthread`, `threads`, and
143+
`bindthread`; deprecated navigation commands remain hidden.
120144
- Mac app settings manage only prompt shortcuts.

docs/concepts/threads-and-workspaces.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,25 @@ endpoint reads only the SQLite projection and returns pagination metadata
3838
(`total`, `offset`, and `has_more`) alongside the requested page. It must not
3939
rescan router thread files on the read path.
4040

41+
The optional `tasks` query parameter selects the filtering domain before
42+
pagination:
43+
44+
- `tasks=include` includes task and non-task threads and is the default when
45+
the parameter is omitted.
46+
- `tasks=exclude` returns only non-task threads.
47+
- `tasks=only` returns only task threads.
48+
49+
Unknown values return HTTP 400. `total`, `offset`, and `has_more` always
50+
describe the selected domain. Existing clients that omit `tasks` retain the
51+
same member set, ordering, pagination, envelope, and row schema.
52+
53+
Channel bots use this same projection for thread management. The
54+
`/threads [page|next|prev]` command browses recent non-task threads in pages of
55+
10, and `/bindthread <n>` binds an absolute row number from pages that endpoint
56+
has already seen. `/newthread` creates a fresh thread. The former
57+
`/threadprev` and `/threadnext` commands are hidden compatibility commands that
58+
now point users to the browse-then-bind flow.
59+
4160
## How a chat becomes a thread
4261

4362
When a message arrives on a channel, Garyx looks up the right thread by:

0 commit comments

Comments
 (0)