@synapsor/runner 1.1.1 → 1.4.1

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 (72) hide show
  1. package/CHANGELOG.md +123 -1
  2. package/README.md +52 -31
  3. package/SECURITY.md +11 -0
  4. package/THREAT_MODEL.md +7 -4
  5. package/dist/cli.d.ts +5 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/local-ui.d.ts.map +1 -1
  8. package/dist/runner.mjs +8103 -2601
  9. package/docs/README.md +23 -3
  10. package/docs/benchmarks/bounded-set-local.md +27 -0
  11. package/docs/bounded-set-writeback.md +298 -0
  12. package/docs/capability-authoring.md +26 -8
  13. package/docs/conformance.md +12 -0
  14. package/docs/current-scope.md +12 -4
  15. package/docs/doctor.md +21 -7
  16. package/docs/dsl-reference.md +91 -5
  17. package/docs/getting-started-own-database.md +26 -3
  18. package/docs/guarded-crud-writeback.md +218 -0
  19. package/docs/limitations.md +30 -9
  20. package/docs/oss-vs-cloud.md +1 -1
  21. package/docs/production.md +52 -20
  22. package/docs/release-notes.md +105 -2
  23. package/docs/release-policy.md +1 -1
  24. package/docs/result-envelope-v2.md +20 -6
  25. package/docs/result-envelope-v3.md +43 -0
  26. package/docs/result-envelope-v4.md +47 -0
  27. package/docs/reversible-change-sets.md +151 -0
  28. package/docs/rfcs/004-guarded-crud-receipt-authority.md +165 -0
  29. package/docs/rfcs/005-bounded-set-writeback.md +164 -0
  30. package/docs/rfcs/006-reviewed-reversible-change-sets.md +152 -0
  31. package/docs/runner-config-reference.md +83 -9
  32. package/docs/running-a-runner-fleet.md +2 -1
  33. package/docs/security-boundary.md +36 -9
  34. package/docs/why-synapsor-vs-app-guardrails.md +169 -0
  35. package/docs/writeback-executors.md +12 -11
  36. package/examples/runner-fleet/seed/mysql.sql +47 -0
  37. package/examples/runner-fleet/seed/postgres.sql +48 -0
  38. package/examples/support-plan-credit/README.md +31 -0
  39. package/examples/support-plan-credit/contract.synapsor.sql +26 -0
  40. package/examples/support-plan-credit/expected-output/tools-preview.txt +1 -0
  41. package/examples/support-plan-credit/seed/001_seed.sql +14 -1
  42. package/examples/support-plan-credit/synapsor.contract.json +101 -1
  43. package/examples/support-plan-credit/synapsor.runner.json +7 -1
  44. package/fixtures/protocol/MANIFEST.json +131 -1
  45. package/fixtures/protocol/change-set.bounded-update.v3.json +57 -0
  46. package/fixtures/protocol/change-set.delete.v2.json +31 -0
  47. package/fixtures/protocol/change-set.insert.v2.json +36 -0
  48. package/fixtures/protocol/change-set.update.v2.json +32 -0
  49. package/fixtures/protocol/compensation-change-set.update.v1.json +40 -0
  50. package/fixtures/protocol/execution-receipt.bounded-update-applied.v3.json +39 -0
  51. package/fixtures/protocol/execution-receipt.compensation-update-applied.v4.json +37 -0
  52. package/fixtures/protocol/execution-receipt.delete-applied.v2.json +25 -0
  53. package/fixtures/protocol/execution-receipt.insert-applied.v2.json +24 -0
  54. package/fixtures/protocol/execution-receipt.reconciliation-required.v2.json +25 -0
  55. package/fixtures/protocol/execution-receipt.update-applied.v2.json +25 -0
  56. package/fixtures/protocol/writeback-job.bounded-update.v3.json +43 -0
  57. package/fixtures/protocol/writeback-job.compensation-update.v4.json +34 -0
  58. package/fixtures/protocol/writeback-job.delete.v2.json +22 -0
  59. package/fixtures/protocol/writeback-job.insert.v2.json +28 -0
  60. package/fixtures/protocol/writeback-job.update.v2.json +24 -0
  61. package/package.json +2 -1
  62. package/schemas/change-set.v2.schema.json +245 -0
  63. package/schemas/change-set.v3.schema.json +112 -0
  64. package/schemas/compensation-change-set.v1.schema.json +32 -0
  65. package/schemas/execution-receipt.v2.schema.json +106 -0
  66. package/schemas/execution-receipt.v3.schema.json +40 -0
  67. package/schemas/execution-receipt.v4.schema.json +22 -0
  68. package/schemas/inverse-descriptor.v1.schema.json +46 -0
  69. package/schemas/synapsor.runner.schema.json +132 -2
  70. package/schemas/writeback-job.v2.schema.json +238 -0
  71. package/schemas/writeback-job.v3.schema.json +53 -0
  72. package/schemas/writeback-job.v4.schema.json +17 -0
