@synapsor/runner 1.4.123 → 1.5.3
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 +144 -19
- package/README.md +132 -135
- package/dist/authoring.d.ts +23 -0
- package/dist/authoring.d.ts.map +1 -0
- package/dist/authoring.mjs +1318 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -5
- package/dist/local-ui.d.ts +14 -0
- package/dist/local-ui.d.ts.map +1 -1
- package/dist/runner.mjs +18427 -7084
- package/dist/runtime.d.ts +50 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.mjs +15094 -0
- package/dist/shadow.d.ts +36 -0
- package/dist/shadow.d.ts.map +1 -0
- package/dist/shadow.mjs +5262 -0
- package/docs/README.md +44 -5
- package/docs/alternatives.md +122 -0
- package/docs/capability-authoring.md +44 -1
- package/docs/client-recipes.md +218 -0
- package/docs/contract-testing.md +9 -7
- package/docs/cursor-plugin.md +78 -0
- package/docs/database-enforced-scope.md +273 -0
- package/docs/dependency-license-inventory.md +6 -2
- package/docs/doctor.md +17 -0
- package/docs/effect-regression.md +179 -0
- package/docs/fresh-developer-usability.md +110 -0
- package/docs/getting-started-own-database.md +103 -2
- package/docs/host-compatibility.md +59 -0
- package/docs/local-mode.md +12 -7
- package/docs/mcp-apps.md +188 -0
- package/docs/mcp-audit.md +220 -2
- package/docs/mcp-client-setup.md +20 -4
- package/docs/mcp-clients.md +41 -4
- package/docs/oss-vs-cloud.md +3 -0
- package/docs/production.md +8 -0
- package/docs/release-notes.md +138 -14
- package/docs/release-policy.md +9 -5
- package/docs/runner-config-reference.md +25 -0
- package/docs/schema-api-candidates.md +68 -0
- package/docs/security-boundary.md +30 -5
- package/docs/shadow-studies.md +241 -0
- package/docs/troubleshooting-first-run.md +46 -0
- package/examples/mcp-postgres-billing-app-handler/app-handler.mjs +9 -1
- package/examples/mcp-postgres-billing-app-handler/schema.sql +11 -0
- package/examples/mcp-postgres-billing-app-handler/synapsor-handler.mjs +3 -0
- package/examples/raw-sql-vs-synapsor/Makefile +2 -2
- package/examples/raw-sql-vs-synapsor/README.md +6 -4
- package/examples/support-billing-agent/Makefile +5 -1
- package/examples/support-billing-agent/README.md +57 -5
- package/examples/support-billing-agent/app/README.md +6 -0
- package/examples/support-billing-agent/app/contract.ts +28 -0
- package/examples/support-billing-agent/app/effect-adapter.mjs +23 -0
- package/examples/support-billing-agent/app/record-shadow-outcomes.mjs +44 -0
- package/examples/support-billing-agent/db/schema.sql +88 -1
- package/examples/support-billing-agent/db/seed.sql +15 -15
- package/examples/support-billing-agent/scripts/run-demo.sh +4 -0
- package/examples/support-billing-agent/scripts/run-evaluation.sh +63 -0
- package/examples/support-billing-agent/shadow-study/cases.jsonl +6 -0
- package/examples/support-billing-agent/shadow-study/outcomes.jsonl +2 -0
- package/examples/support-billing-agent/synapsor.runner.json +17 -5
- package/examples/support-plan-credit/README.md +48 -2
- package/examples/support-plan-credit/mcp-client-examples/README.md +23 -0
- package/examples/support-plan-credit/mcp-client-examples/claude-code.sh +34 -0
- package/examples/support-plan-credit/mcp-client-examples/codex.config.toml +24 -0
- package/examples/support-plan-credit/mcp-client-examples/generic-stdio.mjs +35 -0
- package/examples/support-plan-credit/mcp-client-examples/generic-streamable-http.mjs +31 -0
- package/examples/support-plan-credit/mcp-client-examples/google-adk.py +40 -0
- package/examples/support-plan-credit/mcp-client-examples/langchain.mjs +29 -0
- package/examples/support-plan-credit/mcp-client-examples/llamaindex.py +36 -0
- package/examples/support-plan-credit/mcp-client-examples/openai-agents-stdio.ts +1 -1
- package/examples/support-plan-credit/mcp-client-examples/openai-agents-streamable-http.ts +1 -1
- package/examples/support-plan-credit/mcp-client-examples/vscode.mcp.json +20 -0
- package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.contract-tests.generated.json +221 -0
- package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.ts +34 -0
- package/fixtures/effects/changed/eff_support_late_fee_v1.result.json +58 -0
- package/fixtures/effects/dataset.json +8 -0
- package/fixtures/effects/results/eff_support_late_fee_v1.result.json +59 -0
- package/fixtures/effects/support-late-fee.effect.json +71 -0
- package/fixtures/generators/drizzle/malicious.ts +11 -0
- package/fixtures/generators/drizzle/schema.ts +12 -0
- package/fixtures/generators/openapi/external-ref.yaml +15 -0
- package/fixtures/generators/openapi/openapi.yaml +81 -0
- package/fixtures/generators/prisma/schema.prisma +34 -0
- package/fixtures/mcp-audit/README.md +14 -0
- package/fixtures/mcp-audit/cursor-bypass-config.json +38 -0
- package/fixtures/mcp-audit/dangerous-tools-list.json +33 -0
- package/fixtures/mcp-audit/reviewed-proposal-tools-list.json +60 -0
- package/package.json +29 -3
- package/schemas/effect-dataset.schema.json +19 -0
- package/schemas/effect-fixture.schema.json +191 -0
- package/schemas/effect-result.schema.json +52 -0
- package/schemas/mcp-audit-candidates.schema.json +41 -0
- package/schemas/mcp-audit-report.schema.json +203 -0
- package/schemas/schema-candidate-review.schema.json +167 -0
- package/schemas/schema-candidates.schema.json +49 -0
- package/schemas/synapsor.contract-tests.schema.json +1 -1
- package/schemas/synapsor.runner.schema.json +51 -0
package/docs/release-policy.md
CHANGED
|
@@ -11,8 +11,12 @@ npx -y -p @synapsor/runner synapsor-runner demo --quick
|
|
|
11
11
|
npm install -g @synapsor/runner
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Use
|
|
15
|
-
preview
|
|
14
|
+
Use an exact prerelease or a named preview tag only when intentionally testing
|
|
15
|
+
preview behavior. Check the tags that currently exist before documenting one:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm dist-tag ls @synapsor/runner
|
|
19
|
+
```
|
|
16
20
|
|
|
17
21
|
## Alpha Expectations
|
|
18
22
|
|
|
@@ -128,9 +132,9 @@ internals. Local store migrations may happen inside `1.x` and later minor
|
|
|
128
132
|
versions, but documented CLI inspection commands should remain the supported way
|
|
129
133
|
to read the store.
|
|
130
134
|
|
|
131
|
-
|
|
132
|
-
client snippets.
|
|
133
|
-
|
|
135
|
+
Prerelease users should pin an exact version in package.json, CI, and MCP
|
|
136
|
+
client snippets. Do not assume an `alpha`, `beta`, or `next` tag exists; inspect
|
|
137
|
+
the registry tags first.
|
|
134
138
|
|
|
135
139
|
## Result Envelope Migration
|
|
136
140
|
|
|
@@ -114,6 +114,14 @@ not print database URLs or create the schema.
|
|
|
114
114
|
"write_url_env": "BILLING_POSTGRES_WRITE_URL",
|
|
115
115
|
"read_only": false,
|
|
116
116
|
"statement_timeout_ms": 3000,
|
|
117
|
+
"database_scope": {
|
|
118
|
+
"mode": "postgres_rls",
|
|
119
|
+
"tenant_setting": "synapsor.tenant_id",
|
|
120
|
+
"principal_setting": "synapsor.principal"
|
|
121
|
+
},
|
|
122
|
+
"credential_scope": {
|
|
123
|
+
"mode": "shared"
|
|
124
|
+
},
|
|
117
125
|
"receipts": {
|
|
118
126
|
"authority": "source_db",
|
|
119
127
|
"provisioning": "precreated",
|
|
@@ -138,6 +146,23 @@ adapter-specific reviewed SSL options when used.
|
|
|
138
146
|
|
|
139
147
|
A contract's `SOURCE billing_postgres` must exactly match a `sources` key.
|
|
140
148
|
|
|
149
|
+
`database_scope.mode` defaults to `application`: Runner enforces trusted
|
|
150
|
+
tenant/principal predicates with the configured database credential.
|
|
151
|
+
`postgres_rls` is PostgreSQL-only and requires fixed, distinct
|
|
152
|
+
`tenant_setting` and `principal_setting` names. Runner binds them
|
|
153
|
+
transaction-locally and refuses serving/writeback when RLS, FORCE, role, or
|
|
154
|
+
operation-policy prerequisites cannot be attested.
|
|
155
|
+
|
|
156
|
+
`credential_scope.mode` defaults to `shared`. `tenant_resolver` names a
|
|
157
|
+
programmatic `TenantCredentialResolver`; credentials remain outside the
|
|
158
|
+
contract/config and pools are partitioned by trusted scope and credential
|
|
159
|
+
identity. The stock CLI does not load executable resolver modules, so CLI-only
|
|
160
|
+
deployments should run one tenant-bound process/credential per tenant.
|
|
161
|
+
|
|
162
|
+
See [Database-Enforced Tenant And Principal
|
|
163
|
+
Scope](database-enforced-scope.md) for the guarantee matrix, PostgreSQL policy
|
|
164
|
+
example, resolver interface, doctor canary, and MySQL alternatives.
|
|
165
|
+
|
|
141
166
|
Long-running servers reuse native driver pools. Optional `pool` keys are
|
|
142
167
|
`max_connections` (default 10), `connection_timeout_ms` (3000),
|
|
143
168
|
`idle_timeout_ms` (30000), `queue_timeout_ms` (5000), and `queue_limit`
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Reviewed Candidates From Prisma, Drizzle, And OpenAPI
|
|
2
|
+
|
|
3
|
+
Use these generators after schema or API inspection when you want a review
|
|
4
|
+
starting point without granting authority:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
synapsor-runner init from-prisma ./prisma/schema.prisma \
|
|
8
|
+
--output ./synapsor-prisma-candidates
|
|
9
|
+
|
|
10
|
+
synapsor-runner init from-drizzle ./src/schema.ts \
|
|
11
|
+
--output ./synapsor-drizzle-candidates
|
|
12
|
+
|
|
13
|
+
synapsor-runner init from-openapi ./openapi.yaml \
|
|
14
|
+
--output ./synapsor-openapi-candidates
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Each command writes a separate deterministic directory containing:
|
|
18
|
+
|
|
19
|
+
- `synapsor.candidate.contract.json`: canonical `@synapsor/spec` candidates;
|
|
20
|
+
- `synapsor.candidate.runner.json`: source-less strict Shadow configuration;
|
|
21
|
+
- `synapsor.candidate.contract-tests.json`: deny, redaction, and operator
|
|
22
|
+
boundary test candidates;
|
|
23
|
+
- `generation-review.json`: stable machine-readable review findings;
|
|
24
|
+
- `REVIEW.md`: the human review checklist;
|
|
25
|
+
- `.synapsor-schema-candidates.json`: Runner's ownership marker.
|
|
26
|
+
|
|
27
|
+
No candidate is active. Proposal writeback is `none`, no database source is
|
|
28
|
+
configured, and tenant/principal authority remains a visible placeholder.
|
|
29
|
+
Generation never edits the input or an active Runner configuration.
|
|
30
|
+
|
|
31
|
+
## What Is And Is Not Inferred
|
|
32
|
+
|
|
33
|
+
The generators can suggest structural facts and likely review targets:
|
|
34
|
+
|
|
35
|
+
- objects, fields, table mappings, primary keys, and version-like fields;
|
|
36
|
+
- potential tenant/principal fields based on names;
|
|
37
|
+
- potentially sensitive and kept-out fields;
|
|
38
|
+
- possible read and proposal capability names;
|
|
39
|
+
- operations that need business logic or an app-owned handler.
|
|
40
|
+
|
|
41
|
+
They do not decide which tenant or principal is authoritative, which fields are
|
|
42
|
+
safe to expose, which writes are valid, business bounds, approval policy, or
|
|
43
|
+
auto-approval. A field-name heuristic is not data classification.
|
|
44
|
+
|
|
45
|
+
Review `generation-review.json` and `REVIEW.md`, replace every
|
|
46
|
+
`review_required_*` placeholder, run the generated tests and a
|
|
47
|
+
[Shadow study](shadow-studies.md), then deliberately copy reviewed definitions
|
|
48
|
+
into an active contract through code review.
|
|
49
|
+
|
|
50
|
+
## Input Safety
|
|
51
|
+
|
|
52
|
+
Input is bounded to 2 MiB, 50 objects, 128 fields per object, and 200 generated
|
|
53
|
+
capabilities. Deep or oversized structures fail closed.
|
|
54
|
+
|
|
55
|
+
- Prisma uses Runner's bounded lexer/parser. Datasource URLs, defaults, enum
|
|
56
|
+
values, generators, and plugins are not copied or run.
|
|
57
|
+
- Drizzle is untrusted executable TypeScript. Runner parses a documented static
|
|
58
|
+
`pgTable`/`mysqlTable` object-literal subset with the TypeScript AST. It never
|
|
59
|
+
imports, transpiles, type-checks, executes, or resolves the application.
|
|
60
|
+
Dynamic table names fail; unsupported dynamic columns are called out for
|
|
61
|
+
review.
|
|
62
|
+
- OpenAPI 3 JSON/YAML is parsed locally. Network/external references are
|
|
63
|
+
rejected. Server URLs, examples, defaults, enum values, callbacks, webhooks,
|
|
64
|
+
and credentials are not copied.
|
|
65
|
+
|
|
66
|
+
Existing output is never replaced unless `--force` points to a directory with
|
|
67
|
+
Runner's matching ownership marker. Hand-edited directories are refused even
|
|
68
|
+
with `--force`.
|
|
@@ -36,6 +36,7 @@ The model does not receive:
|
|
|
36
36
|
- a generic `execute_sql` tool;
|
|
37
37
|
- arbitrary table, schema, or column names;
|
|
38
38
|
- database URLs or credentials;
|
|
39
|
+
- contract activation tools;
|
|
39
40
|
- approval tools;
|
|
40
41
|
- commit/writeback tools;
|
|
41
42
|
- trusted tenant or principal authority as ordinary model arguments.
|
|
@@ -50,6 +51,16 @@ Trusted context comes from local configuration, environment bindings, or Cloud
|
|
|
50
51
|
session context in Cloud mode. Tenant, principal, and authorization scope must
|
|
51
52
|
not be accepted from the model as authority.
|
|
52
53
|
|
|
54
|
+
With a shared credential, these checks are application-level enforcement and
|
|
55
|
+
depend on Runner's fixed predicate implementation. Optional PostgreSQL RLS mode
|
|
56
|
+
adds an independent database check for omitted predicates and pooled-context
|
|
57
|
+
leakage. Tenant-bound credentials or isolated deployments provide the stronger
|
|
58
|
+
boundary when a Runner process must not hold organization-wide authority.
|
|
59
|
+
PostgreSQL RLS does not protect against a fully compromised process that can
|
|
60
|
+
choose arbitrary trusted settings, and MySQL has no native RLS equivalent. See
|
|
61
|
+
[Database-Enforced Tenant And Principal
|
|
62
|
+
Scope](database-enforced-scope.md).
|
|
63
|
+
|
|
53
64
|
A capability may declare `principal_scope_key` (DSL: `PRINCIPAL SCOPE KEY`) to
|
|
54
65
|
narrow rows inside a tenant. Runner then adds both fixed, parameterized guards:
|
|
55
66
|
|
|
@@ -74,6 +85,18 @@ explicit host-level administrative boundary.
|
|
|
74
85
|
Proposal tools read the current row through the read credential, store evidence
|
|
75
86
|
and an exact before/after diff, and leave the source database unchanged.
|
|
76
87
|
|
|
88
|
+
Code-first Safe Actions do not create authority when a file is edited. Runner
|
|
89
|
+
parses the restricted TypeScript object without importing or executing it,
|
|
90
|
+
compiles it into a digest-addressed disabled canonical draft, and keeps the
|
|
91
|
+
active contract pointer unchanged. Activation is available only in the
|
|
92
|
+
session-token/CSRF-protected localhost Workbench after a matching real staging
|
|
93
|
+
proposal proves `source_database_changed:false`; the operator must confirm the
|
|
94
|
+
complete digest. A proposal remains pinned to the active contract digest it was
|
|
95
|
+
created under, so later activation cannot reinterpret an old approval. Cloud-
|
|
96
|
+
linked projects route activation through Cloud governance instead of the local
|
|
97
|
+
control. No model-facing MCP tool can validate-and-activate, approve, apply, or
|
|
98
|
+
revert.
|
|
99
|
+
|
|
77
100
|
The local proposal store rejects obvious credential material before persistence:
|
|
78
101
|
database URLs, bearer tokens, Synapsor runner tokens, private-key blocks, and
|
|
79
102
|
secret-like fields such as password, token, API key, private key, cookie,
|
|
@@ -135,11 +158,13 @@ database identifiers such as `../private`, `id/../../tenant_id`, or
|
|
|
135
158
|
`status; DROP TABLE tickets` before adapter execution. Local CLI file paths
|
|
136
159
|
remain explicit user-provided paths; they are not model-facing authority.
|
|
137
160
|
|
|
138
|
-
Local review can happen through the CLI or `synapsor-runner ui`. The UI is a
|
|
139
|
-
review surface with a per-run session token and CSRF protection for
|
|
140
|
-
approve/reject
|
|
141
|
-
|
|
142
|
-
|
|
161
|
+
Local review can happen through the CLI or `synapsor-runner ui`. The UI is a
|
|
162
|
+
localhost review surface with a per-run session token and CSRF protection for
|
|
163
|
+
approve/reject and Safe Action draft controls. Activation additionally requires
|
|
164
|
+
a matching source-unchanged preview plus the complete digest. These operator
|
|
165
|
+
controls are not MCP tools. The UI does not expose raw SQL, database URLs, or
|
|
166
|
+
write credentials, and it does not let a model widen reviewed tables, columns,
|
|
167
|
+
scope, policy, or executor authority.
|
|
143
168
|
|
|
144
169
|
Contract lint and tests are review aids rather than a proof of complete
|
|
145
170
|
security. Capability breadth can still drift as narrow tools accumulate;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Shadow Studies
|
|
2
|
+
|
|
3
|
+
Use a shadow study before granting an agent write authority. Runner serves the
|
|
4
|
+
reviewed proposal capability and records what the agent would have proposed,
|
|
5
|
+
but the proposal cannot be approved, queued, or applied.
|
|
6
|
+
|
|
7
|
+
A study compares two explicit records:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
agent result or proposed effect
|
|
11
|
+
versus
|
|
12
|
+
authoritative outcome recorded by your application or operator
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Runner does not infer a human rejection from an unchanged database row. A case
|
|
16
|
+
without an authoritative outcome remains visibly unmatched.
|
|
17
|
+
|
|
18
|
+
## Safety Boundary
|
|
19
|
+
|
|
20
|
+
Set the Runner configuration to:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"version": 1,
|
|
25
|
+
"mode": "shadow"
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The proposal store enforces the shadow boundary below the CLI and local UI:
|
|
30
|
+
|
|
31
|
+
- a shadow proposal cannot be approved;
|
|
32
|
+
- it cannot become a writeback job or worker item;
|
|
33
|
+
- it cannot invoke direct database writeback or an app-owned handler;
|
|
34
|
+
- no flag bypasses these checks;
|
|
35
|
+
- a report suggestion is inactive and never edits or activates a contract.
|
|
36
|
+
|
|
37
|
+
Shadow data remains in the configured local Runner store. Case and outcome
|
|
38
|
+
imports reject obvious credentials, bearer tokens, database URLs, and
|
|
39
|
+
secret-like fields.
|
|
40
|
+
|
|
41
|
+
## Start A Study
|
|
42
|
+
|
|
43
|
+
Create a study before serving the shadow capability. An empty capability list
|
|
44
|
+
means all shadow capabilities; repeat `--capability` or use comma-separated
|
|
45
|
+
names to narrow it.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
synapsor-runner shadow study create \
|
|
49
|
+
--id sst_support_pilot \
|
|
50
|
+
--name "Support waiver pilot" \
|
|
51
|
+
--capability billing.propose_late_fee_waiver \
|
|
52
|
+
--store ./.synapsor/local.db
|
|
53
|
+
|
|
54
|
+
synapsor-runner mcp serve \
|
|
55
|
+
--config ./synapsor.runner.json \
|
|
56
|
+
--store ./.synapsor/local.db
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
New matching shadow proposals are attached automatically. To attach proposals
|
|
60
|
+
that already exist, or to repair correlation after an interrupted process:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
synapsor-runner shadow study sync sst_support_pilot \
|
|
64
|
+
--store ./.synapsor/local.db
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Record Non-Proposal Results
|
|
68
|
+
|
|
69
|
+
A study also needs tasks where the agent was unable or not allowed to propose.
|
|
70
|
+
Import one JSON object with `case record`, or a JSON array/JSONL file with
|
|
71
|
+
`case import`.
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"request_id": "req-policy-42",
|
|
76
|
+
"tenant_id": "acme",
|
|
77
|
+
"principal": "support-agent-shadow",
|
|
78
|
+
"capability": "billing.propose_late_fee_waiver",
|
|
79
|
+
"business_object": "invoice",
|
|
80
|
+
"object_id": "INV-3042",
|
|
81
|
+
"agent_result": "policy_denied",
|
|
82
|
+
"decision_reason": "amount above reviewed bound",
|
|
83
|
+
"risk_score": 35
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Allowed `agent_result` values are:
|
|
88
|
+
|
|
89
|
+
- `proposed`
|
|
90
|
+
- `policy_denied`
|
|
91
|
+
- `unable_to_propose`
|
|
92
|
+
- `stale_conflict`
|
|
93
|
+
- `invalid_unsafe_scope_attempt`
|
|
94
|
+
|
|
95
|
+
For `proposed`, include normalized `before`, `after`, and `patch` objects under
|
|
96
|
+
`proposed_effect`.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
synapsor-runner shadow case import \
|
|
100
|
+
--study sst_support_pilot \
|
|
101
|
+
--input ./cases.jsonl \
|
|
102
|
+
--store ./.synapsor/local.db
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Imports are limited to 2 MiB and 10,000 records per command.
|
|
106
|
+
|
|
107
|
+
## Record Authoritative Outcomes
|
|
108
|
+
|
|
109
|
+
Export actual outcomes from your application audit log or have a trusted
|
|
110
|
+
operator create them. Each outcome must match an existing case by study,
|
|
111
|
+
request, trusted tenant, business object, and object ID. A supplied proposal ID
|
|
112
|
+
must match too.
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"request_id": "request_123",
|
|
117
|
+
"proposal_id": "wrp_123",
|
|
118
|
+
"tenant_id": "acme",
|
|
119
|
+
"business_object": "invoice",
|
|
120
|
+
"object_id": "INV-3001",
|
|
121
|
+
"actor": "support_lead_1",
|
|
122
|
+
"disposition": "applied",
|
|
123
|
+
"actual_effect": {
|
|
124
|
+
"before": { "late_fee_cents": 5500 },
|
|
125
|
+
"after": { "late_fee_cents": 0 },
|
|
126
|
+
"patch": { "late_fee_cents": 0 }
|
|
127
|
+
},
|
|
128
|
+
"occurred_at": "2026-07-19T12:00:00.000Z",
|
|
129
|
+
"source": "support-system-audit",
|
|
130
|
+
"reference": "ticket:SUP-184",
|
|
131
|
+
"reason": "customer qualified"
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Allowed dispositions are `applied`, `rejected_no_action`, and
|
|
136
|
+
`stale_conflict`. An applied outcome requires an exact normalized effect.
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
synapsor-runner shadow outcome import \
|
|
140
|
+
--study sst_support_pilot \
|
|
141
|
+
--input ./outcomes.jsonl \
|
|
142
|
+
--store ./.synapsor/local.db
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
For the normal application path, record the trusted outcome directly instead
|
|
146
|
+
of assembling JSONL. `@synapsor/runner/shadow` writes through the same scope,
|
|
147
|
+
effect-shape, and secret checks as the import command:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
import { createShadowOutcomeRecorder } from "@synapsor/runner/shadow";
|
|
151
|
+
|
|
152
|
+
const recorder = createShadowOutcomeRecorder({
|
|
153
|
+
storePath: "./.synapsor/local.db",
|
|
154
|
+
studyId: "sst_support_pilot",
|
|
155
|
+
actor: "support_application",
|
|
156
|
+
source: "support-audit-log",
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
recorder.record({
|
|
161
|
+
requestId: "request_123",
|
|
162
|
+
tenantId: "acme",
|
|
163
|
+
businessObject: "invoice",
|
|
164
|
+
objectId: "INV-3001",
|
|
165
|
+
disposition: "applied",
|
|
166
|
+
actualEffect: {
|
|
167
|
+
before: { late_fee_cents: 5500 },
|
|
168
|
+
after: { late_fee_cents: 0 },
|
|
169
|
+
patch: { late_fee_cents: 0 },
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
} finally {
|
|
173
|
+
recorder.close();
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The application owns the truth of this outcome. The helper does not read or
|
|
178
|
+
mutate the source database and cannot approve a shadow proposal.
|
|
179
|
+
|
|
180
|
+
## Review The Report
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
synapsor-runner shadow report \
|
|
184
|
+
--study sst_support_pilot \
|
|
185
|
+
--output ./shadow-report.json \
|
|
186
|
+
--store ./.synapsor/local.db
|
|
187
|
+
|
|
188
|
+
synapsor-runner ui \
|
|
189
|
+
--config ./synapsor.runner.json \
|
|
190
|
+
--store ./.synapsor/local.db
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The stable JSON report preserves total and comparable denominators and
|
|
194
|
+
classifies exact, partial, disagreement, human rejection/no action, policy
|
|
195
|
+
denial, unable-to-propose, stale/conflict, unmatched, and unsafe-scope cases.
|
|
196
|
+
It also includes amount distributions, capability/reason breakdowns, and
|
|
197
|
+
risk-ranked disagreements.
|
|
198
|
+
|
|
199
|
+
The report also shows the explicit trust progression:
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
Observe -> Compare -> Manual review -> Suggested bounded policy
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
It reports an insufficient sample size until the minimum comparable evidence
|
|
206
|
+
exists. A suggested policy remains an inactive review artifact; the report
|
|
207
|
+
never widens or activates the contract.
|
|
208
|
+
|
|
209
|
+
Close a completed study:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
synapsor-runner shadow study close sst_support_pilot \
|
|
213
|
+
--store ./.synapsor/local.db
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Deterministic Reference Data
|
|
217
|
+
|
|
218
|
+
The packaged `examples/support-billing-agent/shadow-study/` directory contains
|
|
219
|
+
six synthetic cases and two authoritative outcomes. It covers an exact $55
|
|
220
|
+
waiver, human rejection, policy denial, stale state, tenant forgery, and an
|
|
221
|
+
unmatched task.
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
synapsor-runner shadow study create \
|
|
225
|
+
--id sst_support_reference \
|
|
226
|
+
--name "Support reference study" \
|
|
227
|
+
--capability billing.propose_late_fee_waiver \
|
|
228
|
+
--store ./.synapsor/shadow-reference.db
|
|
229
|
+
|
|
230
|
+
synapsor-runner shadow case import \
|
|
231
|
+
--study sst_support_reference \
|
|
232
|
+
--input ./examples/support-billing-agent/shadow-study/cases.jsonl \
|
|
233
|
+
--store ./.synapsor/shadow-reference.db
|
|
234
|
+
|
|
235
|
+
node ./examples/support-billing-agent/app/record-shadow-outcomes.mjs \
|
|
236
|
+
./.synapsor/shadow-reference.db sst_support_reference
|
|
237
|
+
|
|
238
|
+
synapsor-runner shadow report \
|
|
239
|
+
--study sst_support_reference \
|
|
240
|
+
--store ./.synapsor/shadow-reference.db
|
|
241
|
+
```
|
|
@@ -12,6 +12,52 @@ Use JSON for automation:
|
|
|
12
12
|
npx -y -p @synapsor/runner synapsor-runner doctor --first-run --json
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Safe Action Draft Does Not Appear As A Tool
|
|
16
|
+
|
|
17
|
+
This is expected before activation. `start --action`, agent edits, `action
|
|
18
|
+
validate`, and `action watch` can create or refresh only a disabled draft. They
|
|
19
|
+
must not alter the active model-facing tools.
|
|
20
|
+
|
|
21
|
+
Check status without exposing credentials:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
synapsor-runner action status --json
|
|
25
|
+
synapsor-runner tools preview \
|
|
26
|
+
--config ./synapsor.runner.json \
|
|
27
|
+
--store ./.synapsor/local.db
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Open the secured Workbench, run the real source-unchanged staging Data PR
|
|
31
|
+
preview, review the complete digest, and activate it there. There is
|
|
32
|
+
intentionally no activation CLI command to hand to a coding agent.
|
|
33
|
+
|
|
34
|
+
## Activated Tool Does Not Appear In Cursor
|
|
35
|
+
|
|
36
|
+
Some MCP hosts do not refresh `tools/list` for a running stdio session. First
|
|
37
|
+
confirm that Runner's active tool surface changed:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
synapsor-runner action status --json
|
|
41
|
+
synapsor-runner mcp status cursor --project --check-launch
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then reconnect or restart the project MCP server as directed by the Workbench.
|
|
45
|
+
Do not work around a stale host session by adding approval, apply, activation,
|
|
46
|
+
credentials, tenant values, or raw SQL to the Cursor configuration.
|
|
47
|
+
|
|
48
|
+
## Safe Action Validation Reports Review Placeholders
|
|
49
|
+
|
|
50
|
+
The composer fails closed while any `__REVIEW_*__` authority placeholder or
|
|
51
|
+
dynamic TypeScript expression remains. Review the reported field and source,
|
|
52
|
+
then make the authority explicit in the restricted `defineCapability({...})`
|
|
53
|
+
object. Runner will not infer trusted tenant/principal bindings, hidden fields,
|
|
54
|
+
write columns, bounds, conflict guards, approval, or executor authority from
|
|
55
|
+
application code.
|
|
56
|
+
|
|
57
|
+
Use the generated explanation and test manifest after validation. Do not edit
|
|
58
|
+
digest-addressed files under `.synapsor/drafts/` or `.synapsor/active/`; edit
|
|
59
|
+
the TypeScript source and validate again.
|
|
60
|
+
|
|
15
61
|
## Smoke Proposal Missing From Another Runner
|
|
16
62
|
|
|
17
63
|
What happened:
|
|
@@ -119,5 +119,13 @@ function writeJson(response, statusCode, body) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
async function loadHandlerHelper() {
|
|
122
|
-
|
|
122
|
+
const bundledHelper = new URL("./synapsor-handler.mjs", import.meta.url);
|
|
123
|
+
try {
|
|
124
|
+
return await import(bundledHelper);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (error?.code !== "ERR_MODULE_NOT_FOUND" || error?.url !== bundledHelper.href) {
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
return await import(new URL("../../packages/handler/dist/index.js", import.meta.url));
|
|
130
|
+
}
|
|
123
131
|
}
|
|
@@ -50,6 +50,16 @@ CREATE TABLE IF NOT EXISTS public.synapsor_writeback_receipts (
|
|
|
50
50
|
completed_at timestamptz
|
|
51
51
|
);
|
|
52
52
|
|
|
53
|
+
CREATE TABLE IF NOT EXISTS public.synapsor_handler_receipts (
|
|
54
|
+
idempotency_key text PRIMARY KEY,
|
|
55
|
+
proposal_id text NOT NULL,
|
|
56
|
+
action text NOT NULL,
|
|
57
|
+
status text NOT NULL,
|
|
58
|
+
receipt_json jsonb NOT NULL,
|
|
59
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
60
|
+
completed_at timestamptz
|
|
61
|
+
);
|
|
62
|
+
|
|
53
63
|
DO $$
|
|
54
64
|
BEGIN
|
|
55
65
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'synapsor_reader') THEN
|
|
@@ -65,5 +75,6 @@ GRANT CONNECT ON DATABASE synapsor_billing_app_handler TO synapsor_reader, synap
|
|
|
65
75
|
GRANT USAGE ON SCHEMA public TO synapsor_reader, synapsor_writer;
|
|
66
76
|
GRANT SELECT ON public.tenants, public.customers, public.invoices, public.account_credits TO synapsor_reader, synapsor_writer;
|
|
67
77
|
GRANT SELECT, INSERT, UPDATE ON public.synapsor_writeback_receipts TO synapsor_writer;
|
|
78
|
+
GRANT SELECT, INSERT, UPDATE ON public.synapsor_handler_receipts TO synapsor_writer;
|
|
68
79
|
GRANT UPDATE (late_fee_cents, waiver_reason, credit_requested_cents, credit_reason, credited_cents, updated_at) ON public.invoices TO synapsor_writer;
|
|
69
80
|
GRANT INSERT ON public.account_credits TO synapsor_writer;
|
|
@@ -168,6 +168,9 @@ class PostgresWritebackHandlerTransaction {
|
|
|
168
168
|
this.receiptTable = receiptTable;
|
|
169
169
|
}
|
|
170
170
|
async ensureReceiptTable() {
|
|
171
|
+
const existing = await this.client.query("SELECT to_regclass($1) AS receipt_table", [this.receiptTableName()]);
|
|
172
|
+
if (existing.rows[0]?.receipt_table)
|
|
173
|
+
return;
|
|
171
174
|
await this.client.query(`
|
|
172
175
|
CREATE TABLE IF NOT EXISTS ${this.receiptTableName()} (
|
|
173
176
|
idempotency_key text PRIMARY KEY,
|
|
@@ -4,8 +4,8 @@ demo:
|
|
|
4
4
|
@printf '%s\n' "Unsafe shortcut:"
|
|
5
5
|
@printf '%s\n' ' agent -> execute_sql("UPDATE invoices SET late_fee_cents = 0 ...")'
|
|
6
6
|
@printf '%s\n' ""
|
|
7
|
-
@printf '%s\n' "Synapsor
|
|
8
|
-
npx -y @synapsor/runner
|
|
7
|
+
@printf '%s\n' "Synapsor guarded-action proof:"
|
|
8
|
+
npx -y @synapsor/runner try --prove --yes --no-open
|
|
9
9
|
@printf '%s\n' ""
|
|
10
10
|
@printf '%s\n' "Risk audit example:"
|
|
11
11
|
npx -y @synapsor/runner audit --example dangerous-db-mcp --format markdown
|
|
@@ -13,10 +13,12 @@ Expected output includes:
|
|
|
13
13
|
|
|
14
14
|
```text
|
|
15
15
|
execute_sql
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
late_fee_cents: 5500 -> 0
|
|
17
|
+
Source changed:
|
|
18
|
+
No
|
|
19
|
+
Guarded commit complete.
|
|
20
|
+
restart-safe retry: yes
|
|
21
|
+
stale apply refused: yes
|
|
20
22
|
```
|
|
21
23
|
|
|
22
24
|
The raw-SQL shortcut is:
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
.PHONY: demo unsafe clean
|
|
1
|
+
.PHONY: demo unsafe evaluate clean
|
|
2
2
|
|
|
3
3
|
demo:
|
|
4
4
|
cd ../.. && examples/support-billing-agent/scripts/run-demo.sh
|
|
5
|
+
cd ../.. && examples/support-billing-agent/scripts/run-evaluation.sh
|
|
6
|
+
|
|
7
|
+
evaluate:
|
|
8
|
+
cd ../.. && examples/support-billing-agent/scripts/run-evaluation.sh
|
|
5
9
|
|
|
6
10
|
unsafe:
|
|
7
11
|
@printf '%s\n' "Unsafe raw-SQL shortcut this demo avoids:"
|