@synapsor/runner 0.1.0-alpha.3 → 0.1.0-alpha.4

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.
@@ -0,0 +1,254 @@
1
+ # Synapsor Runner Open-Source Feature Inventory
2
+
3
+ Date: 2026-06-23
4
+
5
+ Branch inspected: `runner-segment-scale-adoption`
6
+
7
+ This inventory tracks the open-source runner boundary. It is not a list of
8
+ Synapsor Cloud or Synapsor DBMS features.
9
+
10
+ ## Summary
11
+
12
+ Synapsor Runner now has a local evidence/replay ledger for real
13
+ Postgres/MySQL-backed MCP interactions:
14
+
15
+ - config-defined semantic MCP tools instead of raw SQL;
16
+ - trusted tenant/principal context binding;
17
+ - scoped source reads;
18
+ - local evidence bundles and query audit;
19
+ - proposal-first writes with before/proposed diffs;
20
+ - local approval/rejection outside MCP;
21
+ - guarded single-row writeback;
22
+ - writeback receipts;
23
+ - proposal replay;
24
+ - local indexed search over proposals, evidence, query audit, receipts, and
25
+ replay links.
26
+ - npm/package trust checks for the public `synapsor` bin and legacy
27
+ `synapsor-runner` alias;
28
+ - a fixture-only `demo --quick` that seeds an inspectable local ledger without
29
+ Docker;
30
+ - shareable MCP audit JSON/Markdown output;
31
+ - redacted doctor Markdown report output.
32
+
33
+ The local store is SQLite and intended for local/dev/staging usage. It is not a
34
+ hosted central audit ledger, not RBAC/SSO, not cross-runner search, not
35
+ enterprise retention, and not compliance export.
36
+
37
+ ## Implemented Local Concepts
38
+
39
+ | Synapsor concept | Runner implementation | Status |
40
+ | --- | --- | --- |
41
+ | Context bindings | Trusted tenant/principal from env/static dev/HTTP/cloud session config | Implemented |
42
+ | Capabilities | Reviewed semantic MCP tools from `synapsor.runner.json` | Implemented |
43
+ | Evidence bundles | Captured/projected rows and metadata persisted locally | Implemented |
44
+ | Query audit | Source/table/fingerprint/row-count/redacted-parameter records | Implemented |
45
+ | Proposals | Immutable before/proposed change sets | Implemented |
46
+ | Approval | Local CLI/UI approval/rejection outside MCP | Implemented |
47
+ | Guarded writeback | Single-row Postgres/MySQL `UPDATE` with primary-key, tenant, allowed-column, conflict, idempotency, and affected-row checks | Implemented |
48
+ | Receipts | Applied/conflict/failed writeback receipts | Implemented |
49
+ | Replay | Proposal replay from local captured records | Implemented |
50
+ | MCP audit | Static risk review for database MCP tool shapes | Implemented |
51
+ | Local indexed search | CLI filters for activity, proposals, evidence, query audit, and receipts | Implemented |
52
+
53
+ ## New Local Ledger Commands
54
+
55
+ ```bash
56
+ synapsor activity search --tenant acme --object invoice:INV-3001
57
+ synapsor proposals list --tenant acme --capability billing.propose_late_fee_waiver --object invoice:INV-3001 --status approved
58
+ synapsor evidence list --tenant acme --capability billing.inspect_invoice --source app_postgres --table invoices
59
+ synapsor evidence show ev_...
60
+ synapsor evidence export ev_... --format json --output evidence.json
61
+ synapsor evidence export ev_... --format markdown --output evidence.md
62
+ synapsor query-audit list --evidence ev_... --source app_postgres --table invoices
63
+ synapsor query-audit show <audit_id>
64
+ synapsor query-audit export <audit_id> --format json --output audit.json
65
+ synapsor receipts list --proposal wrp_...
66
+ synapsor receipts show <receipt_id>
67
+ synapsor replay show --proposal wrp_...
68
+ synapsor replay show --replay replay_wrp_...
69
+ synapsor replay show --evidence ev_...
70
+ synapsor replay export --proposal wrp_... --format json --output replay.json
71
+ synapsor replay export --proposal wrp_... --format markdown --output replay.md
72
+ synapsor doctor --config synapsor.runner.json --report --redact --output synapsor-doctor.md
73
+ synapsor store stats --store ./.synapsor/local.db
74
+ synapsor store vacuum --store ./.synapsor/local.db
75
+ synapsor store prune --store ./.synapsor/local.db --older-than 30d --dry-run
76
+ ```
77
+
78
+ Unknown top-level commands now return a nonzero error instead of generic help.
79
+ Unsupported flags on the new search/list commands fail clearly rather than
80
+ being silently ignored.
81
+
82
+ ## Local Store Schema And Search
83
+
84
+ The store remains backward-compatible with existing local SQLite files. New
85
+ metadata columns are nullable and are backfilled from existing JSON where safe.
86
+
87
+ New/important searchable metadata:
88
+
89
+ - proposal tenant, principal, capability/action, business object, object id,
90
+ state, source, table, created time;
91
+ - evidence tenant, principal, capability, proposal id, business object, object
92
+ id, source, table, query fingerprint, created time;
93
+ - query audit tenant, proposal id, evidence id, source, table, primary-key
94
+ value, query fingerprint, created time;
95
+ - writeback receipt proposal id, writeback job id, idempotency key, status,
96
+ created time.
97
+
98
+ Indexes are created idempotently on the local store for:
99
+
100
+ - proposals by tenant/time, action/time, capability/time, principal/time,
101
+ object/time, state/time, source/table/time;
102
+ - evidence bundles by tenant/time, proposal id, created time, capability/time,
103
+ principal/time, object/time, source/table/time, query fingerprint/time;
104
+ - evidence items by evidence bundle id;
105
+ - query audit by evidence id, proposal id, source/table/time,
106
+ query fingerprint/time, tenant/time, object/time, primary-key/time;
107
+ - writeback receipts by proposal id, writeback job id, idempotency key,
108
+ status/time;
109
+ - replay records by proposal id and created time;
110
+ - approvals by proposal id and status/time;
111
+ - proposal events by proposal id and kind/time.
112
+
113
+ These are local metadata indexes. Runner does not index customer Postgres/MySQL
114
+ tables.
115
+
116
+ ## Read-Only Evidence
117
+
118
+ Read-only MCP tools can record evidence bundles and query-audit rows. The MCP
119
+ tool response returns an evidence handle. The user can now inspect that handle
120
+ later:
121
+
122
+ ```bash
123
+ synapsor evidence show ev_...
124
+ synapsor evidence list --tenant acme --capability billing.inspect_invoice
125
+ synapsor query-audit list --evidence ev_...
126
+ synapsor activity search --tenant acme --source app_postgres --table invoices
127
+ ```
128
+
129
+ Read-only evidence inspection does not rerun the external database read. It
130
+ shows captured/projected evidence that was already persisted locally.
131
+
132
+ Proposal replay remains proposal-centric. `replay show --evidence ev_...`
133
+ works only when the evidence bundle is linked to a replayable proposal.
134
+
135
+ ## Replay Versus Time Travel
136
+
137
+ Runner replay is local captured interaction replay:
138
+
139
+ ```text
140
+ trusted context
141
+ -> captured source-row excerpt
142
+ -> query audit/fingerprint
143
+ -> proposal before/proposed diff
144
+ -> approval/rejection events
145
+ -> writeback job
146
+ -> terminal receipt
147
+ ```
148
+
149
+ It is not:
150
+
151
+ - external DB time travel;
152
+ - `AS OF` query support over Postgres/MySQL;
153
+ - native branch creation;
154
+ - external DB merge;
155
+ - auto-merge;
156
+ - settlement-policy execution.
157
+
158
+ Synapsor-native branching, time travel, settlement, and workflow DAG execution
159
+ remain proprietary Synapsor Cloud/DBMS features.
160
+
161
+ ## MCP Resource Boundary
162
+
163
+ MCP resources remain read-only:
164
+
165
+ - `synapsor://proposals/{proposal_id}`;
166
+ - `synapsor://evidence/{evidence_bundle_id}`;
167
+ - `synapsor://replay/{replay_id}`.
168
+
169
+ They inspect local store records and do not mutate source databases. They also
170
+ do not list/search all local records through MCP; local search is a CLI/local UI
171
+ concern.
172
+
173
+ ## Not Included In OSS Runner
174
+
175
+ - C++ DBMS internals;
176
+ - native Synapsor branches;
177
+ - external DB time travel;
178
+ - `AS OF` external DB queries;
179
+ - external DB merge;
180
+ - auto-merge;
181
+ - settlement policies;
182
+ - workflow DAG engine;
183
+ - `CREATE AGENT WORKFLOW`;
184
+ - hosted workflow graph builder;
185
+ - governed memory;
186
+ - RBAC/SSO;
187
+ - hosted evidence ledger;
188
+ - central org-wide activity search;
189
+ - managed runner fleet;
190
+ - compliance exports;
191
+ - enterprise retention controls;
192
+ - production CDC machinery.
193
+
194
+ ## Safety Boundary Verification
195
+
196
+ The runner keeps these out of the model-facing MCP tool surface:
197
+
198
+ - `execute_sql`;
199
+ - `raw_sql`;
200
+ - `query_database`;
201
+ - approval tools;
202
+ - reject tools;
203
+ - apply/commit/writeback tools;
204
+ - database URLs;
205
+ - write credentials;
206
+ - model-controlled tenant authority.
207
+
208
+ Approval, rejection, and writeback stay in CLI/UI/app-handler/runner paths
209
+ outside the MCP tool surface.
210
+
211
+ ## Current Rough Edges
212
+
213
+ - The local UI is still proposal-review oriented. The CLI now has first-class
214
+ local evidence/search commands, but UI search/export for evidence/query-audit
215
+ is not yet a full dedicated workflow.
216
+ - Read-only evidence has CLI inspection and activity search, but read-only
217
+ replay is not a standalone replay object unless linked to a proposal.
218
+ - The current direct DB writeback adapter intentionally supports guarded
219
+ single-row `UPDATE` only. App-owned HTTP/command handlers are the path for
220
+ richer business writes.
221
+ - The alpha package requires Node >= 22.5.0 because the local ledger uses
222
+ Node's `node:sqlite` runtime. The package declares this and the bin wrapper
223
+ exits early with a clear message on older Node versions.
224
+
225
+ ## Tests Covering This Inventory
226
+
227
+ Relevant local tests:
228
+
229
+ ```bash
230
+ corepack pnpm exec vitest run packages/proposal-store/src/index.test.ts
231
+ corepack pnpm exec vitest run apps/runner/src/cli.test.ts
232
+ corepack pnpm exec vitest run packages/mcp-server/src/index.test.ts
233
+ corepack pnpm test:first-run
234
+ corepack pnpm test:mcp-client-configs
235
+ ./scripts/verify-public-commands.sh
236
+ ./scripts/verify-packed-runner.sh
237
+ ```
238
+
239
+ The current implementation pass specifically added coverage for:
240
+
241
+ - unknown command nonzero behavior;
242
+ - unsupported search flags failing clearly;
243
+ - proposal filters by tenant/capability/object/status;
244
+ - evidence show/list/export JSON/Markdown;
245
+ - query-audit list/show/export;
246
+ - receipts list/show;
247
+ - replay by proposal id, replay id, and linked evidence id;
248
+ - replay JSON and Markdown export;
249
+ - activity search by tenant/object;
250
+ - local metadata indexes and idempotent migration/reopen.
251
+ - `demo --quick` creating an inspectable local fixture store;
252
+ - built-in audit example JSON/Markdown output;
253
+ - clean packed-tarball command execution.
254
+ - local store stats, vacuum, and dry-run/apply prune.
@@ -3,6 +3,23 @@
3
3
  Synapsor Runner controls a narrow database path for MCP agents. It does not make
