@synapsor/runner 1.1.1 → 1.4.0

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 (67) hide show
  1. package/CHANGELOG.md +92 -1
  2. package/README.md +26 -7
  3. package/THREAT_MODEL.md +7 -4
  4. package/dist/cli.d.ts +5 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/local-ui.d.ts.map +1 -1
  7. package/dist/runner.mjs +8060 -2615
  8. package/docs/README.md +19 -3
  9. package/docs/capability-authoring.md +20 -8
  10. package/docs/conformance.md +12 -0
  11. package/docs/current-scope.md +12 -4
  12. package/docs/doctor.md +21 -7
  13. package/docs/dsl-reference.md +91 -5
  14. package/docs/getting-started-own-database.md +26 -3
  15. package/docs/guarded-crud-writeback.md +218 -0
  16. package/docs/limitations.md +30 -9
  17. package/docs/oss-vs-cloud.md +1 -1
  18. package/docs/production.md +52 -20
  19. package/docs/release-notes.md +82 -2
  20. package/docs/release-policy.md +1 -1
  21. package/docs/result-envelope-v2.md +20 -6
  22. package/docs/result-envelope-v4.md +47 -0
  23. package/docs/reversible-change-sets.md +151 -0
  24. package/docs/rfcs/004-guarded-crud-receipt-authority.md +165 -0
  25. package/docs/rfcs/005-bounded-set-writeback.md +164 -0
  26. package/docs/rfcs/006-reviewed-reversible-change-sets.md +152 -0
  27. package/docs/runner-config-reference.md +83 -9
  28. package/docs/running-a-runner-fleet.md +2 -1
  29. package/docs/security-boundary.md +29 -9
  30. package/docs/writeback-executors.md +12 -11
  31. package/examples/runner-fleet/seed/mysql.sql +47 -0
  32. package/examples/runner-fleet/seed/postgres.sql +48 -0
  33. package/examples/support-plan-credit/README.md +31 -0
  34. package/examples/support-plan-credit/contract.synapsor.sql +26 -0
  35. package/examples/support-plan-credit/expected-output/tools-preview.txt +1 -0
  36. package/examples/support-plan-credit/seed/001_seed.sql +14 -1
  37. package/examples/support-plan-credit/synapsor.contract.json +101 -1
  38. package/examples/support-plan-credit/synapsor.runner.json +7 -1
  39. package/fixtures/protocol/MANIFEST.json +131 -1
  40. package/fixtures/protocol/change-set.bounded-update.v3.json +57 -0
  41. package/fixtures/protocol/change-set.delete.v2.json +31 -0
  42. package/fixtures/protocol/change-set.insert.v2.json +36 -0
  43. package/fixtures/protocol/change-set.update.v2.json +32 -0
  44. package/fixtures/protocol/compensation-change-set.update.v1.json +40 -0
  45. package/fixtures/protocol/execution-receipt.bounded-update-applied.v3.json +39 -0
  46. package/fixtures/protocol/execution-receipt.compensation-update-applied.v4.json +37 -0
  47. package/fixtures/protocol/execution-receipt.delete-applied.v2.json +25 -0
  48. package/fixtures/protocol/execution-receipt.insert-applied.v2.json +24 -0
  49. package/fixtures/protocol/execution-receipt.reconciliation-required.v2.json +25 -0
  50. package/fixtures/protocol/execution-receipt.update-applied.v2.json +25 -0
  51. package/fixtures/protocol/writeback-job.bounded-update.v3.json +43 -0
  52. package/fixtures/protocol/writeback-job.compensation-update.v4.json +34 -0
  53. package/fixtures/protocol/writeback-job.delete.v2.json +22 -0
  54. package/fixtures/protocol/writeback-job.insert.v2.json +28 -0
  55. package/fixtures/protocol/writeback-job.update.v2.json +24 -0
  56. package/package.json +1 -1
  57. package/schemas/change-set.v2.schema.json +245 -0
  58. package/schemas/change-set.v3.schema.json +112 -0
  59. package/schemas/compensation-change-set.v1.schema.json +32 -0
  60. package/schemas/execution-receipt.v2.schema.json +106 -0
  61. package/schemas/execution-receipt.v3.schema.json +40 -0
  62. package/schemas/execution-receipt.v4.schema.json +22 -0
  63. package/schemas/inverse-descriptor.v1.schema.json +46 -0
  64. package/schemas/synapsor.runner.schema.json +132 -2
  65. package/schemas/writeback-job.v2.schema.json +238 -0
  66. package/schemas/writeback-job.v3.schema.json +53 -0
  67. package/schemas/writeback-job.v4.schema.json +17 -0
package/docs/README.md CHANGED
@@ -45,6 +45,14 @@ no-database demo, wire your database, then read deeper concepts.
45
45
  links to the canonical own-database guide.
46
46
  - [Doctor](doctor.md): redacted setup checks, handler probes, direct SQL
47
47
  writeback probes, and receipt-table guidance.