@@ -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.
@@ -1,7 +1,9 @@
1
1
  # Limitations
2
2
 
3
- Synapsor Runner is intentionally narrow. Version 1.1 adds a bounded small-fleet
4
- shape; it does not claim Synapsor Cloud scale or an enterprise SLA.
3
+ Synapsor Runner is intentionally narrow. Version 1.4 adds opt-in reviewed
4
+ compensation on top of guarded bounded-set writeback, single-row CRUD, and
5
+ explicit receipt authority; it does not claim Synapsor Cloud scale or an
6
+ enterprise SLA.
5
7
 
6
8
  ## Supported
7
9
 
@@ -20,8 +22,20 @@ shape; it does not claim Synapsor Cloud scale or an enterprise SLA.
20
22
  - `synapsor.change-set.v1`
21
23
  - `synapsor.writeback-job.v1`
22
24
  - `synapsor.execution-receipt.v1`
25
+ - backward-compatible operation-aware v2 change sets, jobs, and receipts
26
+ - bounded-set v3 change sets, jobs, and receipts
27
+ - compensation change sets and protocol-v4 jobs/receipts with bounded inverse
28
+ descriptors
23
29
  - `synapsor.runner-registration.v1`
24
- - Guarded single-row `UPDATE` for Postgres and MySQL.
30
+ - Guarded single-row `INSERT`, `UPDATE`, and `DELETE` for Postgres and MySQL.
31
+ - Fixed-predicate set `UPDATE`/`DELETE` and exact-review batch `INSERT` with
32
+ mandatory row/value caps, a hard 100-row ceiling, human approval, frozen
33
+ members, atomic apply, and exact receipts.
34
+ - Opt-in reviewed compensation for direct SQL UPDATE, INSERT, soft-delete, and
35
+ exact frozen sets. Revert is a new operator proposal with independent
36
+ approval and a fresh conflict guard.
37
+ - Atomic source receipts with precreated or auto-migrated tables, or
38
+ zero-source-schema Runner-ledger receipts with explicit reconciliation.
25
39
  - App/API handler writeback through approved `http_handler` executors.
26
40
  - Local script writeback through approved `command_handler` executors.
27
41
  - Primary-key guard.
@@ -49,17 +63,19 @@ truth for the model-facing tools.
49
63
  - Arbitrary SQL.
50
64
  - Model-generated SQL.
51
65
  - DDL.
52
- - INSERT.
53
- - DELETE.
54
66
  - UPSERT.
55
- - Multi-row UPDATE.
67
+ - Model-generated/free-form set predicates or dynamic identifiers.
68
+ - Unbounded set writes or more than 100 reviewed members.
69
+ - Policy auto-approval for bounded sets.
56
70
  - Stored procedures.
57
71
  - Cross-database transactions.
58
72
  - Physical branching of Postgres/MySQL.
59
73
  - Full Synapsor workflow/DAG execution.
60
74
  - `CREATE AGENT WORKFLOW` or hosted Synapsor SQL generation.
61
75
  - Auto-merge or settlement policy semantics.
62
- - Automatic rollback of external database writes.
76
+ - Automatic rollback, database time travel, or model-facing revert.
77
+ - Inferred compensation for app-owned handlers or external effects.
78
+ - General restoration of hard-deleted rows, cascades, or trigger side effects.
63
79
  - Model-callable approval or commit tools.
64
80
  - Generic MCP firewall behavior.
65
81
  - Prompt-injection prevention.
@@ -92,8 +108,9 @@ not a hosted central evidence service, organization RBAC/SSO, compliance
92
108
  retention system, or unbounded search engine. Each bridge operation serializes
93
109
  through an advisory lock and fails above configured `max_entries`.
94
110
 
