@synapsor/runner 1.1.0 → 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 +105 -1
  2. package/README.md +29 -7
  3. package/THREAT_MODEL.md +13 -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 +10406 -4932
  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 +36 -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
@@ -0,0 +1,165 @@
1
+ # RFC 004: Guarded CRUD Receipt Authority
2
+
3
+ Status: implementation gate
4
+
5
+ ## Purpose
6
+
7
+ Runner 1.2 adds guarded single-row INSERT and DELETE while making source receipt
8
+ DDL optional. It must preserve the defining boundary: a reviewed, tenant-bound,
9
+ one-row business effect with evidence, external approval, a receipt, and replay.
10
+
11
+ This RFC is required before adapter expansion because receipt placement changes
12
+ which outcomes can be proven after a crash.
13
+
14
+ ## Configuration Model
15
+
16
+ Two decisions are orthogonal:
17
+
18
+ ```text
19
+ receipt_authority: source_db | runner_ledger
20
+ source_receipt_provisioning: precreated | auto_migrate
21
+ ```
22
+
23
+ `source_receipt_provisioning` is valid only for `source_db`.
24
+
25
+ For compatibility, omitted fields mean `source_db` plus `precreated`. A new
26
+ wizard may recommend `auto_migrate`, but it must obtain explicit consent before
27
+ granting Runner DDL authority. `runner_ledger` is opt-in and must explain its
28
+ post-commit ambiguity.
29
+
30
+ Receipt schema/table identifiers are operator config, validated as fixed safe
31
+ identifiers. They are never contract fields or model arguments.
32
+
33
+ ## Terms
34
+
35
+ - **Exactly-once classification:** Runner can prove whether this proposal was
36
+ applied, conflicted, or failed.
37
+ - **At-most-once business effect:** the source cannot perform the reviewed
38
+ effect twice for the same deterministic identity.
39
+ - **Idempotent effect:** repeating an operation converges on the same source
40
+ state, but may not prove who caused it.
41
+ - **Ambiguous outcome:** the source may have committed, but Runner lacks an
42
+ atomic receipt proving that fact. This requires reconciliation.
43
+
44
+ These terms are not interchangeable.
45
+
46
+ ## Claim Matrix
47
+
48
+ The claims below apply equally to Postgres and MySQL. Engine-specific tests must
49
+ prove them separately.
50
+
51
+ | Operation | Authority | Source prerequisite | Normal result | Retry after completed receipt | Crash after source commit, before Runner completion |
52
+ | --- | --- | --- | --- | --- | --- |
53
+ | UPDATE | `source_db` | exact PK+tenant+version guard; source receipt table | atomic one-row UPDATE and receipt | `already_applied` | atomic transaction proves applied or rolled back |
54
+ | INSERT | `source_db` | trusted tenant; reviewed insert columns; proposal-specific PK/UNIQUE dedup key; source receipt table | atomic one-row INSERT and receipt | `already_applied` | atomic transaction proves applied or rolled back |
55
+ | DELETE | `source_db` | exact PK+tenant+version guard; no expanding cascade/write trigger; source receipt table | atomic one-row DELETE and receipt | `already_applied` | atomic transaction proves applied or rolled back |
56
+ | UPDATE | `runner_ledger` | authoritative shared ledger for fleets; exact PK+tenant+version guard; monotonic version advancement in source transaction | intent, one-row UPDATE, terminal ledger receipt | completed matching receipt returns `already_applied` | advanced guard plus incomplete intent is `reconciliation_required` |
57
+ | INSERT | `runner_ledger` | authoritative shared ledger for fleets; proposal-specific source PK/UNIQUE key | intent, one-row INSERT, terminal ledger receipt | completed matching receipt returns `already_applied` | matching unique row plus incomplete intent is reconciled only when provenance is provable; otherwise `reconciliation_required` |
58
+ | DELETE | `runner_ledger` | authoritative shared ledger for fleets; exact PK+tenant+version guard; no expanding side effects | intent, one-row DELETE, terminal ledger receipt | completed matching receipt returns `already_applied` | missing row plus incomplete intent is `reconciliation_required`, not proof of this delete |
59
+
60
+ ## Failure and Crash Matrix
61
+
62
+ | Point/outcome | `source_db` | `runner_ledger` |
63
+ | --- | --- | --- |
64
+ | ledger/source unavailable before intent | no source effect; retryable unavailable | no source effect; retryable unavailable |
65
+ | crash before source BEGIN | no source effect | durable intent remains; safe retry only if no ambiguous attempt was entered |
66
+ | crash after BEGIN, before mutation | transaction rolls back | source transaction rolls back; intent can be marked failed/retried by bounded operator/worker logic |
67
+ | stale/version conflict | conflict receipt commits atomically | conflict terminal event is recorded; no source mutation |
68
+ | crash after mutation, before source COMMIT | transaction rolls back | source transaction rolls back; no business effect |
69
+ | crash after source COMMIT, before Runner completion | impossible as a split outcome because source receipt shares the transaction | terminal state is `reconciliation_required`; never auto-retry |
70
+ | concurrent identical apply | source receipt serializes one outcome | ledger intent plus source guard/unique key permits at most one effect; ambiguous loser reconciles |
71
+ | source unavailable after intent | no source effect | intent remains failed/retryable only while no source transaction could have committed |
72
+
73
+ ## Operation Proof Obligations
74
+
75
+ ### UPDATE
76
+
77
+ Runner-ledger UPDATE requires a real version guard and monotonic advancement in
78
+ the same source transaction. Accepted strategies are a Runner-generated integer
79
+ increment or a database-generated version whose changed value is verified
80
+ before commit. A name such as `updated_at` is not proof. Insufficient timestamp
81
+ precision fails preflight.
82
+
83
+ An advanced source version plus an incomplete intent is ambiguous, not
84
+ `already_applied`.
85
+
86
+ ### INSERT
87
+
88
+ INSERT requires a proposal-specific deterministic identity backed by an
89
+ inspected PRIMARY KEY or UNIQUE constraint. The model cannot set tenant,
90
+ identity/generated columns, or arbitrary columns. The contract declares the
91
+ portable dedup mapping; Runner config declares receipt placement.
92
+
93
+ Ledger memory by itself does not prevent a duplicate INSERT after a process
94
+ crash. A source uniqueness constraint is mandatory.
95
+
96
+ ### DELETE
97
+
98
+ DELETE targets one reviewed row by primary key, trusted tenant, and expected
99
+ version. The proposal freezes allowlisted before evidence. A hard delete is
100
+ refused when cascading foreign keys or write triggers can expand the reviewed
101
+ effect. Soft delete remains the preferred path.
102
+
103
+ A missing row after an incomplete Runner-ledger attempt proves only that the
104
+ row is absent, not that this attempt deleted it.
105
+
106
+ ## Durable Runner-Ledger Intent
107
+
108
+ Before touching the source, Runner durably stores:
109
+
110
+ - proposal ID, version, and hash;
111
+ - capability and operation;
112
+ - trusted tenant and target/dedup identity;
113
+ - expected guard;
114
+ - bounded attempt number;
115
+ - state and timestamps.
116
+
117
+ Allowed states are:
118
+
119
+ ```text
120
+ intent_recorded
121
+ applying
122
+ applied
123
+ already_applied
124
+ conflict
125
+ failed
126
+ reconciliation_required
127
+ ```
128
+
129
+ Transitions append immutable events. An incomplete `applying` attempt is never
130
+ automatically restarted if the source may have committed.
131
+
132
+ ## Reconciliation
133
+
134
+ The operator command re-reads only the reviewed target under trusted tenant
135
+ scope and displays expected versus observed allowlisted metadata. It never
136
+ guesses. A verified operator can mark `applied`, `conflict`, or `failed` with a
137
+ reason; Runner appends a reconciliation event and receipt. Production signed
138
+ identity requirements apply unchanged.
139
+
140
+ ## Topology Rules
141
+
142
+ - local SQLite Runner-ledger authority is allowed only for one local operator
143
+ process;
144
+ - Streamable HTTP fleets and multiple workers require shared Postgres
145
+ `runtime_store` as the authoritative ledger;
146
+ - `mirror` is not authoritative enough for Runner-ledger write intents;
147
+ - unsafe topology fails at config/doctor/start preflight.
148
+
149
+ ## Security Invariants
150
+
151
+ - raw SQL, receipt controls, identifiers, approval, apply, credentials, tenant,
152
+ and principal remain outside model-facing tools;
153
+ - all identifiers are contract/config validated and all values are parameters;
154
+ - unknown, stale, ambiguous, or multi-row effects fail closed;
155
+ - kept-out fields are not persisted in intents, receipts, metrics, or replay;
156
+ - local resource reads continue to reauthorize tenant and principal;
157
+ - no claim of distributed atomic exactly-once is made for `runner_ledger`.
158
+
159
+ ## Bounded Set Gate
160
+
161
+ This RFC does not authorize multi-row writes. Set operations require a separate
162
+ RFC with fixed typed predicates, frozen exact targets, mandatory count/value
163
+ bounds, deterministic row locking, one transaction, exact receipts, and all
164
+ R1-R7 threat mitigations. `MAX ROWS` syntax alone is not shippable.
165
+
@@ -0,0 +1,164 @@
1
+ # RFC 005: Bounded Set Writeback
2
+
3
+ Status: implementation gate
4
+
5
+ ## Purpose
6
+
7
+ Runner 1.3 may execute a reviewed set write only when the set is fixed by the
8
+ contract, frozen before approval, bounded by both row count and business value,
9
+ and applied atomically. A row cap by itself is not a safety boundary.
10
+
11
+ This RFC authorizes three direct operations:
12
+
13
+ - fixed-predicate set UPDATE;
14
+ - fixed-predicate set DELETE;
15
+ - exact-review batch INSERT.
16
+
17
+ Free-form predicates, model-selected columns or operators, unbounded sets,
18
+ UPSERT, DDL, cross-table work, and external effects remain app-owned executor
19
+ work.
20
+
21
+ ## Canonical Model
22
+
23
+ Set operations are explicit. They never reuse a single-row lookup by merely
24
+ raising `max_rows`.
25
+
26
+ ```json
27
+ {
28
+ "operation": {
29
+ "kind": "update",
30
+ "cardinality": "set",
31
+ "selection": {
32
+ "all": [
33
+ { "column": "status", "operator": "eq", "value": "overdue" }
34
+ ]
35
+ },
36
+ "max_rows": 10,
37
+ "aggregate_bounds": [
38
+ { "column": "balance_cents", "measure": "before", "maximum": 50000 }
39
+ ]
40
+ }
41
+ }
42
+ ```
43
+
44
+ The first release accepts only contract literals in `selection`. The typed AST
45
+ reserves no raw SQL escape hatch. The model can trigger the reviewed capability
46
+ but cannot choose a predicate, column, operator, ordering, range, wildcard, or
47
+ set size.
48
+
49
+ Batch INSERT uses an explicitly typed bounded item-array argument. Every item
50
+ is fully materialized in the proposal, receives the trusted tenant, and has a
51
+ source-enforced unique dedup identity. A generic JSON blob is not accepted.
52
+
53
+ ## Hard Limits
54
+
55
+ - `MAX ROWS` is mandatory and must be between 1 and 100.
56
+ - at least one aggregate value bound is mandatory;
57
+ - a proposal query fetches at most `MAX ROWS + 1` rows;
58
+ - at most 64 reviewed columns may be captured per row;
59
+ - before/after payloads and digests are bounded before persistence;
60
+ - overflow rejects the proposal; it is never truncated to the cap.
61
+
62
+ The implementation ceiling of 100 is a safety limit, not a throughput claim.
63
+
64
+ ## Frozen Set
65
+
66
+ At proposal time Runner stores, in deterministic primary-key order:
67
+
68
+ - exact primary key;
69
+ - trusted tenant;
70
+ - expected version for UPDATE/DELETE;
71
+ - allowlisted before and after values;
72
+ - per-row before and after digests;
73
+ - count, aggregate measurements, and one set digest.
74
+
75
+ Apply never re-runs the broad selection predicate. It locks the frozen primary
76
+ keys in the same deterministic order, rechecks tenant and every expected
77
+ version, and mutates the complete set in one transaction. A missing or stale
78
+ member rolls back the whole transaction.
79
+
80
+ ## Approval
81
+
82
+ All set operations require verified human/operator approval in 1.3. Policy
83
+ auto-approval is rejected rather than silently downgraded. Set hard DELETE and
84
+ batch INSERT are never auto-approved. This keeps row/value-aware policy design
85
+ out of the first release.
86
+
87
+ ## Exact Effects
88
+
89
+ The receipt and replay carry every affected primary key and bounded before/
90
+ after digest. Kept-out fields are never fetched merely to improve audit detail.
91
+ The source receipt and mutation share one transaction in `source_db` mode.
92
+ `runner_ledger` mode retains the documented post-source-commit ambiguity and
93
+ uses `reconciliation_required`; it never retries an ambiguous batch.
94
+
95
+ ## Operation Rules
96
+
97
+ ### Set UPDATE
98
+
99
+ - fixed literal predicate;
100
+ - fixed or scalar-argument patch with existing per-field bounds;
101
+ - exact version guard and monotonic version advance for every row;
102
+ - mandatory aggregate bound;
103
+ - one transaction and exact affected-row count.
104
+
105
+ ### Set DELETE
106
+
107
+ - fixed literal predicate;
108
+ - exact version guard for every row;
109
+ - verified human approval;
110
+ - no write triggers or cascading foreign keys;
111
+ - bounded tombstones and exact identities.
112
+
113
+ Soft delete remains the preferred set operation because it is a guarded
114
+ UPDATE and can later be reversed safely.
115
+
116
+ ### Batch INSERT
117
+
118
+ - one typed item array with `max_items` no greater than `MAX ROWS`;
119
+ - every candidate row shown in the proposal;
120
+ - trusted tenant injected by Runner;
121
+ - source UNIQUE/PRIMARY KEY dedup identity for every item;
122
+ - per-item bounds plus a mandatory aggregate bound;
123
+ - all rows and source receipts commit atomically or none do.
124
+
125
+ ## Threat Cases And Fixtures
126
+
127
+ | Threat | Mitigation | Conformance fixture |
128
+ | --- | --- | --- |
129
+ | R1 selection abuse | literal typed predicate; no model predicate inputs | `bounded-set-r1-fixed-selection` |
130
+ | R2 misleading cap | row cap plus aggregate value bound; overflow rejects | `bounded-set-r2-count-value-cap` |
131
+ | R3 TOCTOU drift | frozen PK/version set; apply never broad-requeries | `bounded-set-r3-frozen-drift` |
132
+ | R4 partial application | one transaction; exact count; rollback on any member | `bounded-set-r4-atomic-rollback` |
133
+ | R5 audit collapse | every key and bounded before/after digest in receipt/replay | `bounded-set-r5-exact-receipt` |
134
+ | R6 DELETE cascades | hard DELETE preflight rejects cascades/write triggers | `bounded-set-r6-delete-side-effects` |
135
+ | R7 auto-approval amplification | human approval mandatory; policy mode rejected | `bounded-set-r7-human-approval` |
136
+
137
+ Each fixture must be exercised by canonical validation and runtime/adapter
138
+ tests. A fixture file that is never loaded by a test is not evidence.
139
+
140
+ ## Crash And Receipt Matrix
141
+
142
+ | Outcome | `source_db` | `runner_ledger` |
143
+ | --- | --- | --- |
144
+ | before source BEGIN | no effect | durable intent, no effect |
145
+ | one member stale | complete rollback and conflict receipt | complete rollback and terminal conflict |
146
+ | process death before source commit | complete rollback | complete rollback; intent may be safely resolved as not committed |
147
+ | process death after source commit | source mutation and receipt prove outcome atomically | `reconciliation_required`; never automatic retry |
148
+ | retry after completed receipt | `already_applied` | `already_applied` from authoritative ledger |
149
+
150
+ No mode claims a distributed transaction across Runner's ledger and an external
151
+ source database.
152
+
153
+ ## Release Gate
154
+
155
+ The feature remains unavailable until Postgres and MySQL live tests prove:
156
+
157
+ - cap+1 and aggregate overflow reject before proposal persistence;
158
+ - one stale member and injected failure leave every source row unchanged;
159
+ - lock order is deterministic;
160
+ - exact IDs/digests appear in receipts and replay;
161
+ - hard DELETE side effects and auto-approval are rejected;
162
+ - batch INSERT dedup and crash behavior are safe in both receipt modes;
163
+ - seeded 1, 10, and 100 row measurements are recorded as local evidence only.
164
+
@@ -0,0 +1,152 @@
1
+ # RFC 006: Reviewed Reversible Change Sets
2
+
3
+ Status: implemented and verified for the prepared Runner 1.4 release line
4
+
5
+ ## Problem
6
+
7
+ An applied direct database write currently has evidence, approval, a guarded
8
+ writeback receipt, and replay, but no bounded compensating action. Correcting a
9
+ mistake requires an operator to author a new forward proposal manually.
10
+
11
+ Runner should be able to prepare a reviewed compensation without becoming a
12
+ database time-travel system or introducing an unreviewed rollback path.
13
+
14
+ ## Safety boundary
15
+
16
+ Reversal is opt-in contract authority:
17
+
18
+ ```json
19
+ {
20
+ "reversibility": {
21
+ "mode": "reviewed_inverse"
22
+ }
23
+ }
24
+ ```
25
+
26
+ The declaration is valid only for Runner-owned `direct_sql` writeback. It does
27
+ not apply to app handlers, Cloud workers, email, payments, or any other
28
+ external effect.
29
+
30
+ `synapsor-runner revert <proposal-id>` creates a new proposal. It never:
31
+
32
+ - mutates the source database;
33
+ - approves the proposal;
34
+ - bypasses tenant or operator identity checks;
35
+ - appears in the model-facing MCP tool list.
36
+
37
+ The new proposal must be independently approved and applied through the normal
38
+ trusted operator path.
39
+
40
+ ## Protocol split
41
+
42
+ Published v1, v2, and v3 forward writeback jobs keep their existing meaning.
43
+ Reversible forward jobs carry an additive, explicit inverse-capture request.
44
+ The resulting execution receipt carries a bounded inverse descriptor only
45
+ after a successful, unambiguous source transaction.
46
+
47
+ Compensation is executed through a distinct normalized protocol v4 job. A v4
48
+ job contains:
49
+
50
+ - the exact source, tenant, and primary-key authority;
51
+ - a bounded, deterministically ordered member list;
52
+ - the original single-row or set cardinality;
53
+ - the state expected after the forward write;
54
+ - only the reviewed values to restore or remove;
55
+ - the original row and aggregate caps;
56
+ - the original approval role and quorum;
57
+ - lineage linking root, parent, and reverted proposals and the forward receipt;
58
+ - a maximum lineage depth.
59
+
60
+ This prevents compensation semantics from being inferred from a legacy job.
61
+
62
+ ## Operation semantics
63
+
64
+ ### UPDATE and soft delete
65
+
66
+ The inverse restores only previously reviewed writable values. The row must
67
+ still match the exact reviewed state left by the forward write. A monotonic
68
+ version column advances again during compensation; it is never decremented.
69
+ Any intervening write fails closed.
70
+
71
+ A soft delete is an UPDATE of a reviewed status or deletion marker and follows
72
+ these same rules.
73
+
74
+ ### INSERT
75
+
76
+ The inverse deletes only the exact Runner-created identity. The current row
77
+ must still match the allowlisted values inserted by Runner. Trigger and foreign
78
+ key safety is rechecked immediately before deletion. A new dependent row or a
79
+ changed value blocks compensation.
80
+
81
+ Because this delete removes a row whose complete Runner-supplied values are
82
+ known, its own inverse may reinsert that exact row. It cannot infer or restore
83
+ unreviewed database-generated or trigger-generated state.
84
+
85
+ ### Hard DELETE
86
+
87
+ A general hard DELETE records bounded reviewed before-state evidence but is
88
+ not automatically restorable. Generated identities, hidden required columns,
89
+ cascaded children, and trigger side effects cannot be reconstructed safely.
90
+ The receipt marks the inverse unavailable with specific reason codes and the
91
+ CLI recommends soft delete or an app-owned compensation capability.
92
+
93
+ ## Bounded sets
94
+
95
+ Set compensation uses exactly the original frozen identities. It cannot widen
96
+ the predicate or increase the original row/value caps. Every member is locked
97
+ in deterministic primary-key order and checked before any mutation. One stale
98
+ member rolls back the entire transaction.
99
+
100
+ The inverse descriptor is bounded to 100 members and 256 scalar fields per
101
+ member, matching the public protocol hard ceilings.
102
+
103
+ ## Receipt authority and crash behavior
104
+
105
+ With source-database receipt authority, mutation and source receipt remain in
106
+ one transaction. The inverse descriptor is deterministic from the immutable
107
+ job plus the state verified in that transaction, so an idempotent retry can
108
+ reproduce it without rereading kept-out columns.
109
+
110
+ With Runner-ledger authority, the existing cross-database crash window remains.
111
+ An ambiguous forward or compensation outcome enters
112
+ `reconciliation_required`. Runner does not generate or apply a revert until an
113
+ operator reconciles the original outcome and an applied receipt with an
114
+ available inverse exists.
115
+
116
+ ## Lineage
117
+
118
+ Every compensation proposal records:
119
+
120
+ - `root_proposal_id`;
121
+ - `parent_proposal_id`;
122
+ - `reverts_proposal_id`;
123
+ - `forward_receipt_hash`;
124
+ - `depth`.
125
+
126
+ Depth is capped at 16. The same applied receipt may have only one active or
127
+ completed compensation child. To undo a compensation, the operator reverts the
128
+ child proposal, creating a linear audited chain instead of a cycle.
129
+
130
+ ## Data minimization
131
+
132
+ Inverse capture may retain only:
133
+
134
+ - fixed primary-key and trusted tenant identity;
135
+ - conflict/version metadata;
136
+ - fields in the reviewed write allowlist;
137
+ - Runner-supplied deterministic deduplication fields needed to identify an
138
+ inserted row.
139
+
140
+ Visible read fields that were not writable and all kept-out fields are excluded.
141
+ Reversibility is rejected when the safe inverse cannot be represented with that
142
+ bounded data.
143
+
144
+ ## Explicit non-goals
145
+
146
+ - point-in-time recovery or WAL replacement;
147
+ - automatic rollback;
148
+ - model-facing revert, approve, apply, or receipt tools;
149
+ - restoration of arbitrary hard deletes;
150
+ - compensation of app-owned executors or external effects;
151
+ - cross-table, cross-database, or unbounded compensation;
152
+ - weakening the existing optimistic concurrency and receipt-authority model.
@@ -112,7 +112,13 @@ not print database URLs or create the schema.
112
112
  "read_url_env": "BILLING_POSTGRES_READ_URL",