48
+ - [Guarded Single-Row CRUD Writeback](guarded-crud-writeback.md): native
49
+ INSERT/UPDATE/DELETE, receipt modes, crash semantics, privileges, and
50
+ reconciliation.
51
+ - [Bounded Set Writeback](bounded-set-writeback.md): fixed-predicate set
52
+ UPDATE/DELETE, exact-review batch INSERT, mandatory caps, atomicity, and
53
+ exact receipts.
54
+ - [Reviewed Reversible Change Sets](reversible-change-sets.md): opt-in bounded
55
+ inverse capture and the separate operator proposal/approval/apply flow.
48
56
 
49
57
  ## 05 Generate Capabilities
50
58
 
@@ -89,6 +97,11 @@ no-database demo, wire your database, then read deeper concepts.
89
97
 
90
98
  - [Result Envelope v2](result-envelope-v2.md): stable
91
99
  `ok`/`summary`/`data`/`proposal`/`error` MCP tool results.
100
+ - [Result Envelope v3](result-envelope-v3.md): frozen-set jobs and receipts
101
+ with every bounded member identity and safe digest.
102
+ - [Compensation Protocol v4](result-envelope-v4.md): reviewed inverse
103
+ descriptors, compensation proposals/jobs/receipts, lineage, and
104
+ reconciliation semantics.
92
105
  - [Store Lifecycle](store-lifecycle.md): active-store leases, prune safety,
93
106
  deleted-store behavior, and concurrent server guardrails.
94
107
  - `synapsor-runner activity search`, `evidence`, `query-audit`, `receipts`,
@@ -130,7 +143,10 @@ no-database demo, wire your database, then read deeper concepts.
130
143
  - RFC source context:
131
144
  [001 result envelope](rfcs/001-result-envelope-v2.md),
132
145
  [002 handler helper](rfcs/002-app-owned-handler-helper.md),
133
- [003 integrator teardown](rfcs/003-integrator-feedback-teardown.md).
146
+ [003 integrator teardown](rfcs/003-integrator-feedback-teardown.md),
147
+ [004 guarded CRUD and receipt authority](rfcs/004-guarded-crud-receipt-authority.md),
148
+ [005 bounded set writeback](rfcs/005-bounded-set-writeback.md), and
149
+ [006 reviewed reversible change sets](rfcs/006-reviewed-reversible-change-sets.md).
134
150
 
135
151
  The public docs intentionally stay task-first. Historical implementation
136
152
  reports, release checklists, and internal planning notes are not part of the
@@ -144,8 +160,8 @@ getting-started path.
144
160
  database.
145
161
  - Writeback: execution of an approved proposal outside the model-facing MCP
146
162
  surface.
147
- - Executor: Runner's guarded one-row updater or an app-owned handler for richer
148
- approved business transactions.
163
+ - Executor: Runner's guarded one-row/bounded-set database adapter or an
164
+ app-owned handler for richer approved business transactions.
149
165
 
150
166
  ## Repository Map
151
167
 
@@ -138,6 +138,7 @@ reviewed runner JSON capabilities. Current parity:
138
138
  | proposal `approval` | `APPROVAL ROLE billing_lead` | 0.1 | Local mode records the required role; enforcement is still outside the model-facing MCP tool. |
139
139
  | proposal `approval.required_approvals` | `REQUIRE 2 APPROVALS` | 1.1 | Optional 1..10 distinct-reviewer quorum; defaults to 1. |
140
140
  | proposal `writeback` | `WRITEBACK DIRECT SQL`, `WRITEBACK APP HANDLER EXECUTOR name`, `WRITEBACK CLOUD WORKER`, `WRITEBACK NONE` | 0.1.7 | Handler URLs/tokens stay in `synapsor.runner.json`; contracts carry only the handler name. |
141
+ | proposal `reversibility` | `REVERSIBLE` | 1.4 | Direct SQL only. Captures a bounded inverse after unambiguous apply; operator `revert` creates a new independently approved proposal. |
141
142
  | evidence options | `REQUIRE EVIDENCE` | 0.1 | Detailed evidence sources/handle prefixes are not expressible in DSL yet; use embedded JSON or generated contract JSON for those. |
142
143
 
143
144
  ## Direct Runner Config Path
@@ -319,7 +320,11 @@ Runner rejects model-facing trust-scope arguments.
319
320
 
320
321
  ## Direct SQL Writeback
321
322
 
322
- Use direct SQL writeback only for simple bounded single-row `UPDATE` proposals.
323
+ Use direct SQL writeback for reviewed guarded single-row INSERT, UPDATE, or
324
+ DELETE proposals. Version 1.3 also supports the narrower [bounded set
325
+ form](bounded-set-writeback.md): fixed-predicate UPDATE/DELETE or complete-item
326
+ batch INSERT with mandatory row/value caps, a frozen exact set, human approval,
327
+ atomic apply, and exact receipts.
323
328
  Runner validates:
324
329
 
325
330
  - fixed table and column names;
@@ -331,19 +336,26 @@ Runner validates:
331
336
  - one affected row;
332
337
  - idempotency receipt.
333
338
 
