@remits/remits-cli 0.1.113 → 0.1.115

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,483 @@
1
+ # Support Tickets
2
+
3
+ > A `remits-cli` skill reference. **Load this when** a ticket is part of the request, you are registering to work tickets, or you are the worker inside an autonomous ticket run.
4
+ >
5
+ > The table of contents below carries **real line numbers** (`- L84 Some Heading`), resolved when
6
+ > this file is installed, so they are never stale. Read the head, pick your sections, and offset-read
7
+ > only those. The entry text is the heading verbatim, so it also greps.
8
+
9
+ ## Table of Contents
10
+
11
+ - [Support Ticket Mental Model](#support-ticket-mental-model)
12
+ - [You are an agent, and you register yourself](#you-are-an-agent-and-you-register-yourself)
13
+ - [Autonomous: one ticket, one process](#autonomous-one-ticket-one-process)
14
+ - [Serving only some workstreams](#serving-only-some-workstreams)
15
+ - [The manual loop](#the-manual-loop)
16
+ - [If you are the worker](#if-you-are-the-worker)
17
+ - [Before you edit anything: where you are, and whether you may](#before-you-edit-anything-where-you-are-and-whether-you-may)
18
+ - [Your account's process is binding, and it is already in your brief](#your-accounts-process-is-binding-and-it-is-already-in-your-brief)
19
+ - [Some of that process is enforced, not requested](#some-of-that-process-is-enforced-not-requested)
20
+ - [Seeing the queue as a human does](#seeing-the-queue-as-a-human-does)
21
+ - [Agent components are workers too](#agent-components-are-workers-too)
22
+ - [Moving a ticket through its lifecycle](#moving-a-ticket-through-its-lifecycle)
23
+ - [When a worker needs a decision from a human](#when-a-worker-needs-a-decision-from-a-human)
24
+
25
+ ## Support Ticket Mental Model
26
+
27
+ Support tickets are a **first-class platform capability**, not an account convention. Every Remits
28
+ account reads and writes the `support_tickets` collection without owning a Schema, tickets are anchor
29
+ `Object`s on the account the work belongs to, and the lifecycle vocabulary is defined once in the
30
+ platform.
31
+
32
+ **The platform is deliberately not the standard for ticket workflow.** Different Remits
33
+ platforms and products integrate with different systems — Zendesk, Jira, a customer's own portal —
34
+ and each end client has its own rules for how tickets move. Remits owns the *record* and the *verbs*;
35
+ each front-stage platform builds its own workflow on top through its own Embeddables, Rules, and
36
+ Actions. So what you see through `remits-cli` is the shared record underneath every one of those
37
+ workflows, and never one product's view of it.
38
+
39
+ ### You are an agent, and you register yourself
40
+
41
+ **If the user says anything like "register to become a support agent", or "work support tickets",
42
+ run exactly this and nothing else first:**
43
+
44
+ ```bash
45
+ remits-cli agent serve
46
+ ```
47
+
48
+ No flags, no setup, **no need to be in an account repo** — run it from wherever the session started.
49
+ It registers this terminal for every account repo indexed on this machine that your user can reach,
50
+ against the production platform and the production lane, and then **starts working tickets on its
51
+ own**. It returns immediately; a background supervisor does the rest.
52
+
53
+ Use `remits-cli agent register` instead **only** when the user wants presence without autonomy — a
54
+ human, or you in this very tab, will work the tickets by hand. Registering alone starts nothing: it
55
+ makes the session routable and then waits to be asked.
56
+
57
+ Three tabs running an agent are three agents. Each is independently routable, each reports its own
58
+ activity, and each stops receiving work when its tab closes — a heartbeat anchored to the session dies
59
+ with it, so a crashed agent and a quit agent look identical to the platform.
60
+
61
+ **Nothing is pushed at you.** A terminal mid-task cannot receive a push, so delivery is you asking.
62
+ That is why the routing decision is a durable field on the ticket rather than a message: you can
63
+ restart this terminal, register again, and the work is still there.
64
+
65
+ Two facts about a ticket are separate and must stay separate:
66
+
67
+ | Fact | Field | Means |
68
+ |---|---|---|
69
+ | **Routed** | `routedAgentId` | which agent session should pick this up — delivery |
70
+ | **Claimed** | `claimedAgentId` | a worker process is running on it *right now* |
71
+ | **Owned** | `assignedTo` | who has claimed it — accountability |
72
+ | **Status** | `status` | where it is in the workflow |
73
+
74
+ A ticket routed to you is not yet yours. Claim it with `accept`, and the queue then shows it owned.
75
+
76
+ The **claim** is the supervisor's, not yours — it exists so two workers never start on one ticket,
77
+ and it expires on its own so a killed worker cannot park a ticket forever. You do not manage it.
78
+
79
+ ### Autonomous: one ticket, one process
80
+
81
+ ```bash
82
+ remits-cli agent serve # workers are whichever agent THIS session is
83
+ remits-cli agent serve --worker-agent codex --max-concurrent 2
84
+ remits-cli agent serve --mode investigate # read-only workers: no file edits
85
+ remits-cli agent serve --serves support,sdlc # only these workstreams are routed here
86
+ remits-cli agent workers # what is running right now
87
+ remits-cli agent release # stop serving, go offline
88
+ ```
89
+
90
+ `--mode investigate` is still autonomous ticket work. The supervisor claims a ticket, launches a
91
+ worker, and the worker should read/reproduce/localize the issue and update the ticket with useful
92
+ findings or a handoff. What it must **not** do is become the repository editor: investigation-mode
93
+ polls do not acquire or renew the repo edit lease, and the brief explicitly forbids file edits,
94
+ component staging, commits, and `remits-cli ticket lease`.
95
+
96
+ **Run it in a plain terminal tab.** That tab becomes the agent host: `serve` returns immediately, a
97
+ detached supervisor is anchored to the tab's shell, and closing the tab stops the agent. Nothing in
98
+ that tab is an AI session — the AI only ever appears as the worker processes the supervisor spawns.
99
+
100
+ Starting it from inside an AI session works too and self-detects the worker kind, but it is the
101
+ lesser setup: it parks an interactive session as a heartbeat holder while its workers do the work.
102
+
103
+ When a ticket is routed to this session, the supervisor launches **a fresh headless agent process
104
+ for that ticket**, in that account's repo, with a brief the platform generates. That process exits
105
+ when the ticket is done.
106
+
107
+ **One ticket = one process is the point, and it is why nothing here ever needs `/clear`.** Context
108
+ grooming cannot be a discipline: `/clear` and `/new` are commands a *human types into a TUI*, and no
109
+ model can invoke them — so a long-lived session working ticket after ticket has no way to reset
110
+ itself. A process that exits has nothing to reset. Do not try to solve context growth by being tidy
111
+ inside one session; let the session end.
112
+
113
+ **`serve` returns immediately, and that is correct.** Do not follow it with a wait, a poll, or a
114
+ loop. The supervisor is detached precisely so this tab is free. Report that you are serving and
115
+ stop; you have not left the job half done.
116
+
117
+ What the supervisor handles for you, so you do not have to think about any of it: collecting routed
118
+ work, claiming each ticket so no second worker starts on it, renewing that claim while the worker
119
+ runs, reporting the worker's activity to the dashboard, dropping the claim when it exits, retrying a
120
+ failed ticket once, and releasing a ticket back to the queue when it has failed too often. Closing
121
+ the terminal stops everything and hands any in-flight work back.
122
+
123
+ ### Serving only some workstreams
124
+
125
+ `--serves` declares which of the account's **workstreams** this machine covers, so a fleet can be a
126
+ set of queues rather than a pool of interchangeable machines:
127
+
128
+ ```bash
129
+ remits-cli agent serve --serves support,incident --mode investigate # the support desk box
130
+ remits-cli agent serve --serves sdlc --max-concurrent 2 # the engineering box
131
+ ```
132
+
133
+ The names are the account's own — the platform compares them and learns nothing about any of them.
134
+ Read `remits-cli ticket queue --workstream ...` or the account's `OPERATIONS` prompts to find out
135
+ what this account calls its processes; do not invent names.
136
+
137
+ **Omit it unless the user asked for it.** A session that declares nothing is a generalist and takes
138
+ everything, which is the right default and what every existing fleet does. Specialising has a real
139
+ cost: a specialist is only routed tickets in its workstreams *and its unrouted sweep takes only
140
+ those*, so a workstream no live session covers is routed to nobody. The platform reports that rather
141
+ than quietly overriding the declaration —
142
+
143
+ - `dispatch` answers `reason: 'no_agent_serves_workstream'` and names what the live sessions do cover;
144
+ - `agent map` prints each session's declared queues;
145
+ - the supervisor logs `serve.out_of_scope` when it passes over unrouted work it does not serve.
146
+
147
+ **So after specialising a fleet, run `remits-cli agent map`** and confirm every workstream the
148
+ account ingests is covered by something.
149
+
150
+ ### The manual loop
151
+
152
+ Only when the session registered with `agent register` rather than `agent serve`.
153
+
154
+ ```bash
155
+ remits-cli agent work --wait 600 # returns as soon as work arrives
156
+ remits-cli agent status --state working --ticket 22454 --activity "reproducing the upload failure"
157
+ # ... investigate, fix, verify — keep `status` current as what you are doing changes ...
158
+ # ... close the ticket lifecycle through remits-cli ticket (accept -> status -> complete, ask, or release) ...
159
+ remits-cli agent status --state idle # then ask for work again
160
+ ```
161
+
162
+ **Nothing wakes this tab.** A routed ticket sits there until someone in this session asks for it, so
163
+ if you are working the manual loop you have to keep asking. If you find yourself wishing you could
164
+ be woken up, that is what `agent serve` is.
165
+
166
+ **Report what you are doing.** `agent status` is how an operator watching the dashboard, or another
167
+ agent, knows this session is alive and what it is on. It costs one command and it is the difference
168
+ between a visible queue and a silent one. Update it when you change what you are doing, not on a
169
+ timer.
170
+
171
+ **Work the ticket in the right repo.** A ticket names its `accountId`, and often an
172
+ `implementationAccountId` — the platform/product account whose repo holds the code. Resolve that to a
173
+ local directory through `~/.remits-cli/account-repos.json` and `cd` there before making changes. You
174
+ registered from anywhere; you do not fix anything from anywhere.
175
+
176
+ For subscriber/fork tickets, this is also the investigation starting point. The reporting account may be
177
+ the fork or client that observed the issue, while `implementationAccountId` names the repo whose branch
178
+ contains the front-stage source. Read the ticket, run/inspect the run context (`ticket where` when useful),
179
+ open the indexed implementation repo, then establish the relevant branch's steady starting state before
180
+ the first edit: fetch origin, fast-forward the branch, and confirm local and remote have no commits missing
181
+ from either side. Then read `account-info.json` and the component files. Use `mcp_component_view` /
182
+ `mcp_component_grep` only when that repo is unavailable locally or when explicitly comparing live DB source
183
+ to the files.
184
+
185
+ **Capacity is real, not advisory.** A serving session tells the platform how many workers it can run
186
+ (`--max-concurrent`, default 1), and the router will not send it more than that. So a session at
187
+ capacity is skipped in favour of one that is free, rather than accumulating tickets it will never
188
+ start.
189
+
190
+ **Lane note:** `register` and `serve` default to the production lane because a support agent works real tickets.
191
+ `--data-mode test` registers a fixture agent instead, which will never be routed a production ticket —
192
+ use it only when you are deliberately testing the routing itself.
193
+
194
+ ### If you are the worker
195
+
196
+ You know you are one when `REMITS_SUPPORT_TICKET_ID` is set in your environment. Your whole job is
197
+ that one ticket, and your brief is your prompt — follow it. Two things it says that are worth
198
+ repeating: **report progress** with `remits-cli agent status --ticket <id> --activity "..."` (a
199
+ headless run is invisible otherwise), and **end in a terminal state** — `complete` with a real
200
+ resolution, or `update_status` with what you established and what the next agent should try. Exiting
201
+ quietly leaves a ticket that looks in-flight forever.
202
+
203
+ **If your brief says this is not the first autonomous run on this ticket, believe it.** Earlier runs
204
+ picked it up and handed it back unfinished; whatever they tried did not work, so repeating it will not
205
+ work either. Read what they recorded, then either take a genuinely different approach or **stop and
206
+ ask** (`remits-cli ticket ask`). Asking is a complete, successful outcome, and it is the right one when
207
+ the obstacle is not something a run can remove. Accounts cap how many times a ticket may be picked up
208
+ and handed back — past that the ticket stops being offered to workers at all and waits for a person, so
209
+ a run that exits quietly at the limit has cost every earlier run as well as its own.
210
+
211
+ ### Before you edit anything: where you are, and whether you may
212
+
213
+ Presence answers *who*. Two more facts answer *whether you may edit*, and with git worktrees and
214
+ workspace lanes an account id no longer identifies a working tree — so an agent that does not ask these
215
+ is assuming, and the assumption it makes when it guesses wrong is "this is my repository to edit".
216
+
217
+ ```bash
218
+ remits-cli ticket where --ticket 22454 # repo account, checkout, branch, staging lane, lease, claim, YOUR phase
219
+ remits-cli agent map # every repository, who holds each lease, and where each agent is working
220
+ ```
221
+
222
+ **Reading, reproducing and investigating are parallel-safe and unrestricted. Editing one account's
223
+ repository is exclusive**, enforced by a lease held per repository account per data lane:
224
+
225
+ ```bash
226
+ remits-cli ticket lease --ticket 22454 # take it when you started read-only and reached an actual edit
227
+ remits-cli ticket unlease --ticket 22454 # complete / ask / release already do this for you
228
+ ```
229
+
230
+ Four things are worth knowing and are not obvious:
231
+
232
+ - **A refusal is not an error.** The run continues read-only, and the message names who holds the lease
233
+ **and where they are working**, so you can tell a real conflict from a holder in a different worktree.
234
+ **Do not wait for a lease and do not poll for one** — investigation is most of the work on most
235
+ tickets, and a ticket that turns out to need an edit hands off with `ticket progress --next-step`
236
+ saying exactly what change is needed. A lock people queue on turns one stuck worker into a stalled
237
+ fleet.
238
+ - **A staging workspace does not replace the lease.** Separate lanes stop two runs *resolving* each
239
+ other's staged code; they do nothing about two processes writing the same files or pushing the same
240
+ branch, which is what actually destroys work.
241
+ - **You are not restricted to trunk, and you do not choose the branch.** You work on whatever branch your
242
+ checkout is on — `ticket where` prints it — and nothing switches it underneath you. Separately, if this
243
+ account subscribes to a **component variant branch**, its variant components (including its
244
+ `OPERATIONS` process) resolve for you automatically, server-side, without you doing anything. Those are
245
+ two different facts and only the first is about your files. Load `branch-variants.md` before working in
246
+ a non-trunk checkout.
247
+ - **The edit lease excludes per repository account and lane, NOT per branch.** So an agent editing a
248
+ different branch of this same repository still blocks you, even though your files and your pushes would
249
+ not collide. That is deliberate over-exclusion, not a bug — investigate read-only and hand off, exactly
250
+ as for any other refusal.
251
+ - **A spawned local ticket worker owns its checkout grounding and already has its staging lane.** For
252
+ repository work, `remits-cli agent serve` appends the local repository checkout, target branch and a
253
+ suggested per-ticket worktree path. Before editing, create or choose an isolated worktree appropriate to
254
+ the ticket. For a ticket with no component branch subscription, the target branch is the repository
255
+ default before it is the launch checkout's current branch. The supervisor sets
256
+ `REMITS_WORKSPACE=ticket-<id>` for you; if you choose a detached worktree, export
257
+ `REMITS_GIT_BRANCH=<branch>` before running `remits-cli`. A worktree is a checkout-isolation tool, not
258
+ a freshness proof: fetch, fast-forward and prove `HEAD...origin/<branch>` is `0 0` before the first
259
+ edit, following `multi-agent-development.md`.
260
+ - **GCP Agent components do not get a local checkout.** The same ticket brief can name a component branch
261
+ subscription, but for a GCP Agent component that is component-resolution context, not a filesystem
262
+ instruction.
263
+ - **One editing worker per repository account per lane is still the server lease rule.** The worker-owned
264
+ worktree protects a human's checkout from the spawned worker; it does not make the edit lease
265
+ per-directory. A branch-aware edit lease with same-branch exclusion is a separate design.
266
+
267
+ `unlease` returns **your own** lease and deliberately cannot touch anybody else's. Breaking a stale one
268
+ is a separate, human verb: `remits-cli ticket force-unlease --ticket ID --reason "..."`.
269
+
270
+ ### Your account's process is binding, and it is already in your brief
271
+
272
+ An account declares how its work is done as Prompts with purpose `OPERATIONS`, selected per the ticket's
273
+ `workstream` (`support`, `sdlc`, `incident`, `release`, or whatever that organization calls its
274
+ processes) via `Prompt.category`, with an uncategorised one as the catch-all. The matching text is
275
+ **inlined verbatim into the brief** of every agent that works one of that account's tickets and is
276
+ binding on it — follow it even where it differs from how you would normally proceed, and if it conflicts
277
+ with the brief, follow the process and say so on the ticket.
278
+
279
+ You do not fetch it: if the brief has a "The process that governs this ticket" section, that is it. If
280
+ it says the account documents no process, the likely cause is that the prompt is **staged but not
281
+ committed** — an autonomous worker resolves the committed one, so it is never bound by a procedure
282
+ nobody has reviewed. The repo root also carries a generated `OPERATIONS.md`; that file is generated
283
+ from the prompt, so **edit the prompt, not the file**.
284
+
285
+ `workstream` is not `type`: a type classifies the request, a workstream names the procedure, and they
286
+ cross — a `defect` handled by incident response out of hours goes through the SDLC in the morning.
287
+
288
+ ### Some of that process is enforced, not requested
289
+
290
+ Part of an account's process can be **deterministic**. If your brief has a `## Rules you will be held to`
291
+ section, those rules are enforced by the platform at the write door — the verb is refused, not
292
+ discouraged.
293
+
294
+ - **Read that section before you act.** A refusal you were warned about costs one command; a refusal you
295
+ were not costs the run.
296
+ - **A refusal is an ANSWER.** The message names the rule and says what to do instead. Act on that
297
+ sentence. Do not retry the identical call, and do not go looking for another door — the rules govern the
298
+ DSL, the MCP tools and this CLI identically.
299
+ - **`[require]` rules mean a human.** You cannot acknowledge one: the platform decides "human" from the
300
+ absence of an agent run identity, which your commands always carry. Record what you found with
301
+ `ticket progress` and hand off.
302
+ - **`[warn]` rules let the call through and write a `policy` worklog entry.** Mention it in your handoff.
303
+ - **If you see "POLICY NOT ENFORCED"**, the account's rules could not be parsed. Do not read the absence
304
+ of a refusal as permission — say so on the ticket. It is a defect in the account's `OPERATIONS` prompt.
305
+
306
+ The same rules govern `components stage` and `components commit`. A `no-commit` rule is why
307
+ `remits-cli components commit` refuses rather than asks.
308
+
309
+ ### Seeing the queue as a human does
310
+
311
+ `remits-cli start` opens a browser control center showing the same facts you are acting on: which
312
+ agents are registered and what each is doing, which tickets are open, and which agent each is routed
313
+ to. An operator can route a ticket to a specific agent from there. It is a view, not a second system —
314
+ what it shows and what `remits-cli agent work` returns come from the same records.
315
+
316
+ ### Agent components are workers too
317
+
318
+ An Agent component that works support tickets must register itself in the same presence registry as
319
+ CLI sessions. That makes it visible in `cliAgents()` and `remits-cli agent map`, and lets
320
+ `dispatch(...)` / `ticket route` target it by the same `agentId` field:
321
+
322
+ ```groovy
323
+ def me = workerId() // component:Agent:34
324
+ registerWorker([agentId: me, label: 'Support Triage Agent'])
325
+ def work = workerWork([agentId: me, runId: threadGroupingId()])
326
+ workerStatus([agentId: me, ticketId: work.tickets[0]?.id, activity: 'investigating'])
327
+ ```
328
+
329
+ `workerWork(...)` is the component-side analogue of `remits-cli agent work` — the same code runs behind
330
+ both, so a component and a terminal session get the same answer to the same question. It reads tickets
331
+ routed to that worker, claims what it may start, sweeps eligible unrouted work when it is idle (reported
332
+ separately as `sweptTicketIds`), takes the repository edit lease when available, and returns `phase`,
333
+ `brief`, `briefFacts`, and `editLease`. `phase:'editing'` means the component may edit and stage;
334
+ `phase:'investigating'` means another worker holds the repo lease and the component must stay read-only.
335
+ When a component reaches a resting point, pass `agentId` to `complete(...)`, `ask(...)`, or
336
+ `release(...)` so the ticket verb frees the edit lease and drops its claim.
337
+
338
+ Route to a component by its **id** (`component:Agent:34`) — that is what `workerId()` returns and what
339
+ the component polls on. `ticket route --agent component:Agent:MyAgent` also works: the name is resolved
340
+ and the id form is what gets stored.
341
+
342
+ ### Moving a ticket through its lifecycle
343
+
344
+ **`remits-cli ticket` is the platform surface, and it is what you should reach for.** It reads and
345
+ writes the platform's own ticket record, so the same commands work on every Remits platform.
346
+
347
+ ```bash
348
+ remits-cli ticket read --ticket 22454 # the full record
349
+ remits-cli ticket accept --ticket 22454 # claim it before changing anything
350
+ remits-cli ticket progress --ticket 22454 --summary "Traced it to the posting Action" --category investigation
351
+ remits-cli ticket status --ticket 22454 --status in_progress
352
+ remits-cli ticket complete --ticket 22454 --resolution "What you found and did"
353
+ remits-cli ticket ask --ticket 22454 --question "Re-issue or skip?" --context "412 affected"
354
+ remits-cli ticket answer --ticket 22454 --message "Skip them." # alias: reply
355
+ remits-cli ticket message --ticket 22454 --message "We reproduced it; a fix is staged."
356
+ remits-cli ticket planning --ticket 22454 --board-stage ready_for_qa --blocked-by CAB-112
357
+ remits-cli ticket release --ticket 22454 # hand it back to the queue
358
+ ```
359
+
360
+ **See the whole queue before deciding your ticket is unique.** Alert-raised tickets arrive in
361
+ clusters, and the same root cause routinely appears under several different account names — so the
362
+ first useful question is usually "how many of these are one fix?", and you cannot ask it from a single
363
+ ticket.
364
+
365
+ ```bash
366
+ remits-cli ticket queue --account-id 49 # triage order, most urgent first
367
+ remits-cli ticket queue --account-id 49 --unrouted --status open
368
+ remits-cli ticket queue --account-id 49 --search "firestore index"
369
+ ```
370
+
371
+ The `repo` column is the account the **edit lease** excludes on, so it also tells you which of these
372
+ could be worked at the same time and which will serialize behind one another.
373
+
374
+ Found a second, unrelated problem while working yours? **File it rather than widening the ticket you
375
+ were given** — a ticket that describes two things cannot be closed by either fix.
376
+
377
+ ```bash
378
+ remits-cli ticket create --account-id 49 --subject "Vendor name lost on re-normalization" \
379
+ --type defect --priority medium --reference-id vendor-name-lost
380
+ ```
381
+
382
+ `--reference-id` makes it get-or-create, so a re-run — or another worker reaching the same conclusion
383
+ — reconciles onto the same ticket instead of filing a duplicate.
384
+
385
+ Marking and evidence, so the next reader does not repeat your work:
386
+
387
+ ```bash
388
+ remits-cli ticket tag --ticket 22454 --tags firestore-index,cluster-aug
389
+ remits-cli ticket artifact --ticket 22454 --type log --label "Failing query" --content "..."
390
+ remits-cli ticket note --ticket 22454 --message "Internal: same cause as 23465"
391
+ remits-cli ticket field --ticket 22454 --key customerReference --value CR-9182
392
+ ```
393
+
394
+ `note` is internal — the next worker and a reviewer see it, the requester does not. Use `message` for
395
+ anything the requester should read. `field` writes your **organization's own** field, outside the
396
+ platform's bounded planning slots.
397
+
398
+ **`message`, `answer` and `note` are separated by who reads the result, not by tone.** They are easy to
399
+ confuse and they do different things to the ticket:
400
+
401
+ | Command | Who reads it | What it does to the ticket |
402
+ |---|---|---|
403
+ | `ticket message --message "..."` | the requester | appends a public entry. **Does not send anything** |
404
+ | `ticket note --message "..."` | the next worker, a reviewer | appends an internal entry |
405
+ | `ticket answer --message "..."` | whoever asked | answers the open question and **re-routes**, so a fresh worker resumes |
406
+
407
+ `ticket reply` is an **alias of `answer`** — kept because every existing brief says it. Do not read it as
408
+ "reply to the customer"; that is `message`. (Some account tools spell the conversational verb `reply`,
409
+ which is exactly why this surface teaches `answer`.)
410
+
411
+ **Appending a message is not emailing anyone.** The record is deliberately vendor-agnostic; the account
412
+ owns the transport. To ask for something to actually go out:
413
+
414
+ ```bash
415
+ remits-cli ticket deliver --ticket 22454 --channel email --to ops@acme.test \
416
+ --subject "Waiting on you" --idempotency-key waiting-22454
417
+ remits-cli ticket deliveries --ticket 22454 # what is queued, and what already went
418
+ ```
419
+
420
+ That records an outbox request. An account-owned Rule or scheduled Action sends it and marks the result
421
+ (`ticket delivered --delivery-id ...` / `ticket delivery-failed --delivery-id ... --reason "..."`). Use
422
+ this when a ticket is waiting on somebody who is not watching the queue.
423
+
424
+ ### When a worker needs a decision from a human
425
+
426
+ There are **three** ways a run can end, not two, and the third is what stops an agent guessing:
427
+
428
+ | Outcome | Verb | Means |
429
+ |---|---|---|
430
+ | Done | `ticket complete --resolution` | finished, here is what I did |
431
+ | **Blocked on a choice** | `ticket ask --question` | I understand the work; the DECISION is not mine |
432
+
433
+ | Could not finish | `ticket status --status pending_review` + `ticket progress` | stuck, here is the hand-off |
434
+
435
+ `ask` puts the question on the ticket as an outbound message and parks it. The queue then shows it as
436
+ **awaiting a response** — distinct from "done, review me", which `pending_review` alone cannot express.
437
+
438
+ Someone answers with `remits-cli ticket answer --message "..."` (`reply` is an alias), or through an
439
+ operator Embeddable.
440
+ That **re-routes the ticket by default**, so the supervisor's next poll starts a **fresh worker whose
441
+ brief already contains the exchange**. Resumption costs nothing because a worker is one process per
442
+ ticket — there is no session to restore.
443
+
444
+ Ask when the choice is genuinely not yours: which of two behaviours is wanted, whether to touch live
445
+ data, an ambiguity in the request. Do not ask for reassurance about something you can determine
446
+ yourself, and ask **once**, with the options laid out.
447
+
448
+ Inside an autonomous worker `--ticket` defaults to the ticket that worker was launched for.
449
+
450
+ A refusal is an **answer**: "accept it first", "already assigned to someone else", "reopen it
451
+ first". Act on the sentence — do not go looking for another tool that lets you skip the step.
452
+
453
+ **`mcp_support_ticket` is an account convention, not a platform guarantee.** It belongs to one
454
+ particular System Account: it may not exist on the platform you are on, it scopes to its own
455
+ account, and it will refuse a ticket owned by a different one. Use it only when you are working in
456
+ that account and want its product-specific workflow on top of the record. It offers:
457
+ - `read` — always start here to load current state
458
+ - `accept` — claim the ticket so other agents do not work it concurrently
459
+ - `update_status` — move to `in_progress` or `pending_review`
460
+ - `complete` — resolve the ticket with a summary of what was done
461
+ - `release` — unassign if you cannot continue
462
+ - **Pulling a ticket by number is enough — you do NOT need to know its account first.** The `ticketId` IS the ticket's globally-unique anchor id, and `mcp_support_ticket` resolves the owning account from it for every action that operates on an existing ticket (`read`, `accept`, `update_status`, `complete`, `release`, `record_progress`, `add_artifact`, `get_attachment`). So when a user says "pull ticket 19463", just call `read` with `{ "action": "read", "ticketId": "19463" }` from any authenticated **prod** session (the ticket lives in prod data) — omit `accountId` entirely. The response returns the resolved `accountId`/`accountName` (and `implementationAccountId` when set); use those for any follow-up work. **Only `create` requires an explicit `accountId`** (a brand-new ticket has no anchor to resolve from). If a bare `ticketId` returns "Support ticket not found", double-check you are in `--data-mode prod`, then fall back to passing an explicit `accountId`.
463
+ - `read` can return `documentState:'missing_or_empty'` with `ticket.mirrorOnly:true` and `ticket.canMutate:false`. That means the support-ticket anchor exists and the queue row is real, but the backing Firestore `support_tickets/{ticketId}` document is missing or metadata-only. Treat this as a degraded ticket, not as "ticket not found"; run or request the System Account action `Restore Support Ticket Documents From Anchor Mirrors` for the owning account before lifecycle mutations. The restore recovers mirrored scalar fields, but document-only arrays such as alert snapshots, email threads, attachments, worklog entries, and artifacts may be lost.
464
+ - If a ticket is part of the request, manage the lifecycle proactively. Do not wait for the human user to remind you to read, accept, update, complete, or release it.
465
+
466
+ Ticket-routing context:
467
+ - `accountId` / `accountName` identify the account that owns the ticket.
468
+ - If present, `implementationAccountId` / `implementationAccountName` identify the owning `PLATFORM` or `PRODUCT` implementation context.
469
+ - Prefer `implementationAccountId` when choosing the working directory for a ticket, falling back to `accountId` when the platform/product repo is not available locally. Routing already uses that same ladder, so the repo you were routed through is usually the right one.
470
+ - For enhancement work, prefer the platform/product implementation context when deciding where code changes belong.
471
+ - For defect investigations, start from the owning ticket account, then move to the platform/product context if the root cause is in shared components.
472
+
473
+ Sandbox note:
474
+ - Every `remits-cli` command that reaches the Remits service (`auth`, `tools`, `tool`, `components`, `test`, `token`, and all of `ticket` / `agent`) needs outbound network access.
475
+ - **Inside an autonomous ticket worker this is already arranged** — the supervisor launches the worker with network enabled, in both the editing and the investigating phase. An investigating worker is restricted from *writing the repository*, never from *talking to the platform*: reading its ticket, running a diagnostic and recording a finding are the whole of what investigating means.
476
+ - Elsewhere, `ENOTFOUND`, `EAI_AGAIN`, `ECONNREFUSED`, `EPERM` or a bare exit 6 / `HTTP:000` from a sandboxed session means the command needs escalated permissions or must run outside the sandbox. Do not read it as "the platform is down" — check a second command before concluding anything about the service.
477
+
478
+ Use the same repo/context rules as other tools:
479
+ - If the ticket targets a `CLIENT` account, do not assume that client's repo is the implementation repo. Confirm the parent `PLATFORM` / `PRODUCT` relationship first.
480
+ - Read `~/.remits-cli/account-repos.json` before choosing which local repo to open.
481
+ - If the correct repo for the relevant account type exists locally, switch there, confirm/pull the relevant
482
+ branch when clean, and inspect `account-info.json` and `/components`.
483
+ - If the repo is not available locally, use `mcp_account_view`, `mcp_component_view`, and `mcp_component_grep`.