113
113
  "write_url_env": "BILLING_POSTGRES_WRITE_URL",
114
114
  "read_only": false,
115
- "statement_timeout_ms": 3000
115
+ "statement_timeout_ms": 3000,
116
+ "receipts": {
117
+ "authority": "source_db",
118
+ "provisioning": "precreated",
119
+ "schema": "synapsor",
120
+ "table": "writeback_receipts"
121
+ }
116
122
  }
117
123
  }
118
124
  }
@@ -121,8 +127,13 @@ not print database URLs or create the schema.
121
127
  `engine` is `postgres` or `mysql`. `read_url_env` is required. Use a
122
128
  least-privilege read credential. `write_url_env` is optional unless direct SQL
123
129
  writeback must apply; it should name a separate restricted writer. `read_only:
124
- true` forbids direct SQL writeback. `statement_timeout_ms` is a positive read
125
- timeout. `ssl` carries adapter-specific reviewed SSL options when used.
130
+ true` forbids direct SQL writeback. `statement_timeout_ms` is an
131
+ operator-controlled positive timeout used for reads and direct SQL writeback.
132
+ PostgreSQL applies it as transaction-local `statement_timeout` and
133
+ `lock_timeout`. MySQL applies it to read/preflight execution and rounds it up
134
+ to whole seconds for `innodb_lock_wait_timeout`; MySQL does not provide the
135
+ same general DML statement-timeout guarantee as PostgreSQL. `ssl` carries
136
+ adapter-specific reviewed SSL options when used.
126
137
 
127
138
  A contract's `SOURCE billing_postgres` must exactly match a `sources` key.
128
139
 
@@ -131,8 +142,31 @@ Long-running servers reuse native driver pools. Optional `pool` keys are
131
142
  `idle_timeout_ms` (30000), `queue_timeout_ms` (5000), and `queue_limit`
132
143
  (default `max(10, max_connections * 4)`). Queue overflow returns
133
144
  `SOURCE_POOL_QUEUE_FULL`; acquisition timeout returns `SOURCE_POOL_TIMEOUT`.
145
+ With result envelope v2, these conditions and recognized transient driver
146
+ connection failures are model-facing `TEMPORARILY_UNAVAILABLE` errors with
147
+ `retryable: true` and a bounded `retry_after_ms`. Operational logs retain only
148
+ a normalized safe runtime code, not the driver message, host, or credentials.
134
149
  One-shot CLI commands close their pools.
135
150
 
151
+ ### Source receipts
152
+
153
+ `sources.<name>.receipts.authority` is `source_db` or `runner_ledger`.
154
+
155
+ - `source_db` requires `provisioning: "precreated"` or `"auto_migrate"`.
156
+ Optional fixed `schema` and `table` identifiers select the receipt table.
157
+ The source mutation and receipt share one transaction.
158
+ - `runner_ledger` forbids `provisioning`, `schema`, and `table`. It creates no
159
+ source receipt table. Local SQLite is permitted for one process;
160
+ networked/multi-Runner use requires authoritative shared Postgres
161
+ `runtime_store` and rejects mirror mode.
162
+
163
+ Existing configs without `receipts` retain the compatibility source-receipt
164
+ behavior. New onboarding asks explicitly. Runner-ledger UPDATE requires exact
165
+ version advancement; INSERT requires source-enforced deduplication; DELETE
166
+ requires an exact version guard. Ambiguous post-commit outcomes stop at
167
+ `reconciliation_required`. See
168
+ [Guarded Single-Row CRUD Writeback](guarded-crud-writeback.md).
169
+
136
170
  ## Trusted context
137
171
 
138
172
  ```json