334
- Runner does not expose generic SQL, model-generated SQL, DDL, INSERT, DELETE,
335
- UPSERT, or multi-row writes.
339
+ INSERT additionally requires a source-unique dedup key. DELETE requires an exact
340
+ version guard and fails closed on widening cascades or write triggers.
341
+
342
+ Runner does not expose generic/model-generated SQL, DDL, UPSERT, a model-chosen
343
+ predicate, unbounded writes, or cross-table transactions. Set writes that
344
+ cannot meet every bounded-set invariant stay on the app-owned executor path.
336
345
 
337
346
  Direct SQL writeback uses the source `write_url_env`, such as
338
- `SYNAPSOR_DATABASE_WRITE_URL`. The writer needs permission for
339
- `synapsor_writeback_receipts` or an administrator must pre-create and grant that
340
- table.
347
+ `SYNAPSOR_DATABASE_WRITE_URL`. Select atomic `source_db` receipts (precreated or
348
+ auto-migrated) or zero-source-schema `runner_ledger` receipts. Ledger authority
349
+ has an explicit post-source-commit crash window and reconciliation workflow; it
350
+ is not distributed exactly-once. See
351
+ [Guarded Single-Row CRUD Writeback](guarded-crud-writeback.md).
341
352
 
342
353
  ## App-Owned Executors
343
354
 
344
355
  Use an app-owned executor when an approved proposal needs richer business work:
345
- creating a credit row, inserting an outbox event, updating multiple app tables,
346
- or calling your own service.
356
+ inserting an outbox event, updating multiple app tables, calling your own
357
+ service, or creating a row whose source constraints cannot satisfy native
358
+ single-row INSERT guards.
347
359
 
348
360
  In DSL, keep the reviewed business contract portable and name the executor:
349
361
 
@@ -29,12 +29,24 @@ Current fixture groups:
29
29
  - `auto-approval`
30
30
  - `aggregate-policy-limits`
31
31
  - `numeric-bounds`
32
+ - `bounded-set-threats`
33
+ - `reversible-change-sets`
32
34
 
33
35
  The fixture set is intentionally small in 0.1. It covers the runner-supported
34
36
  semantic surface first: trusted context, scoped reads, evidence handles,
35
37
  proposal boundaries, kept-out fields, manual approval, and replay envelopes.
36
38
  The aggregate-policy fixture additionally proves that reviewed daily ceilings
37
39
  fall back to human review atomically and record the limit that tripped.
40
+ The bounded-set fixture maps threats R1-R7 to executable expectations: fixed
41
+ selection, count/value caps, frozen-version drift checks, atomic rollback,
42
+ exact receipts, hard-delete side-effect refusal, and human approval. Adapter
43
+ tests and `corepack pnpm test:bounded-set` complete the live PostgreSQL/MySQL
44
+ matrix.
45
+ The reversible-change-set fixture proves that undo is a new reviewed
46
+ compensation proposal, not rollback: apply-time receipts capture only
47
+ allowlisted inverse data, compensation uses fresh guards and exact members,
48
+ ambiguous outcomes fail closed pending reconciliation, and hard delete reports
49
+ a specific best-effort-unavailable reason.
38
50
 
39
51
  Additional 0.1 parity coverage currently lives in tests and verification
40
52
  scripts rather than separate `cloud-push/` or `dsl-json-parity/` conformance
@@ -2,13 +2,19 @@
2
2
 
3
3
  The canonical scope page is [Current Limitations](limitations.md).
4
4
 
5
- Current `1.1` scope:
5
+ Current `1.4` scope:
6
6
 
7
7
  - local semantic MCP tools for Postgres/MySQL-backed business actions;
8
8
  - schema inspection and guided config generation;
9
9
  - trusted context from environment/session values;
10
10
  - evidence handles, query audit, proposals, receipts, and local replay;
11
- - guarded single-row `UPDATE` writeback for simple edits;
11
+ - guarded single-row `INSERT`, `UPDATE`, and `DELETE` writeback with explicit
12
+ receipt authority;
13
+ - fixed-predicate set `UPDATE`/`DELETE` and exact-review batch `INSERT`, with
14
+ mandatory row/value caps, frozen members, human approval, atomic execution,
15
+ and protocol-v3 exact receipts;
16
+ - opt-in reviewed compensation for direct SQL UPDATE, INSERT, soft-delete, and
17
+ exact frozen sets, using a separate operator proposal and protocol-v4 receipt;
12
18
  - app-owned `http_handler` and `command_handler` executors for richer approved
13
19
  business transactions;
14
20
  - stdio MCP, Streamable HTTP MCP, and a small JSON-RPC bridge.
@@ -25,9 +31,11 @@ Out of scope:
25
31
 
26
32
  - raw `execute_sql`;
27
33
  - model-generated SQL;
28
- - generic INSERT/DELETE/UPSERT/DDL;
29
- - generic multi-row SQL writeback;
34
+ - UPSERT, DDL, model-generated predicates, or unguarded INSERT/DELETE;
35
+ - model-generated, unbounded, or cross-table set writeback;
30
36
  - physical branching of external Postgres/MySQL;
37
+ - automatic rollback/time travel and inferred compensation for app-owned or
38
+ external effects;
31
39
  - self-hosted Synapsor Cloud;
