@tenonhq/dovetail-servicenow 0.0.51 → 0.0.52

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 CHANGED
@@ -742,8 +742,16 @@ substring-matched, because `"[owner_id]"` contains `"owner"`. `type` is `access_
742
742
  **Uniqueness is not readable.** `v_db_index` has no uniqueness field, so a unique index
743
743
  and an ordinary one are indistinguishable in it: `unique` is left **absent** rather than
744
744
  guessed, and `uniqueness-enforced` is reported in `unverified` on every result. Only a
745
- duplicate-insert test proves enforcement. An empty result more likely means the table name
746
- is wrong than that the table is unindexed - every physical table has a `PRIMARY`.
745
+ duplicate-insert test proves enforcement. An empty result for a table that does not exist
746
+ means the table name is wrong - every physical table has a `PRIMARY`.
747
+
748
+ **Table-per-hierarchy children list their storage root's indexes.** `v_db_index` lists
749
+ indexes by *physical* table, and a table stored in an ancestor's physical table (anything
750
+ extending `task`, for example) has no rows under its own name. When the table exists but
751
+ has no rows, its `super_class` chain is walked to the first ancestor that has them, and
752
+ that root's indexes are listed - with `storageTable` and the note naming the root.
753
+ `index-create` and `add-index` **refuse** such a child on the live path, naming the root,
754
+ rather than build on it or report a false "NOT created".
747
755
 
748
756
  ### Create an index (composite and non-unique included)
749
757
 
@@ -792,6 +800,15 @@ was a silent no-op for that reason.)
792
800
  - **Idempotent.** On the live path `v_db_index` is read first, and an index over *exactly*
793
801
  these columns short-circuits to `already-exists` with no pin, no form session and no
794
802
  write. Column **order** is part of an index's identity - `[a;b]` is not `[b;a]`.
803
+ - **Table-per-hierarchy children are refused.** A table with no `v_db_index` rows of its
804
+ own whose ancestor has them is stored in that ancestor's physical table; the run stops
805
+ before any write and names the root (run against the root if that is what you want).
806
+ - **Identity check (warns, never refuses).** The pin runs through the REST client (an API
807
+ key when one is configured) but the build is scheduled by the form session (always
808
+ `SN_USER`), and the build captures into the *form* user's current set. The REST caller's
809
+ own `sys_user` row is read first; when it is not the form-login user (or cannot be read)
810
+ the run still goes ahead and every result note carries an `IDENTITY WARNING`. The capture
811
+ read-back then reports where the row actually landed (`captured` / `captureFoundIn`).
795
812
  - **The pin is read back.** The set is pinned with Dovetail's own `changeUpdateSet` and
796
813
  `currentUpdateSet` is read; a pin that did not take stops the run before the session
797
814
  opens.
@@ -804,7 +821,8 @@ was a silent no-op for that reason.)
804
821
  values (EMPTY counts). Then the capture row is looked for in the pinned set:
805
822
  `captured:true` only when it was read back; an index that exists but was not captured
806
823
  is `created:true, captured:false` with `update-set-capture` in `unverified` - and
807
- exit code 2, because it will not travel.
824
+ exit code 2, because it will not travel. In that case the capture name is searched
825
+ across every set and the set(s) it actually landed in come back in `captureFoundIn`.
808
826
  - **Uniqueness is still never claimed.** `uniqueness-enforced` stays in `unverified` on
809
827
  every status.
810
828
 
@@ -859,7 +877,8 @@ npx dove-sn create-record \
859
877
  switches the executing user's app scope + update set server-side, inserts, and
860
878
  restores both — so the record is owned by the right app and the insert is captured
861
879
  in the right update set. Like `set-field` it **refuses** schema tables and verifies
862
- via read-back. `--scope` and `--update-set` are required; `--if-absent
880
+ via read-back. It also **refuses** `sys_update_set` — the op cannot set an update
881
+ set's application, so create sets with `dove createUpdateSet` instead. `--scope` and `--update-set` are required; `--if-absent
863
882
  "<encoded-query>"` makes re-runs idempotent (the insert is skipped when the query
864
883
  already matches a row). Exit codes: `0` created / skipped-in-sync / dry-run, `1` bad
865
884
  args, `2` write landed unverified (or skipped with drift). To **update** an existing
@@ -916,14 +935,19 @@ npx dove-sn delete-record \
916
935
  `delete-record` wraps the core `deleteRecord` op. It is **dry-run by default** —
917
936
  nothing is deleted without `--apply` (`--dry-run` wins if both are given). `--sys-id`
918
937
  must be a 32-character lowercase hex id and `--table` a plain table name; both are
919
- validated before any network call. `--update-set` is **required** and sent with the
920
- delete, but **the capture is not pinned yet**: until
921
- [#297](https://github.com/TenonHQ/Dovetail/issues/297) ships server-side, the op ignores
922
- `update_set_sys_id` and captures the delete into the session's **current** update set —
923
- make that the set you want before `--apply`. Every result note repeats this caveat; the
924
- client keeps sending the field so it takes effect the moment the server honours it. Like its siblings it **refuses** schema tables (`sys_db_object` /
925
- `sys_dictionary`). Exit codes: `0` deleted / dry-run, `1` bad args or no such record,
926
- `2` the delete returned but the record is **still present** on read-back.
938
+ validated before any network call. `--update-set` is **required**, must exist and be
939
+ **in progress** (checked on the dry-run too). Until
940
+ [#297](https://github.com/TenonHQ/Dovetail/issues/297) ships server-side the op ignores
941
+ `update_set_sys_id` and captures the delete into the session's **current** update set, so
942
+ the verb pins `--update-set` as current first (refusing, nothing deleted, if the pin does
943
+ not read back) and then reads the DELETE row back from `sys_update_xml`. The result carries
944
+ `captured`, `capturedInto` and `captureState` (`in-set` / `other-set` / `none` /
945
+ `unverified`). Like its siblings it **refuses** schema tables (`sys_db_object` /
946
+ `sys_dictionary`). A failed delete call never skips the read-back. Exit codes: `0` deleted
947
+ and captured in the set, a table that writes no capture at all (`none`, with a note), or a
948
+ dry-run; `1` bad args, no such record, or an unknown/closed update set; `2` the record is
949
+ **still present** on read-back (including a server-refused delete), its state is unknown,
950
+ the pin did not take, or the DELETE landed in a different set / could not be read back.
927
951
 
928
952
  All three verbs are exported for programmatic use:
929
953
 
@@ -941,6 +965,36 @@ var r = await setField({
941
965
  console.log(r.status, r.verified); // "applied" true
942
966
  ```