@@ -227,6 +261,39 @@ fields are documented in the JSON Schema and
227
261
  lookup, visible fields, evidence, patch allowlist, bounds/transitions, conflict
228
262
  guard, approval, and writeback mode.
229
263
 
264
+ ## Bounded set operation fields
265
+
266
+ A set operation is portable reviewed authority, so it can arrive from a
267
+ contract or the equivalent embedded capability:
268
+
269
+ ```json
270
+ {
271
+ "operation": {
272
+ "kind": "update",
273
+ "cardinality": "set",
274
+ "selection": {
275
+ "all": [{ "column": "status", "operator": "eq", "value": "overdue" }]
276
+ },
277
+ "max_rows": 10,
278
+ "aggregate_bounds": [
279
+ { "column": "balance_cents", "measure": "before", "maximum": 50000 }
280
+ ],
281
+ "version_advance": { "column": "version", "strategy": "integer_increment" }
282
+ },
283
+ "approval": { "mode": "human", "required_role": "billing_reviewer" },
284
+ "writeback": { "mode": "direct_sql" }
285
+ }
286
+ ```
287
+
288
+ Set UPDATE/DELETE requires one through eight fixed equality terms in
289
+ `selection.all`; the model cannot add or override them. Every set requires
290
+ `max_rows` from 1 through 100 and one through eight aggregate bounds. Set
291
+ UPDATE requires integer version advancement. Batch INSERT instead declares
292
+ `operation.batch.items_from_arg` pointing to a bounded `object_array` argument
293
+ and item-field dedup components. All forms require human/operator approval and
294
+ direct SQL writeback in 1.3. See [Bounded Set
295
+ Writeback](bounded-set-writeback.md).
296
+
230
297
  ## Executors