32
40
  - unbounded or multi-region shared-ledger scale and a managed Runner fleet;
33
41
  - production SLA or compliance certification.
package/docs/doctor.md CHANGED
@@ -44,8 +44,9 @@ Idempotency-Key
44
44
 
45
45
  ## Direct SQL Writeback Checks
46
46
 
47
- For direct `sql_update` writeback, add `--check-writeback` only after reviewing
48
- the receipt-table DDL/grants:
47
+ For direct guarded single-row CRUD or bounded-set writeback, add
48
+ `--check-writeback` after reviewing the selected receipt mode and
49
+ operation-specific grants:
49
50
 
50
51
  ```bash
51
52
  synapsor-runner doctor --config synapsor.runner.json --check-writeback
@@ -55,17 +56,23 @@ This connects with the trusted writer env var named by `write_url_env` and
55
56
  checks:
56
57
 
57
58
  - writer database connectivity;
58
- - `synapsor_writeback_receipts` permission through the adapter doctor;
59
+ - source receipt migration/permissions when authority is `source_db`;
60
+ - no source receipt DDL/DML when authority is `runner_ledger`;
59
61
  - rollback-only access to each configured proposal target table;
60
- - rollback-only update permission for configured allowed write columns.
62
+ - operation-specific version, unique/dedup, generated-column, trigger,
63
+ cascade, RLS, and DML prerequisites.
64
+
65
+ For bounded sets, the normal doctor report also identifies operation kind,
66
+ fixed selection or exact batch source, row cap, aggregate bounds, and the
67
+ mandatory human/operator approval boundary. It fails validation before any
68
+ proposal exists when those portable guards are incomplete.
61
69
 
62
70
  The target-table probe uses fixed schema/table/column identifiers from the
63
71
  reviewed config. It does not accept model SQL, user SQL, arbitrary table names,
64
72
  or arbitrary column names. It runs inside a transaction and rolls back.
65
73
 
66
- An administrator must pre-create `synapsor_writeback_receipts` and grant the
67
- steady-state writer access. The doctor probe does not execute DDL or require
68
- schema `CREATE`:
74
+ For `source_db` + `precreated`, an administrator creates the receipt table and
75
+ grants steady-state access. Doctor never executes DDL in this mode:
69
76
 
70
77
  ```bash
71
78
  synapsor-runner writeback migration --engine postgres --schema synapsor
@@ -79,6 +86,13 @@ synapsor-runner writeback migration --engine mysql --schema appdb
79
86
  synapsor-runner writeback grants --engine mysql --schema appdb --writer-role "'app_writer'@'%'"
80
87
  ```
81
88
 
89
+ Use `source_db` + `auto_migrate` only when the writer may create the fixed
90
+ receipt table. Use `runner_ledger` for no source receipt table; doctor then
91
+ checks the local/small-fleet topology and crash-reconciliation prerequisites.
92
+ See [Guarded Single-Row CRUD Writeback](guarded-crud-writeback.md).
93
+ For set-specific prerequisites, see [Bounded Set
94
+ Writeback](bounded-set-writeback.md).
95
+
82
96
  Use an app-owned `http_handler` or `command_handler` executor when your
83
97
  application should own richer business writes or receipt storage.
84
98
 
@@ -98,15 +98,16 @@ different capability design.
98
98
  `ALLOW READ` is required and becomes the visible-field allowlist. `KEEP OUT`
99
99
  records fields that must remain outside the model/evidence surface. Keep-out
100
100
  fields must not also be visible. `REQUIRE EVIDENCE` records the scoped read and
101
- query audit. `MAX ROWS n` bounds the result; local 0.1 capabilities are designed
102
- around one primary-key row.
101
+ query audit. For ordinary read/single-row proposal capabilities, `MAX ROWS 1`
102
+ keeps primary-key lookup exact. Bounded set proposals use the separate guarded
103
+ form below.
103
104
 
104
105
  ## Proposal and patch
105
106
 
106
107
  Read capabilities end after the read clauses. A proposal capability adds:
107
108
 
108
109
  ```sql
109
- PROPOSE ACTION waive_late_fee
110
+ PROPOSE ACTION waive_late_fee UPDATE
110
111
  ALLOW WRITE late_fee_cents, waiver_reason
111
112
  PATCH late_fee_cents = 0
112
113
  PATCH waiver_reason = ARG reason
@@ -115,7 +116,8 @@ Read capabilities end after the read clauses. A proposal capability adds:
115
116
  PATCH obsolete_note = NULL
