@datarails-ai/dr-cli 0.0.1-locked → 1.6.71
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/README.md +608 -0
- package/dist/chunk-DWMCBGQH.js +242 -0
- package/dist/cli.js +64468 -0
- package/dist/kb-6UWI7Q32.js +21 -0
- package/dist/scopes-map.json +4412 -0
- package/dist/skills/connector-sandbox/SKILL.md +95 -0
- package/dist/skills/datarails-connect-workato/SKILL.md +140 -0
- package/dist/skills/datarails-connectors-onboard-connector/SKILL.md +813 -0
- package/dist/skills/dr-cli/SKILL.md +659 -0
- package/dist/skills/dr-cli/reference/admin.md +796 -0
- package/dist/skills/dr-cli/reference/budget-builder.md +151 -0
- package/dist/skills/dr-cli/reference/calc-vals.md +73 -0
- package/dist/skills/dr-cli/reference/calcmodels.md +76 -0
- package/dist/skills/dr-cli/reference/cash.md +61 -0
- package/dist/skills/dr-cli/reference/dashboards-widgets.md +85 -0
- package/dist/skills/dr-cli/reference/datamappers.md +150 -0
- package/dist/skills/dr-cli/reference/desk.md +472 -0
- package/dist/skills/dr-cli/reference/filebox-documents.md +373 -0
- package/dist/skills/dr-cli/reference/forecast.md +51 -0
- package/dist/skills/dr-cli/reference/integrations.md +57 -0
- package/dist/skills/dr-cli/reference/lookups.md +168 -0
- package/dist/skills/dr-cli/reference/metrics.md +512 -0
- package/dist/skills/dr-cli/reference/multi-product.md +180 -0
- package/dist/skills/dr-cli/reference/reconciliation-mec.md +76 -0
- package/dist/skills/dr-cli/reference/rh-config.md +84 -0
- package/dist/skills/dr-cli/reference/safety.md +143 -0
- package/dist/skills/dr-cli/reference/spend-control.md +206 -0
- package/dist/skills/dr-cli/reference/templates.md +163 -0
- package/dist/skills/dr-cli/reference/workflows.md +137 -0
- package/dist/skills/dr-error-enrichment/SKILL.md +80 -0
- package/dist/skills/dr-error-enrichment/reference/core.md +257 -0
- package/dist/skills/dr-error-enrichment/reference/groundcover.md +84 -0
- package/dist/skills/dr-error-enrichment/reference/jira-bugs.md +219 -0
- package/dist/skills/dr-error-enrichment/reference/safety-backup.md +122 -0
- package/dist/skills/dr-error-enrichment-author/SKILL.md +219 -0
- package/dist/skills/dr-error-enrichment-improve/SKILL.md +218 -0
- package/dist/skills/dr-error-enrichment-maintain/SKILL.md +237 -0
- package/dist/skills/dr-error-enrichment-reconcile/SKILL.md +167 -0
- package/dist/skills/dr-filebox-api-setup/SKILL.md +170 -0
- package/dist/skills/dr-switch-user/SKILL.md +395 -0
- package/dist/skills/dr-validate/SKILL.md +392 -0
- package/dist/skills/dr-validate/report-template.html +950 -0
- package/dist/skills/fx-conversion-matrix/SKILL.md +763 -0
- package/dist/skills/fx-conversion-matrix/scripts/_args.mjs +52 -0
- package/dist/skills/fx-conversion-matrix/scripts/_xlsx.mjs +241 -0
- package/dist/skills/fx-conversion-matrix/scripts/build-matrix.mjs +366 -0
- package/dist/skills/fx-conversion-matrix/scripts/gen-mappers.mjs +171 -0
- package/dist/skills/fx-conversion-matrix/scripts/rates-to-xlsx.mjs +172 -0
- package/dist/skills/fx-onboard/SKILL.md +325 -0
- package/dist/skills/mec-table-validation/SKILL.md +383 -0
- package/dist/skills/plugins.json +24 -0
- package/dist/skills/validate-workato-migration/SKILL.md +167 -0
- package/dist/skills/validate-workato-migration/reference/gotchas.md +16 -0
- package/dist/skills/validate-workato-migration/reference/staging-orgs.md +26 -0
- package/dist/skills/validate-workato-migration/scripts/compare_files.py +255 -0
- package/dist/zendeskKb-JPSHVOPH.js +243 -0
- package/package.json +56 -9
- package/scripts/postinstall.mjs +91 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Spend Control
|
|
2
|
+
|
|
3
|
+
Datarails Spend Control is its own FinanceOS application (vendor contracts, spend
|
|
4
|
+
tracking, alerts), not part of Planning.
|
|
5
|
+
|
|
6
|
+
- `spend-control billing-frequencies list` — catalog of billing-frequency UUIDs.
|
|
7
|
+
- `spend-control files upload <paths…> [--is-additional] [--json]` — upload
|
|
8
|
+
documents and get back `file_id`s **without running AI extraction**. Use this
|
|
9
|
+
when the contract field values are already known: pass the ids as `file_ids` in
|
|
10
|
+
a `contracts bulk-create` draft or `contracts update` body. One request per file
|
|
11
|
+
(the endpoint takes one file), and deliberately **no retry** — a re-sent upload
|
|
12
|
+
creates a second blob and a second record that nothing references and the
|
|
13
|
+
operator cannot see. On partial failure it exits non-zero naming the failed
|
|
14
|
+
paths; re-run with only those. `--json` emits `{ uploaded, failed }` so a caller
|
|
15
|
+
gets the failed paths machine-readably rather than from stderr.
|
|
16
|
+
- `spend-control files list <contract_id> [--all] [--json]` — the files attached
|
|
17
|
+
to a contract. The table omits the download URL on purpose: every read enriches
|
|
18
|
+
`file_path` into a presigned URL that grants access on its own for ~1h, so a
|
|
19
|
+
listing would print one credential per row. `--json` does include them.
|
|
20
|
+
- `spend-control files get <file_id> [--json]` — one file's metadata plus its
|
|
21
|
+
presigned download URL. The endpoint is POST and named `download`, but it
|
|
22
|
+
returns **metadata, not bytes** — fetch the URL yourself (e.g. `curl -o`) to get
|
|
23
|
+
the content.
|
|
24
|
+
- `spend-control files delete <file_ids…> [--from <file>]` — two-step,
|
|
25
|
+
**permanent**: removes the blob and the record. The service does **not** check
|
|
26
|
+
whether a contract still references the file, so deleting one that a contract's
|
|
27
|
+
`file_ids` points at leaves a dangling reference. Tier A on production tenants.
|
|
28
|
+
- `spend-control contracts list [--all] [--json] [filters…]` — **start here.**
|
|
29
|
+
Every mutation below needs a contract UUID and this is the only way to get one.
|
|
30
|
+
Filters: `--search <vendor>` (case-insensitive), `--status`, `--payment-terms`,
|
|
31
|
+
`--payment-method`, `--vendor-id`, `--owner-id`, `--currency`, `--external-id`,
|
|
32
|
+
`--is-archived <true|false>`, `--start-date-from`, `--end-date-to`,
|
|
33
|
+
`--order-by`, `--page`, `--size`. Comma-separate multi-value filters
|
|
34
|
+
(`--status active,expired`). To sort descending write `--order-by value:desc` —
|
|
35
|
+
**not** `--order-by -value`, which the argument parser reads as the `-v` flag
|
|
36
|
+
and answers with the version string instead of a listing. (`--order-by=-value`,
|
|
37
|
+
with the `=`, also works.) Statuses are **lowercase**: `active`,
|
|
38
|
+
`pending_renewal`, `expired`, `upcoming`, `cancelled`.
|
|
39
|
+
The server caps `--size` at 100 — use `--all` to walk every page (progress goes
|
|
40
|
+
to stderr, so `--json --all | jq` stays clean). Archived contracts are included
|
|
41
|
+
by default and flagged in the ARCHIVED column; pass `--is-archived false` to
|
|
42
|
+
exclude them. `--currency` filters on the contract's own currency;
|
|
43
|
+
`--convert-to` converts the monetary fields server-side and filters nothing.
|
|
44
|
+
- `spend-control contracts get <id> [--convert-to <code>] [--json]` — one contract.
|
|
45
|
+
- `spend-control contracts archive <ids…> [--from <file>]` — two-step; sets
|
|
46
|
+
`is_archived=true` on each id. Reversible with `restore`. Takes ids space- or
|
|
47
|
+
comma-separated, or one per line via `--from` (`#` comments and blanks are
|
|
48
|
+
skipped, so a `list` capture can be hand-edited). Each id is attempted even if
|
|
49
|
+
an earlier one fails; the summary prints `unprocessed ids:` for a retry, and the
|
|
50
|
+
command exits non-zero if any failed.
|
|
51
|
+
- `spend-control contracts restore <ids…> [--from <file>]` — inverse of `archive`.
|
|
52
|
+
- `spend-control contracts delete <ids…> [--from <file>]` — two-step, **permanent**:
|
|
53
|
+
drops the contract, its files in blob storage, and its alerts. No undo, no
|
|
54
|
+
tombstone. Prefer `archive` for cleanup you might want to reverse. On a
|
|
55
|
+
production tenant this needs the interactive word confirmation on top of the
|
|
56
|
+
token (Tier A), so it can't be completed headlessly there.
|
|
57
|
+
- `spend-control contracts extract-from-files <files…> [--default key=value]…
|
|
58
|
+
[-o <path>] [--poll-interval <s>] [--poll-timeout <s>] [--no-async] [--retry <n>]`
|
|
59
|
+
— PDF/DOCX/PNG/JPG → AI-extracted draft JSON. 25 MB per file; magic bytes are
|
|
60
|
+
checked locally before anything uploads. Returned file_ids don't expire, so
|
|
61
|
+
editing the draft over hours/days before create is safe.
|
|
62
|
+
**Async by default:** one upload request per file against the v2 endpoint, then
|
|
63
|
+
extraction is polled. Nothing is capped at 10 files and no request waits on AI,
|
|
64
|
+
so the gateway timeouts the synchronous path produced are structurally absent.
|
|
65
|
+
`--poll-interval` (default 2s) and `--poll-timeout` (default 10m) tune the wait;
|
|
66
|
+
the whole-command `--timeout` defaults to `--poll-timeout` plus 15s per file for
|
|
67
|
+
the uploads, so raising `--poll-timeout` actually takes effect instead of being
|
|
68
|
+
capped by the command deadline.
|
|
69
|
+
Whenever a file does not finish, the draft still carries whatever did, and the
|
|
70
|
+
reason is specific: `--poll-timeout` expired, the command budget expired, or the
|
|
71
|
+
status call itself failed. Each names the `contract_file_id` and says to re-check
|
|
72
|
+
rather than re-upload — a re-upload pays for the extraction twice.
|
|
73
|
+
**Partial success (both paths):** a file that fails does not discard the ones
|
|
74
|
+
that succeeded. The draft is written with what worked, a per-file table goes to
|
|
75
|
+
stderr, and the command exits non-zero naming the failures. Re-run with ONLY
|
|
76
|
+
those paths and merge the drafts — re-sending a file that already succeeded
|
|
77
|
+
uploads and re-extracts it again (fresh AI cost, orphaned file_id). A file whose
|
|
78
|
+
AI step FAILED was still uploaded, so it must not be re-uploaded either.
|
|
79
|
+
**`--no-async`** falls back to the legacy synchronous endpoint: 10 files per
|
|
80
|
+
request with AI running inside the request. Only on that path does `--retry <n>`
|
|
81
|
+
(default 0) apply, re-attempting a failed chunk **only where re-sending is
|
|
82
|
+
safe** — 429, 500/503, network errors — with exponential backoff (1s, 2s, 4s,
|
|
83
|
+
capped at 8s). **Not** retried there: 4xx, because the server rejected the files
|
|
84
|
+
on their merits; and 502/504/524, because a gateway timeout means the upstream is
|
|
85
|
+
probably still extracting, so re-sending would orphan a paid extraction (those
|
|
86
|
+
report as `upstream may still be processing`). A client-side timeout is never
|
|
87
|
+
retried on either path and stops the run, with remaining files reported as
|
|
88
|
+
`not attempted`.
|
|
89
|
+
- `spend-control contracts bulk-create --from <draft.json> [--owner <email>]
|
|
90
|
+
[--billing-frequency <name>] [--allow-cross-tenant] [--dry-run]
|
|
91
|
+
[--timeout <seconds>]` — two-step.
|
|
92
|
+
The draft has `_meta` (read `review_required` / `optional_unfilled` for what AI
|
|
93
|
+
couldn't extract), `defaults`, `contracts[]`. **Tenant safety:** refuses if
|
|
94
|
+
`_meta.tenant_server` ≠ `--server` unless `--allow-cross-tenant`. Per-batch
|
|
95
|
+
all-or-nothing (max 10); on mid-batch failure earlier batches are NOT rolled
|
|
96
|
+
back — created IDs land in a `<draft>.created.txt` sidecar. Vendor via
|
|
97
|
+
`vendor_name` (get-or-create) **or** `vendor_id`, never both. Extract and
|
|
98
|
+
create are separate commands by design (no one-shot).
|
|
99
|
+
**Timeouts and duplicates:** the whole command runs under one budget, now
|
|
100
|
+
30s + 20s per 10-contract batch (37 contracts → 4 batches → 110s); override
|
|
101
|
+
with `--timeout <seconds>`. When a batch fails the CLI always reports how many
|
|
102
|
+
earlier batches committed — including zero — and whether the failure was a
|
|
103
|
+
client-side timeout. A timeout says **nothing** about whether the server
|
|
104
|
+
committed that batch, so verify with `contracts list --search <vendor>` before
|
|
105
|
+
re-running; re-sending the draft unchanged duplicates every row that already
|
|
106
|
+
exists.
|
|
107
|
+
- `spend-control contracts update <id> --from <patch.json>` — partial PATCH.
|
|
108
|
+
|
|
109
|
+
## Vendors (the App Store)
|
|
110
|
+
|
|
111
|
+
A vendor is the App Store unit: `is_visible` decides whether an app is listed
|
|
112
|
+
to the org, categories structure the catalog, owners is who an access request
|
|
113
|
+
routes to. `contracts bulk-create` creates vendors implicitly by `vendor_name`
|
|
114
|
+
(`get_or_create`), so these commands are for the state *around* that.
|
|
115
|
+
|
|
116
|
+
- `spend-control vendors list [--name <text>] [--is-visible <bool>] [--page <n>]
|
|
117
|
+
[--size <n>] [--all] [--json]` — `GET /vendors/`. `--name` is a
|
|
118
|
+
case-insensitive partial match (`name__ilike`). `--all` walks every page in
|
|
119
|
+
100-row chunks, terminating on a short page.
|
|
120
|
+
- `spend-control vendors app-store [--category <name>] [same list flags]` — the
|
|
121
|
+
App Store projection. **Not** the same as `list --is-visible true`: the
|
|
122
|
+
service applies its own rules on top of the filter, so use this one when the
|
|
123
|
+
question is "what does the org actually see".
|
|
124
|
+
- `spend-control vendors with-contracts [--target-date <YYYY-MM-DD>]
|
|
125
|
+
[--without-invoices] [--category <name>] [--page <n>] [--size <n>] [--all]
|
|
126
|
+
[--json]` — adds `total_value` + `currency`
|
|
127
|
+
aggregated over the vendor's contracts for the month containing
|
|
128
|
+
`--target-date` (default today). `--without-invoices` is the "vendors we have
|
|
129
|
+
contracts for but no invoices from" cut.
|
|
130
|
+
- `spend-control vendors duplicates` — vendors with more than one non-archived
|
|
131
|
+
contract; the signal behind the duplicate-contract alert.
|
|
132
|
+
- `spend-control vendors categories` — the vendor category catalog (`value` +
|
|
133
|
+
display name). Distinct from contract categories.
|
|
134
|
+
- `spend-control vendors owners <vendor_id>` — the owner ids behind a vendor.
|
|
135
|
+
Read-only: owners are derived from the vendor's contracts, so to change one,
|
|
136
|
+
patch the contract.
|
|
137
|
+
- `spend-control vendors create <name>` — two-step, but `get_or_create`: a
|
|
138
|
+
repeat run returns the existing vendor rather than duplicating it. Name is
|
|
139
|
+
2–255 characters, checked locally before the request.
|
|
140
|
+
- `spend-control vendors set-visibility <ids…> --visible <true|false>
|
|
141
|
+
[--from <file>]` — two-step, **admin-only** (a non-admin session gets a 403).
|
|
142
|
+
Unlike `contracts archive`, this endpoint is genuinely bulk: one request
|
|
143
|
+
carries every id, so there is no partial-application state — it applies to
|
|
144
|
+
all the ids or to none.
|
|
145
|
+
The command reads current visibility first and sends **only the vendors that
|
|
146
|
+
actually change**, because a uniform write over a mixed set is not reversible
|
|
147
|
+
(`[visible, hidden] → all true → all false` leaves both hidden). The plan step
|
|
148
|
+
prints a per-vendor `before → after` plus the exact `to undo:` command, which
|
|
149
|
+
restores a mixed set correctly. Vendors already at the target are reported and
|
|
150
|
+
skipped; an id matching no vendor is an error, not a silent no-op. Resolving
|
|
151
|
+
prior state costs one read-only page walk of the vendor list (there is no
|
|
152
|
+
`GET /vendors/{id}`).
|
|
153
|
+
|
|
154
|
+
Icon URLs are presigned (~1h bearer credentials), so the tables print icon
|
|
155
|
+
presence only; `--json` includes the URL. Same rule as `files list`.
|
|
156
|
+
|
|
157
|
+
Not wrapped, deliberately: `GET /vendors/search` (deprecated server-side — use
|
|
158
|
+
`list --name`) and `POST /vendors/request-access` (an end-user action that
|
|
159
|
+
emails the vendor owner, not an operator one).
|
|
160
|
+
|
|
161
|
+
## Alerts and renewal alert settings
|
|
162
|
+
|
|
163
|
+
Alerts are derived notifications (renewal, duplication, upcoming/already
|
|
164
|
+
started, already ended) generated from contract data by the `handle_alerts`
|
|
165
|
+
background task. The settings document is org-wide and decides how far ahead of
|
|
166
|
+
an end date they fire and who gets emailed.
|
|
167
|
+
|
|
168
|
+
- `spend-control alerts list [--status active|dismissed] [--all] [--json]` —
|
|
169
|
+
**the service defaults `status` to `active`**, so a bare `list` silently hides
|
|
170
|
+
dismissed alerts. The command prints a note to stderr saying so; to see the
|
|
171
|
+
full set, run it twice. Path is `/alerts` with **no** trailing slash, unlike
|
|
172
|
+
`/contracts/` and `/vendors/`.
|
|
173
|
+
- `spend-control alerts dismiss <ids…> [--from <file>]` — two-step, one POST per
|
|
174
|
+
id (no batch route). **One-way through the API**: the service binds no
|
|
175
|
+
un-dismiss route, so the CLI cannot restore a dismissed alert. The background
|
|
176
|
+
job may re-raise it on its own schedule. Every id is attempted; the summary
|
|
177
|
+
prints `unprocessed ids:` and the command exits non-zero if any failed.
|
|
178
|
+
- `spend-control alerts settings [--json]` — the org's renewal alert settings. A
|
|
179
|
+
null result means the org has never saved them, which is a legitimate state:
|
|
180
|
+
there is no create route, so they must be saved once in the UI first.
|
|
181
|
+
- `spend-control alerts set-settings [--days-before-end-date <n>]
|
|
182
|
+
[--send-to-contract-owner <bool>] [--ignore-cancellation-policy <bool>]
|
|
183
|
+
[--email-notifications <bool>] [--user-ids <a,b,c>]` — two-step. The route is
|
|
184
|
+
a `PUT` over a schema where **every field is required**, so the command reads
|
|
185
|
+
the current document and merges rather than exposing a bare PUT; flags you do
|
|
186
|
+
not pass keep their current values instead of being blanked. The plan step
|
|
187
|
+
prints `before → after` for each changed field, and a no-op update
|
|
188
|
+
short-circuits before minting a token. `--user-ids ""` (explicit empty string)
|
|
189
|
+
clears the extra-recipient list; omitting the flag leaves it alone.
|
|
190
|
+
|
|
191
|
+
## Contract categories and change history
|
|
192
|
+
|
|
193
|
+
- `spend-control contracts categories [--unused] [--json]` — the contract
|
|
194
|
+
category catalog (`value` + display name). `--unused` maps to
|
|
195
|
+
`contract_is_null=true`: categories no contract currently uses. Distinct from
|
|
196
|
+
`vendors categories`.
|
|
197
|
+
- `spend-control contracts change-history <contract_id> [--json]` — **value
|
|
198
|
+
changes only**. The history row carries `previous_amount` / `new_amount`, so
|
|
199
|
+
edits to dates, owner, or category leave no trace here; an empty result means
|
|
200
|
+
"never repriced", not "no such contract".
|
|
201
|
+
|
|
202
|
+
## Customer-facing docs (KB)
|
|
203
|
+
|
|
204
|
+
Help Center → **"Spend Control"** category. Find current articles with
|
|
205
|
+
`dr kb search "spend control contract"` (or browse `dr kb categories` /
|
|
206
|
+
`dr kb sections`). Cite the article; don't paraphrase from memory.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Templates, fields, data-models, dynamic ranges, scans
|
|
2
|
+
|
|
3
|
+
Templates are the customer's data sources ("tables"). Each has a numeric
|
|
4
|
+
**template ID** you need on most downstream commands. `templates get <id>` shows
|
|
5
|
+
field names + **field IDs** (needed for functions, ranges, set-field-type, calc
|
|
6
|
+
models, metrics).
|
|
7
|
+
|
|
8
|
+
## "System name" = `alias`
|
|
9
|
+
|
|
10
|
+
The Datarails UI and Help Center call a table's or field's stable machine
|
|
11
|
+
identifier its **system name**. dr-cli and the monolith API both spell the same
|
|
12
|
+
value `alias`. They are the same thing — if an operator asks you to read or set
|
|
13
|
+
a **system name** on a table or a field, use the `alias` commands below.
|
|
14
|
+
|
|
15
|
+
It is the value that:
|
|
16
|
+
|
|
17
|
+
- `DR.GET(value,"{department}")` resolves against — `{braces}` in an Excel
|
|
18
|
+
function mean "look this up by system name", vs `[brackets]` for the display
|
|
19
|
+
field name.
|
|
20
|
+
- template-library import matches on when mapping a template's tables and
|
|
21
|
+
fields onto the tenant's own (a table or field that already has one cannot be
|
|
22
|
+
reassigned).
|
|
23
|
+
- planning defaults key off (e.g. giving a `Scenario_version` field the system
|
|
24
|
+
name `scenario_cycle` makes default-filter logic recognise it).
|
|
25
|
+
|
|
26
|
+
Unrelated uses of the word "alias" elsewhere in this CLI — command aliases
|
|
27
|
+
(`orgs` → `organizations`), `entities rename --alias` (an entity's *display*
|
|
28
|
+
name), and filebox alias-matching on push — are **not** system names.
|
|
29
|
+
|
|
30
|
+
## Read
|
|
31
|
+
|
|
32
|
+
- `templates list` — `--my-org-only` filters to the operator org's own tables
|
|
33
|
+
(the source the FE target-table pickers use).
|
|
34
|
+
- `templates get <id>` — fields, field IDs, field types. **System names are not
|
|
35
|
+
in the text table** — add `--json` to see the table's `alias` and each
|
|
36
|
+
`fields[].alias`. That is the read path for a system name, so use
|
|
37
|
+
it before assigning one (a table or field that already has a system name
|
|
38
|
+
cannot be remapped by template-library import).
|
|
39
|
+
- `templates sample <id> --rows 10` — newest rows, latest document version only
|
|
40
|
+
(there is no all-versions switch on this route). `--rows` up to 1000 per call;
|
|
41
|
+
`--offset N` pages deeper; `--fields "A,B,C"` projects columns server-side
|
|
42
|
+
(names or field ids); the same filter flags as `aggregate` (below) narrow the
|
|
43
|
+
rows; `--json` for piping (full envelope: `data`, `metadata.pagination`).
|
|
44
|
+
- `templates distinct <id> --field Region` — distinct values of a field, by
|
|
45
|
+
**name or field id** (`--field 47960` works). A Date field returns
|
|
46
|
+
epoch-seconds strings (`"1796083200"`); pass them straight back into a filter.
|
|
47
|
+
- `templates aggregate <id> --group-by Region --metric Amount:SUM` — server-side
|
|
48
|
+
aggregate, no raw rows. Every field token (`--group-by`, the FIELD of
|
|
49
|
+
`--metric`, filter names) is a field **name or id**; an unknown one fails
|
|
50
|
+
before any call, listing the valid `"name" (id)` pairs. A field may be
|
|
51
|
+
aggregated once per call, and not both grouped and aggregated.
|
|
52
|
+
- `data-models list` / `data-models get` — read-only.
|
|
53
|
+
|
|
54
|
+
### Filters on `aggregate` / `sample`
|
|
55
|
+
|
|
56
|
+
- `--filter "NAME=V1,V2"` (include) / `--exclude "NAME=V1,V2"` — list
|
|
57
|
+
filters, repeatable.
|
|
58
|
+
- `--filter-gte "NAME=V"` / `--filter-lte "NAME=V"` /
|
|
59
|
+
`--filter-range "NAME=FROM..TO"` (inclusive) — ordered filters, repeatable.
|
|
60
|
+
- **Date fields:** a value may be a calendar date (`2026-12-01`, `2026-12`,
|
|
61
|
+
`2026-12-01T00:00:00Z`, `12/01/2026`) or Unix epoch **seconds**
|
|
62
|
+
(`1796083200`, the form `distinct` prints). Calendar dates are read as UTC
|
|
63
|
+
midnight (a month alone is its first day) and converted before the call;
|
|
64
|
+
epochs pass through unchanged. Example:
|
|
65
|
+
`--filter-range "Period=2026-01-01..2026-12-01"`,
|
|
66
|
+
`--filter "Period=2026-12"`. (The server matches a Date only through an
|
|
67
|
+
ordered/`equals` condition; the CLI picks that form for you, so never
|
|
68
|
+
hand-build an advanced filter for a date.) A calendar date on a non-Date
|
|
69
|
+
field is refused client-side.
|
|
70
|
+
- Aggregate result rows key a Date dimension by epoch seconds (a number);
|
|
71
|
+
`distinct` returns the same values as strings.
|
|
72
|
+
|
|
73
|
+
### `aggregate` output shape
|
|
74
|
+
|
|
75
|
+
- Text: one table row per group, `(N rows)` footer. `--totals` adds a
|
|
76
|
+
`Totals: <metric>=<value> …` line (the engine grand total).
|
|
77
|
+
- `--json`: a bare **array of group rows only** — no grand-total row and no
|
|
78
|
+
`{"col_keys":…,"row_keys":…}` trailer, so `for r in rows: r["Period"]` is
|
|
79
|
+
safe. With `--totals` the shape is `{"rows": [...], "totals": {...}}`
|
|
80
|
+
(`totals` is `null` when the engine sent none). Totals are exact for
|
|
81
|
+
SUM/COUNT/MIN/MAX, not for AVG or COUNT_UNIQUE.
|
|
82
|
+
|
|
83
|
+
## Write
|
|
84
|
+
|
|
85
|
+
- `templates create`.
|
|
86
|
+
- `templates set-alias <id> --alias <s>` — set a **table's system name**; pass
|
|
87
|
+
`--alias ""` to clear. (The flag is spelled `--alias`; there is no
|
|
88
|
+
`--system-name` spelling.)
|
|
89
|
+
- `templates set-field-alias <field_id> --alias <s>` — set a **field's system
|
|
90
|
+
name** (field id is `fields[].id` from `templates get`); `--alias ""` clears.
|
|
91
|
+
- `templates set-field-type <field_id> --type <Text|Float|Date|Integer>` —
|
|
92
|
+
**two-step confirm** (it can break widgets / dynamic ranges, since the server
|
|
93
|
+
rescans latest versions when the type change requires it). Notes:
|
|
94
|
+
- Allowed types: `Text`, `Float`, `Date`, `Integer`.
|
|
95
|
+
- Bulk: `--file <path>` with a JSON array `[{field_id, type}, …]`. One failed
|
|
96
|
+
field doesn't abort the rest; failures are summarized as `K of N failed` and
|
|
97
|
+
the exit code reflects partial failure.
|
|
98
|
+
- The CLI preserves the field's existing `is_hidden` when flipping type
|
|
99
|
+
(omitting it would silently un-hide hidden fields).
|
|
100
|
+
- `--enforce` bypasses the server's numeric-downgrade guard (which otherwise
|
|
101
|
+
rejects Float/Integer → non-numeric when widgets/Excel reference the field).
|
|
102
|
+
Off by default.
|
|
103
|
+
- A no-op invocation (every requested type already matches) exits success
|
|
104
|
+
without issuing a token.
|
|
105
|
+
|
|
106
|
+
## Dynamic ranges (nested under a template)
|
|
107
|
+
|
|
108
|
+
Always pass `--table <template_id>` (get it from `templates list` first) —
|
|
109
|
+
except on `update`, which resolves the range's own table automatically.
|
|
110
|
+
|
|
111
|
+
- `dynamic-ranges list --table <tid>`, `dynamic-ranges get <range_id> --table <tid>`.
|
|
112
|
+
`get --json` includes the range's current `filters` and rich `fields`.
|
|
113
|
+
- `dynamic-ranges data <range_id> --table <tid> --json` — returns a flat
|
|
114
|
+
list-of-lists; each inner list is one row, values ordered to match the range's
|
|
115
|
+
fields. Drop `--json` for a human table.
|
|
116
|
+
- `dynamic-ranges create --table <tid> --name <s> --fields <csv>` — plus
|
|
117
|
+
optional filters (below).
|
|
118
|
+
- `dynamic-ranges update <range_id>` — read-modify-write; pass only what
|
|
119
|
+
changes (`--name`, `--fields`, filter flags). Omitted parts stay untouched.
|
|
120
|
+
- `dynamic-ranges delete` (two-step confirm).
|
|
121
|
+
|
|
122
|
+
### Filters on create/update
|
|
123
|
+
|
|
124
|
+
- `--filter "FIELD_ID=V1,V2"` (include) / `--exclude "FIELD_ID=V1,V2"`
|
|
125
|
+
(exclude) — repeatable list filters. Keys are numeric **field IDs** from
|
|
126
|
+
`templates get <tid>`, not field names. Values are sent as strings; the
|
|
127
|
+
server coerces them to the field's type. A filter's field does **not**
|
|
128
|
+
have to be in `--fields`.
|
|
129
|
+
- `--filters-json '<array>'` — raw wire objects
|
|
130
|
+
`{field, values, is_excluded?, override_global_filter?}` for advanced
|
|
131
|
+
(`{"type":"advanced","val":[{"condition":"contains","value":"x"}]}`) and
|
|
132
|
+
date (`{"datetype":"range","val":{"fromdate":<epoch>,"todate":<epoch>}}`)
|
|
133
|
+
filters. Advanced conditions: `bw ew contains dn_contains equals gt gte
|
|
134
|
+
lt lte in range total_range "is null"` (`dn_*` = negated).
|
|
135
|
+
- **Update replaces, never merges:** any filter flag on `update` REPLACES
|
|
136
|
+
all existing filters on the range. To add one filter, `get --json` the
|
|
137
|
+
current filters first and resend the full set. `--clear-filters` removes
|
|
138
|
+
them all.
|
|
139
|
+
- Filter field ids are validated against the table before sending (the
|
|
140
|
+
server doesn't check this; a wrong id would silently bind the filter to
|
|
141
|
+
another table's field).
|
|
142
|
+
|
|
143
|
+
## Scans / rescan / data flow
|
|
144
|
+
|
|
145
|
+
- `scans status <template_id>` — current scan state.
|
|
146
|
+
- `rescan table <template_id>` — trigger a **full historical rescan** (re-executes
|
|
147
|
+
every calculated field against every document version ever uploaded, not just the
|
|
148
|
+
latest), then poll `scans status`. **Two-step confirm (Tier B):** run once to see
|
|
149
|
+
the blast radius (document + version counts) and get a token, then re-run with
|
|
150
|
+
`--confirm-token` — token-only, no word gate even on prod (a rescan re-derives
|
|
151
|
+
data, it doesn't delete it). To pick up a datamapper change on the live template
|
|
152
|
+
**without** touching history, use `datamappers update <id> --rescan latest`
|
|
153
|
+
instead (mirrors the UI "Publish and scan"); a full rescan will re-run pre-existing
|
|
154
|
+
calc fields against old rows, so a latent divide-by-null there fails the whole
|
|
155
|
+
scan. Refuses `UsageStats` on every tenant (system data, not customer-owned).
|
|
156
|
+
- `dataflow get` — the dependency graph: inputs → mappers → LUTs → table →
|
|
157
|
+
outputs.
|
|
158
|
+
|
|
159
|
+
## Customer-facing docs (KB)
|
|
160
|
+
|
|
161
|
+
Help Center → **"Getting Data into Datarails"** → Data Management. Find current articles with `dr kb search "tables data management"` (or
|
|
162
|
+
browse `dr kb categories` / `dr kb sections`). Cite the article; don't
|
|
163
|
+
paraphrase from memory.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Workflows v2 (`workflow` + `mec workflow`) and collection processes
|
|
2
|
+
|
|
3
|
+
`workflow` (non-MEC close, budget, forecast, etc.) and `mec workflow` (Month-End
|
|
4
|
+
Close) share one command surface. Each leaf probes a feature flag before any
|
|
5
|
+
other call: `workflow` checks `enable_workflow_v2` (and redirects to
|
|
6
|
+
`mec workflow` if off); `mec workflow` checks `month_end_close`.
|
|
7
|
+
|
|
8
|
+
The only real differences are the **type enum** and a few MEC-only reads (below);
|
|
9
|
+
everything else is identical. Confirm tokens are scoped per family
|
|
10
|
+
(`workflow-*` vs `mec-workflow-*`).
|
|
11
|
+
|
|
12
|
+
## Read
|
|
13
|
+
|
|
14
|
+
- `list [--archived]` (`workflow` also has `--planning`), `get <id>`.
|
|
15
|
+
- `process list [--template <id>] [--active|--inactive]`, `process get <id>`.
|
|
16
|
+
- `step get <process-id> <step-id>`.
|
|
17
|
+
- `task list <pid> <sid>`, `task get <pid> <sid> <tid>`.
|
|
18
|
+
- `member list <template-id>`, `member check <template-id> --emails <e1,e2>`
|
|
19
|
+
(read-only pre-share audit — per-email missing-doc diagnostics).
|
|
20
|
+
- `group list <template-id>`.
|
|
21
|
+
- **MEC-only:** `mec workflow periods` (active close periods);
|
|
22
|
+
`mec workflow accounts tasks --date-tag <unix> --account-name <s>
|
|
23
|
+
--account-group <s> [--entity]` (all three required — tasks for one account in
|
|
24
|
+
a period); `mec workflow accounts task-statuses --date-tag <unix> [--entity]`
|
|
25
|
+
(overview across all accounts in a period).
|
|
26
|
+
|
|
27
|
+
## Write (mutations are two-step confirm)
|
|
28
|
+
|
|
29
|
+
- `create --file <path>` — new template starts inactive.
|
|
30
|
+
- `workflow` type enum: `Financial Reporting | Budget | Forecast | Other`
|
|
31
|
+
(`Month End Close` is rejected with a redirect to `mec workflow`).
|
|
32
|
+
- `mec workflow` requires `type: "Month End Close"` (the four non-MEC types are
|
|
33
|
+
rejected with a redirect to `workflow`).
|
|
34
|
+
- `update <id> --file <path>` (same type check).
|
|
35
|
+
- `activate <id> [--yes-share-data]` — **fires async emails** to assignees on
|
|
36
|
+
every step with `notify_automatically=true`; the plan-step previews the
|
|
37
|
+
recipients and refuses without `--yes-share-data` when any step has tasks.
|
|
38
|
+
- `deactivate <id>`; `archive <id>` / `restore <id>` / `delete <id>` (`delete` is
|
|
39
|
+
an alias for archive — there is no v2 hard-delete).
|
|
40
|
+
- `clone <id> --date-tag <unix> [--copy-from <pid>] [--name <text>]
|
|
41
|
+
[--start-time <unix>] [--tag <id>=<value>] --yes-share-data` — carries
|
|
42
|
+
existing members forward (share flag mandatory).
|
|
43
|
+
- `--date-tag` is **required**; the endpoint 500s without a tag array.
|
|
44
|
+
- `--copy-from` defaults to the template's newest instance by `date_tag_value`.
|
|
45
|
+
Passing nothing does **not** mean "empty instance" — clone always copies.
|
|
46
|
+
If several instances exist and none carries a readable DATE tag, there is no
|
|
47
|
+
"newest" to pick and the command fails asking for an explicit `--copy-from`.
|
|
48
|
+
The plan step's confirm command always pins the resolved id, so a newer
|
|
49
|
+
instance appearing between plan and commit cannot change the source.
|
|
50
|
+
- Omit `--name` and the server derives the period label (e.g.
|
|
51
|
+
`September 2026`). Names must be unique per template; a collision is
|
|
52
|
+
rejected client-side with the taken names listed.
|
|
53
|
+
- `--start-time` is the only way to set the new instance's start time — there
|
|
54
|
+
is no `process update` verb for it.
|
|
55
|
+
- `--tag <id>=<value>` is repeatable, for templates that carry a non-date
|
|
56
|
+
dimension tag (budget cycle / scenario) alongside the period. Tag ids come
|
|
57
|
+
from `config.tags_config[].id` in `workflow get <id> --json`.
|
|
58
|
+
- Under the hood this is the one template route with **no `v2`** segment
|
|
59
|
+
(`POST /api/processes/process_templates/<id>/processes`); the server routes
|
|
60
|
+
both workflow versions through it.
|
|
61
|
+
- `process delete <id>` — cascades all tasks.
|
|
62
|
+
- `step create <pid> --file` / `step update` / `step delete` / `step reorder
|
|
63
|
+
<tid> --file <path> | --order id:n,id:n,…`.
|
|
64
|
+
- `task add-file <pid> <sid> --file <path>` (bulk array), `task add-checklist …`
|
|
65
|
+
(name + description + assignee), `task update --type file|non-file --file`,
|
|
66
|
+
`task update-one <pid> <sid> <tid> --file` (single checklist row),
|
|
67
|
+
`task delete --ids 1,2,3` (token bound to the id set + step id; always sends an
|
|
68
|
+
array body), `task notify --type file|non-file --file` (**fires async email** —
|
|
69
|
+
200 means queued, not delivered; the plan lists every recipient),
|
|
70
|
+
`task approve --file` / `task reject --file`, `task sign-off --type
|
|
71
|
+
file|non-file --ids 1,2,3 --is-sign-off true|false` (true = complete, false =
|
|
72
|
+
re-open). MEC tasks additionally carry `reconciliation_tags`,
|
|
73
|
+
`assignee_close_date_condition`, `reviewer_close_date_condition`.
|
|
74
|
+
In every task `--file`, `assignee`/`reviewer` are **bare user ids**
|
|
75
|
+
(`"assignee": 78`) — the server field is an integer PK, so a nested
|
|
76
|
+
`{"id": 78}` 400s with "A valid integer is required." An `{"id": 78}`
|
|
77
|
+
object is still accepted as input for convenience and flattened before send.
|
|
78
|
+
- `member add <tid> --file {emails,role} --yes-share-data` (runs a `/users/check`
|
|
79
|
+
first and shows per-email missing-doc diagnostics in the plan),
|
|
80
|
+
`member update <tid> <uid> --role ROLE [--yes-share-data]` (share flag required
|
|
81
|
+
when escalating to the owner role), `member remove <tid> <uid>` (does **not**
|
|
82
|
+
revoke document permissions), `member grant-bulk --file
|
|
83
|
+
{emails,entities_ids,role} --yes-share-data` (cascades table permissions for
|
|
84
|
+
linked LUT templates). `group add|update|remove|grant-bulk` mirror this for
|
|
85
|
+
permission groups.
|
|
86
|
+
|
|
87
|
+
## Enums & wire-format gotchas
|
|
88
|
+
|
|
89
|
+
- **Step type:** `Input | Output | Mapping | Tasks | Text | BudgetInput`.
|
|
90
|
+
- **Step position:** `index` and `y_index` are the step's canvas grid slot, not
|
|
91
|
+
pixels. `index` is the column and `y_index` the row within that column, both
|
|
92
|
+
1-based. The columns in use must run 1..N with no gaps: four steps in a line
|
|
93
|
+
are `index` 1, 2, 3, 4 with `y_index` 1; two parallel steps share an `index`
|
|
94
|
+
with `y_index` 1 and 2. A step whose `index` falls outside 1..N renders
|
|
95
|
+
stacked at the canvas origin, and nothing else in the workflow is affected.
|
|
96
|
+
The CLI rejects 0, and the `step create` / `step update` plan prints a
|
|
97
|
+
`warning:` line naming every step the change would leave off the grid. To fix
|
|
98
|
+
a workflow already built that way, run `step update` on each step with a
|
|
99
|
+
corrected `index` / `y_index`.
|
|
100
|
+
- **Roles:** `WORKFLOW_V2_OWNER_ROLE` / `WORKFLOW_V2_EDITOR_ROLE`.
|
|
101
|
+
- The server field is `recurrency_*` (misspelled) — the CLI rejects
|
|
102
|
+
`recurrence_*` with a hint.
|
|
103
|
+
- `process_start_time` and `tag.value` are 10-digit **Unix-seconds** epochs;
|
|
104
|
+
small ints are rejected with a "did you pass day-of-month?" hint.
|
|
105
|
+
|
|
106
|
+
## Collection processes
|
|
107
|
+
|
|
108
|
+
CPs are `COLLECTION`-type buckets — a container of N child fileboxes that share
|
|
109
|
+
the same `tags_config` and mapping. Alias `collection-process`.
|
|
110
|
+
|
|
111
|
+
- `collection-processes list [--scenario S] [--name <substr>] [--owner <email>]`,
|
|
112
|
+
`collection-processes get <id> [--no-cp-data]`.
|
|
113
|
+
- `collection-processes create --name <n> --dimension-name <d>
|
|
114
|
+
(--filebox <name>… | --fileboxes @<path>) [--date-sub-type <t> | --no-date]
|
|
115
|
+
[--scenario S] [--parent <id>] [--config-file <p>] [--template-file <p>]
|
|
116
|
+
[--use-type <id>] [--split-integration <id>]` — two-step; child fileboxes are
|
|
117
|
+
provisioned asynchronously, so poll `get <id>` to confirm.
|
|
118
|
+
- `collection-processes set-tag-config <id> --add NAME… --remove NAME…` —
|
|
119
|
+
two-step; root-document only (existing children keep their snapshots; fileboxes
|
|
120
|
+
added later via `add-fileboxes` inherit the new config).
|
|
121
|
+
- `collection-processes add-fileboxes <id> (--filebox <name>… | --fileboxes @<path>)
|
|
122
|
+
--parent-folder <doc-id> [--dimension-name <d>] [--template-file <p>]` — two-step;
|
|
123
|
+
async.
|
|
124
|
+
- `collection-processes add-folders <id> (--folder <name>… | --folders @<path>)
|
|
125
|
+
--parent-folder <doc-id> [--dimension-name <d>]` — two-step.
|
|
126
|
+
- `collection-processes calculate <id> [--multiple-dimension-tags]` — two-step;
|
|
127
|
+
idempotent server-side.
|
|
128
|
+
- There is no `delete` verb — deletion is UI-gated.
|
|
129
|
+
- `--filebox`/`--fileboxes` use the BE wire format: newline-joined names (the
|
|
130
|
+
`@<path>` file mode reads a newline-separated text file verbatim; no comma
|
|
131
|
+
translation).
|
|
132
|
+
|
|
133
|
+
## Customer-facing docs (KB)
|
|
134
|
+
|
|
135
|
+
Help Center → **"Managing Environment"** → Workflow. Find current articles with `dr kb search "workflow"` (or
|
|
136
|
+
browse `dr kb categories` / `dr kb sections`). Cite the article; don't
|
|
137
|
+
paraphrase from memory.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dr-error-enrichment
|
|
3
|
+
description: Router for the integration error-enrichment rules (regex to readable message + resolution steps) that Datarails shows users when a sync fails. Points at four separately callable task skills — dr-error-enrichment-author, -reconcile, -maintain, -improve — and owns the shared reference material (command surface, matching semantics, safety and backup rules, groundcover mining, known-bug triage) that all four read. Use only when the request names the feature without naming a task, or when you need to choose between the four; each task skill triggers on its own work and does not need this one first. Triggers - "work on the error enrichment rules", "the error-enrichment rules", "the readable-errors feature", "which error-enrichment skill do I need", "where do I start with enrichment rules".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dr-error-enrichment — router
|
|
7
|
+
|
|
8
|
+
## What this is
|
|
9
|
+
|
|
10
|
+
When an integration sync fails, Datarails matches the raw vendor error against a
|
|
11
|
+
table of **enrichment rules**. A rule maps a regex to a readable `description`
|
|
12
|
+
plus `resolution_steps`, and collects thumbs up/down feedback. Rules live
|
|
13
|
+
**per server** in each region's own database — there is no global table and no
|
|
14
|
+
cross-server sync, so the same rule can exist on US and be missing (or wrong)
|
|
15
|
+
on UK.
|
|
16
|
+
|
|
17
|
+
This skill only routes. The actual work lives in four task-specific skills, each
|
|
18
|
+
of which triggers on its own kind of request and is complete on its own — if the
|
|
19
|
+
user already named the task ("approve these drafts", "why does this error show
|
|
20
|
+
no explanation", "compare the servers"), go straight to that skill instead of
|
|
21
|
+
reading this one. Use this page when the request names the feature but not the
|
|
22
|
+
task, or when you are unsure which of the four applies.
|
|
23
|
+
|
|
24
|
+
## Pick a skill
|
|
25
|
+
|
|
26
|
+
| Skill | Use when |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `dr-error-enrichment-author` | The user wants one specific rule written or fixed — create a rule for an error, edit a wrong pattern or message, delete a rule |
|
|
29
|
+
| `dr-error-enrichment-reconcile` | Rules have drifted between regions — diff the servers and converge them on a chosen canonical version |
|
|
30
|
+
| `dr-error-enrichment-maintain` | The recurring health pass — approve high-hit drafts one by one, and fix rules collecting thumbs-down |
|
|
31
|
+
| `dr-error-enrichment-improve` | Continuous improvement — mine groundcover for errors no rule matches, split the ones caused by our own bugs off from the ones worth a rule, and propose rules for the rest |
|
|
32
|
+
|
|
33
|
+
Each one is callable directly; the user does not have to come through this
|
|
34
|
+
skill. If the request names a single rule, go to author; if it names *the
|
|
35
|
+
fleet*, go to reconcile; if it is "are our error messages any good", go to
|
|
36
|
+
maintain, then improve.
|
|
37
|
+
|
|
38
|
+
Sessions often chain: a maintain pass usually ends in author edits, and an
|
|
39
|
+
author change on one server usually ends in reconcile.
|
|
40
|
+
|
|
41
|
+
## Shared reference
|
|
42
|
+
|
|
43
|
+
Read the file that covers what you are about to do — the sub-skills assume it.
|
|
44
|
+
|
|
45
|
+
- [reference/core.md](reference/core.md) — what rules are, who may call the admin
|
|
46
|
+
API, the full `dr error-enrichments` command surface, how matching actually
|
|
47
|
+
works (`re.match` anchoring, rank order, drafts stealing hits), where to get
|
|
48
|
+
raw error text off a tenant, guard rails, common mistakes.
|
|
49
|
+
- [reference/safety-backup.md](reference/safety-backup.md) — target-server
|
|
50
|
+
confirmation, the backup-first commands, the surgical revert path, write
|
|
51
|
+
gating tiers, the `import` policy, and batch-write etiquette.
|
|
52
|
+
- [reference/groundcover.md](reference/groundcover.md) — gcQL for mining
|
|
53
|
+
unmatched errors, groundcover-env to server mapping, and the hash-not-text
|
|
54
|
+
caveat on the `No matching rule` marker.
|
|
55
|
+
- [reference/jira-bugs.md](reference/jira-bugs.md) — the integration-bug sweep
|
|
56
|
+
that decides whether an unmatched error deserves a rule or a bug report: the
|
|
57
|
+
`System area` field, JQL that returns rows, the four verdicts, and how to file
|
|
58
|
+
the missing bug.
|
|
59
|
+
|
|
60
|
+
Confirm-token and prod-gate mechanics themselves live in
|
|
61
|
+
[the `dr-cli` skill's reference/safety.md](../dr-cli/reference/safety.md) — a
|
|
62
|
+
different skill's file, not one of this skill's own `reference/*.md`.
|
|
63
|
+
|
|
64
|
+
## Non-negotiables
|
|
65
|
+
|
|
66
|
+
These hold in every sub-skill; the detail is in
|
|
67
|
+
[reference/safety-backup.md](reference/safety-backup.md).
|
|
68
|
+
|
|
69
|
+
1. **Confirm the target server explicitly** — ask, never infer it from earlier
|
|
70
|
+
conversation. `US`, `US2`, `UK`, `CA`, `EU`, `DEMO` are all prod.
|
|
71
|
+
2. **Back up every reachable server before the first write** of a session, not
|
|
72
|
+
just the target — the export is also the diff baseline. One command:
|
|
73
|
+
`dr error-enrichments export --all-servers --out <dir>`. Tell the user the
|
|
74
|
+
backup path **and which servers actually backed up** — the sweep skips servers
|
|
75
|
+
with no stored login and exits non-zero when one fails, and a server with no
|
|
76
|
+
backup line has no revert path.
|
|
77
|
+
3. **Never reach for `import`** unless the user asked for it by name, and then
|
|
78
|
+
only after telling them it zeroes `hits` / `thumbs_up` / `thumbs_down` and
|
|
79
|
+
reassigns every rule id on that server. It is a restore-from-catastrophe
|
|
80
|
+
tool, not an edit tool.
|