@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.
Files changed (58) hide show
  1. package/README.md +608 -0
  2. package/dist/chunk-DWMCBGQH.js +242 -0
  3. package/dist/cli.js +64468 -0
  4. package/dist/kb-6UWI7Q32.js +21 -0
  5. package/dist/scopes-map.json +4412 -0
  6. package/dist/skills/connector-sandbox/SKILL.md +95 -0
  7. package/dist/skills/datarails-connect-workato/SKILL.md +140 -0
  8. package/dist/skills/datarails-connectors-onboard-connector/SKILL.md +813 -0
  9. package/dist/skills/dr-cli/SKILL.md +659 -0
  10. package/dist/skills/dr-cli/reference/admin.md +796 -0
  11. package/dist/skills/dr-cli/reference/budget-builder.md +151 -0
  12. package/dist/skills/dr-cli/reference/calc-vals.md +73 -0
  13. package/dist/skills/dr-cli/reference/calcmodels.md +76 -0
  14. package/dist/skills/dr-cli/reference/cash.md +61 -0
  15. package/dist/skills/dr-cli/reference/dashboards-widgets.md +85 -0
  16. package/dist/skills/dr-cli/reference/datamappers.md +150 -0
  17. package/dist/skills/dr-cli/reference/desk.md +472 -0
  18. package/dist/skills/dr-cli/reference/filebox-documents.md +373 -0
  19. package/dist/skills/dr-cli/reference/forecast.md +51 -0
  20. package/dist/skills/dr-cli/reference/integrations.md +57 -0
  21. package/dist/skills/dr-cli/reference/lookups.md +168 -0
  22. package/dist/skills/dr-cli/reference/metrics.md +512 -0
  23. package/dist/skills/dr-cli/reference/multi-product.md +180 -0
  24. package/dist/skills/dr-cli/reference/reconciliation-mec.md +76 -0
  25. package/dist/skills/dr-cli/reference/rh-config.md +84 -0
  26. package/dist/skills/dr-cli/reference/safety.md +143 -0
  27. package/dist/skills/dr-cli/reference/spend-control.md +206 -0
  28. package/dist/skills/dr-cli/reference/templates.md +163 -0
  29. package/dist/skills/dr-cli/reference/workflows.md +137 -0
  30. package/dist/skills/dr-error-enrichment/SKILL.md +80 -0
  31. package/dist/skills/dr-error-enrichment/reference/core.md +257 -0
  32. package/dist/skills/dr-error-enrichment/reference/groundcover.md +84 -0
  33. package/dist/skills/dr-error-enrichment/reference/jira-bugs.md +219 -0
  34. package/dist/skills/dr-error-enrichment/reference/safety-backup.md +122 -0
  35. package/dist/skills/dr-error-enrichment-author/SKILL.md +219 -0
  36. package/dist/skills/dr-error-enrichment-improve/SKILL.md +218 -0
  37. package/dist/skills/dr-error-enrichment-maintain/SKILL.md +237 -0
  38. package/dist/skills/dr-error-enrichment-reconcile/SKILL.md +167 -0
  39. package/dist/skills/dr-filebox-api-setup/SKILL.md +170 -0
  40. package/dist/skills/dr-switch-user/SKILL.md +395 -0
  41. package/dist/skills/dr-validate/SKILL.md +392 -0
  42. package/dist/skills/dr-validate/report-template.html +950 -0
  43. package/dist/skills/fx-conversion-matrix/SKILL.md +763 -0
  44. package/dist/skills/fx-conversion-matrix/scripts/_args.mjs +52 -0
  45. package/dist/skills/fx-conversion-matrix/scripts/_xlsx.mjs +241 -0
  46. package/dist/skills/fx-conversion-matrix/scripts/build-matrix.mjs +366 -0
  47. package/dist/skills/fx-conversion-matrix/scripts/gen-mappers.mjs +171 -0
  48. package/dist/skills/fx-conversion-matrix/scripts/rates-to-xlsx.mjs +172 -0
  49. package/dist/skills/fx-onboard/SKILL.md +325 -0
  50. package/dist/skills/mec-table-validation/SKILL.md +383 -0
  51. package/dist/skills/plugins.json +24 -0
  52. package/dist/skills/validate-workato-migration/SKILL.md +167 -0
  53. package/dist/skills/validate-workato-migration/reference/gotchas.md +16 -0
  54. package/dist/skills/validate-workato-migration/reference/staging-orgs.md +26 -0
  55. package/dist/skills/validate-workato-migration/scripts/compare_files.py +255 -0
  56. package/dist/zendeskKb-JPSHVOPH.js +243 -0
  57. package/package.json +56 -9
  58. 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.