116
117
  ```
117
118
 
118
- `PROPOSE ACTION` must precede proposal-only clauses. `ALLOW WRITE` is the exact
119
+ `PROPOSE ACTION name [UPDATE|INSERT|DELETE]` must precede proposal-only clauses.
120
+ The operation defaults to `UPDATE` for compatibility. `ALLOW WRITE` is the exact
119
121
  patch-column allowlist. Each `PATCH` uses one of:
120
122
 
121
123
  - `ARG name`: value comes from a validated model argument;
@@ -126,6 +128,78 @@ patch-column allowlist. Each `PATCH` uses one of:
126
128
  Fixed strings such as `PATCH status = 'approved'` are supported. The patch is
127
129
  saved as a proposal; it is not executed by the model-facing tool.
128
130
 
131
+ `INSERT` requires a source-enforced deduplication identity:
132
+
133
+ ```sql
134
+ PROPOSE ACTION create_credit INSERT
135
+ DEDUP KEY tenant_id = TRUSTED TENANT, request_id = PROPOSAL ID
136
+ ALLOW WRITE customer_id, amount_cents
137
+ PATCH customer_id = ARG customer_id
138
+ PATCH amount_cents = ARG amount_cents
139
+ ```
140
+
141
+ Dedup components are `column = TRUSTED TENANT`, `column = PROPOSAL ID`, or
142
+ `column = FIXED value`. The key must include both trusted tenant and proposal
143
+ identity and must map to an inspected source primary/unique constraint.
144
+
145
+ `DELETE` contains no `ALLOW WRITE` or `PATCH`, requires `CONFLICT GUARD`, and
146
+ cannot use `AUTO APPROVE`. Hard delete always requires human/operator review.
147
+
148
+ Runner-ledger UPDATE must explicitly advance its exact conflict guard:
149
+
150
+ ```sql
151
+ ADVANCE VERSION version USING INTEGER INCREMENT
152
+ ADVANCE VERSION updated_at USING DATABASE GENERATED
153
+ ```
154
+
155
+ Use one strategy, and make its column match `CONFLICT GUARD`.
156
+
157
+ ## Bounded set proposals
158
+
159
+ Add `SET` after the operation to opt into the bounded set model. Existing-row
160
+ UPDATE/DELETE requires a contract-fixed selection, mandatory row and aggregate
161
+ bounds, exact version guards, and human/operator approval:
162
+
163
+ ```sql
164
+ PROPOSE ACTION close_overdue UPDATE SET
165
+ SELECT WHERE status = 'overdue'
166
+ MAX ROWS 10
167
+ MAX TOTAL balance_cents BEFORE 50000
168
+ ALLOW WRITE status
169
+ PATCH status = 'closed'
170
+ ADVANCE VERSION version USING INTEGER INCREMENT
171
+ APPROVAL ROLE billing_reviewer
172
+ WRITEBACK DIRECT SQL
173
+ ```
174
+
175
+ `SELECT WHERE` accepts one or more literal equality terms joined by `AND`.
176
+ Columns, operators, values, ordering, ranges, and raw SQL cannot come from a
177
+ model argument. Runner reads `MAX ROWS + 1` and rejects overflow rather than
178
+ truncating. `MAX TOTAL column BEFORE|AFTER|ABSOLUTE DELTA maximum` is a
179
+ reviewer-visible aggregate value bound.
180
+
181
+ Batch INSERT takes a complete typed item array:
182
+
183
+ ```sql
184
+ ARG items ROWS MAX 10 REQUIRED
185
+ ITEM FIELD items.id STRING REQUIRED MAX LENGTH 128
186
+ ITEM FIELD items.amount_cents NUMBER REQUIRED MIN 1 MAX 2500
187
+ PROPOSE ACTION create_credits INSERT SET
188
+ BATCH ITEMS FROM ARG items
189
+ MAX ROWS 10
190
+ MAX TOTAL amount_cents AFTER 25000
191
+ DEDUP KEY tenant_id = TRUSTED TENANT, id = ITEM id
192
+ ALLOW WRITE amount_cents
193
+ PATCH amount_cents = ITEM amount_cents
194
+ APPROVAL ROLE billing_reviewer
195
+ WRITEBACK DIRECT SQL
196
+ ```
197
+
198
+ The first release caps every set at 100 members and forbids `AUTO APPROVE`.
199
+ Set hard DELETE is trigger/cascade checked and soft delete is preferred. See
200
+ [Bounded Set Writeback](bounded-set-writeback.md) for transaction, receipt,
201
+ reconciliation, and executor boundaries.
202
+
129
203
  ## Bounds and transitions
130
204
 
131
205
  ```sql
@@ -189,11 +263,20 @@ Writeback forms:
189
263
 
190
264
  | Clause | Meaning |
191
265
  | --- | --- |
192
- | `WRITEBACK DIRECT SQL` | Runner performs one guarded single-row update after approval. |
266
+ | `WRITEBACK DIRECT SQL` | Runner performs one guarded single-row INSERT, UPDATE, or DELETE after approval. Runtime receipt authority stays in `synapsor.runner.json`. |
193
267
  | `WRITEBACK APP HANDLER EXECUTOR name` | Runner calls a configured app-owned executor after approval. URL/token wiring stays outside the contract. |
194
268
  | `WRITEBACK CLOUD WORKER` | Delegates approved execution to Cloud worker infrastructure. |
195
269
  | `WRITEBACK NONE` | Proposal-only; local apply is intentionally unavailable. |
196
270
 
