@remits/remits-cli 0.1.167 → 0.1.169

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.
package/lib/local-ai.js CHANGED
@@ -525,7 +525,7 @@ function help() {
525
525
  console.log('Decisions: {"content":"reply","toolCalls":[]} or {"content":null,"toolCalls":[{"name":"tool","arguments":{}}]}');
526
526
  }
527
527
 
528
- async function command({ subcommand, flags, context, root, axios, Client, WebSocket, defaultAgent = 'claude' }) {
528
+ async function command({ subcommand, flags, context, root, axios, Client, WebSocket, defaultAgent = 'claude', requestHeaders }) {
529
529
  if (!subcommand || flags.help || subcommand === 'help') return help();
530
530
  if (!['serve', 'workers', 'work', 'request', 'respond', 'status', 'sessions', 'release'].includes(subcommand)) throw new Error('Unknown ai subcommand');
531
531
  const agent = flags.agent || defaultAgent;
@@ -545,7 +545,7 @@ async function command({ subcommand, flags, context, root, axios, Client, WebSoc
545
545
  try {
546
546
  const result = await axios.post(baseUrl.replace(/\/+$/, '') + '/cli/ai',
547
547
  { ...payload, command: action, workerId: worker.workerId, accountId, dataMode },
548
- { headers: { Authorization: 'Bearer ' + session.token }, timeout: 15000, maxContentLength: MAX_BYTES, maxBodyLength: MAX_BYTES });
548
+ { headers: requestHeaders ? requestHeaders(session.token) : { Authorization: 'Bearer ' + session.token }, timeout: 15000, maxContentLength: MAX_BYTES, maxBodyLength: MAX_BYTES });
549
549
  if (!result.data?.success) throw new Error(result.data?.message || 'Local AI command failed');
550
550
  return result.data;
551
551
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remits/remits-cli",
3
- "version": "0.1.167",
3
+ "version": "0.1.169",
4
4
  "description": "Local CLI for auth, component sync, and live test execution against Remits",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -64,6 +64,9 @@ reference named after it.
64
64
  - **edit → stage → run, every time.** The platform executes whatever is in the staging cache at the
65
65
  moment a run starts. Edit a file, run a test without `remits-cli components stage`, and the test runs
66
66
  the OLD code. This is the single most common mistake. (`development-loop.md`)
67
+ - **Iterate on the failing cases; run the whole suite once before landing.** Waiting on test runs is most
68
+ of a slow build loop, not staging. After each edit run `test run --test X --failed` (only what did not
69
+ pass last time) or `--names "a|b"`, not the full suite. (`development-loop.md`)
67
70
  - **Staging and sync fail early on broken runtime-compiled Groovy.** Stage validates changed source in the
68
71
  active lane; sync validates changed/new compiled components before writing DB rows or branch variants.
69
72
  Agent files are Utility components internally. A stage refused BY COMPILE VALIDATION still wrote the
@@ -100,15 +100,41 @@ whole point is that it needs no supervision. Following the transcript is the way
100
100
  strictly better than a terminal would be: the output is structured, so it can be read as events
101
101
  rather than scraped back out of ANSI text.
102
102
 
103
- **No worker ever commits.** The brief forbids `git commit`/`git push` and nothing in the supervisor
104
- runs git — a worker leaves its changes in the working tree and says so on the ticket, for a human to
105
- review.
103
+ **Whether a worker lands is the account's choice, not the supervisor's.** Nothing in the supervisor runs
104
+ git. The brief states the account's landing profile: by default a person lands (the worker leaves changes in
105
+ the working tree and says so on the ticket); an account with `supportPolicy.landing: agent` lets the worker
106
+ land with `remits-cli components commit`, which re-checks the account's rules when it runs.
107
+
108
+ **Each worker gets its own credential.** For every child the supervisor mints a credential bound to that
109
+ attempt (agent + ticket + run) and revokes it when the child exits; the child authenticates with it
110
+ (`REMITS_AGENT_TOKEN`). The platform therefore knows which participant is calling from the credential, so a
111
+ missing or wrong identity header cannot make a worker a human or another agent. Against an older platform
112
+ the worker simply uses the supervisor's session, as before. Each poll also reports, per running child, when
113
+ it last produced output and its last progress line — separate from the supervisor's own heartbeat, so
114
+ `agent map` can tell a connected supervisor from a child that is actually producing work.
106
115
 
107
116
  ### Resolving which agent a command means
108
117
 
109
- Order: `--agent-id`, then `REMITS_AGENT_ID`, then the single agent registered for this working
110
- directory. With several registered and no way to tell them apart, the command **fails and lists the
111
- candidates** rather than guessing — routing work to the wrong tab is silent, and a message is not.
118
+ For agent lifecycle commands, explicit `--agent-id` or `REMITS_AGENT_ID` wins; otherwise the CLI looks
119
+ for the registration anchored to this terminal's owning process. It never borrows another terminal's
120
+ registration just because it shares the checkout.
121
+
122
+ For platform request attribution, set `REMITS_AGENT_ID` for the agent's shell or pass `--agent-id` on
123
+ each command. Both reach the shared HTTP client, including status, tools and local-AI requests.
124
+ Supervised ticket workers receive `REMITS_AGENT_ID`, `REMITS_WORKER_RUN_ID` and
125
+ `REMITS_SUPPORT_TICKET_ID` automatically. A manually named interactive agent does not need to register
126
+ for ticket routing just to declare its identity. Registered roles and labels are accepted only from a
127
+ registration owned by the authenticated user. `--local-agent` / `REMITS_LOCAL_AGENT_ID` remains a local
128
+ folder choice and does not declare platform agent identity.
129
+
130
+ System → Activity groups these requests under the authenticated user, with separate agent/run sessions
131
+ and source filters. Undeclared CLI requests are labeled **CLI · no declared agent**, never assumed to be
132
+ human. Browser sessions can also be automated. Only platform requests are observed, not local editing
133
+ or reasoning between commands. Creating a preview token does not mark every subsequent browser user
134
+ of that token as an agent.
135
+
136
+ `ticket route --agent <target>` names the destination. Its legacy `--agent-id` alias also names the
137
+ destination, not the caller; use `REMITS_AGENT_ID` to identify the agent making a routing request.
112
138
 
113
139
  ### Which agent gets a ticket
114
140
 
@@ -218,13 +218,21 @@ remits-cli agent register [--label NAME] [--data-mode test|prod] [--account-id I
218
218
  remits-cli agent work [--wait SECONDS] [--json]
219
219
  remits-cli agent status [--state idle|working|paused] [--ticket ID] [--activity "..."] [--step "..."]
220
220
  remits-cli agent list [--account-id ID] [--json]
221
- remits-cli agent map [--account-ids 1,4] [--json] # who is EDITING which repository, from which checkout/branch/staging lane
221
+ remits-cli agent map [--account-ids 1,4] [--json] # who is EDITING which repository, from which checkout/branch/staging lane; first line = live work vs stored (staged/last-reported) facts
222
+ remits-cli capabilities [--json] # collaboration features this platform supports, verbs + how they settle, refusal codes
223
+ remits-cli policy explain --point commit|land|stage|complete|deliver|... [--as human|agent] [--workstream W] [--components type:name[:category],...] [--json] # what the account rules would decide, and why; nothing applied
224
+ remits-cli evidence [--limit N] [--json] | evidence flush # your world-stamped trail; inside a ticket attempt it is also published to the ticket as receipts
222
225
  remits-cli agent inspect [--account-id ID] [--scope self|children] [--user-id ID|--user-email EMAIL] [--limit N] [--json]
223
226
  remits-cli activity inspect [--account-id ID] [--scope self|children|related] [--workspace NAME] [--branch NAME] [--workstream ID] [--user-id ID|--user-email EMAIL] [--limit N] [--json]
224
227
  remits-cli workstream status [--workstream ID|--workspace NAME] [--json]
225
228
  remits-cli agent release
226
- remits-cli ticket read|accept|status|progress|complete|release|reopen|assign|planning --ticket ID [--status S] [--resolution "..."] [--summary "..."] [--category C] [--assignee EMAIL] [--workstream VALUE] [--planned-in VALUE] [--board-stage VALUE] [--rank N] [--size VALUE] [--blocked-by VALUE] [--notes "..."]
227
- remits-cli ticket lease|unlease|force-unlease --ticket ID [--reason "..."] # force-unlease breaks SOMEBODY ELSE'S lease; operator only
229
+ remits-cli ticket read|accept|status|progress|complete|cancel|release|reopen|assign|planning --ticket ID [--status S] [--resolution "..."] [--reason "..."] [--summary "..."] [--category C] [--assignee EMAIL] [--workstream VALUE] [--planned-in VALUE] [--board-stage VALUE] [--rank N] [--size VALUE] [--blocked-by VALUE] [--notes "..."]
230
+ remits-cli ticket cancel --ticket ID --reason "..." [--expected-revision N] # close with disposition cancelled; history kept; stops worker
231
+ remits-cli ticket reset-budget --ticket ID # OPERATOR: restore run and spend budget after review
232
+ remits-cli ticket redeliver-events --ticket ID # OPERATOR: re-dispatch undelivered lifecycle events
233
+ remits-cli ticket lease|unlease|force-unlease --ticket ID [--reason "..."] # force-unlease breaks SOMEBODY ELSE'S lease; operator only. 503 code=unavailable = holder unknown: investigate, retry
234
+ remits-cli ticket handoff --ticket ID --summary "..." [--next-step "..."] [--findings "a|b"] [--artifact-refs a,b] [--evidence-refs r1,r2] [--waiting-on X] [--needs cap] [--workstream W] [--exclude] [--expected-revision N]
235
+ remits-cli ticket decide|complete ... [--expected-revision N] # refused with code=stale_revision (nothing written) if the ticket moved past N
228
236
  remits-cli ticket where --ticket ID # WHERE this work is: repo account, checkout, branch, staging lane, live lease + holder's location, claim, your phase
229
237
  remits-cli ticket ask --ticket ID --question "..." [--context "..."] [--to WHO] # park on a human decision
230
238
  remits-cli ticket answer --ticket ID --message "..." [--route false] # answer it (alias: reply); re-routes by default
@@ -265,7 +273,7 @@ remits-cli components branch <name> --copy-to <newBranch> [--dry-run] [--force]
265
273
  remits-cli components branch <name> --subscribe <accountId> [--parent-account <id>] [--domain <host>] [--dry-run] [--confirm-primary-edge] # make an account resolve this branch
266
274
  remits-cli components branch <name> --unsubscribe <accountId> # return that account to trunk
267
275
  remits-cli components branch <name> --retire [--force] # delete the branch's overlays
268
- remits-cli test run --test <id|name> [--branch <stagingScope>] [--names "a|b"] [--watch true|false] [--wait true|false] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>] [--label <text>] [--param name=value ...] [--repeat N] [--parallel N] [--json]
276
+ remits-cli test run --test <id|name> [--candidate <laneContentHash>] [--branch <stagingScope>] [--names "a|b" | --failed] [--watch true|false] [--wait true|false] [--data-mode test|prod] [--as-account <ID>] [--variant-branch <name|none>] [--label <text>] [--param name=value ...] [--repeat N] [--parallel N] [--json]
269
277
  remits-cli test status --task-id <taskId|groupId> [--branch <stagingScope>] [--data-mode test|prod] [--json] # falls back to the DURABLE run record once the live status expires
270
278
  remits-cli test runs [--test <id|name>] [--limit 20] [--compare] [--all-lanes] [--as-account <ID>] [--json] # durable run history: pass counts, live AI cost, lane, content hash
271
279
  remits-cli test compare --base <taskId> --head <taskId> [--json] # per-case improved / regressed / changed, cost and world deltas
@@ -281,6 +289,10 @@ remits-cli corpus consistency --corpus <name> --case <caseKey> [--json]
281
289
  remits-cli corpus retire --corpus <name> --case <caseKey> [--case ...] [--restore] [--json] # drop a case from future runs; past measurements stay readable
282
290
  ```
283
291
 
292
+ `--candidate <hash>` names the staged content you mean to test (the lane content hash `components status`
293
+ reports); if another process restaged or the lane expired, the run is refused with `stale_revision` instead of
294
+ testing different source. It binds the start of the run, not every later lookup.
295
+
284
296
  `test run` starts a server-side task immediately. By default the CLI process polls until the task is
285
297
  terminal; `--watch false` disables websocket progress streaming but still waits. Pass `--wait false` to
286
298
  return after launch with the task id. A single run with `--names "a|b|c"` selects cases into one suite task,
@@ -589,6 +601,10 @@ For tests specifically:
589
601
  - `--names` is `|`-delimited and may be repeated. A selector containing commas matches a case with that
590
602
  exact name first, else its comma-separated parts; an unmatched selector fails the run instead of
591
603
  reporting zero cases as success.
604
+ - `--failed` re-runs only the cases that did not pass in YOUR last finished run of the suite on this git
605
+ branch and staging workspace (a `--parallel` run is read as its whole group; the launch lane is preferred,
606
+ else the lane the suite recorded in). The launch prints the run it read and the cases. "Nothing failed"
607
+ starts nothing and exits 0; no earlier run is refused. Not combinable with `--names`.
592
608
  - `--as-account <ID>` runs AS a descendant subscriber so its edge selects the component branch
593
609
  (*"what does customer X get?"*); `--variant-branch <name>` probes a branch from any checkout
594
610
  (*"what does branch Y look like?"*), and `--variant-branch none` forces production/subscription semantics.
@@ -421,9 +421,17 @@ This applies to:
421
421
  ```bash
422
422
  remits-cli test run --test <TEST_ID_OR_NAME>
423
423
  remits-cli test run --test "Invoice Tests" --names "specific test case"
424
+ remits-cli test run --test "Invoice Tests" --failed
424
425
  remits-cli test status --task-id <TASK_ID>
425
426
  ```
426
427
 
428
+ **Iterate narrowly, and run the whole suite once before landing.** Waiting on test runs is most of
429
+ an hour-long build loop, not staging. A suite whose cases run one after another re-pays every case's
430
+ fixture work on every rerun. Run the suite once to see what fails. After each edit, run `--failed` (the
431
+ cases that did not pass in your last finished run of this suite, on this git branch and staging
432
+ workspace) or `--names` for the cases your change touches. Then run the full suite once before landing.
433
+ A full rerun after every edit, with every case passing, is the most common way an agent spends an hour.
434
+
427
435
  Tests run on the platform against your staged snapshot. They stream results in real-time. **Read the
428
436
  `World:` block printed before the run** — host, execution account, data lane, component world, staging lane
429
437
  id + content hash, platform sync vs local HEAD. If it is not the world you meant, stop: the result will be
@@ -20,6 +20,7 @@
20
20
  - [Investigation Strategy](#investigation-strategy)
21
21
  - [Presenting Findings](#presenting-findings)
22
22
  - [Verifying a Production Issue Fix](#verifying-a-production-issue-fix)
23
+ - [Missing or deleted business documents](#missing-or-deleted-business-documents)
23
24
 
24
25
  ## The Investigation Model
25
26
 
@@ -281,3 +282,23 @@ When a bug is reported from production, use this pattern:
281
282
  6. If useful, create or update a dedicated embeddable "playground" in test mode to reproduce the scenario in a controlled way.
282
283
 
283
284
  Do not move production customer data into another account's test collection as a routine verification strategy. If you cannot verify with a Test component, Playwright flow, or controlled test-mode embeddable, explain the gap clearly instead of improvising with live production validation.
285
+
286
+ ## Missing or deleted business documents
287
+
288
+ `PROCESSED` is the MySQL object's processing outcome, not a guarantee that its Firestore document
289
+ still exists. Read `documentEvidence` from `mcp_object_activity` or a current object `mcp_record_view`:
290
+ it includes the resolved path, observed data lane, presence, recent revision metadata, and the latest
291
+ Object `Document Delete` receipt when available. `state` is `present`, `missing`, `deleted`, `unknown`
292
+ (read failed), or `out_of_scope`. `deleted` requires retained deletion evidence; absence alone is not proof.
293
+ A `laneMismatch` means the requested read lane differs from the object's persisted lane.
294
+
295
+ `Document Delete` ObjectLog entries expose structured `documentOperation` (outcome, document path,
296
+ user/agent, component/action, node, data lane, scope, run and trace ids). The operation's
297
+ `threadGroupingId` can differ from `request.traceId`; both are retained for correlation. Failed deletes
298
+ never generate success revisions or downstream deletion messages. The processing status remains intact.
299
+
300
+ To read retained history without a live parent document, call `mcp_firestore_search` with one
301
+ `targetAccountId`, `collection`, `documentId`, and `includeHistory: true`. Use `historyLimit` (default 10,
302
+ maximum 100), `includeRevisionSnapshots: true` for the pre-delete business snapshot, or `revisionId`
303
+ for one exact revision. History is explicitly truncated when more rows exist. Legacy revisions may
304
+ identify the user and time without the newer component/request provenance; do not invent missing facts.
@@ -26,6 +26,7 @@
26
26
  - [Moving a ticket through its lifecycle](#moving-a-ticket-through-its-lifecycle)
27
27
  - [When a worker needs a decision from a human](#when-a-worker-needs-a-decision-from-a-human)
28
28
  - [Approvals, reviews and handing work on](#approvals-reviews-and-handing-work-on)
29
+ - [Coming back later, waiting on other tickets, and cost](#coming-back-later-waiting-on-other-tickets-and-cost)
29
30
 
30
31
  ## Support Ticket Mental Model
31
32
 
@@ -346,19 +347,26 @@ discouraged.
346
347
  - **A refusal is an ANSWER.** The message names the rule and says what to do instead. Act on that
347
348
  sentence. Do not retry the identical call, and do not go looking for another door — the rules govern the
348
349
  DSL, the MCP tools and this CLI identically.
349
- - **`[require]` rules mean a human.** You cannot acknowledge one: the platform decides "human" from the
350
- absence of an agent run identity, which your commands always carry. Record what you found with
351
- `ticket progress` and hand off.
350
+ - **`[require]` rules mean a human.** You cannot acknowledge one. A supervised worker authenticates with a
351
+ credential bound to its own attempt, so the platform knows it is an agent from the credential itself —
352
+ omitting identity headers does not change that. Record what you found with `ticket progress` and hand off.
352
353
  - **`[warn]` rules let the call through and write a `policy` worklog entry.** Mention it in your handoff.
353
354
  - **Rules can name your role** (`"when":{"role":"triage"}`, or `"role":{"not":["release_manager"]}` for
354
355
  "only release managers"). Your role is the one your session registered with; you cannot change it from a
355
356
  worker, and passing one on a call does nothing. A refusal naming your role means hand off to the role that
356
357
  may.
357
- - **If you see "POLICY NOT ENFORCED"**, the account's rules could not be parsed. Do not read the absence
358
- of a refusal as permission — say so on the ticket. It is a defect in the account's `OPERATIONS` prompt.
359
-
360
- The same rules govern `components stage` and `components commit`. A `no-commit` rule is why
361
- `remits-cli components commit` refuses rather than asks.
358
+ - **If you see a policy problem banner**, part of the account's rules could not be parsed. The rules listed
359
+ under it are still enforced (a broken replacement keeps its last valid version); do not read the absence
360
+ of a refusal as permission for the rest — say so on the ticket. **`policy_unavailable`** means a published
361
+ policy could not be read at all: landing, committing, completing and sending are refused until it can be.
362
+ Retry later; do not look for another door.
363
+ - **Check before you act**: `remits-cli policy explain --point commit --as agent` says what the rules would
364
+ decide and which rules apply, without applying anything.
365
+
366
+ The same rules govern `components stage` and `components commit`. Whether you may land at all is the
367
+ account's **landing profile**, stated in your brief: by default a person lands (leave changes in the working
368
+ tree and describe them); an account that sets `supportPolicy.landing: agent` lets you land with
369
+ `remits-cli components commit`, which re-checks the rules when it runs. A `no-commit` rule always wins.
362
370
 
363
371
  ### Roles and who may launch them
364
372
 
@@ -432,6 +440,7 @@ remits-cli ticket accept --ticket 22454 # claim it before changing anyt
432
440
  remits-cli ticket progress --ticket 22454 --summary "Traced it to the posting Action" --category investigation
433
441
  remits-cli ticket status --ticket 22454 --status in_progress
434
442
  remits-cli ticket complete --ticket 22454 --resolution "What you found and did"
443
+ remits-cli ticket cancel --ticket 22454 --reason "Customer withdrew request" # outcome no longer wanted; stops running worker
435
444
  remits-cli ticket ask --ticket 22454 --question "Re-issue or skip?" --context "412 affected"
436
445
  remits-cli ticket answer --ticket 22454 --message "Skip them." # alias: reply
437
446
  remits-cli ticket message --ticket 22454 --message "We reproduced it; a fix is staged."
@@ -589,9 +598,33 @@ remits-cli ticket handoff --summary "fix staged in ticket-19463; needs independe
589
598
  ```
590
599
 
591
600
  The handoff is queued even if nobody is online, `--exclude` keeps you off it (independent review needs
592
- that), and your own exit will not undo it. A reviewer decides with `ticket decide --request R3 --decision
601
+ that), and your own exit will not undo it. It is a checkpoint: `--findings`, `--next-step`,
602
+ `--artifact-refs`, `--evidence-refs`, `--waiting-on` and `--needs` land in the next worker's brief, so give
603
+ durable references (artifact ids, receipt ids), never local paths. Add `--expected-revision N` (the revision
604
+ you read) to `handoff`, `decide` or `complete` and a ticket that moved on is refused with `stale_revision`
605
+ instead of being overwritten. Your evidence trail is published to the ticket as receipts automatically;
606
+ `remits-cli evidence flush` sends anything still queued. A reviewer decides with `ticket decide --request R3 --decision
593
607
  approve|changes|reject --revision <rev>`; "changes" sends the work back with the reviewer's note.
594
608
 
609
+ An approval or review can need several people: `request-approval ... --min-approvals 2` stays open until two
610
+ different eligible deciders approve; one rejection settles it.
611
+
612
+ ### Coming back later, waiting on other tickets, and cost
613
+
614
+ ```bash
615
+ remits-cli ticket wake --in 1440 --reason "requester promised logs tomorrow" # or --at <ISO time>; --cancel
616
+ remits-cli ticket depends-on --tickets 19463,19470 # no value clears
617
+ remits-cli ticket cost --usd 1.25 # usually reported for you
618
+ ```
619
+
620
+ - **`wake` instead of waiting.** Never sleep or poll inside a run. Record what you are waiting for, set a
621
+ wake-up, and finish the run; when the time comes a fresh worker gets the ticket with `resumed for: timer`
622
+ and your reason in its brief.
623
+ - **`depends-on`** when this ticket genuinely cannot start until others are resolved. The sweep holds it
624
+ until they are; nothing else has to remember to release it.
625
+ - **Cost** is reported automatically by `agent serve` from each worker's transcript. An account can cap a
626
+ ticket's spend; when it is reached, starts are refused until a person resets the budget.
627
+
595
628
  **`mcp_support_ticket` is an account convention, not a platform guarantee.** It belongs to one
596
629
  particular System Account: it may not exist on the platform you are on, it scopes to its own
597
630
  account, and it will refuse a ticket owned by a different one. Use it only when you are working in
@@ -141,6 +141,29 @@ The account-graph and user surface: the middle ground between `account-info.json
141
141
  compactly, because it is read into your context every session) and `mcp_account_view` (everything about one
142
142
  account).
143
143
 
144
+ **Recurring schedules:** `action_schedules` lists the target account's Action schedules;
145
+ `action_schedule_update` upserts by `targetAccountId` + `actionId` (or an account-scoped `scheduleId`);
146
+ `action_schedule_delete` removes that schedule and its cloud job. Updates accept `cron`, `timeZone`,
147
+ `enabled` and `dryRun`. Each run executes as the schedule's owner (`ownerUserId` in reads), who defaults to
148
+ whoever first saves it and is rechecked at every run. `enabled:false` pauses; `enabled:true` resumes while preserving omitted settings.
149
+ `regression_schedule` reads the per-account regression settings and latest report;
150
+ `regression_schedule_update` accepts the same scheduling fields plus `resultAction`, `suiteConcurrency`
151
+ (1–16) and explicit `ownerUserId`. Existing owners are preserved. Only admins may assign another owner;
152
+ an eligible caller may take ownership themselves.
153
+
154
+ Both use five-field cron and IANA time zones, default UTC. Existing Action schedules retain UTC.
155
+ All writes require production mode; `dryRun:true` validates from either lane without writing or creating
156
+ a cloud job. Action jobs resolve committed enabled `job:true` Actions on the target account; regression
157
+ suites run committed source in test data. Optional `includeSchedulerStatus:true` on reads reports cloud
158
+ registration/next delivery/last transport status; this does not prove Action or regression success.
159
+ An empty regression update does not repair registration: explicitly pass cron/timeZone/enabled.
160
+
161
+ ```bash
162
+ remits-cli tool --name mcp_account_user_admin --data-mode test --input '{"action":"action_schedule_update","targetAccountId":4655,"actionId":21,"cron":"0 1 * * *","timeZone":"America/New_York","enabled":true,"dryRun":true}'
163
+ ```
164
+
165
+ Inspect the preview, then omit `dryRun` and use `--data-mode prod` when authorized to save the schedule.
166
+
144
167
  - `action: 'hierarchy'` (default) — the descendant tree trimmed to `depth` (1-10, default 2; nodes cut off
145
168
  are marked `truncated` and still report their child count) and/or the anchored ancestor chain plus every
146
169
  edge (`direction: 'down'|'up'|'both'`). Nodes include `testAccount`. **This is how you get the deep tree
@@ -299,6 +322,11 @@ evidence at all, is in `investigation.md` → *HTTP audits*.
299
322
  remits-cli tool --name "mcp_firestore_search" --input '{"accountId": 37, "collection": "http-audits/http-audits-2026-05/entries", "filters": [{"field": "direction", "operation": "EQUALS", "value": "OUTBOUND"}, {"field": "request.path", "operation": "EQUALS", "value": "/api/orders"}, {"field": "success", "operation": "EQUALS", "value": false}], "sort": [{"field": "timestamp", "direction": "DESC"}], "pagination": {"limit": 10}}' --data-mode prod
300
323
  ```
301
324
 
325
+ Exact-account history reads also accept `includeHistory` (boolean), `historyLimit` (1–100, default 10),
326
+ `includeRevisionSnapshots` (boolean, default false), and `revisionId` (exact Firestore revision id).
327
+ These require one `targetAccountId`, a collection name and `documentId`. They return `documentEvidence`
328
+ even when the live document is deleted. A normal exact-id miss also returns bounded evidence.
329
+
302
330
  ### `mcp_firestore_patch`
303
331
  Guarded exact-document Firestore patch tool for bounded repairs. It defaults to `dryRun:true` and refuses broad
304
332
  updates, wildcard collections, delete/remove operations, protected identity fields, and `_lastModified*` audit
@@ -329,7 +357,9 @@ Response fields include `dataMode`, `accountId`, `collection`, `documentId`, `dr
329
357
  `diff`. Switch to `"dryRun":false` only after the diff and preconditions are exactly what you intended.
330
358
 
331
359
  ### `mcp_object_activity`
332
- Object lifecycle timeline — metadata + recent activity.
360
+ Object lifecycle timeline — metadata + recent activity. Object metadata includes `documentEvidence`
361
+ with path, current presence/data lane, recent revisions and latest deletion receipt. Document Delete
362
+ activity entries include structured `documentOperation` evidence; PROCESSED is independent of document presence.
333
363
 
334
364
  | Parameter | Required | Description |
335
365
  |-----------|----------|-------------|