4
4
  MCP generally secure and it does not solve prompt injection.
5
5
 
6
+ ## Least-privilege database access
7
+
8
+ Use a read-only database user, restricted views, row-level security, and staging
9
+ data where appropriate. Synapsor Runner is not a replacement for database
10
+ permissions.
11
+
12
+ Database permissions protect the connection. Synapsor Runner shapes the
13
+ model-facing interface: reviewed semantic capabilities, trusted context
14
+ binding, evidence handles, query audit, local inspection, and proposal-first
15
+ writes instead of model-facing commit authority.
16
+
17
+ If all you need is restricted reads, database permissions are a good start.
18
+ Use Synapsor Runner when you also want the agent-facing layer: semantic tools,
19
+ trusted context, evidence handles, query audit, local inspection, and
20
+ proposal-first writes. Proposal workflows add full replay across evidence,
21
+ approval, writeback receipts, and events.
22
+
6
23
  The model-facing MCP server exposes reviewed semantic tools such as
7
24
  `billing.inspect_invoice` and `billing.propose_late_fee_waiver`.
8
25
 
@@ -54,11 +54,62 @@ The model-facing MCP tools are:
54
54
 
55
55
  - `support.inspect_ticket`
56
56
  - `support.propose_ticket_resolution`
57
+ - `support.inspect_customer_account`
58
+ - `support.propose_plan_credit`
57
59
  - `billing.inspect_invoice`
