@synapsor/runner 1.6.0 → 1.6.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 +32 -4
- package/README.md +16 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/local-ui.d.ts +4 -1
- package/dist/local-ui.d.ts.map +1 -1
- package/dist/runner.mjs +1624 -42
- package/dist/runtime.mjs +1011 -25
- package/dist/shadow.mjs +408 -15
- package/docs/app-owned-executors.md +8 -0
- package/docs/capability-authoring.md +45 -0
- package/docs/current-scope.md +5 -0
- package/docs/cursor-plugin.md +2 -2
- package/docs/limitations.md +15 -2
- package/docs/local-mode.md +22 -3
- package/docs/production.md +19 -0
- package/docs/proposal-evidence-freshness.md +334 -0
- package/docs/release-notes.md +30 -4
- package/docs/runner-config-reference.md +46 -0
- package/docs/security-boundary.md +17 -0
- package/docs/store-lifecycle.md +8 -0
- package/docs/troubleshooting-first-run.md +76 -0
- package/docs/writeback-executors.md +8 -0
- package/examples/app-owned-writeback/command-handler.mjs +0 -0
- package/examples/mcp-postgres-billing-app-handler/scripts/run-demo.sh +0 -0
- package/examples/reference-support-billing-app/scripts/run-demo.sh +0 -0
- package/examples/support-billing-agent/scripts/run-demo.sh +0 -0
- package/examples/support-billing-agent/scripts/run-evaluation.sh +0 -0
- package/fixtures/compatibility/published-1.6.0/manifest.json +76 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/aggregate-read.synapsor.sql +21 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/billing-late-fee.synapsor.sql +56 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/bounded-set-multi-term.synapsor.sql +30 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/principal-row-scope.synapsor.sql +23 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/aggregate-read/contract.json +119 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/approval-quorum/contract.json +44 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/bounded-set-threats/contract.json +115 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/principal-row-scope/contract.json +78 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/proposal-capability/contract.json +101 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/reversible-change-sets/contract.json +98 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/valid/basic-read.contract.json +60 -0
- package/fixtures/protocol/MANIFEST.json +37 -7
- package/fixtures/protocol/change-set.freshness-update.v2.json +58 -0
- package/fixtures/protocol/freshness-authority.invoice.v1.json +35 -0
- package/fixtures/protocol/freshness-proof.fresh.v1.json +38 -0
- package/fixtures/protocol/writeback-job.freshness-update.v2.json +50 -0
- package/llms.txt +1 -0
- package/package.json +9 -8
- package/schemas/change-set.v1.schema.json +1 -0
- package/schemas/change-set.v2.schema.json +1 -0
- package/schemas/change-set.v3.schema.json +1 -0
- package/schemas/freshness-authority.v1.schema.json +89 -0
- package/schemas/freshness-proof.v1.schema.json +73 -0
- package/schemas/synapsor.runner.schema.json +32 -0
- package/schemas/writeback-job.v1.schema.json +1 -0
- package/schemas/writeback-job.v2.schema.json +1 -0
- package/schemas/writeback-job.v3.schema.json +1 -0
|
@@ -24,6 +24,14 @@ the receipt, and includes the result in replay.
|
|
|
24
24
|
> error receipts. If you skip those checks, you can reintroduce cross-tenant
|
|
25
25
|
> writes, lost updates, or duplicate writes. Keep handler credentials out of MCP.
|
|
26
26
|
|
|
27
|
+
Runner 1.6.1 strict `proposal_freshness` is limited to same-database direct SQL
|
|
28
|
+
writeback. Runner rejects it for `http_handler`, `command_handler`, and
|
|
29
|
+
cross-source dependencies because a local preflight cannot be atomic with an
|
|
30
|
+
effect executed elsewhere. If supporting evidence matters, the app handler
|
|
31
|
+
must lock and re-read those rows inside its own business transaction and fail
|
|
32
|
+
closed on drift. See
|
|
33
|
+
[Proposal And Evidence Freshness](proposal-evidence-freshness.md).
|
|
34
|
+
|
|
27
35
|
A handler is your application endpoint or script. It is not a second Synapsor
|
|
28
36
|
package that users need to install. Install `@synapsor/runner`, then generate
|
|
29
37
|
or copy a handler template only when your approved write needs app-owned
|
|
@@ -384,6 +384,38 @@ surface.
|
|
|
384
384
|
}
|
|
385
385
|
```
|
|
386
386
|
|
|
387
|
+
### Optional Live Freshness
|
|
388
|
+
|
|
389
|
+
The proposal's exact `conflict_guard` already protects its target at apply.
|
|
390
|
+
Runner 1.6.1 can additionally recheck that target before local approval and can
|
|
391
|
+
bind explicitly reviewed supporting rows to both approval and apply.
|
|
392
|
+
|
|
393
|
+
This is deployment wiring, not DSL or canonical contract syntax:
|
|
394
|
+
|
|
395
|
+
```json
|
|
396
|
+
{
|
|
397
|
+
"proposal_freshness": {
|
|
398
|
+
"billing.propose_late_fee_waiver": {
|
|
399
|
+
"approval": "required",
|
|
400
|
+
"dependencies": [
|
|
401
|
+
{
|
|
402
|
+
"id": "payment_status",
|
|
403
|
+
"capability": "billing.inspect_payment",
|
|
404
|
+
"identity_from_arg": "payment_id",
|
|
405
|
+
"version_column": "version"
|
|
406
|
+
}
|
|
407
|
+
]
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`billing.inspect_payment` must be an existing reviewed single-row read on the
|
|
414
|
+
same source and trusted context. `payment_id` is an existing scalar proposal
|
|
415
|
+
argument; the model cannot choose the dependency table, version column, tenant,
|
|
416
|
+
principal, or whether the check runs. See
|
|
417
|
+
[Proposal And Evidence Freshness](proposal-evidence-freshness.md).
|
|
418
|
+
|
|
387
419
|
## Trusted Context
|
|
388
420
|
|
|
389
421
|
Tenant, principal, approval authority, source ids, and row-version authority
|
|
@@ -468,6 +500,12 @@ Runner validates:
|
|
|
468
500
|
- one affected row;
|
|
469
501
|
- idempotency receipt.
|
|
470
502
|
|
|
503
|
+
When the proposal opts into `proposal_freshness`, the direct SQL adapter also
|
|
504
|
+
locks and compares every declared supporting row inside the same transaction
|
|
505
|
+
before touching the target. A mismatch returns a freshness conflict and zero
|
|
506
|
+
affected rows. Run `doctor --check-writeback` to verify that the writer can
|
|
507
|
+
perform those dependency locking reads.
|
|
508
|
+
|
|
471
509
|
INSERT additionally requires a source-unique dedup key. DELETE requires an exact
|
|
472
510
|
version guard and fails closed on widening cascades or write triggers.
|
|
473
511
|
|
|
@@ -489,6 +527,13 @@ inserting an outbox event, updating multiple app tables, calling your own
|
|
|
489
527
|
service, or creating a row whose source constraints cannot satisfy native
|
|
490
528
|
single-row INSERT guards.
|
|
491
529
|
|
|
530
|
+
Runner's strict `proposal_freshness` overlay does not support app-owned or
|
|
531
|
+
cross-source effects because Runner cannot make their source checks and effect
|
|
532
|
+
atomic. The handler must independently lock/re-read all relevant state and
|
|
533
|
+
enforce the exact proposal, scope, version, transaction, and idempotency
|
|
534
|
+
preconditions described in
|
|
535
|
+
[Proposal And Evidence Freshness](proposal-evidence-freshness.md).
|
|
536
|
+
|
|
492
537
|
In DSL, keep the reviewed business contract portable and name the executor:
|
|
493
538
|
|
|
494
539
|
```sql
|
package/docs/current-scope.md
CHANGED
|
@@ -25,6 +25,9 @@ Current `1.6` scope:
|
|
|
25
25
|
explicit development-only static values;
|
|
26
26
|
- evidence handles, normalized query audit, proposals, receipts, local replay,
|
|
27
27
|
and read-only lifecycle inspection without copying proposal ids;
|
|
28
|
+
- optional proposal/evidence freshness: live target and declared same-source
|
|
29
|
+
supporting-row checks before every approval, proof-bound human/quorum/policy
|
|
30
|
+
decisions, and atomic direct-SQL dependency revalidation at apply;
|
|
28
31
|
- guarded single-row `INSERT`, `UPDATE`, and `DELETE`;
|
|
29
32
|
- fixed-predicate set `UPDATE`/`DELETE` and exact-review batch `INSERT`, with
|
|
30
33
|
mandatory row/value caps, frozen members, human approval, atomic execution,
|
|
@@ -62,6 +65,8 @@ Out of scope:
|
|
|
62
65
|
- model-selected tenant/principal, activation, approval, or commit authority;
|
|
63
66
|
- UPSERT, DDL, free-form predicates, unbounded writes, or cross-table direct
|
|
64
67
|
transactions;
|
|
68
|
+
- strict atomic freshness for app-owned handlers, APIs, or cross-source
|
|
69
|
+
dependencies; those executors own their transaction preconditions;
|
|
65
70
|
- physical branching of external Postgres/MySQL or automatic rollback/time
|
|
66
71
|
travel;
|
|
67
72
|
- self-hosted Synapsor Cloud, an unbounded/multi-region shared ledger, a managed
|
package/docs/cursor-plugin.md
CHANGED
|
@@ -19,7 +19,7 @@ claim that Cursor Marketplace review has completed.
|
|
|
19
19
|
|
|
20
20
|
## What the plugin installs
|
|
21
21
|
|
|
22
|
-
- one production stdio MCP server pinned to `@synapsor/runner@1.6.
|
|
22
|
+
- one production stdio MCP server pinned to `@synapsor/runner@1.6.1`;
|
|
23
23
|
- one `/synapsor-protect` agent command;
|
|
24
24
|
- one discoverable Safe Action skill;
|
|
25
25
|
- one file-scoped rule for `synapsor/actions/**/*.ts`;
|
|
@@ -80,7 +80,7 @@ Required listing media:
|
|
|
80
80
|
|
|
81
81
|
## Submission checklist
|
|
82
82
|
|
|
83
|
-
- [ ] Runner 1.6.
|
|
83
|
+
- [ ] Runner 1.6.1 package and plugin version agree.
|
|
84
84
|
- [ ] `corepack pnpm verify:cursor-plugin` passes from a clean checkout.
|
|
85
85
|
- [ ] Plugin is manually loaded in current stable Cursor at workspace scope.
|
|
86
86
|
- [ ] `/synapsor-protect` drafts and validates without activation.
|
package/docs/limitations.md
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Synapsor Runner is intentionally narrow. Version 1.6 adds deterministic
|
|
4
4
|
whole-application boundary drafting and a local authoring-only Explore ->
|
|
5
|
-
Protect path on top of guarded writes.
|
|
6
|
-
|
|
5
|
+
Protect path on top of guarded writes. Version 1.6.1 also adds opt-in
|
|
6
|
+
proposal/evidence freshness for exact same-source direct-SQL proposals. It does
|
|
7
|
+
not turn Runner into a generic database query tool, claim Synapsor Cloud scale,
|
|
8
|
+
or claim an enterprise SLA.
|
|
7
9
|
|
|
8
10
|
## Supported
|
|
9
11
|
|
|
@@ -15,6 +17,9 @@ database query tool, claim Synapsor Cloud scale, or claim an enterprise SLA.
|
|
|
15
17
|
- Native Postgres/MySQL source pools and operational/fleet-wide rate limits.
|
|
16
18
|
- Verified operator approval through CLI, optional distinct-reviewer quorum,
|
|
17
19
|
and shared local review UI reads.
|
|
20
|
+
- Optional live target and explicitly declared supporting-row freshness checks
|
|
21
|
+
before every approval, with immutable proof binding and final transactional
|
|
22
|
+
revalidation for PostgreSQL/MySQL direct SQL writeback.
|
|
18
23
|
- Separately protected scrapeable metrics and dead-letter recovery commands.
|
|
19
24
|
- Shared-ledger backup/digest verification, clean restore, and
|
|
20
25
|
archive-before-retention.
|
|
@@ -90,6 +95,8 @@ truth for the model-facing tools.
|
|
|
90
95
|
- Policy auto-approval for bounded sets.
|
|
91
96
|
- Stored procedures.
|
|
92
97
|
- Cross-database transactions.
|
|
98
|
+
- Strict atomic proposal freshness across databases, app-owned handlers, or
|
|
99
|
+
external APIs. Those paths must enforce their own final preconditions.
|
|
93
100
|
- Physical branching of Postgres/MySQL.
|
|
94
101
|
- Full Synapsor workflow/DAG execution.
|
|
95
102
|
- `CREATE AGENT WORKFLOW` or hosted Synapsor SQL generation.
|
|
@@ -163,3 +170,9 @@ as equivalent to a durable version column.
|
|
|
163
170
|
Runner-ledger UPDATE and DELETE require an exact guard; UPDATE must advance it
|
|
164
171
|
inside the source transaction. INSERT requires a reviewed source-unique dedup
|
|
165
172
|
identity. See [Guarded Single-Row CRUD Writeback](guarded-crud-writeback.md).
|
|
173
|
+
|
|
174
|
+
Approval-time freshness also depends on a correct exact version column. It
|
|
175
|
+
does not freeze source state until apply. Direct SQL apply rechecks again;
|
|
176
|
+
app-owned/cross-source effects do not receive that Runner-owned atomic
|
|
177
|
+
guarantee. See
|
|
178
|
+
[Proposal And Evidence Freshness](proposal-evidence-freshness.md).
|
package/docs/local-mode.md
CHANGED
|
@@ -140,10 +140,19 @@ Show a proposal:
|
|
|
140
140
|
npx -y -p @synapsor/runner synapsor-runner proposals show wrp_123 --store ./.synapsor/local.db
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
+
Check a freshness-enabled proposal without copying its id:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npx -y -p @synapsor/runner synapsor-runner proposals check-freshness latest \
|
|
147
|
+
--config ./synapsor.runner.json \
|
|
148
|
+
--store ./.synapsor/local.db
|
|
149
|
+
```
|
|
150
|
+
|
|
143
151
|
Approve:
|
|
144
152
|
|
|
145
153
|
```bash
|
|
146
154
|
npx -y -p @synapsor/runner synapsor-runner proposals approve wrp_123 \
|
|
155
|
+
--config ./synapsor.runner.json \
|
|
147
156
|
--store ./.synapsor/local.db \
|
|
148
157
|
--actor local_reviewer \
|
|
149
158
|
--yes
|
|
@@ -151,6 +160,14 @@ npx -y -p @synapsor/runner synapsor-runner proposals approve wrp_123 \
|
|
|
151
160
|
|
|
152
161
|
Before approval, the CLI prints the reviewer-critical proposal details: trusted principal, tenant, target row, primary key, required role, proposal hash/version, allowed columns, conflict guard, evidence bundle/query fingerprint, writeback boundary, source mutation state, and exact before/after diff. Interactive approval still requires typing `yes`; noninteractive scripts must pass `--yes`.
|
|
153
162
|
|
|
163
|
+
When `proposal_freshness` is configured for that capability, approval also
|
|
164
|
+
performs a live read-only check of the target and each declared same-source
|
|
165
|
+
supporting dependency. A fresh result is bound to the approval by proof digest.
|
|
166
|
+
A stale result records no approval and terminally conflicts the proposal; an
|
|
167
|
+
unavailable source records no approval and leaves it pending. Apply always
|
|
168
|
+
revalidates again because source data can change after approval. See
|
|
169
|
+
[Proposal And Evidence Freshness](proposal-evidence-freshness.md).
|
|
170
|
+
|
|
154
171
|
Create a guarded writeback job from an approved proposal:
|
|
155
172
|
|
|
156
173
|
```bash
|
|
@@ -203,9 +220,11 @@ npx -y -p @synapsor/runner synapsor-runner ui --config synapsor.runner.json --st
|
|
|
203
220
|
```
|
|
204
221
|
|
|
205
222
|
The UI shows setup summary, semantic tools, proposal states, exact diffs,
|
|
206
|
-
evidence, approval state, receipts, and replay.
|
|
207
|
-
|
|
208
|
-
approve
|
|
223
|
+
evidence, live freshness state, approval state, receipts, and replay. A
|
|
224
|
+
freshness-required approve control stays disabled until the live result is
|
|
225
|
+
fresh; the approve request performs another live check. The UI binds to
|
|
226
|
+
`127.0.0.1` by default, uses a per-run local session token, and requires CSRF
|
|
227
|
+
protection for approve/reject actions.
|
|
209
228
|
|
|
210
229
|
The UI does not expose raw SQL, database URLs, password values, MCP approval
|
|
211
230
|
tools, MCP commit tools, or controls that widen configured tables/columns.
|
package/docs/production.md
CHANGED
|
@@ -191,6 +191,16 @@ business action, transaction boundaries, and safe errors. See
|
|
|
191
191
|
[Writeback Executors](writeback-executors.md) and
|
|
192
192
|
[Handler Helper](handler-helper.md).
|
|
193
193
|
|
|
194
|
+
For a direct SQL proposal whose approval depends on live source facts, use the
|
|
195
|
+
optional `proposal_freshness` overlay. It rechecks the target and declared
|
|
196
|
+
same-source supporting rows before approval, then locks and rechecks them again
|
|
197
|
+
inside the final PostgreSQL/MySQL transaction. Run
|
|
198
|
+
`doctor --check-writeback` to prove that the writer can perform every
|
|
199
|
+
dependency locking read. App-owned and cross-source effects must enforce their
|
|
200
|
+
own equivalent transactional preconditions; Runner rejects them from strict
|
|
201
|
+
freshness mode. See
|
|
202
|
+
[Proposal And Evidence Freshness](proposal-evidence-freshness.md).
|
|
203
|
+
|
|
194
204
|
## Local Ledger
|
|
195
205
|
|
|
196
206
|
Default local ledger path:
|
|
@@ -589,6 +599,10 @@ Doctor reports should be redacted by default before sharing. They must not
|
|
|
589
599
|
include database passwords, bearer tokens, handler secrets, or raw driver
|
|
590
600
|
connection strings.
|
|
591
601
|
|
|
602
|
+
For freshness-enabled proposals, `--check-writeback` includes rollback-only
|
|
603
|
+
dependency lock probes. A passing metadata/read check alone does not prove that
|
|
604
|
+
the writer has the locking privilege needed at apply.
|
|
605
|
+
|
|
592
606
|
## Logging And Redaction
|
|
593
607
|
|
|
594
608
|
Expected public outputs must avoid secrets in:
|
|
@@ -613,3 +627,8 @@ Before promoting a package or calling a build production-candidate:
|
|
|
613
627
|
The release gate should cover typecheck, focused tests, packed-package install,
|
|
614
628
|
quick demo, own-db fixture, MCP stdio/HTTP checks, direct writeback,
|
|
615
629
|
app-owned executor paths, package dry-run, and docs/package consistency.
|
|
630
|
+
The 1.6.1 security gate additionally runs:
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
corepack pnpm test:proposal-freshness
|
|
634
|
+
```
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# Proposal And Evidence Freshness
|
|
2
|
+
|
|
3
|
+
Runner 1.6.1 adds an optional fail-closed freshness gate for proposals whose
|
|
4
|
+
review depends on live source state.
|
|
5
|
+
|
|
6
|
+
This closes two different review gaps without changing the model-facing MCP
|
|
7
|
+
surface:
|
|
8
|
+
|
|
9
|
+
1. Before a local approval, Runner can re-read the proposal target and refuse
|
|
10
|
+
approval if its exact version changed.
|
|
11
|
+
2. A proposal can declare reviewed supporting rows whose exact versions must
|
|
12
|
+
still match before approval and again at apply.
|
|
13
|
+
|
|
14
|
+
Target-row drift was already blocked by the exact conflict guard at apply.
|
|
15
|
+
Version 1.6.1 does not replace that protection. It adds an earlier review-time
|
|
16
|
+
check and extends the final transaction to declared same-source dependencies.
|
|
17
|
+
|
|
18
|
+
## What Freshness Does And Does Not Mean
|
|
19
|
+
|
|
20
|
+
Keep these controls separate:
|
|
21
|
+
|
|
22
|
+
| Control | What it proves |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| Proposal hash and version | The approval refers to one immutable proposal shape. |
|
|
25
|
+
| Evidence and query fingerprints | The stored review material has not silently changed. |
|
|
26
|
+
| Freshness proof | The target and declared dependencies matched their captured exact versions at one live check. |
|
|
27
|
+
| Approval | A trusted reviewer or policy authorized that exact proposal and proof. |
|
|
28
|
+
| Apply conflict checks | The target and dependencies still match inside the mutation transaction. |
|
|
29
|
+
| Idempotency receipt | Retrying the same completed writeback identity does not duplicate its effect. |
|
|
30
|
+
|
|
31
|
+
A successful approval-time check is not a lock held until apply. Data can
|
|
32
|
+
change one millisecond later, so apply always revalidates again.
|
|
33
|
+
|
|
34
|
+
Runner never refreshes a stale proposal in place. The operator must perform a
|
|
35
|
+
new source read and create a new proposal with a new hash and review.
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
Freshness is a Runner deployment overlay in `synapsor.runner.json`. Version
|
|
40
|
+
1.6.1 adds no DSL clause and no canonical Spec field. Existing hand-authored
|
|
41
|
+
DSL, canonical JSON, contract digests, and `tools/list` output remain unchanged
|
|
42
|
+
when this optional section is absent.
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"proposal_freshness": {
|
|
47
|
+
"billing.propose_credit": {
|
|
48
|
+
"approval": "required",
|
|
49
|
+
"dependencies": [
|
|
50
|
+
{
|
|
51
|
+
"id": "invoice_eligibility",
|
|
52
|
+
"capability": "billing.inspect_invoice",
|
|
53
|
+
"identity_from_arg": "invoice_id",
|
|
54
|
+
"version_column": "updated_at"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The names mean:
|
|
63
|
+
|
|
64
|
+
- `billing.propose_credit`: an existing reviewed proposal capability.
|
|
65
|
+
- `approval: "required"`: every approval decision requires a new short-lived
|
|
66
|
+
live proof.
|
|
67
|
+
- `invoice_eligibility`: an operator-defined stable dependency label.
|
|
68
|
+
- `billing.inspect_invoice`: an existing reviewed single-row read capability.
|
|
69
|
+
- `invoice_id`: an existing bounded scalar argument on the proposal. Its value
|
|
70
|
+
identifies the supporting row through the read capability's fixed lookup.
|
|
71
|
+
- `updated_at`: the exact version/conflict column on that supporting row.
|
|
72
|
+
|
|
73
|
+
The proposal and supporting read must use the same source and trusted context.
|
|
74
|
+
The supporting capability supplies the fixed engine, schema, table, primary
|
|
75
|
+
key, tenant key, optional principal key, and lookup shape. None of those
|
|
76
|
+
identifiers can come from an MCP argument.
|
|
77
|
+
|
|
78
|
+
Strict validation rejects:
|
|
79
|
+
|
|
80
|
+
- unknown or non-proposal policy keys;
|
|
81
|
+
- app-owned or cross-source writeback;
|
|
82
|
+
- unknown, aggregate, protected, or non-single-row dependencies;
|
|
83
|
+
- missing or untrusted tenant scope;
|
|
84
|
+
- incompatible principal scope;
|
|
85
|
+
- missing scalar identity arguments;
|
|
86
|
+
- unsafe or missing version identifiers;
|
|
87
|
+
- duplicate, self-referential, or more than 16 dependencies.
|
|
88
|
+
|
|
89
|
+
Run both static validation and live writeback probes:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
synapsor-runner config validate --config ./synapsor.runner.json
|
|
93
|
+
synapsor-runner doctor --check-writeback --config ./synapsor.runner.json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`doctor --check-writeback` performs rollback-only lock probes. The read role
|
|
97
|
+
needs scoped `SELECT`. The writer must be able to lock every declared
|
|
98
|
+
supporting row in the final transaction. PostgreSQL and MySQL may require a
|
|
99
|
+
narrow update/locking privilege in addition to `SELECT`; use a dedicated
|
|
100
|
+
least-privilege writer and verify the exact grants on the deployed database.
|
|
101
|
+
|
|
102
|
+
## Proposal Creation
|
|
103
|
+
|
|
104
|
+
When a freshness-enabled proposal is created, Runner:
|
|
105
|
+
|
|
106
|
+
1. reads the target through the existing reviewed proposal capability;
|
|
107
|
+
2. reads each declared dependency through its reviewed single-row capability
|
|
108
|
+
in a read-only transaction;
|
|
109
|
+
3. captures only the fixed identity, exact version, and bounded
|
|
110
|
+
evidence/query-audit linkage;
|
|
111
|
+
4. sorts dependencies into deterministic lock order;
|
|
112
|
+
5. binds the resulting `synapsor.freshness-authority.v1` object into the
|
|
113
|
+
immutable change set and proposal hash.
|
|
114
|
+
|
|
115
|
+
The authority contains digests and fixed identifiers. It does not add source
|
|
116
|
+
rows, kept-out values, database URLs, credentials, or a second copy of trusted
|
|
117
|
+
tenant/principal values to the proposal.
|
|
118
|
+
|
|
119
|
+
The published protocol representation is inspectable without reading the
|
|
120
|
+
TypeScript implementation:
|
|
121
|
+
|
|
122
|
+
- `schemas/freshness-authority.v1.schema.json` defines the optional authority
|
|
123
|
+
carried by change sets and writeback jobs.
|
|
124
|
+
- `schemas/freshness-proof.v1.schema.json` defines the immutable live-check
|
|
125
|
+
proof stored in the ledger.
|
|
126
|
+
- `fixtures/protocol/change-set.freshness-update.v2.json`,
|
|
127
|
+
`fixtures/protocol/writeback-job.freshness-update.v2.json`,
|
|
128
|
+
`fixtures/protocol/freshness-authority.invoice.v1.json`, and
|
|
129
|
+
`fixtures/protocol/freshness-proof.fresh.v1.json` are hash-manifested
|
|
130
|
+
conformance examples.
|
|
131
|
+
|
|
132
|
+
The JSON Schemas enforce the public structure. The executable protocol
|
|
133
|
+
validator additionally verifies canonical descriptor, dependency-set, and
|
|
134
|
+
proof digests plus cross-field invariants that JSON Schema cannot express.
|
|
135
|
+
|
|
136
|
+
For UPDATE and DELETE, target mode is `exact_guard`. For a bounded set it is
|
|
137
|
+
`frozen_set`. INSERT has no prior target row, so target freshness is
|
|
138
|
+
`not_applicable`; its source uniqueness and idempotency guards still apply.
|
|
139
|
+
|
|
140
|
+
## Approval-Time Check
|
|
141
|
+
|
|
142
|
+
Inspect the newest proposal without copying an ID:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
synapsor-runner proposals check-freshness latest \
|
|
146
|
+
--config ./synapsor.runner.json \
|
|
147
|
+
--store ./.synapsor/local.db
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Add `--details` for per-check digests or `--json` for the versioned
|
|
151
|
+
`synapsor.proposal-freshness-result.v1` document.
|
|
152
|
+
|
|
153
|
+
Stable exit codes are:
|
|
154
|
+
|
|
155
|
+
| Status | Exit code | Meaning |
|
|
156
|
+
| --- | ---: | --- |
|
|
157
|
+
| `fresh` | 0 | Every required target/dependency version matched. |
|
|
158
|
+
| `not_required` | 0 | Legacy proposal; its normal apply guard still applies. |
|
|
159
|
+
| `stale` | 3 | A target or dependency changed, disappeared, or left trusted scope. |
|
|
160
|
+
| `unavailable` | 4 | The source could not be checked; retry may be appropriate. |
|
|
161
|
+
| `invalid` | 5 | Stored or configured authority did not validate. |
|
|
162
|
+
| `unsupported` | 6 | The topology cannot provide the claimed guarantee. |
|
|
163
|
+
|
|
164
|
+
Approval runs the same live check automatically:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
synapsor-runner proposals approve latest \
|
|
168
|
+
--yes \
|
|
169
|
+
--actor billing_reviewer \
|
|
170
|
+
--config ./synapsor.runner.json \
|
|
171
|
+
--store ./.synapsor/local.db
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
A successful check records an immutable `synapsor.freshness-proof.v1` event.
|
|
175
|
+
The proof binds:
|
|
176
|
+
|
|
177
|
+
- proposal ID, hash, and integer version;
|
|
178
|
+
- dependency-set digest;
|
|
179
|
+
- checked and short validity times;
|
|
180
|
+
- source adapter identity;
|
|
181
|
+
- bounded target/supporting counts and result codes;
|
|
182
|
+
- expected/observed version digests, not source values;
|
|
183
|
+
- the proof's own digest.
|
|
184
|
+
|
|
185
|
+
Every human quorum decision receives a distinct live proof. Policy
|
|
186
|
+
auto-approval uses the same evaluator. A stale check records no approval and
|
|
187
|
+
makes the proposal terminal `conflict`. An unavailable check records no
|
|
188
|
+
approval and leaves it `pending_review`.
|
|
189
|
+
|
|
190
|
+
## Apply-Time Revalidation
|
|
191
|
+
|
|
192
|
+
Apply remains an operator/worker action outside MCP:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
synapsor-runner apply latest \
|
|
196
|
+
--yes \
|
|
197
|
+
--config ./synapsor.runner.json \
|
|
198
|
+
--store ./.synapsor/local.db
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
For direct SQL PostgreSQL/MySQL writeback, Runner:
|
|
202
|
+
|
|
203
|
+
1. begins the existing source transaction;
|
|
204
|
+
2. locks supporting rows in deterministic source/schema/table/identity order;
|
|
205
|
+
3. rechecks each exact version under trusted tenant and principal scope;
|
|
206
|
+
4. locks and rechecks the target row or every frozen set member;
|
|
207
|
+
5. performs the existing guarded mutation only if all checks pass;
|
|
208
|
+
6. finalizes the configured source or Runner-ledger receipt behavior.
|
|
209
|
+
|
|
210
|
+
PostgreSQL uses parameterized `SELECT ... FOR UPDATE` and quoted reviewed
|
|
211
|
+
identifiers. MySQL uses the equivalent InnoDB locking read. A stale supporting
|
|
212
|
+
row returns `FRESHNESS_DEPENDENCY_STALE`, zero affected rows, and rolls back the
|
|
213
|
+
whole transaction. A stale target continues to use the exact target conflict
|
|
214
|
+
path. A stale conflict is never converted to `already_applied`.
|
|
215
|
+
|
|
216
|
+
For a bounded set, one stale member or one stale dependency aborts every member.
|
|
217
|
+
|
|
218
|
+
## Timeline
|
|
219
|
+
|
|
220
|
+
Change before approval:
|
|
221
|
+
|
|
222
|
+
```text
|
|
223
|
+
proposal captures target v4 + invoice v8
|
|
224
|
+
invoice moves to v9
|
|
225
|
+
reviewer approves
|
|
226
|
+
-> live preflight sees v9 != v8
|
|
227
|
+
-> no approval, no source mutation
|
|
228
|
+
-> create a new read and proposal
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Change after approval:
|
|
232
|
+
|
|
233
|
+
```text
|
|
234
|
+
proposal captures target v4 + invoice v8
|
|
235
|
+
approval preflight succeeds and binds proof
|
|
236
|
+
invoice moves to v9
|
|
237
|
+
trusted apply starts
|
|
238
|
+
-> transaction locks invoice and sees v9 != v8
|
|
239
|
+
-> conflict, zero source mutation
|
|
240
|
+
-> create a new read and proposal
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The second timeline is why approval-time freshness can never replace
|
|
244
|
+
apply-time concurrency control.
|
|
245
|
+
|
|
246
|
+
## Supporting Evidence Example
|
|
247
|
+
|
|
248
|
+
Assume `credits.propose_account_credit` changes an account row, but eligibility
|
|
249
|
+
depends on a separate invoice:
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
target account version 12 (unchanged)
|
|
253
|
+
supporting invoice status=open, version 7
|
|
254
|
+
proposal credit $25 because invoice is open
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
If a payment process closes the invoice and advances it to version 8 while the
|
|
258
|
+
account stays at version 12, the target guard alone would still pass. Declaring
|
|
259
|
+
the invoice read as a freshness dependency makes approval or apply fail closed
|
|
260
|
+
on the invoice version mismatch.
|
|
261
|
+
|
|
262
|
+
Freshness checks the reviewed exact version, not whether the current value
|
|
263
|
+
"looks equivalent." Returning a row to its old value does not revive a proposal
|
|
264
|
+
that already became stale.
|
|
265
|
+
|
|
266
|
+
## Ledger, Replay, Reports, And Metrics
|
|
267
|
+
|
|
268
|
+
Use the no-ID lifecycle view:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
synapsor-runner lifecycle --details --store ./.synapsor/local.db
|
|
272
|
+
synapsor-runner replay show latest --details --store ./.synapsor/local.db
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Lifecycle and replay retain the proof chain: authority, proof event, approval
|
|
276
|
+
proof digest, apply result, receipt, and replay linkage. Backup/restore and the
|
|
277
|
+
shared PostgreSQL runtime store preserve the same versioned records.
|
|
278
|
+
|
|
279
|
+
Compliance reports include only bounded freshness metadata and digests. Metrics
|
|
280
|
+
separate checks, stale target/supporting outcomes, blocked approvals, and
|
|
281
|
+
apply-time blocks. Logs never include source rows or credential values.
|
|
282
|
+
|
|
283
|
+
## Cloud And App-Owned Executors
|
|
284
|
+
|
|
285
|
+
Cloud-linked approval remains proposal/digest authority. Cloud may retain
|
|
286
|
+
bounded freshness metadata, but it does not connect to or read the customer
|
|
287
|
+
database. A Runner next to the source revalidates dependencies when it applies
|
|
288
|
+
a Cloud-approved lease. Cloud outage never enables weaker local approval or
|
|
289
|
+
apply authority.
|
|
290
|
+
|
|
291
|
+
Strict freshness currently supports same-database direct SQL writeback only.
|
|
292
|
+
Runner rejects strict freshness configuration for app-owned handlers and
|
|
293
|
+
cross-source dependencies because it cannot place those checks and effects in
|
|
294
|
+
one transaction.
|
|
295
|
+
|
|
296
|
+
An app-owned executor can implement equivalent preconditions, but the
|
|
297
|
+
application must own and test:
|
|
298
|
+
|
|
299
|
+
- source reads and locking;
|
|
300
|
+
- tenant/principal authorization;
|
|
301
|
+
- exact expected versions;
|
|
302
|
+
- transaction boundaries;
|
|
303
|
+
- idempotency and ambiguous outcomes;
|
|
304
|
+
- safe receipts.
|
|
305
|
+
|
|
306
|
+
Do not describe those application guarantees as Runner's atomic direct-SQL
|
|
307
|
+
freshness guarantee.
|
|
308
|
+
|
|
309
|
+
## Security Boundary
|
|
310
|
+
|
|
311
|
+
Freshness adds no model-facing approval, apply, activation, bypass, SQL, table,
|
|
312
|
+
column, scope, or policy argument. Existing `tools/list` output is unchanged.
|
|
313
|
+
|
|
314
|
+
It also does not replace:
|
|
315
|
+
|
|
316
|
+
- correct monotonic version columns;
|
|
317
|
+
- least-privilege read/write roles;
|
|
318
|
+
- PostgreSQL RLS, restricted views, or tenant-bound credentials;
|
|
319
|
+
- application authorization;
|
|
320
|
+
- source backups and recovery testing;
|
|
321
|
+
- operator identity and host/store protection.
|
|
322
|
+
|
|
323
|
+
The dedicated live gate is:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
corepack pnpm test:proposal-freshness
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
It runs disposable PostgreSQL and MySQL scenarios for approval preflight,
|
|
330
|
+
proof-bound human/quorum/policy approval, target/supporting drift before and
|
|
331
|
+
after approval, source and Runner-ledger receipts, bounded-set rollback,
|
|
332
|
+
reversible UPDATE, DELETE, shared runtime state, Cloud-approved local
|
|
333
|
+
revalidation, writer lock probes, idempotent retry, and kept-out-value
|
|
334
|
+
non-disclosure.
|
package/docs/release-notes.md
CHANGED
|
@@ -10,7 +10,34 @@ 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.6.
|
|
13
|
+
## 1.6.1 (prepared, not published)
|
|
14
|
+
|
|
15
|
+
### Fail-closed proposal and evidence freshness
|
|
16
|
+
|
|
17
|
+
- An optional `proposal_freshness` Runner overlay can require a live target and
|
|
18
|
+
explicitly declared same-source supporting-row check immediately before
|
|
19
|
+
every local approval.
|
|
20
|
+
- Each successful human, quorum, or policy approval references a distinct
|
|
21
|
+
short-lived proof bound to the exact proposal hash/version and dependency
|
|
22
|
+
set. Stale and unavailable checks record no approval.
|
|
23
|
+
- Direct PostgreSQL/MySQL apply locks and rechecks dependencies inside the
|
|
24
|
+
mutation transaction. Source drift after approval returns a conflict and
|
|
25
|
+
changes zero rows; bounded sets roll back completely.
|
|
26
|
+
- `proposals check-freshness latest`, Workbench gating, lifecycle, replay,
|
|
27
|
+
compliance reports, metrics, logs, and `doctor --check-writeback` expose the
|
|
28
|
+
bounded proof and operational state without source rows, kept-out values,
|
|
29
|
+
trusted scope values, or credentials.
|
|
30
|
+
- Cloud does not read the source. A local Runner revalidates a Cloud-approved
|
|
31
|
+
job before apply. Strict app-owned and cross-source freshness remains an
|
|
32
|
+
explicitly unsupported topology.
|
|
33
|
+
- Existing contracts, exact digests, DSL, model-facing tool lists, and
|
|
34
|
+
non-freshness deployments keep their prior behavior.
|
|
35
|
+
|
|
36
|
+
Prepared package version: `@synapsor/runner@1.6.1`.
|
|
37
|
+
`@synapsor/spec@1.5.0` and `@synapsor/dsl@1.5.0` are unchanged. Nothing has
|
|
38
|
+
been published by this repository change.
|
|
39
|
+
|
|
40
|
+
## 1.6.0 (published 2026-07-23)
|
|
14
41
|
|
|
15
42
|
### Connect, Explore, Protect
|
|
16
43
|
|
|
@@ -39,9 +66,8 @@ for the Synapsor Cloud CLI.
|
|
|
39
66
|
new fields. Published legacy contracts preserve exact normalization/digests,
|
|
40
67
|
and established CLI/headless/CI routes keep their behavior.
|
|
41
68
|
|
|
42
|
-
|
|
43
|
-
`@synapsor/dsl@1.5.0`, and `@synapsor/spec@1.5.0`.
|
|
44
|
-
by this repository change.
|
|
69
|
+
Published package versions: `@synapsor/runner@1.6.0`,
|
|
70
|
+
`@synapsor/dsl@1.5.0`, and `@synapsor/spec@1.5.0`.
|
|
45
71
|
|
|
46
72
|
## 1.5.4 (published 2026-07-22)
|
|
47
73
|
|
|
@@ -453,6 +453,52 @@ Distinct verified subjects fill quorum slots; one subject cannot count twice.
|
|
|
453
453
|
A rejection is terminal. Policy auto-approval is deferred when quorum is
|
|
454
454
|
greater than one.
|
|
455
455
|
|
|
456
|
+
## Proposal freshness
|
|
457
|
+
|
|
458
|
+
`proposal_freshness` is an optional Runner-only deployment overlay. It does not
|
|
459
|
+
change the public DSL or canonical contract and does not alter legacy proposal
|
|
460
|
+
or `tools/list` behavior when absent.
|
|
461
|
+
|
|
462
|
+
```json
|
|
463
|
+
{
|
|
464
|
+
"proposal_freshness": {
|
|
465
|
+
"billing.propose_credit": {
|
|
466
|
+
"approval": "required",
|
|
467
|
+
"dependencies": [
|
|
468
|
+
{
|
|
469
|
+
"id": "invoice_eligibility",
|
|
470
|
+
"capability": "billing.inspect_invoice",
|
|
471
|
+
"identity_from_arg": "invoice_id",
|
|
472
|
+
"version_column": "updated_at"
|
|
473
|
+
}
|
|
474
|
+
]
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
The top-level key is an existing proposal capability. `approval` must be
|
|
481
|
+
`required`. Every dependency must reference an existing reviewed single-row
|
|
482
|
+
read on the same source/context and map one bounded scalar proposal argument to
|
|
483
|
+
that read's fixed lookup. The read capability supplies schema, table, primary
|
|
484
|
+
key, tenant/principal keys, and source; the model cannot supply or widen them.
|
|
485
|
+
At most 16 dependencies are allowed.
|
|
486
|
+
|
|
487
|
+
The proposal target and dependencies are read again before every approval.
|
|
488
|
+
Direct SQL apply locks and rechecks them again inside the source transaction.
|
|
489
|
+
Strict freshness is rejected for app-owned and cross-source effects because
|
|
490
|
+
Runner cannot make those checks atomic with the external effect.
|
|
491
|
+
|
|
492
|
+
Use:
|
|
493
|
+
|
|
494
|
+
```bash
|
|
495
|
+
synapsor-runner proposals check-freshness latest --config ./synapsor.runner.json
|
|
496
|
+
synapsor-runner doctor --check-writeback --config ./synapsor.runner.json
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
The complete validation, privilege, lifecycle, and error-code contract is in
|
|
500
|
+
[Proposal And Evidence Freshness](proposal-evidence-freshness.md).
|
|
501
|
+
|
|
456
502
|
## Rate limits
|
|
457
503
|
|
|
458
504
|
```json
|