@synapsor/runner 0.1.0-alpha.15 → 0.1.0-alpha.17
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 +56 -6
- package/README.md +126 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/runner.mjs +787 -51
- package/docs/README.md +3 -3
- package/docs/app-owned-executors.md +7 -0
- package/docs/current-scope.md +6 -0
- package/docs/getting-started-own-database.md +42 -14
- package/docs/handler-helper.md +13 -6
- package/docs/local-mode.md +6 -0
- package/docs/release-notes.md +90 -7
- package/docs/release-policy.md +53 -13
- package/docs/result-envelope-v2.md +5 -2
- package/docs/rfcs/002-app-owned-handler-helper.md +1 -1
- package/docs/rfcs/003-integrator-feedback-teardown.md +1 -1
- package/docs/writeback-executors.md +13 -0
- package/examples/app-owned-writeback/README.md +7 -0
- package/examples/app-owned-writeback/command-handler.mjs +9 -0
- package/examples/app-owned-writeback/node-fastify-handler.mjs +9 -0
- package/examples/app-owned-writeback/python-fastapi-handler.py +9 -0
- package/examples/mcp-postgres-billing-app-handler/README.md +7 -0
- package/examples/mcp-postgres-billing-app-handler/app-handler.mjs +7 -9
- package/package.json +1 -1
- package/schemas/onboarding-selection.v1.schema.json +7 -0
- package/schemas/synapsor.runner.schema.json +3 -0
package/docs/README.md
CHANGED
|
@@ -20,7 +20,7 @@ detail.
|
|
|
20
20
|
capabilities, model-facing descriptions, result envelopes, trusted context,
|
|
21
21
|
and writeback guards. JSON Schema:
|
|
22
22
|
`../schemas/synapsor.runner.schema.json`.
|
|
23
|
-
- [Result Envelope v2](result-envelope-v2.md): the
|
|
23
|
+
- [Result Envelope v2](result-envelope-v2.md): the generated-config default
|
|
24
24
|
`ok`/`summary`/`data`/`proposal`/`error` response shape for MCP tools.
|
|
25
25
|
- [Handler Helper](handler-helper.md): TypeScript helper for safe app-owned
|
|
26
26
|
rich-write handlers.
|
|
@@ -60,8 +60,8 @@ detail.
|
|
|
60
60
|
for approved proposals.
|
|
61
61
|
- [App-Owned Executors](app-owned-executors.md): short entry point for rich
|
|
62
62
|
business transactions handled by your app.
|
|
63
|
-
- `synapsor-runner events tail`: local lifecycle events
|
|
64
|
-
`proposal_created`, `proposal_approved`, `writeback_applied`, and
|
|
63
|
+
- `synapsor-runner events tail` and `events webhook`: local lifecycle events
|
|
64
|
+
such as `proposal_created`, `proposal_approved`, `writeback_applied`, and
|
|
65
65
|
`writeback_conflict`.
|
|
66
66
|
|
|
67
67
|
Useful examples:
|
|
@@ -17,6 +17,13 @@ The model-facing MCP tool only creates a proposal. Approval happens outside MCP.
|
|
|
17
17
|
After approval, Runner calls your `http_handler` or `command_handler`, records
|
|
18
18
|
the receipt, and includes the result in replay.
|
|
19
19
|
|
|
20
|
+
> **Important:** your app handler owns the final business write. Runner creates
|
|
21
|
+
> the proposal and calls your handler only after approval, but your handler must
|
|
22
|
+
> still enforce tenant/scope checks, expected-version or conflict guards,
|
|
23
|
+
> idempotency keys, allowed business actions, transaction/rollback, and safe
|
|
24
|
+
> error receipts. If you skip those checks, you can reintroduce cross-tenant
|
|
25
|
+
> writes, lost updates, or duplicate writes. Keep handler credentials out of MCP.
|
|
26
|
+
|
|
20
27
|
A handler is your application endpoint or script. It is not a second Synapsor
|
|
21
28
|
package that users need to install. Install `@synapsor/runner`, then generate
|
|
22
29
|
or copy a handler template only when your approved write needs app-owned
|
package/docs/current-scope.md
CHANGED
|
@@ -13,6 +13,12 @@ Current alpha scope:
|
|
|
13
13
|
business transactions;
|
|
14
14
|
- stdio MCP, Streamable HTTP MCP, and a small JSON-RPC bridge.
|
|
15
15
|
|
|
16
|
+
Stable `0.1.x` compatibility, once `0.1.0` is promoted to `latest`, covers the
|
|
17
|
+
documented `synapsor-runner` binary, config schema version `1`, result envelope
|
|
18
|
+
v2 with v1 opt-out, stdio/Streamable HTTP MCP surfaces, documented MCP client
|
|
19
|
+
snippets, proposal/evidence/replay inspection commands, direct SQL writeback,
|
|
20
|
+
and app-owned executor contracts.
|
|
21
|
+
|
|
16
22
|
Out of scope:
|
|
17
23
|
|
|
18
24
|
- raw `execute_sql`;
|
|
@@ -179,7 +179,8 @@ The wizard:
|
|
|
179
179
|
mode, semantic capability names, and proposal patch mappings;
|
|
180
180
|
- asks review-mode users to choose direct guarded SQL writeback, an app-owned
|
|
181
181
|
HTTP handler, or an app-owned command handler;
|
|
182
|
-
- previews the MCP tools and what is not exposed
|
|
182
|
+
- previews the MCP tools and what is not exposed, then lets you revise visible
|
|
183
|
+
fields or capability names before writing files;
|
|
183
184
|
- attempts a first smoke call when you supplied a real object id and the
|
|
184
185
|
required trusted env vars are present;
|
|
185
186
|
- writes the generated config, `.env.example`, and MCP client snippets only
|
|
@@ -208,12 +209,17 @@ npx -y -p @synapsor/runner@alpha synapsor-runner init \
|
|
|
208
209
|
--mode review \
|
|
209
210
|
--visible-columns id,tenant_id,late_fee_cents,waiver_reason,updated_at \
|
|
210
211
|
--allowed-columns late_fee_cents,waiver_reason \
|
|
211
|
-
--
|
|
212
|
-
--
|
|
213
|
-
--
|
|
212
|
+
--tenant-column tenant_id \
|
|
213
|
+
--id-arg invoice_id \
|
|
214
|
+
--patch late_fee_cents=fixed:0,waiver_reason=arg:reason \
|
|
215
|
+
--patch-bounds late_fee_cents=0:5500 \
|
|
214
216
|
--write-url-env SYNAPSOR_DATABASE_WRITE_URL
|
|
215
217
|
```
|
|
216
218
|
|
|
219
|
+
If you omit `--namespace`, Runner derives the namespace from the table name
|
|
220
|
+
instead of defaulting to `source.*`. Add `--read-tool` and `--proposal-tool`
|
|
221
|
+
when you want exact capability names in the generated contract.
|
|
222
|
+
|
|
217
223
|
For app-owned writeback, replace the direct writer env with a handler executor:
|
|
218
224
|
|
|
219
225
|
```bash
|
|
@@ -225,13 +231,19 @@ npx -y -p @synapsor/runner@alpha synapsor-runner init \
|
|
|
225
231
|
--namespace billing \
|
|
226
232
|
--object-name invoice \
|
|
227
233
|
--mode review \
|
|
228
|
-
--
|
|
229
|
-
--
|
|
234
|
+
--tenant-column tenant_id \
|
|
235
|
+
--id-arg invoice_id \
|
|
236
|
+
--patch late_fee_cents=fixed:0,waiver_reason=arg:reason \
|
|
230
237
|
--writeback http_handler \
|
|
231
238
|
--handler-url-env APP_WRITEBACK_URL \
|
|
232
|
-
--handler-token-env APP_WRITEBACK_TOKEN
|
|
239
|
+
--handler-token-env APP_WRITEBACK_TOKEN \
|
|
240
|
+
--emit-handler
|
|
233
241
|
```
|
|
234
242
|
|
|
243
|
+
Handler-owned configs mark the Runner source as read-only unless you explicitly
|
|
244
|
+
pass `--write-url-env`, so `config validate` does not warn that direct SQL
|
|
245
|
+
writeback is disabled.
|
|
246
|
+
|
|
235
247
|
Use `--writeback command_handler --handler-command-env APP_WRITEBACK_COMMAND`
|
|
236
248
|
when your app-owned writer is a local command/script instead of HTTP.
|
|
237
249
|
|
|
@@ -244,8 +256,7 @@ npx -y -p @synapsor/runner@alpha synapsor-runner init \
|
|
|
244
256
|
--namespace billing \
|
|
245
257
|
--object-name invoice \
|
|
246
258
|
--mode review \
|
|
247
|
-
--patch
|
|
248
|
-
--patch-from-arg waiver_reason=reason
|
|
259
|
+
--patch late_fee_cents=fixed:0,waiver_reason=arg:reason
|
|
249
260
|
```
|
|
250
261
|
|
|
251
262
|
The command uses inspected metadata for primary-key, tenant-key, conflict-column,
|
|
@@ -253,15 +264,15 @@ and default-visible-column suggestions. If a suggestion is ambiguous or missing,
|
|
|
253
264
|
pass explicit flags such as `--primary-key`, `--tenant-key`, and
|
|
254
265
|
`--conflict-column`.
|
|
255
266
|
|
|
256
|
-
Review mode requires at least one explicit `--patch
|
|
257
|
-
`--patch-from-arg`
|
|
258
|
-
tool.
|
|
267
|
+
Review mode requires at least one explicit `--patch` mapping, or the older
|
|
268
|
+
`--patch-fixed` / `--patch-from-arg` flags. Use `--mode read_only` if you only
|
|
269
|
+
want an inspect tool.
|
|
259
270
|
|
|
260
271
|
For bounded business actions, add reviewed value guards:
|
|
261
272
|
|
|
262
273
|
```bash
|
|
263
|
-
--
|
|
264
|
-
--
|
|
274
|
+
--patch-bounds credit_cents=0:10000
|
|
275
|
+
--status-guards status=open:pending_review
|
|
265
276
|
```
|
|
266
277
|
|
|
267
278
|
`--numeric-bound` keeps a proposed numeric column inside a fixed range before a
|
|
@@ -279,6 +290,7 @@ Create `onboarding-selection.json` from one table and one safe business action.
|
|
|
279
290
|
"version": 1,
|
|
280
291
|
"engine": "postgres",
|
|
281
292
|
"mode": "review",
|
|
293
|
+
"result_format": 2,
|
|
282
294
|
"read_url_env": "SYNAPSOR_DATABASE_READ_URL",
|
|
283
295
|
"write_url_env": "SYNAPSOR_DATABASE_WRITE_URL",
|
|
284
296
|
"schema": "public",
|
|
@@ -288,6 +300,8 @@ Create `onboarding-selection.json` from one table and one safe business action.
|
|
|
288
300
|
"conflict_column": "updated_at",
|
|
289
301
|
"namespace": "billing",
|
|
290
302
|
"object_name": "invoice",
|
|
303
|
+
"read_tool": "billing.inspect_invoice",
|
|
304
|
+
"proposal_tool": "billing.propose_late_fee_waiver",
|
|
291
305
|
"visible_columns": ["id", "tenant_id", "late_fee_cents", "waiver_reason", "updated_at"],
|
|
292
306
|
"allowed_columns": ["late_fee_cents", "waiver_reason"],
|
|
293
307
|
"patch": {
|
|
@@ -386,6 +400,20 @@ export SYNAPSOR_TENANT_ID="acme"
|
|
|
386
400
|
export SYNAPSOR_PRINCIPAL="local_operator"
|
|
387
401
|
export SYNAPSOR_RUNNER_HTTP_TOKEN="dev-local-token"
|
|
388
402
|
|
|
403
|
+
npx -y -p @synapsor/runner@alpha synapsor-runner up --serve \
|
|
404
|
+
--config ./synapsor.runner.json \
|
|
405
|
+
--store ./.synapsor/local.db \
|
|
406
|
+
--auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
`up --serve` runs the review-mode checklist first, then starts Streamable HTTP.
|
|
410
|
+
Use `--dry-run` for the checklist only, or `--with-handler` when the config uses
|
|
411
|
+
an app-owned executor and you want Runner to check the handler endpoint before
|
|
412
|
+
serving.
|
|
413
|
+
|
|
414
|
+
The lower-level MCP command starts the same transport directly:
|
|
415
|
+
|
|
416
|
+
```bash
|
|
389
417
|
npx -y -p @synapsor/runner@alpha synapsor-runner mcp serve-streamable-http \
|
|
390
418
|
--config ./synapsor.runner.json \
|
|
391
419
|
--store ./.synapsor/local.db \
|
package/docs/handler-helper.md
CHANGED
|
@@ -17,6 +17,13 @@ The model-facing MCP tool still creates a proposal only. A human/operator
|
|
|
17
17
|
approves outside MCP. After approval, Runner sends the structured writeback
|
|
18
18
|
request to your handler.
|
|
19
19
|
|
|
20
|
+
> **Important:** your app handler owns the final business write. Runner creates
|
|
21
|
+
> the proposal and calls your handler only after approval, but your handler must
|
|
22
|
+
> still enforce tenant/scope checks, expected-version or conflict guards,
|
|
23
|
+
> idempotency keys, allowed business actions, transaction/rollback, and safe
|
|
24
|
+
> error receipts. If you skip those checks, you can reintroduce cross-tenant
|
|
25
|
+
> writes, lost updates, or duplicate writes. Keep handler credentials out of MCP.
|
|
26
|
+
|
|
20
27
|
## Scope
|
|
21
28
|
|
|
22
29
|
Current alpha scope:
|
|
@@ -41,7 +48,7 @@ request/receipt schema and the FastAPI template in `examples/app-owned-writeback
|
|
|
41
48
|
|
|
42
49
|
The helper implementation exists in this source repo under `packages/handler`
|
|
43
50
|
and is used by the app-owned executor example and tests. It is not published as
|
|
44
|
-
a standalone
|
|
51
|
+
a standalone npm package yet.
|
|
45
52
|
|
|
46
53
|
If you installed `@synapsor/runner` from npm, use one of these alpha paths:
|
|
47
54
|
|
|
@@ -50,10 +57,6 @@ If you installed `@synapsor/runner` from npm, use one of these alpha paths:
|
|
|
50
57
|
- run `examples/mcp-postgres-billing-app-handler/`, which includes a bundled
|
|
51
58
|
`synapsor-handler.mjs` shim inside the runner package.
|
|
52
59
|
|
|
53
|
-
The `@synapsor/handler` import below is the source-checkout API and the planned
|
|
54
|
-
standalone package API. Do not `npm install @synapsor/handler` until that
|
|
55
|
-
package is published.
|
|
56
|
-
|
|
57
60
|
## Schemas
|
|
58
61
|
|
|
59
62
|
Published schemas:
|
|
@@ -67,8 +70,12 @@ the alpha migration.
|
|
|
67
70
|
|
|
68
71
|
## TypeScript Usage From A Source Checkout
|
|
69
72
|
|
|
73
|
+
The source checkout has an internal helper under `packages/handler`. It is not
|
|
74
|
+
an npm install path. Import it by workspace-relative path while developing this
|
|
75
|
+
repository, or use the bundled example shim in the packaged runner.
|
|
76
|
+
|
|
70
77
|
```ts
|
|
71
|
-
import { createWritebackHandler } from "
|
|
78
|
+
import { createWritebackHandler } from "../packages/handler/src/index.js";
|
|
72
79
|
|
|
73
80
|
export const handler = createWritebackHandler({
|
|
74
81
|
tokenEnv: "SYNAPSOR_APP_HANDLER_TOKEN",
|
package/docs/local-mode.md
CHANGED
|
@@ -274,6 +274,7 @@ or prune it without touching your source Postgres/MySQL database:
|
|
|
274
274
|
```bash
|
|
275
275
|
synapsor-runner store stats --store ./.synapsor/local.db
|
|
276
276
|
synapsor-runner events tail --store ./.synapsor/local.db
|
|
277
|
+
synapsor-runner events webhook --url http://127.0.0.1:8788/synapsor/events --kind proposal_created --store ./.synapsor/local.db
|
|
277
278
|
synapsor-runner store vacuum --store ./.synapsor/local.db
|
|
278
279
|
synapsor-runner store prune --store ./.synapsor/local.db --older-than 30d --dry-run
|
|
279
280
|
synapsor-runner store prune --store ./.synapsor/local.db --older-than 30d --yes
|
|
@@ -286,6 +287,11 @@ ledger, including proposal creation, approval/rejection, writeback jobs, and
|
|
|
286
287
|
writeback applied/conflict/failed receipts. Add `--follow` to keep polling a
|
|
287
288
|
running local store.
|
|
288
289
|
|
|
290
|
+
`events webhook` pushes the same local lifecycle events to a local/dev/staging
|
|
291
|
+
HTTP endpoint, one event envelope per POST. Use it for a review UI, Slack bridge,
|
|
292
|
+
or app-local notification path when polling is awkward. It is not a hosted
|
|
293
|
+
central ledger and does not expose database credentials.
|
|
294
|
+
|
|
289
295
|
`store prune` defaults to dry-run. `store reset` requires `--yes` and removes
|
|
290
296
|
only the local SQLite ledger files. MCP server modes write a small active-store
|
|
291
297
|
lease next to the SQLite file; destructive store operations refuse while that
|
package/docs/release-notes.md
CHANGED
|
@@ -11,6 +11,81 @@ npx -y -p @synapsor/runner@alpha synapsor-runner demo --quick
|
|
|
11
11
|
The OSS runner command is `synapsor-runner`. The `synapsor` command is reserved
|
|
12
12
|
for the Synapsor Cloud CLI.
|
|
13
13
|
|
|
14
|
+
## 0.1.0-alpha.17
|
|
15
|
+
|
|
16
|
+
### Scripted Onboarding
|
|
17
|
+
|
|
18
|
+
- `onboard db` and `init` now have a prompt-free path for scripts, CI, and LLM
|
|
19
|
+
agents. Use `--yes`, `--non-interactive`, or `--answers <file.json>`.
|
|
20
|
+
- Added friendly flags that match the first-run mental model:
|
|
21
|
+
`--tenant-column`, `--id-arg`, `--patch column=fixed:value|arg:name`,
|
|
22
|
+
`--patch-bounds`, `--status-guards`, `--read-description`,
|
|
23
|
+
`--read-returns-hint`, `--read-tool`, `--proposal-tool`,
|
|
24
|
+
`--handler-output`, and `--emit-handler`.
|
|
25
|
+
- Answers-file onboarding writes the reviewed config, `.env.example`, MCP
|
|
26
|
+
snippets, and optional handler template without opening a TTY prompt.
|
|
27
|
+
- When `--namespace` is omitted, generated capability names now derive a
|
|
28
|
+
namespace from the selected table instead of falling back to `source.*`.
|
|
29
|
+
- The guided wizard now has a final "what I am about to write" preview where
|
|
30
|
+
users can revise visible fields or capability names before files are written.
|
|
31
|
+
- README Start Here now tells users to run `tools preview` and `smoke call`
|
|
32
|
+
before wiring an MCP client.
|
|
33
|
+
- `events webhook` / `events push` can POST local proposal/writeback lifecycle
|
|
34
|
+
events to a local/dev/staging HTTP endpoint for review UIs or notifications.
|
|
35
|
+
|
|
36
|
+
### Writeback Readiness
|
|
37
|
+
|
|
38
|
+
- App-owned executor configs generated by Runner now mark the Runner source as
|
|
39
|
+
`read_only: true` when no writer env is supplied. `config validate` no longer
|
|
40
|
+
reports `WRITEBACK_DISABLED` for handler-owned writeback paths.
|
|
41
|
+
- Direct SQL review-mode proposals still surface `WRITEBACK_DISABLED` if the
|
|
42
|
+
source has no `write_url_env`, because Runner cannot apply those proposals
|
|
43
|
+
without a trusted writer connection.
|
|
44
|
+
|
|
45
|
+
### Handler Docs
|
|
46
|
+
|
|
47
|
+
- README and runner README now include a short "How An External Handler Works"
|
|
48
|
+
section: agent proposes, human approves outside MCP, Runner POSTs to your
|
|
49
|
+
endpoint, and your code writes in its own transaction.
|
|
50
|
+
- Published docs/examples no longer include install-looking imports for a
|
|
51
|
+
separate handler package. Use `synapsor-runner handler template ...` or the
|
|
52
|
+
bundled `synapsor-handler.mjs` shim in the app-owned example.
|
|
53
|
+
|
|
54
|
+
## 0.1.0-alpha.16
|
|
55
|
+
|
|
56
|
+
### Review-Mode Bring-Up
|
|
57
|
+
|
|
58
|
+
- Added `synapsor-runner up` as the local review-mode orientation command. It
|
|
59
|
+
validates the config/store, checks active store leases, summarizes
|
|
60
|
+
model-facing tools, identifies direct SQL versus app-owned executor writeback
|
|
61
|
+
paths, and prints the next smoke, approval, apply, replay, UI, and doctor
|
|
62
|
+
commands.
|
|
63
|
+
- `up` is guidance-only by default. `up --serve` starts the standard MCP
|
|
64
|
+
Streamable HTTP server after the same validation and guidance.
|
|
65
|
+
- `up --dry-run` gives the full checklist without starting a server.
|
|
66
|
+
- `up --handler-check` or `up --with-handler` runs the redacted handler
|
|
67
|
+
env/reachability doctor path before serving.
|
|
68
|
+
- The guided wizard now writes model-facing capability descriptions,
|
|
69
|
+
per-argument descriptions, returns hints, and defaults generated configs to
|
|
70
|
+
`result_format: 2`.
|
|
71
|
+
- `result_format: 2` gives MCP clients a stable envelope with `ok`, `summary`,
|
|
72
|
+
`data`, `proposal`, `error`, `evidence`, `source_database_changed`, and
|
|
73
|
+
`_meta.canonical_capability`. Pass `--result-format v1` or
|
|
74
|
+
`"result_format": 1` only when an older client needs the legacy shape.
|
|
75
|
+
- `tools list`, `tools list --aliases`, and
|
|
76
|
+
`mcp client-config --include-instructions` help users inspect exposed tools
|
|
77
|
+
and generate client snippets without source reading.
|
|
78
|
+
|
|
79
|
+
### Handler Security
|
|
80
|
+
|
|
81
|
+
- Generated handler templates, template-list output, app-owned writeback docs,
|
|
82
|
+
and examples now explicitly warn that the app handler owns the final business
|
|
83
|
+
write. Handlers must re-check tenant/scope, expected-version or conflict
|
|
84
|
+
guard, idempotency, allowed business action, transaction/rollback, and safe
|
|
85
|
+
error receipts before mutating application state.
|
|
86
|
+
- The guided review-mode wizard can now write a starter handler template when
|
|
87
|
+
the app-owned HTTP or command handler path is selected.
|
|
88
|
+
|
|
14
89
|
## 0.1.0-alpha.15
|
|
15
90
|
|
|
16
91
|
### Handler Wording Clarification
|
|
@@ -23,10 +98,10 @@ for the Synapsor Cloud CLI.
|
|
|
23
98
|
|
|
24
99
|
### Handler Helper And Changelog Clarity
|
|
25
100
|
|
|
26
|
-
- Public docs now state that
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
101
|
+
- Public docs now state that the handler helper is not a standalone npm package
|
|
102
|
+
yet. The helper currently ships as source under `packages/handler` and as the
|
|
103
|
+
bundled `synapsor-handler.mjs` shim in the app-owned executor example
|
|
104
|
+
included with `@synapsor/runner`.
|
|
30
105
|
- `CHANGELOG.md` is included in the `@synapsor/runner` npm tarball.
|
|
31
106
|
|
|
32
107
|
## 0.1.0-alpha.13
|
|
@@ -160,6 +235,9 @@ for the Synapsor Cloud CLI.
|
|
|
160
235
|
writeback jobs, execution receipts, and events.
|
|
161
236
|
- `synapsor-runner events tail` prints local lifecycle events from the SQLite
|
|
162
237
|
ledger and can follow new proposal/writeback events while a local flow runs.
|
|
238
|
+
- `synapsor-runner events webhook` pushes those local lifecycle events to a
|
|
239
|
+
local/dev/staging HTTP endpoint for review UIs or notifications without
|
|
240
|
+
polling. It is not a hosted central ledger.
|
|
163
241
|
- MCP server modes write an active-store lease next to the local SQLite file.
|
|
164
242
|
Destructive `store prune --yes` refuses while that lease points at a live
|
|
165
243
|
process unless `--force` is provided.
|
|
@@ -209,13 +287,18 @@ changing. A stable `0.1.0` release should only be tagged after:
|
|
|
209
287
|
For the local tarball before publish, run:
|
|
210
288
|
|
|
211
289
|
```bash
|
|
212
|
-
./scripts/verify-
|
|
213
|
-
./scripts/verify-packed-own-db.sh
|
|
290
|
+
./scripts/verify-release-gate.sh
|
|
214
291
|
```
|
|
215
292
|
|
|
216
293
|
After publishing an alpha, verify the public package from a clean temporary
|
|
217
294
|
directory:
|
|
218
295
|
|
|
219
296
|
```bash
|
|
220
|
-
./scripts/verify-
|
|
297
|
+
VERIFY_PUBLISHED_ALPHA=1 ./scripts/verify-release-gate.sh 0.1.0-alpha.17
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
After publishing/promoting stable `latest`, verify the stable channel:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
./scripts/verify-published-stable.sh 0.1.0
|
|
221
304
|
```
|
package/docs/release-policy.md
CHANGED
|
@@ -5,7 +5,7 @@ or an exact version:
|
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
7
|
npx -y -p @synapsor/runner@alpha synapsor-runner demo --quick
|
|
8
|
-
npm install -g @synapsor/runner@0.1.0-alpha.
|
|
8
|
+
npm install -g @synapsor/runner@0.1.0-alpha.17
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Do not rely on the untagged `latest` dist-tag until a stable release is
|
|
@@ -38,16 +38,52 @@ A stable `0.1.0` release should only be tagged after:
|
|
|
38
38
|
- npm README commands match the published package;
|
|
39
39
|
- `synapsor-runner demo --quick` works from a clean directory;
|
|
40
40
|
- own-database onboarding works from a clean directory;
|
|
41
|
+
- one-command review-mode `synapsor-runner up` is verified from a clean
|
|
42
|
+
directory and clearly prints model-facing tools, writeback path, handler
|
|
43
|
+
requirements, and next commands;
|
|
44
|
+
- review-mode wizard output is verified for one read capability plus one
|
|
45
|
+
proposal capability;
|
|
46
|
+
- handler template security warnings are verified in docs, CLI output, and
|
|
47
|
+
generated templates;
|
|
41
48
|
- stdio MCP and Streamable HTTP MCP are both verified;
|
|
42
49
|
- OpenAI alias mode is verified;
|
|
43
50
|
- direct SQL writeback requirements are documented and tested;
|
|
44
51
|
- app-owned executor requirements are documented and tested;
|
|
45
52
|
- local evidence/proposal/receipt/replay inspection works;
|
|
46
53
|
- current limitations are accurate.
|
|
54
|
+
- at least one external developer can follow the README without reading source;
|
|
55
|
+
- there are no known docs/code mismatches around transport, credentials,
|
|
56
|
+
receipt tables, or handler expectations.
|
|
57
|
+
|
|
58
|
+
## Stable Compatibility Promise
|
|
59
|
+
|
|
60
|
+
After `0.1.0` is promoted to the untagged `latest` dist-tag, Synapsor Runner
|
|
61
|
+
keeps these public surfaces compatible through the `0.1.x` line unless a
|
|
62
|
+
release note marks a deprecation first:
|
|
63
|
+
|
|
64
|
+
- the `synapsor-runner` binary name and README quickstart commands;
|
|
65
|
+
- `synapsor.runner.json` schema version `1` for documented fields;
|
|
66
|
+
- result envelope v2 for new configs, with the documented v1 opt-out;
|
|
67
|
+
- stdio MCP and Streamable HTTP MCP command surfaces;
|
|
68
|
+
- generated MCP client snippets for documented clients;
|
|
69
|
+
- proposal, approval, guarded writeback, receipt, evidence, query-audit, and
|
|
70
|
+
replay inspection commands;
|
|
71
|
+
- direct SQL writeback and app-owned executor contracts documented in README
|
|
72
|
+
and `docs/writeback-executors.md`.
|
|
73
|
+
|
|
74
|
+
Stable does not promise production SLA, hosted Cloud features, compliance
|
|
75
|
+
certification, physical Postgres/MySQL branching, generic SQL writeback,
|
|
76
|
+
generic multi-row writes, or compatibility for undocumented local SQLite
|
|
77
|
+
internals. Local store migrations may happen inside `0.1.x`, but documented CLI
|
|
78
|
+
inspection commands should remain the supported way to read the store.
|
|
79
|
+
|
|
80
|
+
Serious alpha users should pin an exact alpha version in package.json, CI, and
|
|
81
|
+
MCP client snippets. Use `@alpha` only when intentionally testing the moving
|
|
82
|
+
preview channel.
|
|
47
83
|
|
|
48
84
|
## Result Envelope Migration
|
|
49
85
|
|
|
50
|
-
`
|
|
86
|
+
New `init` and `onboard db` configs default to:
|
|
51
87
|
|
|
52
88
|
```json
|
|
53
89
|
{
|
|
@@ -55,32 +91,36 @@ A stable `0.1.0` release should only be tagged after:
|
|
|
55
91
|
}
|
|
56
92
|
```
|
|
57
93
|
|
|
58
|
-
|
|
94
|
+
Existing hand-written configs without `result_format` keep the legacy runtime
|
|
95
|
+
default for compatibility. To force v2 or opt an older client back to v1, use:
|
|
59
96
|
|
|
60
97
|
```bash
|
|
61
98
|
synapsor-runner mcp serve --result-format v2
|
|
62
99
|
synapsor-runner mcp serve-streamable-http --result-format v2
|
|
100
|
+
synapsor-runner mcp serve --result-format v1
|
|
63
101
|
```
|
|
64
102
|
|
|
65
|
-
|
|
66
|
-
|
|
103
|
+
v2 makes `ok` the only required branch point for MCP client code. Do not remove
|
|
104
|
+
the v1 escape hatch until the stable compatibility policy says legacy result
|
|
105
|
+
shapes are no longer supported.
|
|
67
106
|
|
|
68
107
|
## Publish Checklist
|
|
69
108
|
|
|
70
109
|
Before publishing a new alpha:
|
|
71
110
|
|
|
72
111
|
```bash
|
|
73
|
-
|
|
74
|
-
corepack pnpm exec vitest run packages/mcp-server/src/index.test.ts apps/runner/src/cli.test.ts
|
|
75
|
-
./scripts/verify-packed-runner.sh
|
|
76
|
-
npm pack --dry-run
|
|
77
|
-
git diff --check
|
|
112
|
+
./scripts/verify-release-gate.sh
|
|
78
113
|
```
|
|
79
114
|
|
|
80
115
|
After publishing:
|
|
81
116
|
|
|
82
117
|
```bash
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
118
|
+
VERIFY_PUBLISHED_ALPHA=1 ./scripts/verify-release-gate.sh 0.1.0-alpha.17
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Before promoting `latest` to stable, bump and publish a non-prerelease version,
|
|
122
|
+
then verify the stable channel from a clean temporary directory:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
./scripts/verify-published-stable.sh 0.1.0
|
|
86
126
|
```
|
|
@@ -144,5 +144,8 @@ INTERNAL
|
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
Current alpha implementation redacts raw connection and driver messages from v2
|
|
147
|
-
MCP results.
|
|
148
|
-
|
|
147
|
+
MCP results. New `init` and `onboard db` configs write `result_format: 2` by
|
|
148
|
+
default. Existing hand-written configs without `result_format` keep the legacy
|
|
149
|
+
runtime default for compatibility; pass `--result-format v2` when serving an
|
|
150
|
+
older config to force the v2 envelope, or `--result-format v1` for an older
|
|
151
|
+
client that still depends on the legacy shape.
|
|
@@ -56,7 +56,7 @@ Receipt the handler must return (today's status vocabulary, kept):
|
|
|
56
56
|
## Helper API (TypeScript — first-party, since the runner is TS/Node)
|
|
57
57
|
|
|
58
58
|
```ts
|
|
59
|
-
import { createWritebackHandler } from "
|
|
59
|
+
import { createWritebackHandler } from "../packages/handler/src/index.js";
|
|
60
60
|
|
|
61
61
|
export const handler = createWritebackHandler({
|
|
62
62
|
// 1. Authenticity: helper verifies bearer AND the HMAC signature for you.
|
|
@@ -15,7 +15,7 @@ The SQLite store is shared state, and the running server holds it open. Deleting
|
|
|
15
15
|
Direct writeback does CREATE TABLE IF NOT EXISTS synapsor_writeback_receipts, which a least-privilege writer can't do (PG15+ no CREATE on public). Now documented, but doctor could detect it and print the exact GRANT/DDL (or a flag to pre-create). I had to invent the dedicated-schema trick myself.
|
|
16
16
|
|
|
17
17
|
5. App-owned handlers re-implement security by hand.
|
|
18
|
-
The executor contract is "match the example": you must re-check tenant, parse change_set, enforce the expected_version stale-row guard, and do idempotency yourself. It's easy to write an insecure handler (skip the version check, forget tenant scope). There's no handler SDK/helper and no request signing — the only auth is a bearer token, so the handler trusts the POST body's tenant/version. A
|
|
18
|
+
The executor contract is "match the example": you must re-check tenant, parse change_set, enforce the expected_version stale-row guard, and do idempotency yourself. It's easy to write an insecure handler (skip the version check, forget tenant scope). There's no published handler SDK/helper and no request signing — the only auth is a bearer token, so the handler trusts the POST body's tenant/version. A first-party handler helper (verify + parse + enforce guards, optional HMAC signature) would remove a whole class of mistakes.
|
|
19
19
|
|
|
20
20
|
6. Versioning discipline.
|
|
21
21
|
@alpha is a moving tag and behavior changed meaningfully across alpha.6→11 (transport, arg types string→number, credential resolution). I had to pin and bump six times. A stable channel + changelog + semver promise is table stakes before serious devs build on it.
|
|
@@ -115,6 +115,13 @@ The handler receives proposal fields, the exact patch, evidence metadata,
|
|
|
115
115
|
guards, and an idempotency key. It does not receive arbitrary model SQL or DB
|
|
116
116
|
credentials from Synapsor Runner.
|
|
117
117
|
|
|
118
|
+
> **Important:** your app handler owns the final business write. Runner creates
|
|
119
|
+
> the proposal and calls your handler only after approval, but your handler must
|
|
120
|
+
> still enforce tenant/scope checks, expected-version or conflict guards,
|
|
121
|
+
> idempotency keys, allowed business actions, transaction/rollback, and safe
|
|
122
|
+
> error receipts. If you skip those checks, you can reintroduce cross-tenant
|
|
123
|
+
> writes, lost updates, or duplicate writes. Keep handler credentials out of MCP.
|
|
124
|
+
|
|
118
125
|
When `signing_secret_env` is set, Runner signs the exact JSON body with HMAC
|
|
119
126
|
SHA-256 and sends:
|
|
120
127
|
|
|
@@ -220,6 +227,12 @@ npx -y -p @synapsor/runner@alpha synapsor-runner handler template command \
|
|
|
220
227
|
The command receives the same structured JSON request on stdin and should print
|
|
221
228
|
a JSON receipt body on stdout.
|
|
222
229
|
|
|
230
|
+
> **Important:** command handlers have the same responsibility as HTTP
|
|
231
|
+
> handlers. Re-check tenant/scope, expected-version or conflict guard,
|
|
232
|
+
> idempotency, allowed business action, transaction/rollback, and safe error
|
|
233
|
+
> receipt before mutating state. Otherwise the script can reintroduce
|
|
234
|
+
> cross-tenant writes, lost updates, or duplicate writes.
|
|
235
|
+
|
|
223
236
|
Use `examples/app-owned-writeback/command-handler.mjs` as a starting point when
|
|
224
237
|
your safest apply path is an app script or job runner.
|
|
225
238
|
|
|
@@ -14,6 +14,13 @@ The model-facing MCP tool still only creates a proposal. Approval happens
|
|
|
14
14
|
outside MCP. After approval, `synapsor-runner apply` sends a structured request
|
|
15
15
|
to your handler, and the handler returns an execution receipt for replay.
|
|
16
16
|
|
|
17
|
+
> **Important:** your app handler owns the final business write. Runner creates
|
|
18
|
+
> the proposal and calls your handler only after approval, but your handler must
|
|
19
|
+
> still enforce tenant/scope checks, expected-version or conflict guards,
|
|
20
|
+
> idempotency keys, allowed business actions, transaction/rollback, and safe
|
|
21
|
+
> error receipts. If you skip those checks, you can reintroduce cross-tenant
|
|
22
|
+
> writes, lost updates, or duplicate writes. Keep handler credentials out of MCP.
|
|
23
|
+
|
|
17
24
|
## Config Snippet
|
|
18
25
|
|
|
19
26
|
Add an executor and point one proposal capability at it:
|
|
@@ -26,6 +26,15 @@ if (request.dry_run) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/*
|
|
29
|
+
* IMPORTANT: your app handler owns the final business write.
|
|
30
|
+
* Runner creates the proposal and calls your handler only after approval,
|
|
31
|
+
* but your handler must still enforce tenant/scope, expected-version or
|
|
32
|
+
* conflict guard, idempotency key, allowed business action,
|
|
33
|
+
* transaction/rollback, and safe error receipt.
|
|
34
|
+
*
|
|
35
|
+
* If you skip those checks, you can reintroduce cross-tenant writes,
|
|
36
|
+
* lost updates, or duplicate writes. Keep handler credentials out of MCP.
|
|
37
|
+
*
|
|
29
38
|
* Put your app-owned command transaction here.
|
|
30
39
|
*
|
|
31
40
|
* Examples:
|
|
@@ -28,6 +28,15 @@ app.post("/synapsor/writeback", async (request, reply) => {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/*
|
|
31
|
+
* IMPORTANT: your app handler owns the final business write.
|
|
32
|
+
* Runner creates the proposal and calls your handler only after approval,
|
|
33
|
+
* but your handler must still enforce tenant/scope, expected-version or
|
|
34
|
+
* conflict guard, idempotency key, allowed business action,
|
|
35
|
+
* transaction/rollback, and safe error receipt.
|
|
36
|
+
*
|
|
37
|
+
* If you skip those checks, you can reintroduce cross-tenant writes,
|
|
38
|
+
* lost updates, or duplicate writes. Keep handler credentials out of MCP.
|
|
39
|
+
*
|
|
31
40
|
* Put your app-owned transaction here.
|
|
32
41
|
*
|
|
33
42
|
* Examples:
|
|
@@ -36,6 +36,15 @@ def writeback(request: HandlerRequest, authorization: str | None = Header(defaul
|
|
|
36
36
|
"details": {"dry_run": True},
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
# IMPORTANT: your app handler owns the final business write.
|
|
40
|
+
# Runner creates the proposal and calls your handler only after approval,
|
|
41
|
+
# but your handler must still enforce tenant/scope, expected-version or
|
|
42
|
+
# conflict guard, idempotency key, allowed business action,
|
|
43
|
+
# transaction/rollback, and safe error receipt.
|
|
44
|
+
#
|
|
45
|
+
# If you skip those checks, you can reintroduce cross-tenant writes,
|
|
46
|
+
# lost updates, or duplicate writes. Keep handler credentials out of MCP.
|
|
47
|
+
#
|
|
39
48
|
# Put your app-owned transaction here.
|
|
40
49
|
#
|
|
41
50
|
# Examples:
|
|
@@ -26,6 +26,13 @@ App-owned rich writeback:
|
|
|
26
26
|
The model never receives `execute_sql`, approval tools, commit/apply tools,
|
|
27
27
|
database URLs, or write credentials.
|
|
28
28
|
|
|
29
|
+
> **Important:** the app handler owns the final business write. Runner creates
|
|
30
|
+
> the proposal and calls the handler only after approval, but the handler must
|
|
31
|
+
> still enforce tenant/scope checks, expected-version or conflict guards,
|
|
32
|
+
> idempotency keys, allowed business actions, transaction/rollback, and safe
|
|
33
|
+
> error receipts. If those checks are skipped, the app can reintroduce
|
|
34
|
+
> cross-tenant writes, lost updates, or duplicate writes.
|
|
35
|
+
|
|
29
36
|
## Run
|
|
30
37
|
|
|
31
38
|
From the repository root:
|
|
@@ -54,6 +54,12 @@ async function shutdown() {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
async function applyAccountCredit(job, tx) {
|
|
57
|
+
/*
|
|
58
|
+
* IMPORTANT: this app handler owns the final business write.
|
|
59
|
+
* The helper has already verified auth, tenant scope, expected version,
|
|
60
|
+
* idempotency, and transaction wrapping before this function runs. Keep that
|
|
61
|
+
* pattern if you replace the helper or move this logic into your app.
|
|
62
|
+
*/
|
|
57
63
|
const amountCents = Number(job.patch.credit_requested_cents);
|
|
58
64
|
const reason = String(job.patch.credit_reason || "approved account credit");
|
|
59
65
|
if (!Number.isInteger(amountCents) || amountCents <= 0) {
|
|
@@ -113,13 +119,5 @@ function writeJson(response, statusCode, body) {
|
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
async function loadHandlerHelper() {
|
|
116
|
-
|
|
117
|
-
return await import("@synapsor/handler");
|
|
118
|
-
} catch (workspaceError) {
|
|
119
|
-
try {
|
|
120
|
-
return await import(new URL("./synapsor-handler.mjs", import.meta.url));
|
|
121
|
-
} catch {
|
|
122
|
-
throw workspaceError;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
122
|
+
return await import(new URL("./synapsor-handler.mjs", import.meta.url));
|
|
125
123
|
}
|