271
+ `REVERSIBLE` opts a direct SQL proposal into reviewed inverse capture. It does
272
+ not add an MCP tool or automatic rollback. UPDATE requires an exact conflict
273
+ guard and `ADVANCE VERSION ... USING INTEGER INCREMENT`; INSERT requires a
274
+ deterministic primary-key dedup component; policy auto-approval and app-owned
275
+ handlers are rejected. After an unambiguous apply, an operator may run
276
+ `synapsor-runner revert <proposal_id>` to create a separately reviewed
277
+ compensation proposal. See [Reviewed Reversible Change
278
+ Sets](reversible-change-sets.md).
279
+
197
280
  ## Workflow declarations
198
281
 
199
282
  ```sql
@@ -218,5 +301,8 @@ Unknown clauses fail instead of being ignored. Cloud-generated concepts such as
218
301
  `ROOT EXTERNAL`, `JOIN EXTERNAL`, `RETURN ANSWER WITH CITATIONS`, `AUTO BRANCH`,
219
302
  and `AUTO MERGE` are not local DSL 0.1 clauses.
220
303
 
304
+ Bounded multi-row direct writes and reversible change sets are not part of the
305
+ 1.2 DSL. Use an app-owned executor until their separate safety gates ship.
306
+
221
307
  The canonical JSON output, not parser implementation details, is the portable
222
308
  contract. Validate generated JSON with `synapsor-runner contract validate`.
@@ -3,9 +3,9 @@
3
3
  Use this path after the Docker demo passes and you want to try Synapsor Runner
4
4
  against a staging or disposable Postgres/MySQL database.
5
5
 
6
- Do not start with your most sensitive production database. The current alpha
7
- runner is a local commit-safety runtime for reviewed single-row business
8
- actions, not a production certification.
6
+ Do not start with your most sensitive production database. Runner is a
7
+ commit-safety runtime for reviewed single-row business actions, not a
8
+ production certification.
9
9
 
10
10
  If you only ran `synapsor-runner demo --quick`, you have tested the fixture-only
11
11
  teaching path and local ledger commands. This page is the real own-database
@@ -170,6 +170,8 @@ The wizard:
170
170
  mode, semantic capability names, and proposal patch mappings;
171
171
  - asks review-mode users to choose direct guarded SQL writeback, an app-owned
172
172
  HTTP handler, or an app-owned command handler;
173
+ - for direct SQL, asks for UPDATE/INSERT/DELETE and source receipt
174
+ auto-migrate, precreated receipt, or zero-source-schema Runner-ledger mode;
173
175
  - previews the MCP tools and what is not exposed, then lets you revise visible
174
176
  fields or capability names before writing files;
175
177
  - attempts a first smoke call when you supplied a real object id and the
@@ -238,6 +240,27 @@ writeback is disabled.
238
240
  Use `--writeback command_handler --handler-command-env APP_WRITEBACK_COMMAND`
239
241
  when your app-owned writer is a local command/script instead of HTTP.
240
242
 
243
+ For native single-row CRUD, use operation-aware onboarding. INSERT requires a
244
+ source primary/unique dedup key; DELETE requires an exact conflict guard and
245
+ safe cascade/trigger inspection:
246
+
247
+ ```bash
248
+ npx -y -p @synapsor/runner synapsor-runner onboard db \
249
+ --from-env DATABASE_URL \
250
+ --table account_credits \
251
+ --mode review \
252
+ --operation insert \
253
+ --dedup-columns request_id \
254
+ --receipt-mode runner_ledger \
255
+ --patch amount_cents=arg:amount_cents \
256
+ --write-url-env SYNAPSOR_DATABASE_WRITE_URL \
257
+ --yes
258
+ ```
259
+
260
+ Review [Guarded Single-Row CRUD Writeback](guarded-crud-writeback.md) before
261
+ choosing receipt authority. Runner-ledger mode creates no source receipt table,
262
+ but ambiguous post-commit crashes stop for operator reconciliation.
263
+
241
264
  Or generate from a saved inspection snapshot without reconnecting:
242
265
 
243
266
  ```bash