95
- Only homogeneous 1.1 fleet operation is currently verified. Mixed 1.0/1.1
96
- rolling operation is not claimed. See [Running A Small Runner
111
+ Only homogeneous 1.4 fleet operation is claimed for protocol-v4 compensation
112
+ jobs. Mixed-minor v3/v4 rolling compensation is not claimed.
113
+ See [Running A Small Runner
97
114
  Fleet](running-a-runner-fleet.md).
98
115
 
99
116
  Use this wording:
@@ -108,3 +125,7 @@ Do not describe external approval as merge.
108
125
  ## Weak Conflict Guards
109
126
 
110
127
  A version/timestamp column is the preferred conflict guard. A weak row-hash guard can be acknowledged for local/dev use, but it should not be presented as equivalent to a durable version column.
128
+
129
+ Runner-ledger UPDATE and DELETE require an exact guard; UPDATE must advance it
130
+ inside the source transaction. INSERT requires a reviewed source-unique dedup
131
+ identity. See [Guarded Single-Row CRUD Writeback](guarded-crud-writeback.md).
@@ -21,7 +21,7 @@ and investigation surfaces for enabled design-partner deployments.
21
21
  | Capabilities | Local semantic MCP tools | Registry, version history, and capability inspection |
22
22
  | Evidence and replay | Local SQLite ledger by default; optional shared Postgres runtime store | Shared activity and evidence surfaces where enabled |
23
23
  | Approval | Local CLI or localhost UI | Team approval surfaces where enabled |
24
- | Writeback | Guarded one-row update or app-owned executor | Cloud-linked jobs with local execution; managed production orchestration is future work |
24
+ | Writeback | Guarded one-row CRUD, fixed/frozen bounded sets, or app-owned executor | Cloud-linked jobs with local execution; managed production orchestration is future work |
25
25
  | MCP risk audit | Static local audit | Organization-wide continuous audit is future work |
26
26
  | Identity | Local operator boundary | Workspace RBAC where configured; SAML and SCIM are future work |
27
27
  | Operations | Customer-operated single node or bounded small fleet | Managed fleet remains Cloud work; no enterprise SLA in the current beta |
@@ -10,7 +10,7 @@ MCP agent
10
10
  -> scoped Postgres/MySQL read
11
11
  -> evidence/query audit/proposal
12
12
  -> approval outside MCP
13
- -> guarded one-row update or app-owned executor
13
+ -> guarded one-row CRUD, bounded frozen set, or app-owned executor
14
14
  -> receipt/replay
15
15
  ```
16
16
 
@@ -58,7 +58,14 @@ Production-candidate OSS scope:
58
58
  - asymmetric RS256/ES256 session authentication, readiness probes, separately
59
59
  protected metrics, source pools, and fleet-wide rate limits;
60
60
  - distinct-reviewer approval quorum from the canonical contract;
61
- - direct guarded single-row `UPDATE` for simple approved edits;
61
+ - direct guarded single-row `INSERT`, `UPDATE`, and `DELETE` for approved
62
+ operations whose source constraints satisfy the operation-specific guards;
63
+ - fixed-predicate set `UPDATE`/`DELETE` and exact-review batch `INSERT`, with
64
+ human approval, mandatory row/value caps, a hard 100-row ceiling, frozen
65
+ members, atomic execution, and exact per-member receipts;
66
+ - opt-in reviewed compensation for direct SQL UPDATE, INSERT, soft-delete, and
67
+ exact frozen sets, using a separate operator proposal, fresh guards, and
68
+ protocol-v4 receipts;
62
69
  - app-owned `http_handler` or `command_handler` executors for richer approved
63
70
  business transactions;
64
71
  - stdio MCP and Streamable HTTP MCP.
@@ -66,8 +73,12 @@ Production-candidate OSS scope:
66
73
  Out of scope:
67
74
 
68
75
  - raw `execute_sql` or model-generated SQL;
69
- - generic direct `INSERT`, `DELETE`, `UPSERT`, DDL, or multi-row SQL writeback;
76
+ - direct `UPSERT`, DDL, model-generated predicates, unbounded set writes,
77
+ cross-table transactions, or INSERT/DELETE whose source constraints cannot
78
+ prove the reviewed effect;
70
79
  - physical branching of external Postgres/MySQL;
80
+ - automatic rollback/time travel, hard-delete restoration, and inferred
81
+ compensation for app-owned or external effects;
71
82
  - workflow DAGs, auto-merge/settlement, hosted team administration, SSO/SCIM,
72
83
  multi-region ledger replication, or compliance retention;
73
84
  - making prompt injection impossible.
@@ -85,11 +96,12 @@ Read credential:
85
96
 
86
97
  Write credential for direct `sql_update`:
87
98
 
88
- - can update only the allowed business columns;
99
+ - can use only the operation-specific business DML and reviewed columns;
89
100
  - cannot modify primary-key or tenant columns;
90
- - can `SELECT`/`INSERT`/`UPDATE` the administrator-created
91
- `synapsor_writeback_receipts` table;
92
- - does not need schema `CREATE` during doctor or apply;
101
+ - has source receipt-table `SELECT`/`INSERT`/`UPDATE` only when receipt
102
+ authority is `source_db`;
103
+ - needs schema `CREATE` only for explicitly selected `auto_migrate` receipt
104
+ provisioning;
93
105
  - is never exposed to MCP clients.
94
106
 
95
107
  Example config:
@@ -110,10 +122,17 @@ Example config:
110
122
  `write_url_env`. `SYNAPSOR_DATABASE_URL` is only a legacy fallback for direct
111
123
  worker flows that do not pass a local config.
112
124
 
113
- ## Receipt Table
125
+ Set `statement_timeout_ms` on each production source. PostgreSQL direct
126
+ writeback uses it for transaction-local statement and lock limits. MySQL uses
127
+ it for preflight execution and InnoDB lock waits; because MySQL does not apply
128
+ `max_execution_time` to general DML, keep bounded sets small and enforce
129
+ source-side operational query limits too.
114
130
 
115
- Direct SQL writeback stores idempotency receipts in the source database. Runner
116
- expects an administrator to create this table before steady-state operation:
131
+ ## Receipt Authority
132
+
133
+ Direct SQL writeback supports atomic `source_db` receipts and
134
+ zero-source-schema `runner_ledger` receipts. For source receipts, choose an
135
+ administrator-precreated table or explicit idempotent auto-migration:
117
136
 
118
137
  ```sql
119
138
  CREATE TABLE IF NOT EXISTS synapsor_writeback_receipts (...);
@@ -129,26 +148,36 @@ synapsor-runner doctor --config synapsor.runner.json --check-writeback
129
148
 
130
149
  For MySQL, replace `postgres` with `mysql`.
131
150
 
132
- Grant only receipt-table `SELECT`/`INSERT`/`UPDATE` and schema usage to the
133
- writer; schema `CREATE` is not required by doctor or apply. Use an app-owned
134
- executor when receipt storage belongs inside your application boundary.
151
+ Grant only receipt-table `SELECT`/`INSERT`/`UPDATE` and schema usage in
152
+ `precreated` mode. `auto_migrate` additionally needs bounded `CREATE`.
153
+ `runner_ledger` needs neither source receipt-table grants nor `CREATE`, but an
154
+ ambiguous crash after source commit requires operator reconciliation and is not
155
+ distributed exactly-once. See
156
+ [Guarded Single-Row CRUD Writeback](guarded-crud-writeback.md) for the complete
157
+ mode, operation, and privilege matrix.
135
158
 
136
159
  ## Direct Writeback Vs App-Owned Executor
137
160
 
138
- Use direct `sql_update` when the approved change is:
161
+ Use direct writeback when the approved change is either:
139
162
 
140
163
  ```text
141
- one existing row -> one allowed-column patch -> one guarded UPDATE
164
+ one reviewed row -> one guarded INSERT/UPDATE/DELETE -> one exact effect
165
+
166
+ or
167
+
168
+ one fixed reviewed rule/item list -> <= 100 frozen rows -> one atomic bounded set
142
169
  ```
143
170
 
144
171
  Use an app-owned executor when the change is richer:
145
172
 
146
- - insert a credit/refund/review row;
147
- - update more than one row;
173
+ - choose rows from a model-supplied predicate or exceed the reviewed cap;
148
174
  - touch more than one table;
149
175
  - emit an event or call another internal service;
150
176
  - enforce business logic that belongs in your app.
151
177
 
178
+ Bounded sets must satisfy every invariant in [Bounded Set
179
+ Writeback](bounded-set-writeback.md); otherwise they remain executor work.
180
+
152
181
  Handlers must re-check tenant/scope, expected version, idempotency, allowed
153
182
  business action, transaction boundaries, and safe errors. See
154
183
  [Writeback Executors](writeback-executors.md) and
@@ -336,9 +365,12 @@ synapsor-runner apply --all-approved --yes \
336
365
  ```
337
366
 
338
367
  Each proposal is independent: a stale-row conflict does not abort later jobs.
339
- The final summary reports applied, conflict, and skipped IDs. Re-running is
340
- idempotent through durable receipts. Do not schedule batch apply for a policy
341
- that has no reviewed aggregate limits.
368
+ In `runtime_store` mode, the batch holds one authoritative bridge while every
369
+ selected item applies, then syncs the resulting states, receipts, and events
370
+ back together. The final summary reports applied, conflict, and skipped IDs;
371
+ every skipped item includes a safe reason. Re-running is idempotent through
372
+ durable receipts. Do not schedule batch apply for a policy that has no reviewed
373
+ aggregate limits.
342
374
 
343
375
  Runner writes newline-delimited JSON events to stderr for model-facing tool
344
376
  rejections, operator decisions, and terminal writeback outcomes. These events