58
60
  - `billing.propose_late_fee_waiver`
61
+ - `orders.inspect_order`
62
+ - `orders.propose_status_change`
59
63
 
60
64
  The model does not receive approval tools, commit tools, write credentials, raw SQL, arbitrary table names, arbitrary column names, or tenant authority.
61
65
 
66
+ ## Safe Write Examples
67
+
68
+ After `synapsor demo` or after starting this fixture manually, try the same
69
+ proposal-first loop without connecting an MCP client:
70
+
71
+ ```bash
72
+ npx -y -p @synapsor/runner@alpha synapsor propose billing.propose_late_fee_waiver --sample
73
+ npx -y -p @synapsor/runner@alpha synapsor proposals show latest
74
+ npx -y -p @synapsor/runner@alpha synapsor proposals approve latest --yes
75
+ npx -y -p @synapsor/runner@alpha synapsor apply latest
76
+ npx -y -p @synapsor/runner@alpha synapsor replay latest
77
+ ```
78
+
79
+ Expected safety output:
80
+
81
+ ```text
82
+ Source DB changed:
83
+ no
84
+
85
+ Guarded writeback applied.
86
+ * proposal approved: yes
87
+ * primary key matched: yes
88
+ * tenant guard matched: yes
89
+ * allowed columns only: yes
90
+ * conflict guard passed: yes
91
+ ```
92
+
93
+ Other proposal examples use the same review/apply/replay path:
94
+
95
+ ```bash
96
+ npx -y -p @synapsor/runner@alpha synapsor propose support.propose_plan_credit --sample
97
+ npx -y -p @synapsor/runner@alpha synapsor propose orders.propose_status_change --sample
98
+ ```
99
+
100
+ Safety guarantees:
101
+
102
+ - proposals store evidence, a before/after diff, trusted tenant/principal
103
+ context, and query audit before any write;
104
+ - approval stays outside the model-facing MCP tool surface;
105
+ - writeback is single-row, primary-key targeted, tenant guarded,
106
+ allowed-column checked, idempotent, and conflict guarded;
107
+ - stale rows return `conflict` and are replayable.
108
+
109
+ Current limitation: v0.1 is intentionally local and single-user. It does not
110
+ provide hosted RBAC, workflow DAGs, branches, settlement policies, auto-merge,
111
+ or production-scale runner orchestration.
112
+
62
113
  ## Review And Replay