@@ -0,0 +1,218 @@
1
+ # Guarded Single-Row CRUD Writeback
2
+
3
+ Synapsor Runner can apply reviewed, approved, single-row `INSERT`, `UPDATE`,
4
+ and `DELETE` operations to Postgres and MySQL. The model only creates a
5
+ proposal. Approval and apply remain outside MCP.
6
+
7
+ This is not a general SQL surface. Every direct operation is generated from a
8
+ validated contract and must preserve these invariants:
9
+
10
+ - fixed schema, table, primary key, tenant key, and columns;
11
+ - tenant supplied by trusted context, never by the model;
12
+ - parameterized values and no model-generated SQL or predicates;
13
+ - one reviewed row and exactly one source effect;
14
+ - source-enforced conflict or deduplication guard;
15
+ - approval before apply;
16
+ - durable intent, receipt, events, and replay;
17
+ - ambiguous outcomes fail closed for operator reconciliation.
18
+
19
+ Use [bounded set writeback](bounded-set-writeback.md) only when a fixed
20
+ predicate or complete reviewed item list can satisfy its stronger caps,
21
+ freezing, atomicity, and receipt rules. Use an [app-owned
22
+ executor](writeback-executors.md) for free-form/unbounded multi-row or
23
+ multi-table transactions, UPSERT, DDL, external effects, or business logic that
24
+ does not fit either direct boundary.
25
+
26
+ ## Operation guarantees
27
+
28
+ | Operation | Required source guard | Direct-write behavior |
29
+ | --- | --- | --- |
30
+ | `UPDATE` | Primary key, trusted tenant, exact version/conflict column | Patches only allowlisted columns and affects exactly one row. In Runner-ledger mode, the version must advance in the same source transaction. |
31
+ | `INSERT` | Source `PRIMARY KEY` or `UNIQUE` constraint over the reviewed dedup identity | Runner injects tenant and proposal-derived identity values, inserts only allowlisted fields, and requires exactly one row. |
32
+ | `DELETE` | Primary key, trusted tenant, exact version column | Deletes exactly one reviewed row. Hard delete requires human/operator approval and is refused when Runner detects write triggers or widening cascades. Prefer soft delete as guarded `UPDATE`. |
33
+
34
+ Existing proposal capabilities with no `operation` field continue to mean
35
+ single-row `UPDATE`.
36
+
37
+ ## Receipt authority
38
+
39
+ Receipt authority and source-table provisioning are separate decisions:
40
+
41
+ | Authority | Provisioning | Source schema change | Completion classification |
42
+ | --- | --- | --- | --- |
43
+ | `source_db` | `precreated` | Administrator creates the fixed receipt table; Runner never runs DDL. | Mutation and receipt commit in one source transaction. Strongest already-applied versus conflict classification. |
44
+ | `source_db` | `auto_migrate` | Runner runs an idempotent, reviewed receipt-table migration. Writer needs bounded `CREATE` permission. | Same atomic source transaction after migration. |
45
+ | `runner_ledger` | Not applicable | No Synapsor receipt table or receipt DML in the source database. | Durable Runner intent precedes the source transaction. A crash after source commit but before ledger completion may require reconciliation. |
46
+
47
+ `runner_ledger` does not claim distributed exactly-once semantics across two
48
+ databases. Source-enforced versions and unique keys prevent a known duplicate
49
+ effect. If Runner cannot prove whether an interrupted attempt committed, it
50
+ records `reconciliation_required`, stops automatic retry, and preserves the
51
+ intent for an operator.
52
+
53
+ Local SQLite is valid for one local operator process. A networked or
54
+ multi-Runner deployment using `runner_ledger` requires
55
+ `storage.shared_postgres.mode = "runtime_store"` so the intent ledger is
56
+ authoritative before any source mutation. Mirror mode is rejected.
57
+
58
+ ## Runtime configuration
59
+
60
+ Atomic source receipts with an administrator-created table:
61
+
62
+ ```json
63
+ {
64
+ "sources": {
65
+ "billing_postgres": {
66
+ "engine": "postgres",
67
+ "read_url_env": "BILLING_POSTGRES_READ_URL",
68
+ "write_url_env": "BILLING_POSTGRES_WRITE_URL",
69
+ "receipts": {
70
+ "authority": "source_db",
71
+ "provisioning": "precreated",
72
+ "schema": "synapsor",
73
+ "table": "writeback_receipts"
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ Set `provisioning` to `auto_migrate` to let Runner create that fixed table.
81
+ Use Runner-ledger authority with no source receipt fields:
82
+
83
+ ```json
84
+ {
85
+ "sources": {
86
+ "billing_postgres": {
87
+ "engine": "postgres",
88
+ "read_url_env": "BILLING_POSTGRES_READ_URL",
89
+ "write_url_env": "BILLING_POSTGRES_WRITE_URL",
90
+ "receipts": { "authority": "runner_ledger" }
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ Receipt schema and table names are fixed, validated runtime identifiers. They
97
+ are not portable contract fields or model arguments.
98
+
99
+ ## Contract and DSL examples
100
+
101
+ Runner-ledger `UPDATE` must advance its exact guard:
102
+
103
+ ```sql
104
+ PROPOSE ACTION waive_late_fee UPDATE
105
+ ALLOW WRITE late_fee_cents, waiver_reason
106
+ PATCH late_fee_cents = 0
107
+ PATCH waiver_reason = ARG reason
108
+ ADVANCE VERSION version USING INTEGER INCREMENT
109
+ APPROVAL ROLE billing_reviewer
110
+ WRITEBACK DIRECT SQL
111
+ ```
112
+
113
+ Guarded `INSERT` needs a source-unique, Runner-supplied dedup identity:
114
+
115
+ ```sql
116
+ PROPOSE ACTION create_credit INSERT
117
+ DEDUP KEY tenant_id = TRUSTED TENANT, request_id = PROPOSAL ID
118
+ ALLOW WRITE customer_id, amount_cents, reason
119
+ PATCH customer_id = ARG customer_id
120
+ PATCH amount_cents = ARG amount_cents
121
+ PATCH reason = ARG reason
122
+ BOUND amount_cents 1..50000
123
+ APPROVAL ROLE support_reviewer
124
+ WRITEBACK DIRECT SQL
125
+ ```
126
+
127
+ The source must enforce the declared dedup columns with a primary key or unique
128
+ constraint. Ledger memory by itself is not an INSERT deduplication guard.
129
+
130
+ Hard `DELETE` has no patch and cannot use policy auto-approval:
131
+
132
+ ```sql
133
+ PROPOSE ACTION delete_session DELETE
134
+ APPROVAL ROLE security_reviewer
135
+ WRITEBACK DIRECT SQL
136
+ ```
137
+
138
+ The capability still declares `PRIMARY KEY`, `TENANT KEY`, `CONFLICT GUARD`,
139
+ reviewed visible fields, evidence, and `MAX ROWS 1` in its enclosing block.
140
+
141
+ ## Database privileges
142
+
143
+ Keep read and write credentials separate. The writer needs only the target
144
+ table operations declared by its capabilities plus the selected receipt mode.
145
+
146
+ | Mode | Postgres writer | MySQL writer |
147
+ | --- | --- | --- |
148
+ | `source_db` + `precreated` | Schema usage, reviewed target `SELECT` and operation-specific `INSERT`/`UPDATE`/`DELETE`, sequence use for identity INSERT, receipt-table `SELECT`/`INSERT`/`UPDATE`; no `CREATE`. | Reviewed target `SELECT` and operation-specific `INSERT`/`UPDATE`/`DELETE`, receipt-table `SELECT`/`INSERT`/`UPDATE`; no `CREATE`. |
149
+ | `source_db` + `auto_migrate` | Same, plus bounded schema `CREATE` for the fixed receipt table. | Same, plus database `CREATE` for the fixed receipt table. |
150
+ | `runner_ledger` | Reviewed target `SELECT` and operation-specific DML only; no source receipt or `CREATE` privilege. | Reviewed target `SELECT` and operation-specific DML only; no source receipt or `CREATE` privilege. |
151
+
152
+ Hard DELETE requires enough metadata visibility to prove that the effect cannot
153
+ widen. Postgres reads system catalogs. MySQL requires scoped `TRIGGER` metadata
154
+ visibility on the target table and global `PROCESS` visibility for incoming
155
+ InnoDB foreign-key metadata. If those privileges are unacceptable, use a soft
156
+ delete capability or an app-owned executor; Runner fails closed rather than
157
+ claiming an unverified one-row effect.
158
+
159
+ The same MySQL metadata visibility is required when reviewed compensation may
160
+ delete a row, including reversal of an INSERT. See [Reviewed Reversible Change
161
+ Sets](reversible-change-sets.md). App-owned executors are not automatically
162
+ reversible; they need an application-defined, separately reviewed compensation
163
+ capability.
164
+
165
+ Generate or inspect source-receipt SQL without exposing credentials:
166
+
167
+ ```bash
168
+ synapsor-runner writeback migration --engine postgres --schema synapsor --table writeback_receipts
169
+ synapsor-runner writeback grants --engine postgres --writer-role app_writer --schema synapsor --table writeback_receipts
170
+ synapsor-runner doctor --config ./synapsor.runner.json --check-writeback
171
+ synapsor-runner tools preview --config ./synapsor.runner.json --store ./.synapsor/local.db
172
+ ```
173
+
174
+ `precreated` uses rollback-only permission probes and never executes `CREATE`.
175
+ `doctor` also checks operation-specific version, unique-key, generated-column,
176
+ trigger, cascade, and topology prerequisites.
177
+
178
+ ## Reconciliation
179
+
180
+ An incomplete Runner-ledger intent is not proof of success or failure. Inspect
181
+ only the reviewed target projection under the trusted tenant, then record a
182
+ verified operator decision:
183
+
184
+ ```bash
185
+ synapsor-runner writeback reconcile list \
186
+ --status reconciliation_required \
187
+ --config ./synapsor.runner.json \
188
+ --store ./.synapsor/local.db
189
+
190
+ synapsor-runner writeback reconcile inspect latest \
191
+ --config ./synapsor.runner.json \
192
+ --store ./.synapsor/local.db
193
+
194
+ synapsor-runner writeback reconcile resolve wbi:... \
195
+ --outcome applied \
196
+ --reason "verified source state" \
197
+ --yes \
198
+ --config ./synapsor.runner.json \
199
+ --store ./.synapsor/local.db
200
+ ```
201
+
202
+ Resolution appends immutable events and a reconciliation receipt. It does not
203
+ rewrite history or retry the source mutation. Configured production operator
204
+ identity is required when enabled.
205
+
206
+ ## Verification
207
+
208
+ The repository includes a disposable Postgres/MySQL test that proves every
209
+ receipt mode, native INSERT/UPDATE/DELETE, idempotent retry, stale and tenant
210
+ guards, two-store races, crash boundaries, no source receipt table in
211
+ Runner-ledger mode, and hard-delete trigger/cascade refusal:
212
+
213
+ ```bash
214
+ corepack pnpm test:guarded-crud
215
+ ```
216
+
217
+ Use synthetic staging data first. Run `doctor --check-writeback` and review
218
+ `tools preview` before connecting an MCP client or applying a proposal.