@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.
- package/CHANGELOG.md +123 -1
- package/README.md +52 -31
- package/SECURITY.md +11 -0
- package/THREAT_MODEL.md +7 -4
- package/dist/cli.d.ts +5 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/local-ui.d.ts.map +1 -1
- package/dist/runner.mjs +8103 -2601
- package/docs/README.md +23 -3
- package/docs/benchmarks/bounded-set-local.md +27 -0
- package/docs/bounded-set-writeback.md +298 -0
- package/docs/capability-authoring.md +26 -8
- package/docs/conformance.md +12 -0
- package/docs/current-scope.md +12 -4
- package/docs/doctor.md +21 -7
- package/docs/dsl-reference.md +91 -5
- package/docs/getting-started-own-database.md +26 -3
- package/docs/guarded-crud-writeback.md +218 -0
- package/docs/limitations.md +30 -9
- package/docs/oss-vs-cloud.md +1 -1
- package/docs/production.md +52 -20
- package/docs/release-notes.md +105 -2
- package/docs/release-policy.md +1 -1
- package/docs/result-envelope-v2.md +20 -6
- package/docs/result-envelope-v3.md +43 -0
- package/docs/result-envelope-v4.md +47 -0
- package/docs/reversible-change-sets.md +151 -0
- package/docs/rfcs/004-guarded-crud-receipt-authority.md +165 -0
- package/docs/rfcs/005-bounded-set-writeback.md +164 -0
- package/docs/rfcs/006-reviewed-reversible-change-sets.md +152 -0
- package/docs/runner-config-reference.md +83 -9
- package/docs/running-a-runner-fleet.md +2 -1
- package/docs/security-boundary.md +36 -9
- package/docs/why-synapsor-vs-app-guardrails.md +169 -0
- package/docs/writeback-executors.md +12 -11
- package/examples/runner-fleet/seed/mysql.sql +47 -0
- package/examples/runner-fleet/seed/postgres.sql +48 -0
- package/examples/support-plan-credit/README.md +31 -0
- package/examples/support-plan-credit/contract.synapsor.sql +26 -0
- package/examples/support-plan-credit/expected-output/tools-preview.txt +1 -0
- package/examples/support-plan-credit/seed/001_seed.sql +14 -1
- package/examples/support-plan-credit/synapsor.contract.json +101 -1
- package/examples/support-plan-credit/synapsor.runner.json +7 -1
- package/fixtures/protocol/MANIFEST.json +131 -1
- package/fixtures/protocol/change-set.bounded-update.v3.json +57 -0
- package/fixtures/protocol/change-set.delete.v2.json +31 -0
- package/fixtures/protocol/change-set.insert.v2.json +36 -0
- package/fixtures/protocol/change-set.update.v2.json +32 -0
- package/fixtures/protocol/compensation-change-set.update.v1.json +40 -0
- package/fixtures/protocol/execution-receipt.bounded-update-applied.v3.json +39 -0
- package/fixtures/protocol/execution-receipt.compensation-update-applied.v4.json +37 -0
- package/fixtures/protocol/execution-receipt.delete-applied.v2.json +25 -0
- package/fixtures/protocol/execution-receipt.insert-applied.v2.json +24 -0
- package/fixtures/protocol/execution-receipt.reconciliation-required.v2.json +25 -0
- package/fixtures/protocol/execution-receipt.update-applied.v2.json +25 -0
- package/fixtures/protocol/writeback-job.bounded-update.v3.json +43 -0
- package/fixtures/protocol/writeback-job.compensation-update.v4.json +34 -0
- package/fixtures/protocol/writeback-job.delete.v2.json +22 -0
- package/fixtures/protocol/writeback-job.insert.v2.json +28 -0
- package/fixtures/protocol/writeback-job.update.v2.json +24 -0
- package/package.json +2 -1
- package/schemas/change-set.v2.schema.json +245 -0
- package/schemas/change-set.v3.schema.json +112 -0
- package/schemas/compensation-change-set.v1.schema.json +32 -0
- package/schemas/execution-receipt.v2.schema.json +106 -0
- package/schemas/execution-receipt.v3.schema.json +40 -0
- package/schemas/execution-receipt.v4.schema.json +22 -0
- package/schemas/inverse-descriptor.v1.schema.json +46 -0
- package/schemas/synapsor.runner.schema.json +132 -2
- package/schemas/writeback-job.v2.schema.json +238 -0
- package/schemas/writeback-job.v3.schema.json +53 -0
- package/schemas/writeback-job.v4.schema.json +17 -0
package/docs/release-notes.md
CHANGED
|
@@ -10,7 +10,110 @@ npx -y -p @synapsor/runner synapsor-runner demo --quick
|
|
|
10
10
|
The OSS runner command is `synapsor-runner`. The `synapsor` command is reserved
|
|
11
11
|
for the Synapsor Cloud CLI.
|
|
12
12
|
|
|
13
|
-
## 1.1
|
|
13
|
+
## 1.4.1 (prepared, not published)
|
|
14
|
+
|
|
15
|
+
### Bounded-set digest compatibility patch
|
|
16
|
+
|
|
17
|
+
- Contract-authored bounded-set proposals now use deterministic recursive
|
|
18
|
+
object-key ordering for member and set digest material.
|
|
19
|
+
- Valid proposals created by `1.4.0` remain applyable; the compatibility path
|
|
20
|
+
accepts only the known deterministic `1.4.0` serializations reconstructed
|
|
21
|
+
from the complete stored reviewed data.
|
|
22
|
+
- Genuine member, version, value, aggregate, membership, or tenant drift still
|
|
23
|
+
fails closed before source mutation on PostgreSQL and MySQL.
|
|
24
|
+
- The Runner package now includes the linked bounded-set guide and validates
|
|
25
|
+
all shipped local Markdown links while packaging.
|
|
26
|
+
- The DSL package description and README no longer label the current `1.4.x`
|
|
27
|
+
package as a `0.1 preview`. Canonical contract `spec_version: "0.1"` is
|
|
28
|
+
unchanged.
|
|
29
|
+
- Adds an honest prompt-and-application-guardrails decision guide covering SQL
|
|
30
|
+
authority, hand-built semantic tools, structural enforcement, build-vs-adopt
|
|
31
|
+
fit, and regulated-data boundaries.
|
|
32
|
+
|
|
33
|
+
Prepared package versions: `@synapsor/dsl@1.4.1` and
|
|
34
|
+
`@synapsor/runner@1.4.1`. `@synapsor/spec` remains `1.4.0`.
|
|
35
|
+
|
|
36
|
+
## 1.4.0 (prepared, not published)
|
|
37
|
+
|
|
38
|
+
### Reviewed Reversible Change Sets
|
|
39
|
+
|
|
40
|
+
- Adds canonical and DSL opt-in reversibility for direct SQL operations with
|
|
41
|
+
human/operator approval and exact version/dedup guards.
|
|
42
|
+
- Records only bounded, allowlisted inverse data after an unambiguous apply.
|
|
43
|
+
- Adds operator-only `revert`, which creates a new independently reviewed
|
|
44
|
+
proposal and never writes, approves, or becomes model-facing.
|
|
45
|
+
- Proves UPDATE, INSERT, soft-delete, and exact bounded-set compensation on
|
|
46
|
+
PostgreSQL and MySQL, including stale-state refusal, atomic set rollback,
|
|
47
|
+
crash reconciliation, inverse redaction, receipts, and replay.
|
|
48
|
+
- Keeps hard DELETE restoration, app-owned handlers, payments, messages, and
|
|
49
|
+
other external effects outside Runner's automatic compensation claim.
|
|
50
|
+
|
|
51
|
+
Prepared package versions: `@synapsor/spec@1.4.0`,
|
|
52
|
+
`@synapsor/dsl@1.4.0`, and `@synapsor/runner@1.4.0`.
|
|
53
|
+
|
|
54
|
+
## 1.3.0 (prepared, not published)
|
|
55
|
+
|
|
56
|
+
### Bounded Set Writeback
|
|
57
|
+
|
|
58
|
+
- Adds fixed-predicate set UPDATE/DELETE and exact-review batch INSERT for
|
|
59
|
+
PostgreSQL and MySQL, capped at 100 rows.
|
|
60
|
+
- Requires mandatory row and aggregate-value bounds, a frozen exact target set,
|
|
61
|
+
integer version guards for set UPDATE, human/operator approval, atomic apply,
|
|
62
|
+
and per-member receipt/replay evidence.
|
|
63
|
+
- Rejects cap overflow instead of truncating, rolls back the full transaction
|
|
64
|
+
on any stale or failing member, and refuses hard set DELETE when hidden
|
|
65
|
+
triggers or widening cascades are present.
|
|
66
|
+
- Keeps model-generated predicates, policy auto-approval for sets, unbounded
|
|
67
|
+
batches, UPSERT, cross-table transactions, and external effects on the
|
|
68
|
+
app-owned executor path.
|
|
69
|
+
- Adds `corepack pnpm test:bounded-set`, which runs the safety matrix and local
|
|
70
|
+
1/10/100-row measurements against disposable PostgreSQL and MySQL.
|
|
71
|
+
|
|
72
|
+
Prepared package versions: `@synapsor/spec@1.3.0`,
|
|
73
|
+
`@synapsor/dsl@1.3.0`, and `@synapsor/runner@1.3.0`.
|
|
74
|
+
|
|
75
|
+
## 1.2.0 (prepared, not published)
|
|
76
|
+
|
|
77
|
+
### Guarded CRUD And Receipt Authority
|
|
78
|
+
|
|
79
|
+
- Adds canonical and DSL operation semantics for guarded single-row INSERT,
|
|
80
|
+
UPDATE, and DELETE while preserving legacy UPDATE contracts.
|
|
81
|
+
- Adds atomic source receipts with precreated or auto-migrated tables and an
|
|
82
|
+
opt-in Runner-ledger mode that creates no source receipt table.
|
|
83
|
+
- Adds durable writeback intents and a fail-closed operator reconciliation
|
|
84
|
+
workflow for ambiguous ledger/source crash windows.
|
|
85
|
+
- Extends onboarding, doctor, preview, schema inspection, protocol v2,
|
|
86
|
+
Postgres/MySQL adapters, the support-plan-credit example, and disposable live
|
|
87
|
+
conformance tests.
|
|
88
|
+
- Keeps multi-row writes, UPSERT, DDL, cross-table work, and external effects
|
|
89
|
+
on the app-owned executor path.
|
|
90
|
+
|
|
91
|
+
Prepared package versions: `@synapsor/spec@1.2.0`,
|
|
92
|
+
`@synapsor/dsl@1.2.0`, and `@synapsor/runner@1.2.0`.
|
|
93
|
+
|
|
94
|
+
## 1.1.2 (prepared, not published)
|
|
95
|
+
|
|
96
|
+
### Retry And Shared Batch Correctness
|
|
97
|
+
|
|
98
|
+
- Makes transient source pool and recognized database saturation errors
|
|
99
|
+
consistently retryable with a bounded retry hint and safe normalized logs.
|
|
100
|
+
- Fixes `apply --all-approved` in shared `runtime_store` mode so every selected
|
|
101
|
+
proposal uses the same authoritative bridge and durable outcome ledger.
|
|
102
|
+
|
|
103
|
+
Prepared package version: `@synapsor/runner@1.1.2`. Canonical Spec and DSL stay
|
|
104
|
+
at `1.1.0`.
|
|
105
|
+
|
|
106
|
+
## 1.1.1
|
|
107
|
+
|
|
108
|
+
### Resource Read Authorization
|
|
109
|
+
|
|
110
|
+
- Reauthorizes local proposal, evidence, and replay resources against the
|
|
111
|
+
current trusted tenant and principal instead of treating opaque handles as
|
|
112
|
+
bearer authority.
|
|
113
|
+
|
|
114
|
+
Released package version: `@synapsor/runner@1.1.1`.
|
|
115
|
+
|
|
116
|
+
## 1.1.0
|
|
14
117
|
|
|
15
118
|
### Bounded Small-Fleet Runtime
|
|
16
119
|
|
|
@@ -27,7 +130,7 @@ for the Synapsor Cloud CLI.
|
|
|
27
130
|
- Adds [Running A Small Runner Fleet](running-a-runner-fleet.md) and the
|
|
28
131
|
`corepack pnpm test:fleet` synthetic two-Runner verification.
|
|
29
132
|
|
|
30
|
-
|
|
133
|
+
Released package versions: `@synapsor/spec@1.1.0`,
|
|
31
134
|
`@synapsor/dsl@1.1.0`, and `@synapsor/runner@1.1.0`.
|
|
32
135
|
|
|
33
136
|
## 1.0.0
|
package/docs/release-policy.md
CHANGED
|
@@ -123,7 +123,7 @@ through the `1.x` line unless a release note marks a deprecation first:
|
|
|
123
123
|
|
|
124
124
|
Stable does not promise production SLA, hosted Cloud features, compliance
|
|
125
125
|
certification, physical Postgres/MySQL branching, generic SQL writeback,
|
|
126
|
-
|
|
126
|
+
free-form/unbounded set writes, or compatibility for undocumented local SQLite
|
|
127
127
|
internals. Local store migrations may happen inside `1.x` and later minor
|
|
128
128
|
versions, but documented CLI inspection commands should remain the supported way
|
|
129
129
|
to read the store.
|
|
@@ -86,6 +86,7 @@ database.
|
|
|
86
86
|
"approval_required": true,
|
|
87
87
|
"writeback": {
|
|
88
88
|
"mode": "direct_update",
|
|
89
|
+
"operation": "update",
|
|
89
90
|
"applied": false
|
|
90
91
|
},
|
|
91
92
|
"next": "A human must approve outside this model-facing tool surface; nothing is committed yet."
|
|
@@ -101,12 +102,22 @@ database.
|
|
|
101
102
|
}
|
|
102
103
|
```
|
|
103
104
|
|
|
104
|
-
For app-owned executors, `proposal.writeback.mode` is `app_handler`.
|
|
105
|
+
For app-owned executors, `proposal.writeback.mode` is `app_handler`. Native
|
|
106
|
+
guarded proposals use `direct_insert`, `direct_update`, or `direct_delete`, and
|
|
107
|
+
`proposal.writeback.operation` is `insert`, `update`, or `delete`. Every
|
|
108
|
+
proposal result still reports `source_database_changed: false` until external
|
|
109
|
+
apply.
|
|
110
|
+
|
|
111
|
+
For bounded sets, the same model-facing v2 envelope reports the operation,
|
|
112
|
+
reviewed member count, row cap, aggregate values/maximums, set digest, and exact
|
|
113
|
+
allowlisted member diffs. Protocol v3 is used behind the envelope for the
|
|
114
|
+
frozen job and receipt; see [Result Envelope v3](result-envelope-v3.md).
|
|
105
115
|
|
|
106
116
|
## Error Result
|
|
107
117
|
|
|
108
|
-
Model-facing errors are safe and stable.
|
|
109
|
-
|
|
118
|
+
Model-facing errors are safe and stable. Structured tool-rejection logs also
|
|
119
|
+
omit raw driver messages, hosts, and credentials; use operator-controlled
|
|
120
|
+
readiness and database diagnostics to investigate the underlying dependency.
|
|
110
121
|
|
|
111
122
|
```json
|
|
112
123
|
{
|
|
@@ -119,7 +130,8 @@ or ledger inspection, not in the MCP result.
|
|
|
119
130
|
"error": {
|
|
120
131
|
"code": "TEMPORARILY_UNAVAILABLE",
|
|
121
132
|
"message": "The database is temporarily unavailable. Retry later.",
|
|
122
|
-
"retryable": true
|
|
133
|
+
"retryable": true,
|
|
134
|
+
"retry_after_ms": 1000
|
|
123
135
|
},
|
|
124
136
|
"evidence": null,
|
|
125
137
|
"source_database_changed": false,
|
|
@@ -143,8 +155,10 @@ TEMPORARILY_UNAVAILABLE
|
|
|
143
155
|
INTERNAL
|
|
144
156
|
```
|
|
145
157
|
|
|
146
|
-
|
|
147
|
-
|
|
158
|
+
Runner redacts raw connection and driver messages from v2 MCP results and
|
|
159
|
+
structured operational logs. Recognized transient pool, PostgreSQL, MySQL, and
|
|
160
|
+
network failures include a bounded `retry_after_ms`; unrelated database errors
|
|
161
|
+
remain non-retryable. New `init` and `onboard db` configs write `result_format: 2` by
|
|
148
162
|
default. Existing hand-written configs without `result_format` keep the legacy
|
|
149
163
|
runtime default for compatibility; pass `--result-format v2` when serving an
|
|
150
164
|
older config to force the v2 envelope, or `--result-format v1` for an older
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Result Envelope V3
|
|
2
|
+
|
|
3
|
+
Protocol v3 is used only for bounded set direct writeback. Protocol v1 and v2
|
|
4
|
+
remain accepted for legacy UPDATE and guarded single-row CRUD.
|
|
5
|
+
|
|
6
|
+
The public artifacts are:
|
|
7
|
+
|
|
8
|
+
- `synapsor.change-set.v3`;
|
|
9
|
+
- `synapsor.writeback-job.v3`;
|
|
10
|
+
- `synapsor.execution-receipt.v3`.
|
|
11
|
+
|
|
12
|
+
Each v3 object names one operation: `set_update`, `set_delete`, or
|
|
13
|
+
`batch_insert`. It carries the same frozen-set authority:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"max_rows": 10,
|
|
18
|
+
"row_count": 2,
|
|
19
|
+
"aggregate_bounds": [
|
|
20
|
+
{ "column": "balance_cents", "measure": "before", "maximum": 20000, "actual": 15000 }
|
|
21
|
+
],
|
|
22
|
+
"members": [
|
|
23
|
+
{
|
|
24
|
+
"primary_key": { "column": "id", "value": "INV-1" },
|
|
25
|
+
"expected_version": { "column": "version", "value": 1 },
|
|
26
|
+
"before_digest": "sha256:...",
|
|
27
|
+
"after_digest": "sha256:..."
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"set_digest": "sha256:..."
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The complete protocol fixtures live in [`fixtures/protocol`](../fixtures/protocol)
|
|
35
|
+
and the JSON Schemas live in [`schemas`](../schemas). The execution receipt
|
|
36
|
+
reports every `target_identity` and matching `member_effect`, plus receipt
|
|
37
|
+
authority, set digest, exact affected count, safe status/error, and receipt
|
|
38
|
+
hash. An applied result is invalid unless its affected count and member-effect
|
|
39
|
+
count match the frozen identity count.
|
|
40
|
+
|
|
41
|
+
`runner_ledger` may return `reconciliation_required` with an intent ID when a
|
|
42
|
+
cross-database crash window prevents proof of commit. That is a terminal
|
|
43
|
+
operator state, not a retry instruction or an `already_applied` guess.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Compensation Protocol v4
|
|
2
|
+
|
|
3
|
+
Protocol v4 is used only for reviewed compensation. Existing v1 single-row,
|
|
4
|
+
v2 CRUD, and v3 bounded-set objects keep their meaning.
|
|
5
|
+
|
|
6
|
+
Public objects:
|
|
7
|
+
|
|
8
|
+
- `synapsor.compensation-change-set.v1`;
|
|
9
|
+
- `synapsor.inverse-descriptor.v1`;
|
|
10
|
+
- `synapsor.writeback-job.v4`;
|
|
11
|
+
- `synapsor.execution-receipt.v4`.
|
|
12
|
+
|
|
13
|
+
The compensation change set is a normal proposal with a new proposal ID,
|
|
14
|
+
principal, tenant scope, evidence, approval requirement, integrity hash, and a
|
|
15
|
+
link to the exact applied forward receipt. It does not authorize execution.
|
|
16
|
+
|
|
17
|
+
The inverse descriptor records:
|
|
18
|
+
|
|
19
|
+
- `availability` plus specific `reason_codes` when unavailable;
|
|
20
|
+
- `restore_update`, `remove_insert`, or `restore_insert`;
|
|
21
|
+
- single-row or exact-set cardinality;
|
|
22
|
+
- fixed source, table, tenant guard, and allowed columns;
|
|
23
|
+
- exact primary-key members, expected forward state, and restore values;
|
|
24
|
+
- original row/aggregate caps and integer version advancement;
|
|
25
|
+
- root/parent/reverted proposal lineage and bounded depth.
|
|
26
|
+
|
|
27
|
+
A v4 writeback job is created only after the compensation proposal is
|
|
28
|
+
independently approved. The adapter locks and validates every member before
|
|
29
|
+
mutation, applies in one source transaction, and emits a v4 receipt. A
|
|
30
|
+
successful receipt contains the compensation's own bounded inverse. A stale or
|
|
31
|
+
missing member returns conflict without a partial effect.
|
|
32
|
+
|
|
33
|
+
With Runner-ledger receipt authority, an unacknowledged commit or rollback
|
|
34
|
+
enters `reconciliation_required`; no inverse is synthesized until a verified
|
|
35
|
+
operator resolves the outcome.
|
|
36
|
+
|
|
37
|
+
Canonical JSON Schemas are under:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
schemas/inverse-descriptor.v1.schema.json
|
|
41
|
+
schemas/compensation-change-set.v1.schema.json
|
|
42
|
+
schemas/writeback-job.v4.schema.json
|
|
43
|
+
schemas/execution-receipt.v4.schema.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
See [Reviewed Reversible Change Sets](reversible-change-sets.md) for the
|
|
47
|
+
operator flow and supported operation matrix.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Reviewed Reversible Change Sets
|
|
2
|
+
|
|
3
|
+
Synapsor Runner 1.4 can capture a bounded inverse for an opt-in direct database
|
|
4
|
+
write. This is **reviewed compensation**, not rollback or database time travel.
|
|
5
|
+
|
|
6
|
+
The operator flow is deliberately two separate approval loops:
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
forward proposal -> approval -> guarded apply -> applied receipt with inverse
|
|
10
|
+
|
|
|
11
|
+
v
|
|
12
|
+
operator runs revert -> new proposal -> independent approval -> guarded apply
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`synapsor-runner revert` never writes, approves, or bypasses a guard. It is an
|
|
16
|
+
operator CLI command and is not exposed as an MCP tool.
|
|
17
|
+
|
|
18
|
+
## Authoring
|
|
19
|
+
|
|
20
|
+
Add `REVERSIBLE` only to a direct SQL proposal whose inverse Runner can prove:
|
|
21
|
+
|
|
22
|
+
```sql
|
|
23
|
+
CREATE CAPABILITY billing.propose_plan_credit
|
|
24
|
+
USING CONTEXT trusted_operator
|
|
25
|
+
SOURCE billing_postgres
|
|
26
|
+
ON public.customers
|
|
27
|
+
PRIMARY KEY id
|
|
28
|
+
TENANT KEY tenant_id
|
|
29
|
+
CONFLICT GUARD version
|
|
30
|
+
LOOKUP customer_id BY id
|
|
31
|
+
ARG customer_id STRING REQUIRED MAX LENGTH 128
|
|
32
|
+
ARG amount_cents NUMBER REQUIRED MIN 1 MAX 2500
|
|
33
|
+
ALLOW READ id, tenant_id, plan_credit_cents, version
|
|
34
|
+
REQUIRE EVIDENCE
|
|
35
|
+
MAX ROWS 1
|
|
36
|
+
PROPOSE ACTION grant_plan_credit UPDATE
|
|
37
|
+
ALLOW WRITE plan_credit_cents
|
|
38
|
+
PATCH plan_credit_cents = ARG amount_cents
|
|
39
|
+
BOUND plan_credit_cents 0..2500
|
|
40
|
+
ADVANCE VERSION version USING INTEGER INCREMENT
|
|
41
|
+
APPROVAL ROLE support_reviewer
|
|
42
|
+
WRITEBACK DIRECT SQL
|
|
43
|
+
REVERSIBLE
|
|
44
|
+
END
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The canonical JSON equivalent is:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"proposal": {
|
|
52
|
+
"reversibility": { "mode": "reviewed_inverse" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Validation rejects `REVERSIBLE` when writeback uses an app handler or Cloud
|
|
58
|
+
worker, approval is policy-driven, or an UPDATE lacks exact integer version
|
|
59
|
+
advancement. INSERT also requires a deterministic primary-key dedup component.
|
|
60
|
+
|
|
61
|
+
## Operator Flow
|
|
62
|
+
|
|
63
|
+
After an unambiguous forward apply:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
synapsor-runner revert <forward_proposal_id> \
|
|
67
|
+
--config ./synapsor.runner.json \
|
|
68
|
+
--store ./.synapsor/local.db \
|
|
69
|
+
--reason "Correct the reviewed credit"
|
|
70
|
+
|
|
71
|
+
synapsor-runner proposals show latest --details --store ./.synapsor/local.db
|
|
72
|
+
synapsor-runner proposals approve latest --yes --store ./.synapsor/local.db
|
|
73
|
+
synapsor-runner apply latest --config ./synapsor.runner.json --store ./.synapsor/local.db
|
|
74
|
+
synapsor-runner replay show latest --store ./.synapsor/local.db
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The compensation proposal inherits the original reviewer role and quorum. A
|
|
78
|
+
signed operator identity is required when configured. The source remains
|
|
79
|
+
unchanged until the compensation proposal is separately approved and applied.
|
|
80
|
+
|
|
81
|
+
## Supported Inverses
|
|
82
|
+
|
|
83
|
+
| Forward operation | Reviewed compensation |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| Single-row UPDATE | Restores only allowlisted prior values after checking the exact state and version left by the forward write. The version advances again; it is never decremented. |
|
|
86
|
+
| Soft delete | Same as UPDATE when deletion is a reviewed status or timestamp patch. |
|
|
87
|
+
| Single-row INSERT | Deletes only the exact Runner-created identity after checking current reviewed state, tenant, triggers, and foreign-key effects. |
|
|
88
|
+
| Bounded set UPDATE | Restores exactly the original frozen members under the original count/value caps in one transaction. One stale member aborts all members. |
|
|
89
|
+
| Exact-review batch INSERT | Removes exactly the inserted member identities, atomically, after the same safety preflight. |
|
|
90
|
+
| Hard DELETE | Not generally restorable. The receipt reports specific unavailable reason codes instead of claiming a safe inverse. |
|
|
91
|
+
|
|
92
|
+
Kept-out and non-writable fields are never captured just to make compensation
|
|
93
|
+
possible. Each successful compensation captures its own bounded inverse, so an
|
|
94
|
+
operator may propose a revert of the compensation. Lineage is linear, duplicate
|
|
95
|
+
children are rejected, and traversal is capped at 16 proposals.
|
|
96
|
+
|
|
97
|
+
## Fail-Closed Cases
|
|
98
|
+
|
|
99
|
+
Runner refuses to create or apply compensation when:
|
|
100
|
+
|
|
101
|
+
- the forward outcome is missing, failed, or requires reconciliation;
|
|
102
|
+
- the applied receipt has no available inverse;
|
|
103
|
+
- another active compensation already targets that receipt;
|
|
104
|
+
- the tenant or operator identity does not match trusted authority;
|
|
105
|
+
- the row changed after the forward write;
|
|
106
|
+
- an inserted row is missing or has changed;
|
|
107
|
+
- a new trigger, foreign-key dependent, or widening cascade prevents a bounded
|
|
108
|
+
effect proof;
|
|
109
|
+
- any member of a bounded set is stale or missing;
|
|
110
|
+
- lineage is invalid, cyclic, or beyond the depth limit.
|
|
111
|
+
|
|
112
|
+
With `runner_ledger` receipt authority, a crash after source commit but before
|
|
113
|
+
ledger completion remains ambiguous. Forward and compensation writes enter
|
|
114
|
+
`reconciliation_required`; Runner does not call them reverted or retry them
|
|
115
|
+
automatically. Reconcile the outcome first through the verified operator
|
|
116
|
+
workflow.
|
|
117
|
+
|
|
118
|
+
## Database Metadata Privileges
|
|
119
|
+
|
|
120
|
+
Postgres uses catalog visibility available to the configured writer for
|
|
121
|
+
trigger and foreign-key preflight.
|
|
122
|
+
|
|
123
|
+
MySQL compensation that may delete a row, including reversal of INSERT,
|
|
124
|
+
requires enough metadata visibility to prove no hidden widening effects:
|
|
125
|
+
|
|
126
|
+
- scoped `TRIGGER` visibility on the target table;
|
|
127
|
+
- global `PROCESS` visibility for incoming InnoDB foreign-key metadata.
|
|
128
|
+
|
|
129
|
+
If those privileges are not acceptable, use a reversible soft-delete UPDATE or
|
|
130
|
+
an explicitly reviewed app-owned compensation capability. Runner fails closed.
|
|
131
|
+
|
|
132
|
+
## App-Owned Executors
|
|
133
|
+
|
|
134
|
+
Runner does not infer compensation for `http_handler`, `command_handler`,
|
|
135
|
+
payments, messages, or other external effects. The application must expose a
|
|
136
|
+
separate reviewed compensation capability and enforce its own tenant, version,
|
|
137
|
+
idempotency, and transaction rules. A handler returning a receipt does not make
|
|
138
|
+
its side effect Runner-reversible.
|
|
139
|
+
|
|
140
|
+
## Verification
|
|
141
|
+
|
|
142
|
+
The disposable PostgreSQL/MySQL gate proves UPDATE, INSERT, soft-delete, exact
|
|
143
|
+
bounded-set compensation, independent approval, stale-state refusal,
|
|
144
|
+
hard-delete unavailability, inverse redaction, receipts, and replay:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
corepack pnpm test:reversible
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
See [RFC 006](rfcs/006-reviewed-reversible-change-sets.md) for the normative
|
|
151
|
+
safety design and [Current Limitations](limitations.md) for the wider boundary.
|
|
@@ -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
|
+
|