63
114
 
64
115
  After a proposal exists:
@@ -11,6 +11,8 @@ CREATE TABLE IF NOT EXISTS public.customers (
11
11
  name text NOT NULL,
12
12
  email text,
13
13
  plan text NOT NULL,
14
+ plan_credit_cents integer NOT NULL DEFAULT 0,
15
+ credit_reason text,
14
16
  created_at timestamptz NOT NULL DEFAULT now(),
15
17
  updated_at timestamptz NOT NULL DEFAULT now()
16
18
  );
@@ -36,6 +38,15 @@ CREATE TABLE IF NOT EXISTS public.invoices (
36
38
  updated_at timestamptz NOT NULL DEFAULT now()
37
39
  );
38
40
 
41
+ CREATE TABLE IF NOT EXISTS public.orders (
42
+ id text PRIMARY KEY,
43
+ tenant_id text NOT NULL REFERENCES public.tenants(id),
44
+ customer_id text NOT NULL REFERENCES public.customers(id),
45
+ status text NOT NULL,
46
+ status_change_reason text,
47
+ updated_at timestamptz NOT NULL DEFAULT now()
48
+ );
49
+
39
50
  DO $$
40
51
  BEGIN
41
52
  IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'synapsor_reader') THEN
@@ -50,6 +61,8 @@ $$;
50
61
  GRANT CONNECT ON DATABASE synapsor_reference_support_billing TO synapsor_reader, synapsor_writer;
51
62
  GRANT USAGE ON SCHEMA public TO synapsor_reader, synapsor_writer;
52
63
  GRANT CREATE ON SCHEMA public TO synapsor_writer;
53
- GRANT SELECT ON public.tenants, public.customers, public.support_tickets, public.invoices TO synapsor_reader, synapsor_writer;
64
+ GRANT SELECT ON public.tenants, public.customers, public.support_tickets, public.invoices, public.orders TO synapsor_reader, synapsor_writer;
65
+ GRANT UPDATE (plan_credit_cents, credit_reason, updated_at) ON public.customers TO synapsor_writer;
54
66
  GRANT UPDATE (status, resolution_note, updated_at) ON public.support_tickets TO synapsor_writer;
