@datarails-ai/dr-cli 0.0.1-locked → 1.6.70

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,122 @@
1
+ # Safety and backup — invariants, write gating, import policy
2
+
3
+ Read [the `dr-cli` skill's reference/safety.md](../../dr-cli/reference/safety.md)
4
+ for the underlying confirm-token and prod-gate mechanics. That file lives in the
5
+ `dr-cli` skill, not this one — this skill's safety file is the one you are
6
+ reading.
7
+
8
+ ## Three invariants for every server-modifying run
9
+
10
+ **1. Confirm the target server explicitly.** Ask (AskUserQuestion) which server
11
+ to change — never infer it from earlier context in the conversation. Prod keys
12
+ are `US`, `US2`, `UK`, `CA`, `EU`, `DEMO` (DEMO is customer-facing prod, not a
13
+ sandbox). Demonstrate on `DEV` / `TEST-*` first when the user is undecided.
14
+
15
+ **2. Back up every reachable server first.** Before the first write of a
16
+ session, one command — `export` takes the same sweep flags as `list` / `stats`,
17
+ and `--out` is a **directory** when sweeping, one `<SERVER>.csv` per server:
18
+
19
+ ```bash
20
+ dr error-enrichments export --all-servers --out ~/dr-enrichment-backups/$(date -u +%Y-%m-%dT%H%M%SZ)
21
+ ```
22
+
23
+ Back up **all** servers, not just the target: a reconcile session usually
24
+ touches more than one, and the diff baseline is worth as much as the restore.
25
+ Use `--servers A,B` to narrow it deliberately, and `--include-test` to pull
26
+ `DEV` / `TEST-*` in as well. `--out` is **required** when sweeping and must name
27
+ a directory — concatenating N servers into one CSV produces N header rows that
28
+ `import` cannot read back, which is the one thing a backup has to survive.
29
+
30
+ **Report which servers actually backed up, before the first write.** The sweep
31
+ writes the files it could and keeps going, printing one stdout line per server —
32
+ `Wrote N rules to <dir>/<SERVER>.csv` for the ones that worked, and a
33
+ `<SERVER> — skipped: …` / `<SERVER> — error: …` line for the ones that did not.
34
+ A server with no stored keychain identity is **skipped** and does **not** make
35
+ the command fail; only a real export failure does. So exit 0 is *not*
36
+ "everything is backed up" — read the per-server lines and tell the user by name
37
+ which servers have a CSV and which do not. A server you are about to write to
38
+ with no backup of its own has no revert path and no diff baseline: fix the login,
39
+ or get an explicit decision to proceed without one, before writing to it.
40
+
41
+ A single server still works the way it always did — `export --server <S> --out
42
+ <file>.csv`, where `--out` is a plain file. Use that only when you have
43
+ deliberately narrowed the scope to one server. Do **not** rebuild the sweep as a
44
+ shell loop: a loop that ends each iteration in `|| echo "skip $S"` turns a failed
45
+ export into one quiet line, and a server with no backup then reads exactly like
46
+ a server with one.
47
+
48
+ **3. Know the revert path before you need it — and prefer the surgical one.**
49
+ The CSV backup carries `id`, so a revert is a per-rule replay:
50
+
51
+ - rule changed → `update <id>` back to the backed-up field values
52
+ - rule you created → `delete <id>`
53
+ - rule you deleted → `create` from the backed-up row (it returns a **new id**)
54
+
55
+ Batch forms make this cheap: one `update --payload-file` with an array of
56
+ `{id, ...original fields}` undoes a whole batch in a single confirmed run.
57
+ Do **not** reach for `import` to undo anything — see the import policy below.
58
+
59
+ ## Write gating
60
+
61
+ Every mutation is two-step: run it without `--confirm-token`, relay the printed
62
+ plan to the user, then re-run with the token. Tiers (`src/commands/_prodGateTiers.ts`):
63
+
64
+ - **`create` / `update` — Tier B.** Token only, on every server. Batch forms are
65
+ tiered separately (`error-enrichment-batch|create`, `error-enrichment-batch|update`)
66
+ and are **also Tier B**: a single token covers the whole set.
67
+ - **`delete` / `import` — Tier A.** On a prod server the binary additionally
68
+ demands a challenge word typed by a human in a desktop dialog or TTY; it
69
+ refuses non-zero in a headless shell. Don't invent a chat-side word ritual —
70
+ get the user's "yes", re-run with the token, and let the binary prompt them.
71
+ A batch `delete` (`error-enrichment-batch|delete`) is Tier A too, and **one**
72
+ gate covers the whole set — which is exactly why the plan's per-rule lines have
73
+ to be read rule by rule rather than counted.
74
+
75
+ A batch write binds its token to the targets *and* the field values, so an edited
76
+ payload file invalidates it; a single-target write binds the target only, so an
77
+ edited field flag does **not**. The full rule is in
78
+ [core.md](core.md) under *Confirm-token binding* — read it before you reuse any
79
+ token.
80
+
81
+ ## Import policy
82
+
83
+ **Default to `update` / `create` for every fleet change.** Batch `update` (shared
84
+ patch or per-rule array) and batch `create` cover reconcile, approval sweeps, and
85
+ message rewrites — that is what they exist for. `import` is not part of the
86
+ normal loop and must not be selected on your own initiative, however convenient
87
+ the CSV looks.
88
+
89
+ Use `import` **only when the user explicitly asks for it by name** (a genuine
90
+ restore-from-catastrophe). Even then, before running it, state plainly that
91
+ `import` is wipe-and-replace — the server deletes **every** rule on that server
92
+ and bulk-creates from the CSV — and that it therefore:
93
+
94
+ - **resets `hits`, `thumbs_up`, and `thumbs_down` to 0.** The import path ignores
95
+ those columns. That is the feedback history the whole maintenance flow runs on:
96
+ no hits means no `needs_attention` list, no `top_hits`, no thumbs-down ranking,
97
+ and the loss is permanent — nothing recomputes it.
98
+ - **assigns brand-new ids.** The `id` column in the CSV is not honoured, so every
99
+ rule id on that server changes and any id quoted earlier in the conversation,
100
+ in a ticket, or in another server's mapping goes stale.
101
+ - **drops server-only rules** that are not in the CSV.
102
+
103
+ Then get an explicit confirmation of *that* — the analytics loss specifically —
104
+ before running it. If the user's actual goal was "make server B look like server
105
+ A", say so and offer the per-rule route instead.
106
+
107
+ ## Batch-write etiquette
108
+
109
+ - **Relay the whole plan.** It names every affected rule — a batch `delete` prints
110
+ one compact line per rule, counters included — and a summarised "12 rules" hides
111
+ the one wrong id. Paste the list.
112
+ - **One server per run.** Keep a fleet change as N separate confirmed runs so each
113
+ server's plan is reviewed against that server's current state.
114
+ - **Re-read the summary table after execution.** The run continues past failures
115
+ and exits non-zero, so the summary — not the exit code, not the plan — is the
116
+ record of what landed and which new ids `create` returned. Retry only the failed
117
+ ids.
118
+ - **Verify after writing** with `dr error-enrichments get <ids...> --server <S>`,
119
+ which takes the same id list you just wrote. With `--json`, remember the shape
120
+ flips: a bare object for one id, an array for two or more, and an id that failed
121
+ to read is missing from the array rather than represented in it — check the exit
122
+ code and the stderr warnings, not just the payload ([core.md](core.md)).
@@ -0,0 +1,219 @@
1
+ ---
2
+ name: dr-error-enrichment-author
3
+ description: Author, edit, or delete integration error-enrichment rules — the regex-to-readable-message rules Datarails shows a user when a sync fails. Drafts and server-validates the pattern, gets the customer-facing wording signed off, picks a rank, writes it through the confirm gate, and offers propagation to the other servers. Reviewing and approving existing drafts belongs to dr-error-enrichment-maintain, not here. Use when a specific error needs a rule, or an existing rule needs fixing or removing. Triggers - "add an error enrichment rule", "make this error message readable", "fix the wording of this error", "this error shows no explanation", "why does this integration error show no explanation", "delete an enrichment rule", "test this error regex", "the regex isn't matching".
4
+ ---
5
+
6
+ # dr-error-enrichment-author — author, edit, or delete a rule
7
+
8
+ ## Read first
9
+
10
+ This flow assumes both shared references and does not repeat them:
11
+
12
+ - [../dr-error-enrichment/reference/core.md](../dr-error-enrichment/reference/core.md)
13
+ — what a rule is, permissions, the full `dr error-enrichments` command surface,
14
+ how matching actually works (`re.match` anchoring, rank order, drafts stealing
15
+ the hit), where to get raw error text off a tenant.
16
+ - [../dr-error-enrichment/reference/safety-backup.md](../dr-error-enrichment/reference/safety-backup.md)
17
+ — confirming the target server, backing up before the first write, the revert
18
+ path, the Tier A/B write gates, and why `import` is never an edit tool.
19
+
20
+ Confirm the target server and take the backup **before** step 8 below, not after:
21
+ `dr error-enrichments export --server <S> --out /tmp/ee-<S>.csv` for the one
22
+ server you are writing to, or
23
+ `dr error-enrichments export --all-servers --out /tmp/ee-backup/` (with a sweep
24
+ `--out` is a directory, one `<SERVER>.csv` per server) when the session is likely
25
+ to spread to the fleet.
26
+
27
+ ## Authoring flow
28
+
29
+ 1. **Get real samples.** Ask for the raw error text. If the user only names a
30
+ symptom, pull samples from the tenant (`integrations get`,
31
+ `integrations query-status --json`) or mine groundcover — the query mechanics
32
+ and the env-to-server mapping are in
33
+ [../dr-error-enrichment/reference/groundcover.md](../dr-error-enrichment/reference/groundcover.md),
34
+ and `dr-error-enrichment-improve` is the flow built on it. A rule written from
35
+ a paraphrase matches nothing.
36
+
37
+ 2. **Check for an existing rule first.** A near-miss rule is usually better
38
+ edited than duplicated:
39
+
40
+ ```bash
41
+ dr error-enrichments list --server <S> --integration-type <T> --json
42
+ ```
43
+
44
+ Read what the vendor already covers, so the rank you pick next doesn't
45
+ shadow a good rule.
46
+
47
+ 3. **Draft the pattern** with a leading `.*` and escaped metacharacters, keeping
48
+ the volatile parts (ids, paths, hostnames, quantities) as `.*` or character
49
+ classes so it generalises past this one sample.
50
+
51
+ 4. **Validate it server-side** against the real samples plus at least one error
52
+ that must *not* match:
53
+
54
+ ```bash
55
+ dr error-enrichments test-regex --server <S> --pattern '.*INVALID_LOGIN.*' \
56
+ --sample 'Error: INVALID_LOGIN for user x' --sample 'Unrelated timeout'
57
+ ```
58
+
59
+ Iterate until matches and non-matches are both right. `valid: false` means
60
+ the regex itself doesn't compile. `test-regex` runs the same `re.match` plus
61
+ newline-stripping the resolver does — trust it over local regex reasoning,
62
+ and never hand-verify a pattern you could test.
63
+
64
+ 5. **Draft the message and steps.** `description` states what went wrong in the
65
+ customer's language — no stack traces, no internal identifiers, no vendor
66
+ error code as the whole message. `resolution_steps` are ordered, concrete
67
+ actions the customer or CSM can actually take. Propose both to the user and
68
+ let them approve the wording; this text ships to customers.
69
+
70
+ 6. **Pick a rank.** Rank is priority within the vendor: specific rules above
71
+ general catch-alls, or the general one matches first and hides the better
72
+ message. The server shifts the sibling rows to make room — pass the rank you
73
+ want and never renumber siblings by hand.
74
+
75
+ 7. **Set `classification`** (`DataSource` | `Filebox` | `Unclassified`) and
76
+ decide `approved`. Create as a draft (`--approved false`) only when the
77
+ pattern still needs field validation, and say out loud what that costs: a
78
+ draft steals the hit from any lower-ranked approved rule and shows the user
79
+ nothing, so a draft left in place is an active user-facing gap, not a
80
+ harmless staging state.
81
+
82
+ 8. **Write it** through the two-step confirm (run without `--confirm-token`,
83
+ relay the plan, re-run with the token), then verify:
84
+
85
+ ```bash
86
+ dr error-enrichments get <id> --server <S>
87
+ ```
88
+
89
+ 9. **Propagate.** A rule worth adding on one server is almost always worth
90
+ adding on the others. Offer `dr-error-enrichment-reconcile` rather than
91
+ leaving the fleet inconsistent.
92
+
93
+ ## Several rules in one pass
94
+
95
+ The CRUD subcommands take batches. Same gating, same plan-then-token cycle —
96
+ one confirm covers the batch.
97
+
98
+ **Create many.** `--payload-file` accepts either one rule object (as before) or
99
+ an **array** of rule objects:
100
+
101
+ ```bash
102
+ cat > /tmp/new-rules.json <<'JSON'
103
+ [
104
+ {"integration_type": "NetSuite", "rank": 3, "re_pattern": ".*INVALID_LOGIN.*",
105
+ "description": "...", "resolution_steps": ["...", "..."], "approved": true},
106
+ {"integration_type": "NetSuite", "rank": 4, "re_pattern": ".*SSS_INVALID_SCRIPT.*",
107
+ "description": "...", "resolution_steps": ["..."], "approved": false}
108
+ ]
109
+ JSON
110
+ dr error-enrichments create --payload-file /tmp/new-rules.json --server <S>
111
+ ```
112
+
113
+ **One patch across several rules** — positional ids, field flags applied to
114
+ every id:
115
+
116
+ ```bash
117
+ dr error-enrichments update 12 19 24 --approved true --server <S>
118
+ ```
119
+
120
+ That `--approved true` form is **the last step of the approval protocol, not a
121
+ shortcut past it**. `dr-error-enrichment-maintain` owns approval: it pulls each
122
+ draft, shows it to the user against approved siblings for the same vendor, tests
123
+ the pattern against a real sample, and takes a per-rule confirmation. Only drafts
124
+ that have each been through that cycle may be flipped together here. If the
125
+ request that reached you is "approve rules 12, 19 and 24", the answer is to run
126
+ the maintain protocol first — never to run this line.
127
+
128
+ `--rank` is rejected with several ids: rank is a per-rule priority within a
129
+ vendor and the server shifts siblings on every change, so one shared rank across
130
+ several rules is never what you meant. Change ranks one rule at a time, or give
131
+ each rule its own rank in an array payload.
132
+
133
+ **A different patch per rule** — an **array** of `{id, ...fields}` entries and
134
+ no positional ids:
135
+
136
+ ```bash
137
+ cat > /tmp/patches.json <<'JSON'
138
+ [
139
+ {"id": 12, "rank": 2},
140
+ {"id": 19, "description": "...", "resolution_steps": ["..."]}
141
+ ]
142
+ JSON
143
+ dr error-enrichments update --payload-file /tmp/patches.json --server <S>
144
+ ```
145
+
146
+ **Several deletions under one gate:**
147
+
148
+ ```bash
149
+ dr error-enrichments delete 31 32 33 --server <S>
150
+ ```
151
+
152
+ The delete plan prints one compact line per rule — id, integration type, rank,
153
+ pattern, and the hits/thumbs counters — so relay the plan verbatim: that block is
154
+ the user's only view of what is about to be destroyed.
155
+
156
+ Verify a batch afterwards with the same id list:
157
+
158
+ ```bash
159
+ dr error-enrichments get 12 19 24 --json --server <S>
160
+ ```
161
+
162
+ `get --json` returns a bare object for one id and an **array** for two or more,
163
+ so parse accordingly.
164
+
165
+ Four things to hold onto with batches:
166
+
167
+ - **A failure does not stop the run.** Each item is sent in turn, per-item
168
+ failures are recorded, and the command prints a summary table naming what
169
+ succeeded (with the new id for each created rule) and what failed, then exits
170
+ non-zero. The table — not the exit code — is what tells you which items
171
+ actually landed; retry only those.
172
+ - **The confirm token binds the field values too.** A batch plan hashes the
173
+ targets *and* the values being written, so editing the payload file or changing
174
+ a field flag between plan and confirm invalidates the token — re-run the plan
175
+ step and relay the new plan. (A single-target write binds only its target, as
176
+ it always has, so the same care applies there by hand.)
177
+ - **Array entries are validated before anything is sent.** A wrong field type or
178
+ a repeated id is rejected up front, and the message names the offending entry
179
+ by its position in the file (`entry 3: …`) — fix that entry and re-plan rather
180
+ than retrying with the old token.
181
+ - **A bulk create into one `integration_type` shifts that vendor's ranks.** Each
182
+ insert makes room for itself, so ranks you assumed while drafting may not be
183
+ the ranks you end up with. Re-run
184
+ `dr error-enrichments list --server <S> --integration-type <T> --json`
185
+ afterwards and check the order is what you intended.
186
+
187
+ Batch writes are still one server per run — there is no cross-server write.
188
+
189
+ ## Deleting a rule
190
+
191
+ Show the user the rule's `hits`, `thumbs_up`, and `thumbs_down` from the plan
192
+ step before confirming. A rule with meaningful hits is usually better re-pointed
193
+ with `update` (new pattern, better wording) than deleted, because deleting it
194
+ drops its feedback history with it — and that history is what
195
+ `dr-error-enrichment-maintain` ranks its work from. Delete is Tier A: on a prod
196
+ server the binary itself prompts a human for a challenge word, so get the user's
197
+ "yes", re-run with the token, and let the binary do the asking.
198
+
199
+ ## Pitfalls
200
+
201
+ | Mistake | Fix |
202
+ |---|---|
203
+ | Pattern without a leading `.*` | `re.match` anchors at the start — write `.*NEEDLE.*` |
204
+ | Writing a pattern expecting `\n` | Newlines are stripped before matching; the error is one long line |
205
+ | Hand-verifying a regex you could test | `test-regex` runs the server's own matcher — use it, with a non-matching sample too |
206
+ | Pattern fitted to one sample | Generalise ids, paths, hostnames, quantities to `.*` or a character class |
207
+ | Adding a rule that duplicates a near-miss | `list --integration-type <T>` first; edit the existing rule instead |
208
+ | A general rule ranked above a specific one | The general one matches first and hides the better message — rank specific first |
209
+ | Renumbering siblings by hand after a rank change | The server shifts them; pass the rank you want and leave the rest alone |
210
+ | Leaving a draft in place "for now" | It steals the hit and shows nothing — approve it, fix it, or lower its rank |
211
+ | Vendor codes or stack traces in `description` | Customer-facing text — plain language, wording approved by the user |
212
+ | Expecting `--resolution-step` with no value to clear the steps | PATCH-omit semantics — clear with `--payload-file` and `{"resolution_steps": []}` |
213
+ | Reading only the exit code of a batch | It is non-zero if *any* item failed — read the summary table for what landed |
214
+ | Flipping `--approved true` over a list of drafts here | Approval is `dr-error-enrichment-maintain`'s protocol — one rule at a time, then batch the confirmed set |
215
+ | Re-using a batch token after editing the payload file | The batch token binds the values too; edit means re-plan |
216
+ | `--rank` with several positional ids | Rejected — ranks are per rule; use one id at a time or an array payload |
217
+ | Assuming drafted ranks survive a bulk create | Each insert shifts siblings; re-`list` the vendor afterwards |
218
+ | Deleting a rule with real hits | Re-point it with `update`; deleting discards the feedback history |
219
+ | Writing on one server and stopping | Offer `dr-error-enrichment-reconcile` — per-server drift is the problem this tooling exists to fix |
@@ -0,0 +1,218 @@
1
+ ---
2
+ name: dr-error-enrichment-improve
3
+ description: Continuous improvement for integration error enrichment - mine groundcover for sync errors that no rule matches yet, rank them by volume, triage out the ones caused by our own bugs, and turn the rest into proposed rules. Use when the user wants to know what the rule table is missing rather than to edit a rule they already have in mind. Triggers - "which errors have no rule", "find unmatched integration errors", "suggest new enrichment rules", "what should we add to the error enrichment rules", "mine groundcover for integration errors", "what are we not explaining to customers", "is this error a bug or a missing rule".
4
+ ---
5
+
6
+ # dr-error-enrichment-improve — find the errors nothing matches
7
+
8
+ This is the outward-looking pass of the error-enrichment family: every other flow
9
+ starts from a rule that exists, this one starts from the errors that have none.
10
+ You are not fixing a rule the user named — you are finding the errors that reach
11
+ customers with **no explanation at all**, ranking them by how much traffic they
12
+ represent, sorting out which of them are our own bugs, and proposing rules for
13
+ what is left.
14
+
15
+ That sort is not a formality. A rule is a message to a customer about something
16
+ they can act on; written over a defect of ours it tells them to fix what they
17
+ cannot, and it makes the defect stop looking like one. **Some candidates should
18
+ become bugs, not rules** — and a run that ends in three bug reports and no rules
19
+ did its job.
20
+
21
+ Authoring the chosen rules is `dr-error-enrichment-author`. Approving drafts and
22
+ fixing disliked rules is `dr-error-enrichment-maintain`.
23
+
24
+ ## Read first
25
+
26
+ - [../dr-error-enrichment/reference/groundcover.md](../dr-error-enrichment/reference/groundcover.md)
27
+ — the gcQL query mechanics, the env-to-server mapping, and the hash-not-text
28
+ caveat. This flow is mostly that file; do not compose queries from memory.
29
+ - [../dr-error-enrichment/reference/core.md](../dr-error-enrichment/reference/core.md)
30
+ — matching semantics. A proposal that ignores `re.match` anchoring is dead on
31
+ arrival, and a proposed rank that sits under an existing catch-all never fires.
32
+ - [../dr-error-enrichment/reference/jira-bugs.md](../dr-error-enrichment/reference/jira-bugs.md)
33
+ — the bug sweep in step 6: the `System area` field and its exact option value,
34
+ the JQL that actually returns rows, how to tell our defect from the customer's
35
+ condition, and how to file the bug if one is missing.
36
+ - [../dr-error-enrichment/reference/safety-backup.md](../dr-error-enrichment/reference/safety-backup.md)
37
+ — before any of the proposals become writes. The backup is one command:
38
+ `dr error-enrichments export --all-servers --out /tmp/ee-backup/` (with a
39
+ sweep, `--out` is a directory and each server lands as its own
40
+ `<SERVER>.csv`).
41
+
42
+ ## The loop
43
+
44
+ **1. Pick the region and the window, and prove they line up.** Ask which server
45
+ the user intends to improve, then map it to the groundcover env before querying —
46
+ `UK` is `prod-uk*`, `CA` is `prod-canada-central`, `DEMO` is `prod-demo`, and
47
+ `US`/`US2` are ambiguous across the three US envs and `EU` is not mapped at
48
+ all, so resolve those explicitly (see the reference). Mining one region's logs to justify a rule you then write to
49
+ another is the failure mode this step exists to prevent. Start with a tight
50
+ window and widen only after it returns rows.
51
+
52
+ **2. Find the unmatched-resolve marker lines.** The monolith logs every resolve
53
+ that matched nothing with `[ErrorEnrichment] No matching rule`. Query on the bare
54
+ keyword — a leading wildcard can't use the full-text index and times out on a
55
+ busy env:
56
+
57
+ ```
58
+ ErrorEnrichment | stats count()
59
+ ErrorEnrichment | fields _time, content | limit 20
60
+ ```
61
+
62
+ **3. Group by vendor to find the worst connector.** The marker's fields are
63
+ printf-formatted inside the message text, not indexed attributes, so extract them
64
+ first or you get one silent empty group:
65
+
66
+ ```
67
+ ErrorEnrichment | extract_regexp "vendor_name=(?P<vendor>[^ ]+)" from content
68
+ | stats by (vendor) count() as unmatched | sort by (unmatched desc) | limit 20
69
+ ```
70
+
71
+ **4. Group by error hash to rank distinct offenders.** Identical
72
+ `err_sha256_prefix` values are the same normalised error, so this counts *distinct*
73
+ unmatched errors rather than volume of one noisy tenant. The top rows are the
74
+ highest-value *candidates* — a lot of traffic each, still to be cleared by
75
+ step 6:
76
+
77
+ ```
78
+ ErrorEnrichment | extract_regexp "err_sha256_prefix=(?P<sig>[0-9a-f]+)" from content
79
+ | stats by (sig) count() as hits | sort by (hits desc) | limit 50
80
+ ```
81
+
82
+ **5. Recover the raw text for each candidate.** The marker carries only a hash and
83
+ a length **by design** — vendor errors can embed customer paths and tokens, so the
84
+ text is deliberately kept out of the log. Get it from the tenant instead
85
+ (`integrations get <id>`, or `integrations query-status --json` for the `query_id`
86
+ the marker names) or from the vendor sync error logs around the same timestamp and
87
+ `datasource_id`. Without real text you cannot write a pattern, only guess at one.
88
+
89
+ **6. Triage each candidate against the integration bugs before it becomes a
90
+ proposal.** Read
91
+ [jira-bugs.md](../dr-error-enrichment/reference/jira-bugs.md) for the exact
92
+ queries; the shape of the step is:
93
+
94
+ - Sweep the open integration bugs **and** the ones fixed in the last ~60 days —
95
+ `cf[10717] = "Integration"` in project `DR` (the option is singular
96
+ `Integration`; a wrong value returns zero rows and no error). A signature whose
97
+ volume stops the week a fix shipped needs nothing at all.
98
+ - The `System area` field is optional on the create screen and often empty, so
99
+ follow with a free-text `text ~ "<stable phrase>"` pass with no area filter
100
+ before calling a signature unticketed.
101
+ - Then classify each candidate: **customer- or vendor-side** → propose a rule;
102
+ **our defect with a ticket** → no rule, report the signature against the bug
103
+ key, because your volume figure is priority evidence that bug does not have
104
+ yet; **our defect with no ticket** → no rule, flag it and offer to open one;
105
+ **unclear** → no rule, say what would settle it.
106
+ - Classify on **whose code raised it**, not on what the error looks like.
107
+ Tracebacks, `NoneType`, un-enriched exception bodies, 5xx and timeouts all
108
+ reach us from vendors and from the customer's own machine too — they are clues
109
+ that raise the question. Settle it with the log source, the host, and whose
110
+ endpoint answered; if those don't converge the verdict is **unclear**, which is
111
+ still not a rule.
112
+ - Opening the bug is outward-facing: show the summary and body first and wait for
113
+ a yes, group related signatures into one ticket, and set `customfield_10717`
114
+ (`{"id": "20748"}`) or the next sweep cannot see it.
115
+ - **Redact the error text in the draft you show, before anything is filed** —
116
+ credentials and connection strings, usernames, hosts/DSNs/paths, customer and
117
+ account identifiers, and any row-level financial values. Keep the exception
118
+ class, message template, module, vendor error code and the
119
+ `err_sha256_prefix`; that is what the fix needs. The checklist is in
120
+ [jira-bugs.md](../dr-error-enrichment/reference/jira-bugs.md) — nothing
121
+ sanitises this for you.
122
+
123
+ If the Atlassian tools are not available in the session, **say so and mark the
124
+ proposals un-triaged** — a skipped sweep produces a list that looks exactly like
125
+ a triaged one.
126
+
127
+ **7. Sanity-check that nothing already covers it.** A "missing" rule is often an
128
+ existing rule that is too narrow, or one that is a draft and therefore invisible:
129
+
130
+ ```bash
131
+ dr error-enrichments list --server <S> --integration-type <T> --json
132
+ ```
133
+
134
+ Check the vendor's existing ranks at the same time — a proposal has to sit above
135
+ whatever catch-all would otherwise swallow it.
136
+
137
+ **8. Draft one proposal per surviving candidate.** Each proposal states: the
138
+ vendor (`integration_type`), the proposed pattern, the proposed `description` and
139
+ `resolution_steps` in customer language, the samples it was built from, and the
140
+ estimated volume it would cover (the `hits` count from step 4). Volume is what
141
+ makes the list rankable — without it the user is choosing blind.
142
+
143
+ ## Handing the results off
144
+
145
+ A run has **two** outputs, and the second one is not a footnote. Report them as
146
+ separate lists:
147
+
148
+ 1. **Ranked rule proposals** — the candidates that are genuinely the customer's
149
+ or the vendor's to fix.
150
+ 2. **Defects** — the candidates held back in step 6, each with its volume and
151
+ either the bug key it belongs to or an offer to file one. Say plainly that
152
+ these deliberately get no rule and why.
153
+
154
+ Present the proposals and let the user choose which to write. Do not write the
155
+ whole list because it is on the list.
156
+
157
+ - Route the chosen ones to `dr-error-enrichment-author` for the per-rule
158
+ treatment, **or** write them together as a batch:
159
+ `dr error-enrichments create --payload-file proposals.json --server <S>` with an
160
+ array of rule objects. Batch writes are sequential and a per-item failure does
161
+ not stop the run — read the summary table afterwards to see which ids landed.
162
+ Every entry is type-validated before anything is sent, and the error names the
163
+ offending entry by its position in the file (`entry 3: …`), so fix that entry
164
+ and re-plan. Re-planning is
165
+ required anyway: the batch confirm token binds the field values as well as the
166
+ targets, so a payload edited after the plan was relayed will not redeem.
167
+ - **Validate every pattern with `test-regex` before it goes into the payload
168
+ file**, against the real samples *and* at least one error that must not match.
169
+ A batch create does not validate patterns for you.
170
+ - Create as drafts (`approved: false`) when a pattern still needs field
171
+ validation. Say out loud what that costs: a draft steals the hit and shows the
172
+ user nothing, so it is strictly worse than no rule until it is approved. A draft
173
+ is a commitment to come back — book the follow-up through
174
+ `dr-error-enrichment-maintain`.
175
+
176
+ ## Prove the pipeline before reporting zero
177
+
178
+ An empty gcQL result is indistinguishable from a wrong env, a too-narrow window,
179
+ or a misspelled field — none of those error, they all return nothing. Run a query
180
+ you *expect* to return rows (`ErrorEnrichment | stats count()`) and confirm the
181
+ env resolves before telling the user there are no unmatched errors. "We're fully
182
+ covered" is a much more expensive thing to say wrongly than "I couldn't reach the
183
+ logs".
184
+
185
+ The bug sweep fails the same silent way: a wrong `System area` option value
186
+ returns zero rows without erroring, so "no known bug" and "bad JQL" look
187
+ identical. Prove that query returns rows too before you let a candidate through
188
+ as nobody's fault but the customer's.
189
+
190
+ ## Customer data
191
+
192
+ Raw vendor error text is customer data. Use it to build the rule; keep it out of
193
+ chat summaries beyond what the rule actually needs; put sanitised text in
194
+ `examples`. The hash-only marker exists for this reason — don't undo it by pasting
195
+ whole error bodies into the conversation, or into a Jira ticket, which is a wider
196
+ audience still.
197
+
198
+ ## Pitfalls
199
+
200
+ | Mistake | Fix |
201
+ |---|---|
202
+ | `stats by (vendor_name)` on the marker line | Those are text inside `content`, not fields — `extract_regexp` first, or you get one empty group |
203
+ | `content:*ErrorEnrichment*` | Leading wildcard can't use the index; query the bare keyword `ErrorEnrichment` |
204
+ | Treating an empty result as "no unmatched errors" | Verify env, window, and field names with a query you expect to return rows |
205
+ | Mining `prod-uk` and writing the rule to `US` | Resolve the env-to-server mapping first; volume evidence is per region |
206
+ | Expecting raw error text from the marker | It is hash + length only; pull the text from the tenant or the vendor sync logs |
207
+ | Ranking candidates by raw line count | Group by `err_sha256_prefix` — one noisy tenant is not the same as a widespread error |
208
+ | Proposing a rule for an error our own code raised | Triage against the integration bugs first — a rule over a defect blames the customer and buries the bug |
209
+ | Skipping the bug sweep because the errors "look like vendor errors" | Tracebacks and 5xx from our services hide in vendor-shaped noise; the sweep is two queries |
210
+ | Sweeping only *open* bugs | A fix shipped last week disqualifies the rule too — include `resolved >= -60d` |
211
+ | Trusting `cf[10717]` alone, or writing `"Integrations"` | The field is optional and often empty, and the option is singular `Integration` — add a `text ~` pass, and remember a bad option value returns zero rows without erroring |
212
+ | Filing a bug per signature, or filing one unasked | Group related signatures, show the ticket first, and set `System area` so the next sweep sees it |
213
+ | Presenting proposals when the Jira sweep could not run | Say they are un-triaged — the list looks the same either way |
214
+ | Proposing a pattern with no leading `.*` | `re.match` anchors at the start; it will match nothing in production |
215
+ | Writing the whole proposal list unasked | Present ranked, let the user pick — each rule is user-facing text |
216
+ | Batch-creating patterns straight from the proposal | `test-regex` every one first; `create` does not validate matching behaviour |
217
+ | Editing `proposals.json` after relaying the plan | The batch token binds the values too — re-run the plan step and relay it again |
218
+ | Leaving new drafts behind | A draft steals the hit and shows nothing — hand it to `dr-error-enrichment-maintain` |