943
967
 
968
+ ### Register UI component events (sync-ux-events)
969
+
970
+ UI Builder can only map an event a component dispatches when it exists as a
971
+ `sys_ux_event` record **and** is listed in the component macroponent's
972
+ `dispatched_events`. A component deploy does not reliably create either, so
973
+ `sync-ux-events` reads the component's `now-ui.json` and reconciles every
974
+ `components.<tag>.actions[]` entry against the instance.
975
+
976
+ ```bash
977
+ # Dry-run (the default) — per action: ok / create / link / drift / ambiguous, plus orphans
978
+ npx dove-sn sync-ux-events --file path/to/now-ui.json
979
+
980
+ # Apply — creates the missing events and appends them to dispatched_events
981
+ npx dove-sn sync-ux-events --file path/to/now-ui.json \
982
+ --component cadso-journey-builder --update-set <sys_id> --apply
983
+ ```
984
+
985
+ The macroponent is the `sys_ux_macroponent` (category `component`) whose
986
+ `root_component` is the `sys_ux_lib_component` with that tag; events are created in
987
+ the macroponent's scope via the scope-aware `createRecord` op and the list edit goes
988
+ through `pushWithUpdateSet`, both captured into `--update-set`. The write is
989
+ **append-only** — existing `dispatched_events` entries are never dropped or reordered —
990
+ and is re-read to verify. Label/description **drift** and **orphans** (linked but no
991
+ longer declared) are reported, never changed; an event name carried by several records
992
+ is **ambiguous** and never written. Event names and tags are charset-validated before
993
+ they reach a query. Re-runs are no-ops. Exit codes: `0` in sync / dry-run / applied and
994
+ verified; `1` bad args or unreadable file; `2` an unresolved or ambiguous
995
+ component/event, or a write that did not verify. MCP: `sync_ux_events` (dry-run unless
996
+ `confirm:true`).
997
+
944
998
  ### Invoke an arbitrary REST operation