55
67
  GRANT UPDATE (late_fee_cents, waiver_reason, updated_at) ON public.invoices TO synapsor_writer;
68
+ GRANT UPDATE (status, status_change_reason, updated_at) ON public.orders TO synapsor_writer;
@@ -4,12 +4,12 @@ VALUES
4
4
  ('otherco', 'OtherCo Labs', '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z')
5
5
  ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, updated_at = EXCLUDED.updated_at;
6
6
 
7
- INSERT INTO public.customers (id, tenant_id, name, email, plan, created_at, updated_at)
7
+ INSERT INTO public.customers (id, tenant_id, name, email, plan, plan_credit_cents, credit_reason, created_at, updated_at)
8
8
  VALUES
9
- ('cust_acme_1', 'acme', 'Acme Robotics', 'ops@example.invalid', 'enterprise', '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z'),
10
- ('cust_acme_2', 'acme', 'Acme Field Ops', 'field@example.invalid', 'builder', '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z'),
11
- ('cust_other_1', 'otherco', 'OtherCo Labs', 'ops@otherco.invalid', 'builder', '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z')
12
- ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, name = EXCLUDED.name, email = EXCLUDED.email, plan = EXCLUDED.plan, updated_at = EXCLUDED.updated_at;
9
+ ('cust_acme_1', 'acme', 'Acme Robotics', 'ops@example.invalid', 'enterprise', 0, NULL, '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z'),
10
+ ('cust_acme_2', 'acme', 'Acme Field Ops', 'field@example.invalid', 'builder', 0, NULL, '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z'),
11
+ ('cust_other_1', 'otherco', 'OtherCo Labs', 'ops@otherco.invalid', 'builder', 0, NULL, '2026-06-20T10:00:00Z', '2026-06-20T10:00:00Z')
12
+ ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, name = EXCLUDED.name, email = EXCLUDED.email, plan = EXCLUDED.plan, plan_credit_cents = EXCLUDED.plan_credit_cents, credit_reason = EXCLUDED.credit_reason, updated_at = EXCLUDED.updated_at;
13
13
 
14
14
  INSERT INTO public.support_tickets (id, tenant_id, customer_id, subject, status, resolution_note, updated_at)
15
15
  VALUES
@@ -24,3 +24,10 @@ VALUES
24
24
  ('INV-3002', 'acme', 'cust_acme_2', 'paid', 0, 0, NULL, '2026-06-20T14:40:00Z'),
25
25
  ('INV-9001', 'otherco', 'cust_other_1', 'overdue', 25500, 5500, NULL, '2026-06-20T14:31:08Z')
26
26
  ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, customer_id = EXCLUDED.customer_id, status = EXCLUDED.status, balance_cents = EXCLUDED.balance_cents, late_fee_cents = EXCLUDED.late_fee_cents, waiver_reason = EXCLUDED.waiver_reason, updated_at = EXCLUDED.updated_at;
27
+
28
+ INSERT INTO public.orders (id, tenant_id, customer_id, status, status_change_reason, updated_at)
29
+ VALUES
30
+ ('O-1001', 'acme', 'cust_acme_1', 'paid', NULL, '2026-06-20T13:00:00Z'),
31
+ ('O-1002', 'acme', 'cust_acme_2', 'processing', NULL, '2026-06-20T13:05:00Z'),
32
+ ('O-9001', 'otherco', 'cust_other_1', 'paid', NULL, '2026-06-20T13:00:00Z')
33
+ ON CONFLICT (id) DO UPDATE SET tenant_id = EXCLUDED.tenant_id, customer_id = EXCLUDED.customer_id, status = EXCLUDED.status, status_change_reason = EXCLUDED.status_change_reason, updated_at = EXCLUDED.updated_at;
@@ -83,6 +83,56 @@
83
83
  "conflict_guard": { "column": "updated_at" },
84
84
  "approval": { "mode": "human", "required_role": "support_lead" }
85
85
  },