231
298
 
232
299
  ```json
@@ -247,7 +314,10 @@ guard, approval, and writeback mode.
247
314
  }
248
315
  ```
249
316
 
250
- Types are `sql_update`, `http_handler`, and `command_handler`. HTTP handlers use
317
+ Types are `sql_update`, `http_handler`, and `command_handler`. The legacy
318
+ `sql_update` executor identifier now dispatches the reviewed native operation
319
+ (`insert`, `update`, or `delete`) and remains named for config compatibility.
320
+ HTTP handlers use
251
321
  `url_env`, optional `POST|PUT|PATCH`, bearer auth, signing secret, and timeout.
252
322
  Command handlers use `command_env` and timeout. Secrets stay in the environment.
253
323
  See [Writeback Executors](writeback-executors.md).
@@ -382,9 +452,10 @@ capability permissions, and trusted session metadata.
382
452
 
383
453
  ## Direct SQL readiness
384
454
 
385
- An administrator must apply `writeback migration` once. The steady-state writer
386
- needs target-table SELECT/allowed UPDATE plus receipt-table
387
- SELECT/INSERT/UPDATE; it does not need schema CREATE.
455
+ For `source_db` + `precreated`, an administrator applies `writeback migration`
456
+ once. The steady-state writer needs target-table `SELECT` and only the reviewed
457
+ operation DML plus receipt-table `SELECT`/`INSERT`/`UPDATE`; it does not need
458
+ schema `CREATE`.
388
459
 
