@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.
Files changed (81) hide show
  1. package/CHANGELOG.md +68 -2
  2. package/README.md +131 -124
  3. package/dist/authoring.mjs +405 -9
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/local-ui.d.ts +6 -1
  6. package/dist/local-ui.d.ts.map +1 -1
  7. package/dist/runner.mjs +17206 -12169
  8. package/dist/runtime.mjs +11249 -8542
  9. package/dist/shadow.mjs +408 -15
  10. package/docs/README.md +14 -6
  11. package/docs/aggregate-reads.md +22 -0
  12. package/docs/app-owned-executors.md +8 -0
  13. package/docs/auto-boundary-and-scoped-explore.md +338 -0
  14. package/docs/capability-authoring.md +75 -0
  15. package/docs/conformance.md +16 -0
  16. package/docs/current-scope.md +60 -28
  17. package/docs/cursor-plugin.md +20 -3
  18. package/docs/dsl-reference.md +78 -0
  19. package/docs/getting-started-own-database.md +39 -35
  20. package/docs/limitations.md +37 -7
  21. package/docs/local-mode.md +22 -3
  22. package/docs/production.md +19 -0
  23. package/docs/proposal-evidence-freshness.md +334 -0
  24. package/docs/release-notes.md +63 -4
  25. package/docs/runner-config-reference.md +46 -0
  26. package/docs/schema-api-candidates.md +28 -1
  27. package/docs/security-boundary.md +17 -0
  28. package/docs/store-lifecycle.md +8 -0
  29. package/docs/troubleshooting-first-run.md +174 -0
  30. package/docs/writeback-executors.md +8 -0
  31. package/examples/app-owned-writeback/command-handler.mjs +0 -0
  32. package/examples/auto-boundary-churn/README.md +23 -0
  33. package/examples/auto-boundary-churn/app/page.tsx +8 -0
  34. package/examples/auto-boundary-churn/docker-compose.yml +16 -0
  35. package/examples/auto-boundary-churn/package.json +18 -0
  36. package/examples/auto-boundary-churn/prisma/schema.prisma +36 -0
  37. package/examples/auto-boundary-churn/seed/postgres.sql +126 -0
  38. package/examples/mcp-postgres-billing-app-handler/scripts/run-demo.sh +0 -0
  39. package/examples/reference-support-billing-app/scripts/run-demo.sh +0 -0
  40. package/examples/support-billing-agent/scripts/run-demo.sh +0 -0
  41. package/examples/support-billing-agent/scripts/run-evaluation.sh +0 -0
  42. package/fixtures/compatibility/published-1.5.4/manifest.json +76 -0
  43. package/fixtures/compatibility/published-1.5.4/sources/packages/dsl/examples/aggregate-read.synapsor.sql +21 -0
  44. package/fixtures/compatibility/published-1.5.4/sources/packages/dsl/examples/billing-late-fee.synapsor.sql +56 -0
  45. package/fixtures/compatibility/published-1.5.4/sources/packages/dsl/examples/bounded-set-multi-term.synapsor.sql +30 -0
  46. package/fixtures/compatibility/published-1.5.4/sources/packages/dsl/examples/principal-row-scope.synapsor.sql +23 -0
  47. package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/aggregate-read/contract.json +119 -0
  48. package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/approval-quorum/contract.json +44 -0
  49. package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/bounded-set-threats/contract.json +115 -0
  50. package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/principal-row-scope/contract.json +78 -0
  51. package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/proposal-capability/contract.json +101 -0
  52. package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/conformance/reversible-change-sets/contract.json +98 -0
  53. package/fixtures/compatibility/published-1.5.4/sources/packages/spec/fixtures/valid/basic-read.contract.json +60 -0
  54. package/fixtures/compatibility/published-1.6.0/manifest.json +76 -0
  55. package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/aggregate-read.synapsor.sql +21 -0
  56. package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/billing-late-fee.synapsor.sql +56 -0
  57. package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/bounded-set-multi-term.synapsor.sql +30 -0
  58. package/fixtures/compatibility/published-1.6.0/sources/packages/dsl/examples/principal-row-scope.synapsor.sql +23 -0
  59. package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/aggregate-read/contract.json +119 -0
  60. package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/approval-quorum/contract.json +44 -0
  61. package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/bounded-set-threats/contract.json +115 -0
  62. package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/principal-row-scope/contract.json +78 -0
  63. package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/proposal-capability/contract.json +101 -0
  64. package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/conformance/reversible-change-sets/contract.json +98 -0
  65. package/fixtures/compatibility/published-1.6.0/sources/packages/spec/fixtures/valid/basic-read.contract.json +60 -0
  66. package/fixtures/protocol/MANIFEST.json +37 -7
  67. package/fixtures/protocol/change-set.freshness-update.v2.json +58 -0
  68. package/fixtures/protocol/freshness-authority.invoice.v1.json +35 -0
  69. package/fixtures/protocol/freshness-proof.fresh.v1.json +38 -0
  70. package/fixtures/protocol/writeback-job.freshness-update.v2.json +50 -0
  71. package/llms.txt +38 -0
  72. package/package.json +12 -8
  73. package/schemas/change-set.v1.schema.json +1 -0
  74. package/schemas/change-set.v2.schema.json +1 -0
  75. package/schemas/change-set.v3.schema.json +1 -0
  76. package/schemas/freshness-authority.v1.schema.json +89 -0
  77. package/schemas/freshness-proof.v1.schema.json +73 -0
  78. package/schemas/synapsor.runner.schema.json +50 -1
  79. package/schemas/writeback-job.v1.schema.json +1 -0
  80. package/schemas/writeback-job.v2.schema.json +1 -0
  81. 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,8 @@
1
+ export default function ChurnOverview() {
2
+ return (
3
+ <main>
4
+ <h1>Subscription churn review</h1>
5
+ <p>This synthetic staging application is used by the Synapsor Auto Boundary acceptance test.</p>
6
+ </main>
7
+ );
8
+ }
@@ -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());
@@ -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