945
999
 
946
1000
  Invoke any authenticated ServiceNow REST operation — an application's own
@@ -1198,8 +1252,8 @@ existing record), `create_record` (insert one record) and `delete_record` (delet
1198
1252
  record — dry-run by default, `confirm:true` to apply, `updateSetSysId` required, the
1199
1253
  record read back before AND after so success is only reported once it is confirmed
1200
1254
  gone) — all read-back-verified; `set_field` / `create_record` are captured in the
1201
- update set you pass, while `delete_record` captures into the session's current set
1202
- until [#297](https://github.com/TenonHQ/Dovetail/issues/297) ships — `host_assets` (deploy a built
1255
+ update set you pass, while `delete_record` pins the set as current and reads the DELETE
1256
+ capture back (`captureState`) until [#297](https://github.com/TenonHQ/Dovetail/issues/297) ships — `host_assets` (deploy a built
1203
1257
  dist/), plus the Flow Designer
1204
1258
  tools `flow_view` (read a flow/subflow's step graph), `action_view` (read an action
1205
1259
  type's model), `action_edit` (structurally edit a published action type — per-step
package/dist/cli.js CHANGED
@@ -86,6 +86,7 @@ const deleteRecord_1 = require("./deleteRecord");
86
86
  const invokeRest_1 = require("./invokeRest");
87
87
  const publishApp_1 = require("./publishApp");
88
88
  const hostAssets_1 = require("./hostAssets");
89
+ const uxEvents_1 = require("./uxEvents");
89
90
  const flowDesigner_formatter_2 = require("./flowDesigner-formatter");
90
91
  function parseArgs(argv) {
91
92
  // `dove-sn --help` has no verb: a leading flag means an empty command and the
@@ -1572,7 +1573,14 @@ async function runIndexList(flags) {
1572
1573
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
1573
1574
  return 0;
1574
1575
  }
1575
- process.stdout.write(result.table + " — " + result.indexes.length + " index(es)\n");
1576
+ process.stdout.write(result.table +
1577
+ " — " +
1578
+ result.indexes.length +
1579
+ " index(es)" +
1580
+ (result.storageTable && result.storageTable !== result.table
1581
+ ? " (stored in " + result.storageTable + "'s physical table — these are its indexes)"
1582
+ : "") +
1583
+ "\n");
1576
1584
  for (var i = 0; i < result.indexes.length; i += 1) {
1577
1585
  var idx = result.indexes[i];
1578
1586
  process.stdout.write(" " +
@@ -2006,18 +2014,76 @@ async function runCreateRecord(flags) {
2006
2014
  return 2;
2007
2015
  return 0;
2008
2016
  }
2017
+ /**
2018
+ * dove-sn sync-ux-events:
2019
+ * --file <path/to/now-ui.json>
2020
+ * [--component <tag>]
2021
+ * [--update-set <sys_id>] (required with --apply)
2022
+ * [--apply] [--dry-run] [--json]
2023
+ * DRY-RUN BY DEFAULT. Exit codes: 0 in sync / dry-run / applied+verified, 1 bad args or
2024
+ * unreadable file, 2 unresolved/ambiguous component or event, or an unverified write.
2025
+ */
2026
+ async function runSyncUxEvents(flags, bare) {
2027
+ var bareErr = bareStringFlagError("sync-ux-events", bare);
2028
+ if (bareErr) {
2029
+ process.stderr.write(bareErr);
2030
+ return 1;
2031
+ }
2032
+ var file = flags.file;
2033
+ if (!file) {
2034
+ process.stderr.write("sync-ux-events: --file <now-ui.json> is required\n");
2035
+ return 1;
2036
+ }
2037
+ var apply = flags.apply === "true" && flags["dry-run"] !== "true";
2038
+ var nowUi;
2039
+ try {
2040
+ nowUi = JSON.parse(fs.readFileSync(path.resolve(file), "utf8"));
2041
+ }
2042
+ catch (err) {
2043
+ process.stderr.write("sync-ux-events: cannot read " + file + ": " + (err instanceof Error ? err.message : String(err)) + "\n");
2044
+ return 1;
2045
+ }
2046
+ var result;
2047
+ try {
2048
+ result = await (0, uxEvents_1.syncUxEvents)({
2049
+ client: (0, client_1.createClient)({}),
2050
+ nowUi: nowUi,
2051
+ component: flags.component,
2052
+ updateSetSysId: flags["update-set"],
2053
+ apply: apply,
2054
+ });
2055
+ }
2056
+ catch (err) {
2057
+ var message = err instanceof Error ? err.message : String(err);
2058
+ if (message.indexOf("ux-events: ") === 0)
2059
+ message = message.slice("ux-events: ".length);
2060
+ process.stderr.write("sync-ux-events: " + message + "\n");
2061
+ return 1;
2062
+ }
2063
+ if (flags.json === "true") {
2064
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2065
+ }
2066
+ else {
2067
+ process.stdout.write((0, uxEvents_1.formatUxEventSync)(result) + "\n");
2068
+ }
2069
+ return result.ok ? 0 : 2;
2070
+ }
2009
2071
  /**
2010
2072
  * dove-sn delete-record:
2011
2073
  * --table x_cadso_core_metric_point_type
2012
2074
  * --sys-id <32-hex sys_id> (the record to delete)
2013
- * --update-set <sys_id> (required — the delete is captured here, never the
2014
- * session default; server honours it once #297 ships)
2075
+ * --update-set <sys_id> (required — must exist and be in progress; pinned as
2076
+ * the session's current set before the delete, and the
2077
+ * DELETE capture row is read back from sys_update_xml)
2015
2078
  * [--apply] (DRY-RUN BY DEFAULT — nothing is deleted without it)
2016
2079
  * [--dry-run] [--json] (--dry-run wins over --apply)
2017
2080
  * Reads the record BEFORE (a missing record is an error, not a no-op delete) and AFTER
2018
2081
  * (success is only reported once the record is confirmed gone).
2019
- * Exit codes: 0 deleted/dry-run, 1 bad args or missing record, 2 delete returned but the
2020
- * record is STILL PRESENT on read-back.
2082
+ * Exit codes: 0 deleted (and captured in the requested set) / dry-run, 1 bad args, missing
2083
+ * record or unknown/closed update set, 2 the record is STILL PRESENT on read-back (including a
2084
+ * delete the server refused with an error), its state is unknown, the update-set pin did not
2085
+ * take (nothing deleted), or it was deleted but the DELETE capture landed in a different set or
2086
+ * could not be read back. A table with no update-set capture at all exits 0 with a note.
2021
2087
  */
2022
2088
  async function runDeleteRecord(flags) {
2023
2089
  var table = flags.table;
@@ -2063,7 +2129,15 @@ async function runDeleteRecord(flags) {
2063
2129
  result.sysId +
2064
2130
  " → update set " +
2065
2131
  result.updateSetSysId +
2132
+ (result.updateSetName ? " (" + result.updateSetName + ")" : "") +
2066
2133
  (result.verified ? " — verified gone" : "") +
2134
+ (result.status === "deleted"
2135
+ ? (result.captureState === "in-set"
2136
+ ? ", capture verified"
2137
+ : result.captureState === "none"
2138
+ ? ", no update-set capture (table not recorded?)"
2139
+ : ", NOT captured in that set")
2140
+ : "") +
2067
2141
  "\n" +
2068
2142
  result.note +
2069
2143
  "\n");
@@ -2073,6 +2147,12 @@ async function runDeleteRecord(flags) {
2073
2147
  }
2074
2148
  if (result.status === "failed")
2075
2149
  return 2;
2150
+ // Gone but the DELETE landed in ANOTHER set (the requested set would promote without it),
2151
+ // or the capture could not be read back: neither may read as success to a script. A table
2152
+ // that is not recorded in update sets at all ("none") is a plain data delete — exit 0, and
2153
+ // the note says the delete will not travel.
2154
+ if (result.status === "deleted" && (result.captureState === "other-set" || result.captureState === "unverified"))
2155
+ return 2;
2076
2156
  return 0;
2077
2157
  }
2078
2158
  /**
@@ -2704,6 +2784,9 @@ async function dispatch(parsed) {
2704
2784
  if (parsed.command === "create-record") {
2705
2785
  return await runCreateRecord(parsed.flags);
2706
2786
  }
2787
+ if (parsed.command === "sync-ux-events") {
2788
+ return await runSyncUxEvents(parsed.flags, parsed.bare);
2789
+ }
2707
2790
  if (parsed.command === "delete-record") {
2708
2791
  return await runDeleteRecord(parsed.flags);
2709
2792
  }
package/dist/cliUsage.js CHANGED
@@ -408,7 +408,7 @@ exports.VERB_USAGE = {
408
408
  {
409
409
  flag: "update-set",
410
410
  value: "<sys_id>",
411
- note: "Sent with the delete; see the #297 note below for where the capture lands today.",
411
+ note: "Must exist and be in progress (checked on the dry-run too); pinned as current before the delete.",
412
412
  },
413
413
  ],
414
414
  optional: [
@@ -420,10 +420,36 @@ exports.VERB_USAGE = {
420
420
  gateNote: "The dry-run prints the record snapshot that would be deleted.",
421
421
  example: "dove-sn delete-record --table x_cadso_core_metric_point_type --sys-id <32-hex sys_id> --update-set <sys_id> --apply",
422
422
  notes: [
423
- "Reads the record BEFORE (a missing record is an error, never a no-op delete) and AFTER (exit 2 if it is still present).",
424
- "Until TenonHQ/Dovetail#297 ships server-side the delete op IGNORES --update-set and captures into the session's current update set — make that the set you want before --apply.",
423
+ "Reads the record BEFORE (a missing record is an error, never a no-op delete) and AFTER (exit 2 if it is still present — including when the server refused the delete with an error).",
424
+ "Until TenonHQ/Dovetail#297 ships server-side the delete op IGNORES --update-set and captures into the session's current update set — so the verb pins --update-set as current first (refusing, nothing deleted, if the pin does not read back) and reads the DELETE row back from sys_update_xml: exit 2 when it landed in a different set or the read-back failed. A table that writes no update-set capture at all exits 0 with a note (captureState \"none\") — that delete will not travel.",
425
425
  ],
426
426
  },
427
+ "sync-ux-events": {
428
+ summary: "Register a UI component's now-ui.json actions as sys_ux_event records on its macroponent",
429
+ required: [
430
+ { flag: "file", value: "<now-ui.json>", note: "Path to the component's now-ui.json; every component with `actions` is synced." },
431
+ ],
432
+ optional: [
433
+ { flag: "component", value: "<tag>", note: "Limit to one component tag, e.g. cadso-journey-builder." },
434
+ {
435
+ flag: "update-set",
436
+ value: "<sys_id>",
437
+ note: "Required with --apply — the event creates and the macroponent edit are captured here.",
438
+ },
439
+ { flag: "apply", note: "Write (without it the run is a dry-run)." },
440
+ { flag: "dry-run", note: "Force a dry-run even with --apply." },
441
+ JSON_FLAG,
442
+ ],
443
+ gate: "apply",
444
+ gateNote: "The dry-run prints each action as ok / create / link / drift / ambiguous, plus orphans.",
445
+ example: "dove-sn sync-ux-events --file path/to/now-ui.json --component cadso-journey-builder --update-set <sys_id> --apply",
446
+ notes: [
447
+ "Macroponent = sys_ux_macroponent whose root_component is the sys_ux_lib_component with that tag (category component). Events are created in the macroponent's scope.",
448
+ "Append-only: missing events are created and their sys_ids appended to dispatched_events; existing entries are never dropped or reordered. Label/description drift and orphaned (linked but undeclared) events are reported, never changed.",
449
+ "Exit codes: 0 in sync / dry-run / applied and verified, 1 bad args or unreadable file, 2 unresolved or ambiguous component/event, or a write that did not verify.",
450
+ ],
451
+ stringFlags: ["file", "component", "update-set"],
452
+ },
427
453
  "host-assets": {
428
454
  summary: "Deploy a built dist/ to ServiceNow (carrier sys_ui_script + attachment + m2m)",
429
455
  required: [
@@ -9,7 +9,8 @@
9
9
  * The INSERT counterpart to `set-field`.
10
10
  *
11
11
  * NOT for schema tables (sys_db_object / sys_dictionary) — that's
12
- * create-table / add-column. To UPDATE an existing record, use `set-field`.
12
+ * create-table / add-column. NOT for sys_update_set — that's
13
+ * `dove createUpdateSet`. To UPDATE an existing record, use `set-field`.
13
14
  */
14
15
  import type { ServiceNowClient } from "./client";
15
16
  import type { RecordWriteResult } from "./setField";
@@ -10,7 +10,8 @@
10
10
  * The INSERT counterpart to `set-field`.
11
11
  *
12
12
  * NOT for schema tables (sys_db_object / sys_dictionary) — that's
13
- * create-table / add-column. To UPDATE an existing record, use `set-field`.
13
+ * create-table / add-column. NOT for sys_update_set — that's
14
+ * `dove createUpdateSet`. To UPDATE an existing record, use `set-field`.
14
15
  */
15
16
  Object.defineProperty(exports, "__esModule", { value: true });
16
17
  exports.createRecord = createRecord;
@@ -19,6 +20,11 @@ const setField_1 = require("./setField");
19
20
  // Same guard as set-field: schema tables are never written as data — routed to
20
21
  // the dedicated schema verbs instead so we never orphan or corrupt metadata.
21
22
  var REFUSED_TABLES = ["sys_db_object", "sys_dictionary"];
23
+ // The generic createRecord op never sets `application`, so an update set
24
+ // inserted through it lands in the session app while the read-back (name only)
25
+ // still reports success. Update sets go through the scope-correct
26
+ // createUpdateSet op instead.
27
+ var UPDATE_SET_TABLE = "sys_update_set";
22
28
  async function createRecord(params) {
23
29
  var client = params.client || (0, client_1.createClient)({});
24
30
  var table = params.table;
@@ -29,6 +35,11 @@ async function createRecord(params) {
29
35
  throw new Error("create-record: refusing to write " + table + " as data — it is a schema table. "
30
36
  + "Use add-column / create-table for schema changes.");
31
37
  }
38
+ if (table === UPDATE_SET_TABLE) {
39
+ throw new Error("create-record: refusing to create a sys_update_set record — the generic createRecord op "
40
+ + "does not set its application, so the set would land in the session app. "
41
+ + "Use `dove createUpdateSet --name <name> --scope <scope>` instead.");
42
+ }
32
43
  var fieldNames = params.fields ? Object.keys(params.fields) : [];
33
44
  if (fieldNames.length === 0) {
34
45
  throw new Error("create-record: at least one field (--fields key=value) is required.");
@@ -7,12 +7,15 @@
7
7
  *
8
8
  * Wraps the Dovetail core Scripted REST `deleteRecord` op. DRY-RUN BY DEFAULT:
9
9
  * without confirm:true nothing is deleted; dryRun:true forces a dry-run even
10
- * with confirm. updateSetSysId is REQUIRED and sent with the delete, but the
11
- * capture is NOT pinned yet: until TenonHQ/Dovetail#297 ships server-side the
12
- * op ignores update_set_sys_id and captures into the session's current update
13
- * set. Every result note says so, so no caller reads "pinned" into a delete
14
- * that was not. The client keeps sending the field so it takes effect the
15
- * moment the server honours it.
10
+ * with confirm. updateSetSysId is REQUIRED and resolved up front (it must exist
11
+ * and be in progress — checked on the dry-run too). Until TenonHQ/Dovetail#297
12
+ * ships the server op ignores update_set_sys_id and captures into the session's
13
+ * current update set, so the verb does what every other deleteRecord caller does:
14
+ * it pins the set as current (changeUpdateSet) and reads the pin back, refusing
15
+ * to delete when it did not take. After the delete it reads the DELETE row back
16
+ * from sys_update_xml and reports where it actually landed (captured /
17
+ * capturedInto) — the pin alone is not proof (#297 saw it not stick cross-scope).
18
+ * The client keeps sending the field so it takes effect once the server honours it.
16
19
  *
17
20
  * NOT for schema tables (sys_db_object / sys_dictionary) — dropping a table or
18
21
  * column is a privileged lifecycle op, not a data delete.
@@ -24,9 +27,9 @@ export interface DeleteRecordParams {
24
27
  /** sys_id of the record to delete — 32 lowercase hex characters. */
25
28
  sysId: string;
26
29
  /**
27
- * Update set the delete should be captured into. Required and sent, but honoured only
28
- * once TenonHQ/Dovetail#297 ships — until then the capture lands in the session's
29
- * current update set (see UPDATE_SET_CAVEAT).
30
+ * Update set the delete should be captured into. Required; must exist and be in progress.
31
+ * Pinned as the session's current set before the delete (the server op ignores the field
32
+ * until TenonHQ/Dovetail#297 ships) and verified via the sys_update_xml capture row.
30
33
  */
31
34
  updateSetSysId?: string;
32
35
  /** The delete gate: the record is only deleted when confirm is exactly true. */
@@ -43,14 +46,34 @@ export interface DeleteRecordResult {
43
46
  before: Record<string, string>;
44
47
  /** True only when the post-delete read-back confirmed the record is gone. */
45
48
  verified: boolean;
49
+ /** Name of the requested update set, resolved before anything else is done. */
50
+ updateSetName: string;
51
+ /**
52
+ * True only when the DELETE's sys_update_xml capture row was read back IN the requested
53
+ * update set. A deleted record with captured:false will NOT travel with that set.
54
+ */
55
+ captured: boolean;
56
+ /** The update set the DELETE capture row actually landed in; null when none was found. */
57
+ capturedInto: {
58
+ sysId: string;
59
+ name: string;
60
+ } | null;
61
+ /**
62
+ * Where the DELETE capture stands: "in-set" (captured in the requested set), "other-set"
63
+ * (captured, but into a different set — the requested set promotes without it),
64
+ * "none" (no capture row anywhere — the table is likely not recorded in update sets),
65
+ * "unverified" (the capture read-back failed), or "n/a" (dry-run / nothing deleted).
66
+ */
67
+ captureState: "in-set" | "other-set" | "none" | "unverified" | "n/a";
46
68
  note: string;
47
69
  }
48
70
  export declare var TABLE_NAME_PATTERN: RegExp;
49
71
  export declare var SYS_ID_PATTERN: RegExp;
50
72
  /**
51
- * Appended to every result note while TenonHQ/Dovetail#297 is open: the server op does not
52
- * yet honour update_set_sys_id, so the capture lands in the session's current update set.
53
- * Delete this (and its test) when #297 ships.
73
+ * Appended to the dry-run and deleted notes while TenonHQ/Dovetail#297 is open: the server op
74
+ * does not yet honour update_set_sys_id, so the delete is captured into the session's current
75
+ * update set — which is why the verb pins it first and reads the capture row back. Revisit
76
+ * (and its test) when #297 ships.
54
77
  */
55
78
  export declare var UPDATE_SET_CAVEAT: string;
56
79
  export declare function validateDeleteTable(table: unknown): string;