389
460
  ```bash
390
461
  synapsor-runner writeback migration --engine postgres --schema synapsor
@@ -393,5 +464,8 @@ synapsor-runner writeback grants --engine postgres \
393
464
  synapsor-runner doctor --config ./synapsor.runner.json --check-writeback
394
465
  ```
395
466
 
396
- The doctor probe uses a rolled-back receipt insert and target-table check. It
397
- does not mutate business rows.
467
+ `source_db` + `auto_migrate` runs that fixed migration idempotently and needs
468
+ bounded `CREATE`. `runner_ledger` skips source receipt DDL/DML and validates its
469
+ authoritative ledger topology. Doctor uses rollback-only receipt and target
470
+ checks and does not mutate business rows. See
471
+ [Guarded Single-Row CRUD Writeback](guarded-crud-writeback.md).
@@ -89,7 +89,8 @@ worker termination during an open write transaction rolls back and recovers
89
89
  worker termination after commit recovers as already-applied
90
90
  source-down, read-only-ledger, and timeout readiness failures recover without restart
91
91
  shared dead letters requeue or discard with receipts and events preserved
92
- Postgres and MySQL pool queues fail fast at configured bounds
92
+ Postgres and MySQL pool queues fail fast at configured bounds with retry hints
93
+ shared runtime-store batch apply preserves one authoritative bridge and durable receipts
93
94
  backup digest and clean restore match
94
95
  retention archives before delete and preserves active proposals
95
96
  ```