@synapsor/runner 1.5.4 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -2
- package/README.md +131 -124
- package/dist/authoring.mjs +405 -9
- package/dist/cli.d.ts.map +1 -1
- package/dist/local-ui.d.ts +6 -1
- package/dist/local-ui.d.ts.map +1 -1
- package/dist/runner.mjs +17206 -12169
- package/dist/runtime.mjs +11249 -8542
- package/dist/shadow.mjs +408 -15
- package/docs/README.md +14 -6
- package/docs/aggregate-reads.md +22 -0
- package/docs/app-owned-executors.md +8 -0
- package/docs/auto-boundary-and-scoped-explore.md +338 -0
- package/docs/capability-authoring.md +75 -0
- package/docs/conformance.md +16 -0
- package/docs/current-scope.md +60 -28
- package/docs/cursor-plugin.md +20 -3
- package/docs/dsl-reference.md +78 -0
- package/docs/getting-started-own-database.md +39 -35
- package/docs/limitations.md +37 -7
- package/docs/local-mode.md +22 -3
- package/docs/production.md +19 -0
- package/docs/proposal-evidence-freshness.md +334 -0
- package/docs/release-notes.md +63 -4
- package/docs/runner-config-reference.md +46 -0
- package/docs/schema-api-candidates.md +28 -1
- package/docs/security-boundary.md +17 -0
- package/docs/store-lifecycle.md +8 -0
- package/docs/troubleshooting-first-run.md +174 -0
- package/docs/writeback-executors.md +8 -0
- package/examples/app-owned-writeback/command-handler.mjs +0 -0
- package/examples/auto-boundary-churn/README.md +23 -0
- package/examples/auto-boundary-churn/app/page.tsx +8 -0
- package/examples/auto-boundary-churn/docker-compose.yml +16 -0
- package/examples/auto-boundary-churn/package.json +18 -0
- package/examples/auto-boundary-churn/prisma/schema.prisma +36 -0
- package/examples/auto-boundary-churn/seed/postgres.sql +126 -0
- package/examples/mcp-postgres-billing-app-handler/scripts/run-demo.sh +0 -0
- package/examples/reference-support-billing-app/scripts/run-demo.sh +0 -0
- package/examples/support-billing-agent/scripts/run-demo.sh +0 -0
- package/examples/support-billing-agent/scripts/run-evaluation.sh +0 -0
- package/fixtures/compatibility/published-1.5.4/manifest.json +76 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/dsl/examples/aggregate-read.synapsor.sql +21 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/dsl/examples/billing-late-fee.synapsor.sql +56 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/dsl/examples/bounded-set-multi-term.synapsor.sql +30 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/dsl/examples/principal-row-scope.synapsor.sql +23 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/aggregate-read/contract.json +119 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/approval-quorum/contract.json +44 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/bounded-set-threats/contract.json +115 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/principal-row-scope/contract.json +78 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/proposal-capability/contract.json +101 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/reversible-change-sets/contract.json +98 -0
- package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/valid/basic-read.contract.json +60 -0
- package/fixtures/compatibility/published-1.6.0/manifest.json +76 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/aggregate-read.synapsor.sql +21 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/billing-late-fee.synapsor.sql +56 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/bounded-set-multi-term.synapsor.sql +30 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/principal-row-scope.synapsor.sql +23 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/aggregate-read/contract.json +119 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/approval-quorum/contract.json +44 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/bounded-set-threats/contract.json +115 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/principal-row-scope/contract.json +78 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/proposal-capability/contract.json +101 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/reversible-change-sets/contract.json +98 -0
- package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/valid/basic-read.contract.json +60 -0
- package/fixtures/protocol/MANIFEST.json +37 -7
- package/fixtures/protocol/change-set.freshness-update.v2.json +58 -0
- package/fixtures/protocol/freshness-authority.invoice.v1.json +35 -0
- package/fixtures/protocol/freshness-proof.fresh.v1.json +38 -0
- package/fixtures/protocol/writeback-job.freshness-update.v2.json +50 -0
- package/llms.txt +38 -0
- package/package.json +12 -8
- package/schemas/change-set.v1.schema.json +1 -0
- package/schemas/change-set.v2.schema.json +1 -0
- package/schemas/change-set.v3.schema.json +1 -0
- package/schemas/freshness-authority.v1.schema.json +89 -0
- package/schemas/freshness-proof.v1.schema.json +73 -0
- package/schemas/synapsor.runner.schema.json +50 -1
- package/schemas/writeback-job.v1.schema.json +1 -0
- package/schemas/writeback-job.v2.schema.json +1 -0
- package/schemas/writeback-job.v3.schema.json +1 -0
|
@@ -12,6 +12,104 @@ Use JSON for automation:
|
|
|
12
12
|
npx -y -p @synapsor/runner synapsor-runner doctor --first-run --json
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Fresh Start Did Not Enter Auto Boundary
|
|
16
|
+
|
|
17
|
+
Auto Boundary is the default only for a fresh interactive `start --from-env`
|
|
18
|
+
with no existing config, selector, answers file, machine-output flag, or other
|
|
19
|
+
automation input. This preserves every established 1.x route.
|
|
20
|
+
|
|
21
|
+
Check the generated state:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
synapsor-runner boundary status --json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
To draft explicitly without prompts or a browser:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
synapsor-runner boundary draft \
|
|
31
|
+
--from-env DATABASE_URL \
|
|
32
|
+
--schema public \
|
|
33
|
+
--project-root . \
|
|
34
|
+
--json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Scoped Explore Is Not Advertised
|
|
38
|
+
|
|
39
|
+
This is correct unless all authoring prerequisites pass. Explore is disabled by
|
|
40
|
+
default and never appears in production, unknown-profile, shared HTTP, remote,
|
|
41
|
+
or non-loopback `tools/list`.
|
|
42
|
+
|
|
43
|
+
Check:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
synapsor-runner boundary status --json
|
|
47
|
+
synapsor-runner boundary diff --json
|
|
48
|
+
synapsor-runner mcp status cursor --project
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The exact boundary digest must be active, the profile must explicitly be
|
|
52
|
+
`development` or `staging`, the generation lock and compiler/Spec versions
|
|
53
|
+
must be current, and the inspected database role must still be SELECT-only,
|
|
54
|
+
non-owner, non-superuser, and not `BYPASSRLS`. Runner also enforces a read-only
|
|
55
|
+
transaction for every Explore call.
|
|
56
|
+
|
|
57
|
+
A write-capable, owner, superuser, `BYPASSRLS`, or unverifiable credential may
|
|
58
|
+
still inspect metadata with a warning. It cannot enable source-row Explore.
|
|
59
|
+
Use a dedicated staging reader instead of weakening this check.
|
|
60
|
+
|
|
61
|
+
## Cursor Has Production Tools Instead Of Authoring Tools
|
|
62
|
+
|
|
63
|
+
Install the managed local authoring entry only after boundary activation:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
synapsor-runner mcp install cursor \
|
|
67
|
+
--project \
|
|
68
|
+
--authoring \
|
|
69
|
+
--project-root . \
|
|
70
|
+
--yes
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Authoring status reports exactly `app.describe_data` and `app.explore_data`.
|
|
74
|
+
After Protect and exact-digest activation, replace that entry with the
|
|
75
|
+
production config. The protected named capability remains available while
|
|
76
|
+
Explore disappears:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
synapsor-runner mcp install cursor \
|
|
80
|
+
--project \
|
|
81
|
+
--config ./synapsor.runner.json \
|
|
82
|
+
--store ./.synapsor/local.db \
|
|
83
|
+
--yes
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Boundary Became Stale After A Schema Or Grant Change
|
|
87
|
+
|
|
88
|
+
Generated authority is bound to the schema, role, grants, ownership, RLS
|
|
89
|
+
posture, compiler, and canonical Spec fingerprint:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
synapsor-runner boundary diff --json
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Additive fields receive no implicit authority. Breaking or posture drift fails
|
|
96
|
+
closed only for lock-bound generated authority. Regenerate, review the semantic
|
|
97
|
+
diff, and activate the new exact digest. Existing manually authored projects
|
|
98
|
+
without a generation lock retain their previous behavior.
|
|
99
|
+
|
|
100
|
+
## Aggregate Explore Suppressed Or Refused A Result
|
|
101
|
+
|
|
102
|
+
Suppression and budget failures are security behavior, not query failures.
|
|
103
|
+
Workbench shows the reviewed minimum cohort, maximum groups, response limits,
|
|
104
|
+
and durable extraction/differencing budgets. You cannot widen them in a model
|
|
105
|
+
argument.
|
|
106
|
+
|
|
107
|
+
Use a larger legitimate cohort or protect a narrower reviewed question. Do not
|
|
108
|
+
work around suppression with repeated slightly different filters: Runner
|
|
109
|
+
records normalized redacted plan metadata and exhausts the differencing budget.
|
|
110
|
+
Returned rows/groups, trusted tenant/principal values, credentials, and raw
|
|
111
|
+
sensitive literals are not stored in the query audit.
|
|
112
|
+
|
|
15
113
|
## Safe Action Draft Does Not Appear As A Tool
|
|
16
114
|
|
|
17
115
|
This is expected before activation. `start --action`, agent edits, `action
|
|
@@ -266,6 +364,82 @@ Fix:
|
|
|
266
364
|
Use a read-only credential for MCP reads and a separate writer credential only
|
|
267
365
|
for trusted apply.
|
|
268
366
|
|
|
367
|
+
## Freshness Check Is Stale
|
|
368
|
+
|
|
369
|
+
What happened:
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
Freshness: stale
|
|
373
|
+
code: FRESHNESS_TARGET_STALE
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
or:
|
|
377
|
+
|
|
378
|
+
```text
|
|
379
|
+
code: FRESHNESS_DEPENDENCY_STALE
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Why it matters:
|
|
383
|
+
|
|
384
|
+
The proposal target or one explicitly declared supporting row no longer has
|
|
385
|
+
the exact version captured by the immutable proposal. Runner records no
|
|
386
|
+
approval and never updates the old proposal to match current data.
|
|
387
|
+
|
|
388
|
+
Fix:
|
|
389
|
+
|
|
390
|
+
Perform a new reviewed source read and create a new proposal. Do not retry
|
|
391
|
+
approval on the stale proposal or edit its stored JSON.
|
|
392
|
+
|
|
393
|
+
## Freshness Check Is Unavailable
|
|
394
|
+
|
|
395
|
+
What happened:
|
|
396
|
+
|
|
397
|
+
```text
|
|
398
|
+
Freshness: unavailable
|
|
399
|
+
code: FRESHNESS_TEMPORARILY_UNAVAILABLE
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Why it matters:
|
|
403
|
+
|
|
404
|
+
Runner could not prove current source state. Unavailability is not treated as
|
|
405
|
+
fresh, and no approval was recorded.
|
|
406
|
+
|
|
407
|
+
Fix:
|
|
408
|
+
|
|
409
|
+
Restore the read connection or resolve the transient database/pool issue, then
|
|
410
|
+
retry:
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
synapsor-runner proposals check-freshness latest \
|
|
414
|
+
--config ./synapsor.runner.json \
|
|
415
|
+
--store ./.synapsor/local.db
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Freshness Writer Lock Probe Failed
|
|
419
|
+
|
|
420
|
+
What happened:
|
|
421
|
+
|
|
422
|
+
`doctor --check-writeback` reports that the writer cannot lock a declared
|
|
423
|
+
supporting dependency.
|
|
424
|
+
|
|
425
|
+
Why it matters:
|
|
426
|
+
|
|
427
|
+
Approval-time reads alone cannot provide the final atomic guarantee. The direct
|
|
428
|
+
SQL adapter must lock and compare every dependency inside the mutation
|
|
429
|
+
transaction.
|
|
430
|
+
|
|
431
|
+
Fix:
|
|
432
|
+
|
|
433
|
+
Grant only the narrow table/column privilege required for the writer's
|
|
434
|
+
`SELECT ... FOR UPDATE` locking read, then rerun:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
synapsor-runner doctor --check-writeback --config ./synapsor.runner.json
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Do not replace this with an overprivileged owner/superuser role. See
|
|
441
|
+
[Proposal And Evidence Freshness](proposal-evidence-freshness.md).
|
|
442
|
+
|
|
269
443
|
## MCP Client Config Contains A Secret
|
|
270
444
|
|
|
271
445
|
What happened:
|
|
@@ -250,3 +250,11 @@ Executor secrets are never exposed over MCP. The model never receives:
|
|
|
250
250
|
|
|
251
251
|
`MCP tool call = request/proposal authority. Trusted runner = execution
|
|
252
252
|
authority.`
|
|
253
|
+
|
|
254
|
+
Optional Runner `proposal_freshness` adds pre-approval and final-transaction
|
|
255
|
+
checks only to same-source direct SQL writeback. It is deliberately rejected
|
|
256
|
+
for HTTP/command handlers and cross-source dependencies: Runner cannot claim an
|
|
257
|
+
atomic source check around an externally owned transaction. Handler authors
|
|
258
|
+
must implement equivalent scope, exact-version, locking, idempotency, and
|
|
259
|
+
receipt semantics themselves. See
|
|
260
|
+
[Proposal And Evidence Freshness](proposal-evidence-freshness.md).
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Auto Boundary Churn Fixture
|
|
2
|
+
|
|
3
|
+
This synthetic staging fixture is the measured Runner 1.6.0 golden path:
|
|
4
|
+
PostgreSQL, Next.js, Prisma, Cursor-compatible local MCP, and the local
|
|
5
|
+
Workbench.
|
|
6
|
+
|
|
7
|
+
The database role is SELECT-only, non-owner, non-superuser, and subject to
|
|
8
|
+
forced tenant/principal RLS. The data includes reviewed churn cohorts, a
|
|
9
|
+
cross-tenant denial case, kept-out fields, and cohorts below the privacy
|
|
10
|
+
threshold.
|
|
11
|
+
|
|
12
|
+
Run the automated acceptance path from the repository root:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
corepack pnpm test:auto-boundary-explore
|
|
16
|
+
corepack pnpm test:auto-boundary-explore:packed
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The packed gate uses only packed public artifacts for the complete journey:
|
|
20
|
+
disabled whole-schema draft, digest activation, two Cursor authoring tools,
|
|
21
|
+
privacy-suppressed weekly churn analysis, denial and differencing tests,
|
|
22
|
+
Protect This Query, production Explore removal, and the surviving named
|
|
23
|
+
capability. No source row is mutated.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:16
|
|
4
|
+
environment:
|
|
5
|
+
POSTGRES_DB: synapsor_auto_boundary
|
|
6
|
+
POSTGRES_USER: synapsor_admin
|
|
7
|
+
POSTGRES_PASSWORD: synapsor_admin_password
|
|
8
|
+
ports:
|
|
9
|
+
- "127.0.0.1:55460:5432"
|
|
10
|
+
healthcheck:
|
|
11
|
+
test: ["CMD-SHELL", "test \"$(psql -U synapsor_admin -d synapsor_auto_boundary -tAc \"SELECT pg_postmaster_start_time() > initialized_at FROM public.synapsor_fixture_ready LIMIT 1\" 2>/dev/null | xargs)\" = t"]
|
|
12
|
+
interval: 1s
|
|
13
|
+
timeout: 3s
|
|
14
|
+
retries: 40
|
|
15
|
+
volumes:
|
|
16
|
+
- ./seed/postgres.sql:/docker-entrypoint-initdb.d/001-schema.sql:ro
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "synapsor-auto-boundary-churn",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev"
|
|
7
|
+
},
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@prisma/client": "^6.0.0",
|
|
10
|
+
"next": "^15.0.0",
|
|
11
|
+
"react": "^19.0.0",
|
|
12
|
+
"react-dom": "^19.0.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"prisma": "^6.0.0",
|
|
16
|
+
"typescript": "^5.8.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
generator client {
|
|
2
|
+
provider = "prisma-client-js"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
datasource db {
|
|
6
|
+
provider = "postgresql"
|
|
7
|
+
url = env("DATABASE_URL")
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
model Account {
|
|
11
|
+
id String @id
|
|
12
|
+
tenantId String @map("tenant_id")
|
|
13
|
+
ownerId String @map("owner_id")
|
|
14
|
+
region String
|
|
15
|
+
segment String
|
|
16
|
+
customerEmail String @map("customer_email")
|
|
17
|
+
internalRiskScore Int @map("internal_risk_score")
|
|
18
|
+
churnEvents ChurnEvent[]
|
|
19
|
+
|
|
20
|
+
@@map("accounts")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
model ChurnEvent {
|
|
24
|
+
id String @id
|
|
25
|
+
tenantId String @map("tenant_id")
|
|
26
|
+
ownerId String @map("owner_id")
|
|
27
|
+
accountId String @map("account_id")
|
|
28
|
+
reasonCategory String @map("reason_category")
|
|
29
|
+
monthlyRevenueCents Int @map("monthly_revenue_cents")
|
|
30
|
+
churnedAt DateTime @map("churned_at") @db.Timestamptz(6)
|
|
31
|
+
privateNote String @map("private_note")
|
|
32
|
+
account Account @relation(fields: [accountId], references: [id])
|
|
33
|
+
|
|
34
|
+
@@index([tenantId, churnedAt])
|
|
35
|
+
@@map("churn_events")
|
|
36
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
CREATE TABLE public.accounts (
|
|
2
|
+
id text PRIMARY KEY,
|
|
3
|
+
tenant_id text NOT NULL,
|
|
4
|
+
owner_id text NOT NULL,
|
|
5
|
+
region text NOT NULL CHECK (region IN ('east', 'north', 'south', 'west')),
|
|
6
|
+
segment text NOT NULL CHECK (segment IN ('enterprise', 'growth', 'startup')),
|
|
7
|
+
customer_email text NOT NULL,
|
|
8
|
+
internal_risk_score integer NOT NULL
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
CREATE TABLE public.churn_events (
|
|
12
|
+
id text PRIMARY KEY,
|
|
13
|
+
tenant_id text NOT NULL,
|
|
14
|
+
owner_id text NOT NULL,
|
|
15
|
+
account_id text NOT NULL REFERENCES public.accounts(id) ON DELETE RESTRICT,
|
|
16
|
+
reason_category text NOT NULL CHECK (reason_category IN ('onboarding', 'price', 'product', 'service')),
|
|
17
|
+
monthly_revenue_cents integer NOT NULL CHECK (monthly_revenue_cents >= 0),
|
|
18
|
+
churned_at timestamptz NOT NULL,
|
|
19
|
+
private_note text NOT NULL
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
CREATE INDEX churn_events_tenant_time_idx
|
|
23
|
+
ON public.churn_events(tenant_id, churned_at);
|
|
24
|
+
|
|
25
|
+
WITH groups(prefix, tenant_id, owner_id, region, segment, reason_category, churned_at, group_size, revenue) AS (
|
|
26
|
+
VALUES
|
|
27
|
+
('p1-west-price', 'acme', 'pm-1', 'west', 'growth', 'price', '2026-06-03T12:00:00Z'::timestamptz, 5, 12000),
|
|
28
|
+
('p1-east-service', 'acme', 'pm-1', 'east', 'enterprise', 'service', '2026-06-10T12:00:00Z'::timestamptz, 5, 22000),
|
|
29
|
+
('p1-north-product', 'acme', 'pm-1', 'north', 'startup', 'product', '2026-06-17T12:00:00Z'::timestamptz, 2, 5000),
|
|
30
|
+
('p2-west-price', 'acme', 'pm-1', 'west', 'growth', 'price', '2026-07-08T12:00:00Z'::timestamptz, 10, 13000),
|
|
31
|
+
('p2-east-service', 'acme', 'pm-1', 'east', 'enterprise', 'service', '2026-07-15T12:00:00Z'::timestamptz, 7, 23000),
|
|
32
|
+
('p2-south-onboarding', 'acme', 'pm-1', 'south', 'startup', 'onboarding', '2026-07-22T12:00:00Z'::timestamptz, 5, 7000),
|
|
33
|
+
('p2-north-product', 'acme', 'pm-1', 'north', 'startup', 'product', '2026-07-22T12:00:00Z'::timestamptz, 1, 6000),
|
|
34
|
+
('other-west-price', 'globex', 'pm-2', 'west', 'enterprise', 'price', '2026-07-08T12:00:00Z'::timestamptz, 8, 99000)
|
|
35
|
+
)
|
|
36
|
+
INSERT INTO public.accounts (
|
|
37
|
+
id,
|
|
38
|
+
tenant_id,
|
|
39
|
+
owner_id,
|
|
40
|
+
region,
|
|
41
|
+
segment,
|
|
42
|
+
customer_email,
|
|
43
|
+
internal_risk_score
|
|
44
|
+
)
|
|
45
|
+
SELECT
|
|
46
|
+
prefix || '-' || item,
|
|
47
|
+
tenant_id,
|
|
48
|
+
owner_id,
|
|
49
|
+
region,
|
|
50
|
+
segment,
|
|
51
|
+
prefix || '-' || item || '@example.invalid',
|
|
52
|
+
900 + item
|
|
53
|
+
FROM groups
|
|
54
|
+
CROSS JOIN LATERAL generate_series(1, group_size) AS item;
|
|
55
|
+
|
|
56
|
+
WITH groups(prefix, tenant_id, owner_id, reason_category, churned_at, group_size, revenue) AS (
|
|
57
|
+
VALUES
|
|
58
|
+
('p1-west-price', 'acme', 'pm-1', 'price', '2026-06-03T12:00:00Z'::timestamptz, 5, 12000),
|
|
59
|
+
('p1-east-service', 'acme', 'pm-1', 'service', '2026-06-10T12:00:00Z'::timestamptz, 5, 22000),
|
|
60
|
+
('p1-north-product', 'acme', 'pm-1', 'product', '2026-06-17T12:00:00Z'::timestamptz, 2, 5000),
|
|
61
|
+
('p2-west-price', 'acme', 'pm-1', 'price', '2026-07-08T12:00:00Z'::timestamptz, 10, 13000),
|
|
62
|
+
('p2-east-service', 'acme', 'pm-1', 'service', '2026-07-15T12:00:00Z'::timestamptz, 7, 23000),
|
|
63
|
+
('p2-south-onboarding', 'acme', 'pm-1', 'onboarding', '2026-07-22T12:00:00Z'::timestamptz, 5, 7000),
|
|
64
|
+
('p2-north-product', 'acme', 'pm-1', 'product', '2026-07-22T12:00:00Z'::timestamptz, 1, 6000),
|
|
65
|
+
('other-west-price', 'globex', 'pm-2', 'price', '2026-07-08T12:00:00Z'::timestamptz, 8, 99000)
|
|
66
|
+
)
|
|
67
|
+
INSERT INTO public.churn_events (
|
|
68
|
+
id,
|
|
69
|
+
tenant_id,
|
|
70
|
+
owner_id,
|
|
71
|
+
account_id,
|
|
72
|
+
reason_category,
|
|
73
|
+
monthly_revenue_cents,
|
|
74
|
+
churned_at,
|
|
75
|
+
private_note
|
|
76
|
+
)
|
|
77
|
+
SELECT
|
|
78
|
+
'event-' || prefix || '-' || item,
|
|
79
|
+
tenant_id,
|
|
80
|
+
owner_id,
|
|
81
|
+
prefix || '-' || item,
|
|
82
|
+
reason_category,
|
|
83
|
+
revenue,
|
|
84
|
+
churned_at,
|
|
85
|
+
'synthetic kept-out note ' || prefix || '-' || item
|
|
86
|
+
FROM groups
|
|
87
|
+
CROSS JOIN LATERAL generate_series(1, group_size) AS item;
|
|
88
|
+
|
|
89
|
+
ALTER TABLE public.accounts ENABLE ROW LEVEL SECURITY;
|
|
90
|
+
ALTER TABLE public.accounts FORCE ROW LEVEL SECURITY;
|
|
91
|
+
ALTER TABLE public.churn_events ENABLE ROW LEVEL SECURITY;
|
|
92
|
+
ALTER TABLE public.churn_events FORCE ROW LEVEL SECURITY;
|
|
93
|
+
|
|
94
|
+
CREATE POLICY accounts_trusted_scope ON public.accounts
|
|
95
|
+
FOR SELECT
|
|
96
|
+
USING (
|
|
97
|
+
tenant_id = current_setting('app.tenant_id', true)
|
|
98
|
+
AND owner_id = current_setting('app.principal', true)
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
CREATE POLICY churn_events_trusted_scope ON public.churn_events
|
|
102
|
+
FOR SELECT
|
|
103
|
+
USING (
|
|
104
|
+
tenant_id = current_setting('app.tenant_id', true)
|
|
105
|
+
AND owner_id = current_setting('app.principal', true)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
DO $$
|
|
109
|
+
BEGIN
|
|
110
|
+
CREATE ROLE synapsor_churn_reader LOGIN PASSWORD 'synapsor_churn_reader_password';
|
|
111
|
+
EXCEPTION WHEN duplicate_object THEN NULL;
|
|
112
|
+
END
|
|
113
|
+
$$;
|
|
114
|
+
|
|
115
|
+
ALTER ROLE synapsor_churn_reader SET default_transaction_read_only = on;
|
|
116
|
+
GRANT CONNECT ON DATABASE synapsor_auto_boundary TO synapsor_churn_reader;
|
|
117
|
+
GRANT USAGE ON SCHEMA public TO synapsor_churn_reader;
|
|
118
|
+
GRANT SELECT ON public.accounts, public.churn_events TO synapsor_churn_reader;
|
|
119
|
+
|
|
120
|
+
-- The image entrypoint runs these statements under a temporary postmaster and
|
|
121
|
+
-- then restarts PostgreSQL. Compose reports healthy only after that final
|
|
122
|
+
-- postmaster starts, preventing first-client disconnects in CI.
|
|
123
|
+
CREATE TABLE public.synapsor_fixture_ready (
|
|
124
|
+
initialized_at timestamptz NOT NULL
|
|
125
|
+
);
|
|
126
|
+
INSERT INTO public.synapsor_fixture_ready (initialized_at) VALUES (clock_timestamp());
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "synapsor.published-compatibility.v1",
|
|
3
|
+
"published_packages": {
|
|
4
|
+
"@synapsor/runner": {
|
|
5
|
+
"version": "1.5.4",
|
|
6
|
+
"npm_shasum": "63ef030b0360118082bc670239ad4927673b6bc7"
|
|
7
|
+
},
|
|
8
|
+
"@synapsor/dsl": {
|
|
9
|
+
"version": "1.4.4",
|
|
10
|
+
"npm_shasum": "dee671d3c3d25f5434b488f80b3041d0c5606c2f"
|
|
11
|
+
},
|
|
12
|
+
"@synapsor/spec": {
|
|
13
|
+
"version": "1.4.2",
|
|
14
|
+
"npm_shasum": "cb9aa813f481ef14d542a1896c228159aab86353"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"contracts": [
|
|
18
|
+
{
|
|
19
|
+
"path": "packages/spec/fixtures/valid/basic-read.contract.json",
|
|
20
|
+
"source_sha256": "5ec499965f639d784603b7942390d18a3e742d79e4d510dbea470e166a743972",
|
|
21
|
+
"canonical_sha256": "9d3addfde77eb6528aae7b9e81ae0f582b5c002aa77ebb2d7381fe663f5f5b1e"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "packages/spec/fixtures/conformance/aggregate-read/contract.json",
|
|
25
|
+
"source_sha256": "830bc208b749bb9ed6a4b4468571d58d68bc8a2b6e1072fc45fb87d1c68697e7",
|
|
26
|
+
"canonical_sha256": "ccc6ea17f449bfc207d1cea70ef3320d575c450a3904c850dcb631132c6e5602"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"path": "packages/spec/fixtures/conformance/proposal-capability/contract.json",
|
|
30
|
+
"source_sha256": "d4c25fb8f79f04c13bdb3daf8c18a44d4f1c61596660a67bf35d46d0f94f4100",
|
|
31
|
+
"canonical_sha256": "d895081246d41a072892b5dde6929099302492a77a229033fbfa5052cce34b18"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"path": "packages/spec/fixtures/conformance/bounded-set-threats/contract.json",
|
|
35
|
+
"source_sha256": "c902d45457789a6d87504ca434c8b299f3e09be779f3bfdf7bbd8684d575a10e",
|
|
36
|
+
"canonical_sha256": "64c7078ff1687762d95c17873238759b3572dce499f903a8d6f1aaa8faae5f24"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"path": "packages/spec/fixtures/conformance/reversible-change-sets/contract.json",
|
|
40
|
+
"source_sha256": "7e4f8da90a4b235097f76e70a7f550153bbf62d219e7ef6f4f2194f6c36fa205",
|
|
41
|
+
"canonical_sha256": "181f2e93ad3c53b9a1345a4ed6c8b5e24ab0201c00159f38659f0c2a00c037de"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"path": "packages/spec/fixtures/conformance/principal-row-scope/contract.json",
|
|
45
|
+
"source_sha256": "00a05c1f4e82cc9c4e5d2d68c9c9201cd9512ac42d63292314bb7b4845026996",
|
|
46
|
+
"canonical_sha256": "fc96a5327ab0f4b311c90da9b5585348e81349ed66bd3a22e41e5f29804c32a8"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"path": "packages/spec/fixtures/conformance/approval-quorum/contract.json",
|
|
50
|
+
"source_sha256": "dde6b328f648ba60d837fa363494d764d7eb0d2d9a8a81ff70a9c077edde410c",
|
|
51
|
+
"canonical_sha256": "9407a7d0cc4439c773c3caf721aaed6c6e163c403fa67518632829d8c5de33e7"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"dsl_sources": [
|
|
55
|
+
{
|
|
56
|
+
"path": "packages/dsl/examples/aggregate-read.synapsor.sql",
|
|
57
|
+
"source_sha256": "bccac4c2eb0ab7b2fc099c7fb3f6fdc25ee2e6e534282aa19b46f0aec8191b8d",
|
|
58
|
+
"canonical_sha256": "0cc2d892f00a31a3582d773812a26c6c45c0ce343e1d8d09da226a5bbb7c93eb"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"path": "packages/dsl/examples/billing-late-fee.synapsor.sql",
|
|
62
|
+
"source_sha256": "f5c462cff7aa0cd01a08543c6096ee1d225ebba449d65b7470815b1aa7ec42cd",
|
|
63
|
+
"canonical_sha256": "d495df575d64e798a730dad385e097dfc2c2f371a1505865bc45179ad3c479f2"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"path": "packages/dsl/examples/bounded-set-multi-term.synapsor.sql",
|
|
67
|
+
"source_sha256": "0ae199034ebba8b25146435c911576118f948b55b54636a5e68f46002cd1c6dd",
|
|
68
|
+
"canonical_sha256": "5b3e84cf8f5062a8afcd50763bde907e6a141977456e74a81d01f8b3a484e422"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"path": "packages/dsl/examples/principal-row-scope.synapsor.sql",
|
|
72
|
+
"source_sha256": "08ccd769d633417ea45d478383d9628991f43262223f12c78bbd776202b10253",
|
|
73
|
+
"canonical_sha256": "e29f2819c267210e54f783c40d0f550d11d0d1de844dbe1590ed8c3405af3cf7"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
CREATE AGENT CONTEXT finance_operator
|
|
2
|
+
BIND tenant_id FROM ENVIRONMENT SYNAPSOR_TENANT_ID REQUIRED
|
|
3
|
+
BIND principal FROM ENVIRONMENT SYNAPSOR_PRINCIPAL REQUIRED
|
|
4
|
+
TENANT BINDING tenant_id
|
|
5
|
+
PRINCIPAL BINDING principal
|
|
6
|
+
END
|
|
7
|
+
|
|
8
|
+
CREATE CAPABILITY billing.overdue_balance_total
|
|
9
|
+
DESCRIPTION 'Return one suppressed overdue-balance aggregate for the trusted tenant.'
|
|
10
|
+
RETURNS HINT 'Returns one scalar or a suppression result; never member rows.'
|
|
11
|
+
USING CONTEXT finance_operator
|
|
12
|
+
SOURCE local_postgres
|
|
13
|
+
ON public.invoices
|
|
14
|
+
PRIMARY KEY id
|
|
15
|
+
TENANT KEY tenant_id
|
|
16
|
+
AGGREGATE READ SUM balance_cents
|
|
17
|
+
SELECT WHERE status = 'overdue'
|
|
18
|
+
MIN GROUP SIZE 5
|
|
19
|
+
KEEP OUT customer_email, private_notes
|
|
20
|
+
REQUIRE EVIDENCE
|
|
21
|
+
END
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
CREATE AGENT CONTEXT local_operator
|
|
2
|
+
BIND tenant_id FROM ENVIRONMENT SYNAPSOR_TENANT_ID REQUIRED
|
|
3
|
+
BIND principal FROM ENVIRONMENT SYNAPSOR_PRINCIPAL REQUIRED
|
|
4
|
+
TENANT BINDING tenant_id
|
|
5
|
+
PRINCIPAL BINDING principal
|
|
6
|
+
END
|
|
7
|
+
|
|
8
|
+
CREATE CAPABILITY billing.inspect_invoice
|
|
9
|
+
DESCRIPTION 'Inspect one invoice in the trusted tenant before proposing a waiver.'
|
|
10
|
+
RETURNS HINT 'Returns reviewed invoice fields plus evidence/query-audit handles.'
|
|
11
|
+
USING CONTEXT local_operator
|
|
12
|
+
SOURCE local_postgres
|
|
13
|
+
ON public.invoices
|
|
14
|
+
PRIMARY KEY id
|
|
15
|
+
TENANT KEY tenant_id
|
|
16
|
+
CONFLICT GUARD updated_at
|
|
17
|
+
LOOKUP invoice_id BY id
|
|
18
|
+
ARG invoice_id STRING REQUIRED MAX LENGTH 128 DESCRIPTION 'Invoice id such as INV-3001.'
|
|
19
|
+
ALLOW READ id, tenant_id, customer_id, status, late_fee_cents, waiver_reason, updated_at
|
|
20
|
+
KEEP OUT card_token, internal_risk_score
|
|
21
|
+
REQUIRE EVIDENCE
|
|
22
|
+
MAX ROWS 1
|
|
23
|
+
END
|
|
24
|
+
|
|
25
|
+
CREATE CAPABILITY billing.propose_late_fee_waiver
|
|
26
|
+
DESCRIPTION 'Propose waiving one invoice late fee after inspecting invoice and policy evidence.'
|
|
27
|
+
RETURNS HINT 'Returns a review-required proposal id, exact diff, evidence handle, and source_database_changed:false.'
|
|
28
|
+
USING CONTEXT local_operator
|
|
29
|
+
SOURCE local_postgres
|
|
30
|
+
ON public.invoices
|
|
31
|
+
PRIMARY KEY id
|
|
32
|
+
TENANT KEY tenant_id
|
|
33
|
+
CONFLICT GUARD updated_at
|
|
34
|
+
LOOKUP invoice_id BY id
|
|
35
|
+
ARG invoice_id STRING REQUIRED MAX LENGTH 128 DESCRIPTION 'Invoice id such as INV-3001.'
|
|
36
|
+
ARG waiver_reason TEXT REQUIRED MAX LENGTH 500 DESCRIPTION 'Business reason for the proposed waiver.'
|
|
37
|
+
ALLOW READ id, tenant_id, customer_id, status, late_fee_cents, waiver_reason, updated_at
|
|
38
|
+
KEEP OUT card_token, internal_risk_score
|
|
39
|
+
REQUIRE EVIDENCE
|
|
40
|
+
MAX ROWS 1
|
|
41
|
+
PROPOSE ACTION billing.waive_late_fee
|
|
42
|
+
ALLOW WRITE late_fee_cents, waiver_reason
|
|
43
|
+
PATCH late_fee_cents = 0
|
|
44
|
+
PATCH waiver_reason = ARG waiver_reason
|
|
45
|
+
APPROVAL ROLE billing_lead
|
|
46
|
+
WRITEBACK DIRECT SQL
|
|
47
|
+
END
|
|
48
|
+
|
|
49
|
+
CREATE AGENT WORKFLOW billing.late_fee_review
|
|
50
|
+
USING CONTEXT local_operator
|
|
51
|
+
ALLOW CAPABILITY billing.inspect_invoice
|
|
52
|
+
ALLOW CAPABILITY billing.propose_late_fee_waiver
|
|
53
|
+
REQUIRE EVIDENCE
|
|
54
|
+
APPROVAL REQUIRED ROLE billing_lead
|
|
55
|
+
CHECKPOINT PROPOSAL ONLY
|
|
56
|
+
END
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
CREATE AGENT CONTEXT local_operator
|
|
2
|
+
BIND tenant_id FROM ENVIRONMENT SYNAPSOR_TENANT_ID REQUIRED
|
|
3
|
+
BIND principal FROM ENVIRONMENT SYNAPSOR_PRINCIPAL REQUIRED
|
|
4
|
+
TENANT BINDING tenant_id
|
|
5
|
+
PRINCIPAL BINDING principal
|
|
6
|
+
END
|
|
7
|
+
|
|
8
|
+
CREATE CAPABILITY cases.close_high_risk
|
|
9
|
+
DESCRIPTION 'Close active high-risk support cases within reviewed row and value caps.'
|
|
10
|
+
RETURNS HINT 'Returns a proposal for the exact reviewed set; source rows remain unchanged.'
|
|
11
|
+
USING CONTEXT local_operator
|
|
12
|
+
SOURCE support_db
|
|
13
|
+
ON public.support_cases
|
|
14
|
+
PRIMARY KEY id
|
|
15
|
+
TENANT KEY tenant_id
|
|
16
|
+
CONFLICT GUARD version
|
|
17
|
+
LOOKUP reason BY id
|
|
18
|
+
ARG reason STRING REQUIRED MAX LENGTH 100
|
|
19
|
+
ALLOW READ id, tenant_id, risk_level, case_status, exposure_cents, version
|
|
20
|
+
REQUIRE EVIDENCE
|
|
21
|
+
PROPOSE ACTION close_high_risk UPDATE SET
|
|
22
|
+
SELECT WHERE risk_level = 'high' AND case_status = 'active'
|
|
23
|
+
MAX ROWS 10
|
|
24
|
+
MAX TOTAL exposure_cents BEFORE 50000
|
|
25
|
+
ALLOW WRITE case_status
|
|
26
|
+
PATCH case_status = 'closed'
|
|
27
|
+
ADVANCE VERSION version USING INTEGER INCREMENT
|
|
28
|
+
APPROVAL ROLE support_manager
|
|
29
|
+
WRITEBACK DIRECT SQL
|
|
30
|
+
END
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
CREATE AGENT CONTEXT care_session
|
|
2
|
+
BIND hospital_id FROM HTTP_CLAIM hospital_id REQUIRED
|
|
3
|
+
BIND principal FROM HTTP_CLAIM sub REQUIRED
|
|
4
|
+
TENANT BINDING hospital_id
|
|
5
|
+
PRINCIPAL BINDING principal
|
|
6
|
+
END
|
|
7
|
+
|
|
8
|
+
CREATE CAPABILITY care.inspect_assigned_patient
|
|
9
|
+
DESCRIPTION 'Inspect one patient assigned to the authenticated case manager within the trusted hospital.'
|
|
10
|
+
RETURNS HINT 'Returns reviewed patient fields and evidence only when both tenant and assignee locks match.'
|
|
11
|
+
USING CONTEXT care_session
|
|
12
|
+
SOURCE local_postgres
|
|
13
|
+
ON public.patients
|
|
14
|
+
PRIMARY KEY id
|
|
15
|
+
TENANT KEY hospital_id
|
|
16
|
+
PRINCIPAL SCOPE KEY assigned_to
|
|
17
|
+
LOOKUP patient_id BY id
|
|
18
|
+
ARG patient_id STRING REQUIRED MAX LENGTH 128 DESCRIPTION 'Reviewed patient identifier.'
|
|
19
|
+
ALLOW READ id, hospital_id, display_name, care_status, updated_at
|
|
20
|
+
KEEP OUT assigned_to, diagnosis_notes, insurance_member_id
|
|
21
|
+
REQUIRE EVIDENCE
|
|
22
|
+
MAX ROWS 1
|
|
23
|
+
END
|