86
+ {
87
+ "name": "support.inspect_customer_account",
88
+ "kind": "read",
89
+ "source": "app_postgres",
90
+ "context": "local_operator",
91
+ "target": {
92
+ "schema": "public",
93
+ "table": "customers",
94
+ "primary_key": "id",
95
+ "tenant_key": "tenant_id"
96
+ },
97
+ "args": {
98
+ "customer_id": { "type": "string", "required": true, "max_length": 128 }
99
+ },
100
+ "lookup": { "id_from_arg": "customer_id" },
101
+ "visible_columns": ["id", "tenant_id", "name", "plan", "plan_credit_cents", "credit_reason", "updated_at"],
102
+ "evidence": "required",
103
+ "max_rows": 1
104
+ },
105
+ {
106
+ "name": "support.propose_plan_credit",
107
+ "kind": "proposal",
108
+ "source": "app_postgres",
109
+ "context": "local_operator",
110
+ "target": {
111
+ "schema": "public",
112
+ "table": "customers",
113
+ "primary_key": "id",
114
+ "tenant_key": "tenant_id"
115
+ },
116
+ "args": {
117
+ "customer_id": { "type": "string", "required": true, "max_length": 128 },
118
+ "credit_cents": { "type": "number", "required": true, "minimum": 0, "maximum": 5000 },
119
+ "reason": { "type": "string", "required": true, "max_length": 500 }
120
+ },
121
+ "lookup": { "id_from_arg": "customer_id" },
122
+ "visible_columns": ["id", "tenant_id", "name", "plan", "plan_credit_cents", "credit_reason", "updated_at"],
123
+ "evidence": "required",
124
+ "max_rows": 1,
125
+ "patch": {
126
+ "plan_credit_cents": { "from_arg": "credit_cents" },
127
+ "credit_reason": { "from_arg": "reason" }
128
+ },
129
+ "allowed_columns": ["plan_credit_cents", "credit_reason"],
130
+ "numeric_bounds": {
131
+ "plan_credit_cents": { "minimum": 0, "maximum": 5000 }
132
+ },
133
+ "conflict_guard": { "column": "updated_at" },
134
+ "approval": { "mode": "human", "required_role": "support_lead" }
135
+ },
86
136
  {
87
137
  "name": "billing.inspect_invoice",
88
138
  "kind": "read",
@@ -131,6 +181,61 @@
131
181
  },
132
182
  "conflict_guard": { "column": "updated_at" },
133
183
  "approval": { "mode": "human", "required_role": "billing_lead" }
184
+ },
185
+ {
186
+ "name": "orders.inspect_order",
187
+ "kind": "read",
188
+ "source": "app_postgres",
189
+ "context": "local_operator",
190
+ "target": {
191
+ "schema": "public",
192
+ "table": "orders",
193
+ "primary_key": "id",
194
+ "tenant_key": "tenant_id"
195
+ },
196
+ "args": {
197
+ "order_id": { "type": "string", "required": true, "max_length": 128 }
198
+ },
199
+ "lookup": { "id_from_arg": "order_id" },
200
+ "visible_columns": ["id", "tenant_id", "customer_id", "status", "status_change_reason", "updated_at"],
201
+ "evidence": "required",
202
+ "max_rows": 1
203
+ },
204
+ {
205
+ "name": "orders.propose_status_change",
206
+ "kind": "proposal",
207
+ "source": "app_postgres",
208
+ "context": "local_operator",
209
+ "target": {
210
+ "schema": "public",
211
+ "table": "orders",
212
+ "primary_key": "id",
213
+ "tenant_key": "tenant_id"
214
+ },
215
+ "args": {
216
+ "order_id": { "type": "string", "required": true, "max_length": 128 },
217
+ "status": { "type": "string", "required": true, "enum": ["ready_to_ship", "canceled"] },
218
+ "reason": { "type": "string", "required": true, "max_length": 500 }
219
+ },
220
+ "lookup": { "id_from_arg": "order_id" },
221
+ "visible_columns": ["id", "tenant_id", "customer_id", "status", "status_change_reason", "updated_at"],
222
+ "evidence": "required",
223
+ "max_rows": 1,
224
+ "patch": {
225
+ "status": { "from_arg": "status" },
226
+ "status_change_reason": { "from_arg": "reason" }
227
+ },
228
+ "allowed_columns": ["status", "status_change_reason"],
229
+ "transition_guards": {
230
+ "status": {
231
+ "allowed": {
232
+ "paid": ["ready_to_ship", "canceled"],
233
+ "processing": ["ready_to_ship"]
234
+ }
235
+ }
236
+ },
237
+ "conflict_guard": { "column": "updated_at" },
238
+ "approval": { "mode": "human", "required_role": "orders_lead" }
134
239
  }
135
240
  ]
136
241
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synapsor/runner",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "Commit-safe MCP runner for Postgres and MySQL agents",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",