@synapsor/runner 1.4.121 → 1.4.123

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 (38) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +25 -15
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/runner.mjs +2855 -347
  6. package/docs/README.md +1 -0
  7. package/docs/capability-authoring.md +59 -1
  8. package/docs/cloud-cli.md +248 -0
  9. package/docs/cloud-mode.md +88 -6
  10. package/docs/cloud-push.md +22 -5
  11. package/docs/conformance.md +6 -0
  12. package/docs/contract-review.md +33 -0
  13. package/docs/contract-testing.md +30 -1
  14. package/docs/dsl-reference.md +11 -0
  15. package/docs/hosted-cloud-linked-verification.md +97 -0
  16. package/docs/oss-vs-cloud.md +13 -6
  17. package/docs/release-notes.md +48 -0
  18. package/docs/rfcs/007-trusted-principal-row-scope.md +75 -0
  19. package/docs/runner-bundles.md +24 -0
  20. package/docs/running-a-runner-fleet.md +8 -0
  21. package/docs/security-boundary.md +19 -2
  22. package/examples/runner-fleet/seed/mysql.sql +5 -1
  23. package/examples/runner-fleet/seed/postgres.sql +5 -1
  24. package/examples/support-plan-credit/README.md +8 -6
  25. package/fixtures/contracts/capability-surface-fitness.contract.json +190 -0
  26. package/fixtures/dsl/principal-row-scope.synapsor.sql +23 -0
  27. package/fixtures/protocol/MANIFEST.json +10 -10
  28. package/package.json +1 -1
  29. package/schemas/change-set.v1.schema.json +14 -0
  30. package/schemas/change-set.v2.schema.json +14 -0
  31. package/schemas/change-set.v3.schema.json +2 -0
  32. package/schemas/compensation-change-set.v1.schema.json +3 -2
  33. package/schemas/inverse-descriptor.v1.schema.json +2 -0
  34. package/schemas/synapsor.contract-tests.schema.json +11 -1
  35. package/schemas/writeback-job.v1.schema.json +14 -0
  36. package/schemas/writeback-job.v2.schema.json +14 -0
  37. package/schemas/writeback-job.v3.schema.json +2 -0
  38. package/schemas/writeback-job.v4.schema.json +2 -2
@@ -0,0 +1,97 @@
1
+ # Hosted Cloud-Linked Verification
2
+
3
+ This is the manual release gate for the complete OSS Runner and single-node
4
+ Synapsor Cloud boundary. It is intentionally opt-in because it creates Cloud
5
+ records and mutates a synthetic source row.
6
+
7
+ The verifier installs packed or published Cloud CLI and Runner packages in a
8
+ clean temporary directory and proves:
9
+
10
+ - public protocol discovery;
11
+ - packed CLI human `whoami`, secure one-time service-key output, and immediate
12
+ service-key revocation;
13
+ - canonical contract push parity between `synapsor contracts push` and
14
+ `synapsor-runner cloud push` with one immutable version identity;
15
+ - source-scoped Runner token creation, rotation, registration idempotency, and
16
+ heartbeat;
17
+ - credential-free Runner bundle download;
18
+ - scoped MCP read and cross-tenant denial;
19
+ - proposal creation without a source write and automatic durable-outbox
20
+ delivery without a manual `cloud sync` command;
21
+ - authenticated Cloud approval and guarded local writeback through the packed
22
+ Runner's public worker CLI;
23
+ - two registered Runner processes competing for one exclusive job lease;
24
+ - duplicate-claim idempotency, rejection, and stale-version conflict;
25
+ - linked Cloud activity integrity and canonical export;
26
+ - reviewed activation and rollback of a risk-increasing contract version; and
27
+ - immediate Runner-token revocation across doctor, registration, claim, and
28
+ result submission.
29
+
30
+ It never accepts production data. Use a disposable project and a synthetic
31
+ Postgres or MySQL database that can be reset after the run.
32
+
33
+ ## Prerequisites
34
+
35
+ Build the workspace and pack both packages before a pre-publish check:
36
+
37
+ ```bash
38
+ corepack pnpm install
39
+ corepack pnpm build
40
+ corepack pnpm --filter @synapsor/runner pack --pack-destination /tmp
41
+ corepack pnpm --filter @synapsor/cli pack --pack-destination /tmp
42
+ ```
43
+
44
+ Prepare:
45
+
46
+ - a disposable Cloud project and imported synthetic source;
47
+ - an authenticated human session token obtained through the normal browser/
48
+ device login flow;
49
+ - separate least-privilege read and write source URLs;
50
+ - a canonical contract with one scoped read and one proposal capability; and
51
+ - three independent proposal fixture rows: apply, reject, and stale-conflict.
52
+
53
+ Read tokens and database URLs from a secret prompt or secret manager. Do not
54
+ put them in Git, shell history, command-line arguments, or the Runner bundle.
55
+
56
+ ## Run
57
+
58
+ Set the non-secret fixture identifiers and JSON assertions for your synthetic
59
+ database. The exact required variables and their meanings are always available
60
+ without credentials:
61
+
62
+ ```bash
63
+ corepack pnpm verify:hosted-cloud-linked -- --help
64
+ ```
65
+
66
+ Then opt in explicitly and run:
67
+
68
+ ```bash
69
+ export SYNAPSOR_HOSTED_E2E=1
70
+ export SYNAPSOR_E2E_DISPOSABLE_PROJECT=1
71
+ export SYNAPSOR_CLOUD_BASE_URL="https://dev-api.synapsor.ai"
72
+ export SYNAPSOR_RUNNER_PACKAGE_SPEC="/tmp/synapsor-runner-<version>.tgz"
73
+ export SYNAPSOR_CLI_PACKAGE_SPEC="/tmp/synapsor-cli-<version>.tgz"
74
+
75
+ corepack pnpm verify:hosted-cloud-linked
76
+ ```
77
+
78
+ The command refuses to run unless both opt-in flags are present. HTTPS is
79
+ required unless `SYNAPSOR_E2E_ALLOW_HTTP=1` is explicitly set for a local
80
+ control-plane test. The generated Runner token is revoked in a `finally` block,
81
+ and the temporary directory is removed unless
82
+ `SYNAPSOR_E2E_KEEP_TEMP=1` is set for debugging.
83
+
84
+ ## Passing Result
85
+
86
+ A pass prints only a boolean summary with all checks set to `true`. The
87
+ `token_rotated`, `registration_idempotent`, `two_runner_exclusive_claim`,
88
+ `registration_heartbeat`, `proposal_reviewed_metadata`,
89
+ `two_runner_exclusive_claim`, `receipt_replay_linked`, `activity_exported`,
90
+ `contract_governance`, and `revoked_operations_blocked` fields prove those
91
+ additional boundaries explicitly. It does not print bearer tokens, database
92
+ URLs, source rows, or kept-out values. Confirm the synthetic fixture is reset
93
+ after the run before reusing the project.
94
+
95
+ This gate is evidence for the existing single-node design-partner boundary. It
96
+ does not establish multi-region availability, managed Runner hosting, SSO/SCIM,
97
+ legal hold, formal compliance certification, or an enterprise SLA.
@@ -9,9 +9,12 @@ evidence and proposals, keeps approval outside MCP, applies or routes approved
9
9
  writeback, and records receipts and replay in the default local SQLite ledger
10
10
  or an opt-in shared Postgres runtime store.
11
11
 
12
- Cloud is the team control plane. It stores versioned contracts, produces
13
- downloadable Runner bundles, and provides shared activity, evidence, approval,
14
- and investigation surfaces for enabled design-partner deployments.
12
+ Cloud is the team control plane. In the design-partner deployment it stores
13
+ versioned contracts, issues source-scoped Runner identities, produces
14
+ credential-free bundles, receives reviewed local proposals, records human
15
+ decisions, leases approved jobs to compatible local Runners, and links safe
16
+ activity/receipt/replay metadata. The database connection and enforcement
17
+ runtime stay local.
15
18
 
16
19
  | Need | OSS Runner | Synapsor Cloud |
17
20
  | --- | --- | --- |
@@ -19,9 +22,9 @@ and investigation surfaces for enabled design-partner deployments.
19
22
  | Contract source | Local files reviewed in Git | Shared registry with immutable versions and digests |
20
23
  | Trusted context | Local environment/session bindings | Registered bindings plus deployment-specific Cloud session context |
21
24
  | Capabilities | Local semantic MCP tools | Registry, version history, and capability inspection |
22
- | Evidence and replay | Local SQLite ledger by default; optional shared Postgres runtime store | Shared activity and evidence surfaces where enabled |
23
- | Approval | Local CLI or localhost UI | Team approval surfaces where enabled |
24
- | Writeback | Guarded one-row CRUD, fixed/frozen bounded sets, or app-owned executor | Cloud-linked jobs with local execution; managed production orchestration is future work |
25
+ | Evidence and replay | Local SQLite ledger by default; optional shared Postgres runtime store | Redacted shared chronology and references; full evidence payload stays local by default |
26
+ | Approval | Local CLI or localhost UI | Human-authenticated shared approval inbox; unavailable to MCP/Runner tokens |
27
+ | Writeback | Guarded one-row CRUD, fixed/frozen bounded sets, or app-owned executor | Durable approval/job/lease coordination; the local Runner rechecks and executes |
25
28
  | MCP risk audit | Static local audit | Organization-wide continuous audit is future work |
26
29
  | Identity | Local operator boundary | Workspace RBAC where configured; SAML and SCIM are future work |
27
30
  | Operations | Customer-operated single node or bounded small fleet | Managed fleet remains Cloud work; no enterprise SLA in the current beta |
@@ -45,6 +48,10 @@ environment-variable wiring, and MCP client templates.
45
48
  3. Keep contracts in Git and exercise proposal, approval, writeback, and replay.
46
49
  4. Push the validated contract when the team needs a shared registry and review
47
50
  surface.
51
+ 5. Create a source-scoped Runner token, download a bundle, and verify the
52
+ registration heartbeat.
53
+ 6. Sync one staging proposal, approve it in Cloud, and verify the local guarded
54
+ write plus linked receipt/replay chronology.
48
55
 
49
56
  See [Cloud Push](cloud-push.md), [Runner Bundles](runner-bundles.md), and
50
57
  [Cloud Mode](cloud-mode.md) for commands and deployment details. See
@@ -10,6 +10,54 @@ npx -y -p @synapsor/runner synapsor-runner demo --quick
10
10
  The OSS runner command is `synapsor-runner`. The `synapsor` command is reserved
11
11
  for the Synapsor Cloud CLI.
12
12
 
13
+ ## 1.4.123 (prepared, not published)
14
+
15
+ ### Advisory capability-surface fitness lint
16
+
17
+ - `contract lint` now reports high-signal breadth-drift advisories for generic
18
+ query/predicate-style string arguments, capability density above eight on one
19
+ target, operation names that do not read as business actions, and structural
20
+ near-duplicates with identical or loosened arguments.
21
+ - Findings are deterministic across declaration order and share stable codes in
22
+ text, JSON, and SARIF. JSON/SARIF include reviewer-safe metrics and structural
23
+ differences without reading a database or environment values.
24
+ - Advisory-only lint still exits successfully by default. Teams may opt into a
25
+ CI policy gate with `--strict` or `--fail-on warning`; canonical validity and
26
+ runtime enforcement are unchanged.
27
+
28
+ Prepared package version: `@synapsor/runner@1.4.123`. Spec, DSL, and Cloud CLI
29
+ packages are unchanged.
30
+
31
+ ## 1.4.122 (2026-07-16)
32
+
33
+ ### Trusted principal scope and Cloud-linked authority
34
+
35
+ - Contracts may bind a reviewed target column to a required trusted principal
36
+ in addition to the existing tenant lock. Runner applies both predicates in
37
+ SQL and never accepts the principal value from model arguments.
38
+ - Same-tenant rows owned by another principal and cross-principal evidence,
39
+ proposal, receipt, and replay handles return the same generic miss as absent
40
+ or cross-tenant resources.
41
+ - PostgreSQL/MySQL live tests cover scoped read/propose/insert/update/delete,
42
+ aggregate and bounded-set operations, conflict/idempotency, compensation,
43
+ signed HTTP sessions, and generic denial behavior.
44
+ - In explicit `cloud_linked` mode, Cloud is authoritative for governance while
45
+ the local/shared Runner store remains the durable operational spool. An
46
+ idempotent outbox synchronizes bounded proposal/activity/result metadata;
47
+ full evidence, source rows, SQL details, replay payloads, and credentials
48
+ remain local.
49
+ - Cloud-linked approval and apply cannot fall back to local operator commands.
50
+ The trusted Runner still verifies the exact local contract, proposal hash,
51
+ tenant/principal guards, bounds, conflict checks, and receipt rules before
52
+ source mutation.
53
+ - A separate `@synapsor/cli@0.1.0-beta.1` package manages Cloud contracts,
54
+ projects, scoped credentials, Runner connections, proposal decisions, and
55
+ audit records. Runner keeps the `synapsor-runner` binary and local boundary.
56
+
57
+ Prepared package versions: `@synapsor/spec@1.4.2`,
58
+ `@synapsor/dsl@1.4.3`, `@synapsor/runner@1.4.122`, and
59
+ `@synapsor/cli@0.1.0-beta.1`.
60
+
13
61
  ## 1.4.121 (prepared, not published)
14
62
 
15
63
  ### Contract trust surface and bounded-set parser correctness
@@ -0,0 +1,75 @@
1
+ # RFC 007: Trusted Principal Row Scope
2
+
3
+ Status: accepted for implementation
4
+
5
+ ## Problem
6
+
7
+ Runner already binds a trusted tenant and principal to a context, but the
8
+ principal is currently identity/audit metadata rather than a database row
9
+ predicate. Two users in one tenant can therefore share a tenant-scoped
10
+ capability even when each should see only rows assigned to that user.
11
+
12
+ ## Canonical Shape
13
+
14
+ Add one optional subject field:
15
+
16
+ ```json
17
+ {
18
+ "tenant_key": "hospital_id",
19
+ "principal_scope_key": "assigned_to"
20
+ }
21
+ ```
22
+
23
+ The value for `principal_scope_key` always comes from the capability context's
24
+ required `principal_binding`. The contract does not store a principal value and
25
+ the model does not supply one.
26
+
27
+ The DSL form is:
28
+
29
+ ```sql
30
+ TENANT KEY hospital_id
31
+ PRINCIPAL SCOPE KEY assigned_to
32
+ ```
33
+
34
+ No second representation is introduced. Existing contracts without the field
35
+ retain their current tenant-scoped semantics.
36
+
37
+ ## Invariant
38
+
39
+ ```text
40
+ effective row authority = tenant predicate
41
+ AND principal predicate
42
+ AND reviewed fixed guards
43
+ ```
44
+
45
+ Principal scope is invalid without tenant scope or without a required trusted
46
+ principal binding. It cannot replace tenant scope, use a model argument, OR
47
+ with another predicate, default to match-all, or be bypassed dynamically. A
48
+ tenant-wide supervisor uses a separately reviewed capability without principal
49
+ scope.
50
+
51
+ ## Enforcement
52
+
53
+ The principal predicate is a bound SQL parameter in every applicable read,
54
+ proposal, aggregate, bounded-set, CRUD, compensation, and apply-time query.
55
+ INSERT forces the reviewed column from trusted context. Executor envelopes carry
56
+ an immutable reviewed scope guard. The principal scope is included in proposal
57
+ integrity and writeback authority.
58
+
59
+ Cloud receives only the reviewed column/binding metadata and a stable safe
60
+ fingerprint. The local proposal retains the trusted value needed for SQL. A
61
+ Cloud job cannot substitute it; Runner verifies the job fingerprint, proposal
62
+ hash, and contract digest before mutation.
63
+
64
+ Evidence, proposal, receipt, activity, and replay lookups recheck tenant and
65
+ principal authority. Opaque handles are not bearer credentials. A same-tenant
66
+ cross-principal miss has the same public result as a cross-tenant or absent row,
67
+ and the query performs no separate existence probe.
68
+
69
+ ## Compatibility And Release
70
+
71
+ The field changes `@synapsor/spec`, `@synapsor/dsl`, and Runner behavior. It is
72
+ covered by canonical normalization/digest tests, TypeScript/C++ round-trip
73
+ fixtures, PostgreSQL/MySQL live tests, and packed-package tests. The next Runner
74
+ release remains on the `1.4.x` line and targets `1.4.122` if unused; this RFC
75
+ does not justify a `1.5.0` jump.
@@ -11,6 +11,7 @@ Every bundle contains:
11
11
  ```text
12
12
  synapsor.contract.json
13
13
  synapsor.runner.json
14
+ synapsor.cloud.json
14
15
  .env.example
15
16
  README.md
16
17
  mcp-client-examples/
@@ -43,9 +44,27 @@ set -a && . ./.env && set +a
43
44
  npx -y -p @synapsor/runner synapsor-runner contract validate ./synapsor.contract.json
44
45
  npx -y -p @synapsor/runner synapsor-runner config validate --config ./synapsor.runner.json
45
46
  npx -y -p @synapsor/runner synapsor-runner tools preview --config ./synapsor.runner.json --store ./.synapsor/local.db
47
+ npx -y -p @synapsor/runner synapsor-runner cloud connect --config ./synapsor.cloud.json
46
48
  npx -y -p @synapsor/runner synapsor-runner mcp serve --config ./synapsor.runner.json --store ./.synapsor/local.db
47
49
  ```
48
50
 
51
+ For proposal contracts, run the trusted worker in a separate
52
+ operator-controlled terminal:
53
+
54
+ ```bash
55
+ set -a && . ./.env && set +a
56
+ npx -y -p @synapsor/runner synapsor-runner start --config ./synapsor.runner.json --store ./.synapsor/local.db
57
+ ```
58
+
59
+ After an MCP tool creates a local proposal, sync its reviewed diff and safe
60
+ references to Cloud:
61
+
62
+ ```bash
63
+ npx -y -p @synapsor/runner synapsor-runner cloud sync latest \
64
+ --config ./synapsor.cloud.json \
65
+ --store ./.synapsor/local.db
66
+ ```
67
+
49
68
  For the OpenAI Agents SDK, use the included TypeScript examples. Their stdio
50
69
  command enables `--alias-mode openai`; the Streamable HTTP example includes the
51
70
  matching server command. Claude, Cursor, and generic templates use canonical
@@ -57,3 +76,8 @@ Approval and writeback remain outside the model-facing MCP tool surface. Use
57
76
  The bundle's `.env.example` contains names and placeholders only. Fill it for a
58
77
  local or staging source, source it into the shell or process that launches your
59
78
  MCP client, and keep the resulting `.env` out of source control.
79
+
80
+ The Cloud `source_id` scopes the Runner token and job queue. The contract's
81
+ source alias selects the local database adapter. They are intentionally
82
+ different identifiers. Cloud jobs never contain a database URL or handler
83
+ secret.
@@ -132,6 +132,14 @@ Every served capability context must bind tenant and principal from
132
132
  `HTTP_CLAIM`. Runner rejects an environment-bound contract mixed into this
133
133
  server with `TRUSTED_CONTEXT_PROVIDER_CONFLICT` before tools are served.
134
134
 
135
+ For same-tenant per-user rows, declare `PRINCIPAL SCOPE KEY assigned_to` (or
136
+ the corresponding canonical `principal_scope_key`) on the capability. The
137
+ signed `sub` claim then becomes a bound SQL predicate composed with the signed
138
+ tenant claim. Session token fingerprint pinning prevents swapping identities
139
+ within an MCP session. `tools preview` displays both locks, and
140
+ `corepack pnpm test:principal-scope` proves two concurrent principals cannot
141
+ read each other's rows or evidence handles on Postgres and MySQL.
142
+
135
143
  Use an HTTPS JWKS URL from an allowlisted identity system. Runner rejects
136
144
  redirects, oversized responses, unknown algorithms, `none`, missing/unknown
137
145
  `kid`, bad issuer/audience, expired/not-yet-valid tokens, and private JWK
@@ -50,6 +50,20 @@ Trusted context comes from local configuration, environment bindings, or Cloud
50
50
  session context in Cloud mode. Tenant, principal, and authorization scope must
51
51
  not be accepted from the model as authority.
52
52
 
53
+ A capability may declare `principal_scope_key` (DSL: `PRINCIPAL SCOPE KEY`) to
54
+ narrow rows inside a tenant. Runner then adds both fixed, parameterized guards:
55
+
56
+ ```text
57
+ tenant_key = verified tenant AND principal_scope_key = verified principal
58
+ ```
59
+
60
+ This applies to single-row reads and writes, aggregates, bounded sets, inserts,
61
+ deletes, executor envelopes, and reviewed compensation. Missing principal
62
+ context fails closed. A principal lock never replaces tenant scope and has no
63
+ dynamic supervisor bypass. Use a separately reviewed tenant-wide capability
64
+ for broader roles. RLS and restricted database roles remain valuable defense in
65
+ depth; the contract does not replace them.
66
+
53
67
  Proposal, evidence, and replay handles are references, not bearer credentials.
54
68
  Before returning a local MCP resource, Runner resolves the resource's owning
55
69
  capability context again and requires both tenant and principal to match the
@@ -76,7 +90,7 @@ outside the model-facing MCP server and verifies:
76
90
  - local approval state;
77
91
  - proposal and job digests;
78
92
  - target schema/table;
79
- - primary-key and tenant guards;
93
+ - primary-key, tenant, and declared principal-row guards;
80
94
  - allowed mutable columns;
81
95
  - conflict/version guard;
82
96
  - idempotency key;
@@ -128,7 +142,10 @@ credentials, approval tools, commit tools, or controls that widen reviewed
128
142
  tables/columns.
129
143
 
130
144
  Contract lint and tests are review aids rather than a proof of complete
131
- security. Scoped report exports omit evidence rows and kept-out values and are
145
+ security. Capability breadth can still drift as narrow tools accumulate;
146
+ surface-fitness lint makes high-signal generic, dense, poorly named, and
147
+ near-duplicate surfaces visible for review but does not enforce good design.
148
+ Scoped report exports omit evidence rows and kept-out values and are
132
149
  tamper-evident when their digest/signature verifies; local SQLite is not an
133
150
  immutable compliance service. Graduated-trust evaluation is operator-only,
134
151
  disabled by default, and can recommend/export a separate artifact but cannot
@@ -4,6 +4,7 @@ CREATE TABLE invoices (
4
4
  status varchar(64) NOT NULL,
5
5
  late_fee_cents integer NOT NULL,
6
6
  waiver_reason varchar(500),
7
+ assigned_to varchar(128) NOT NULL DEFAULT 'case_manager_a',
7
8
  updated_at datetime(6) NOT NULL
8
9
  );
9
10
 
@@ -41,7 +42,10 @@ INSERT INTO guard_crud_items (id, tenant_id, request_id, value_cents, version) V
41
42
  ALTER TABLE guard_crud_items AUTO_INCREMENT = 2001;
42
43
 
43
44
  INSERT INTO invoices (id, tenant_id, status, late_fee_cents, waiver_reason, updated_at) VALUES
44
- ('MYSQL-ACME', 'acme', 'overdue', 700, NULL, '2026-07-12 12:00:00.000000');
45
+ ('MYSQL-ACME', 'acme', 'overdue', 700, NULL, '2026-07-12 12:00:00.000000'),
46
+ ('MYSQL-PRINCIPAL-A', 'hospital_a', 'principal_fixture', 100, NULL, '2026-07-12 12:00:00.000000'),
47
+ ('MYSQL-PRINCIPAL-B', 'hospital_a', 'principal_fixture', 200, NULL, '2026-07-12 12:00:00.000000');
48
+ UPDATE invoices SET assigned_to = 'case_manager_b' WHERE id = 'MYSQL-PRINCIPAL-B';
45
49
 
46
50
  CREATE VIEW slow_invoices AS
47
51
  SELECT invoices.*, SLEEP(0.35) AS synthetic_delay
@@ -4,6 +4,7 @@ CREATE TABLE public.invoices (
4
4
  status text NOT NULL,
5
5
  late_fee_cents integer NOT NULL,
6
6
  waiver_reason text,
7
+ assigned_to text NOT NULL DEFAULT 'case_manager_a',
7
8
  updated_at timestamptz NOT NULL
8
9
  );
9
10
 
@@ -73,7 +74,10 @@ INSERT INTO public.invoices (id, tenant_id, status, late_fee_cents, waiver_reaso
73
74
  ('INV-DEAD-DISCARD', 'acme', 'overdue', 1750, NULL, '2026-07-12T12:00:00Z'),
74
75
  ('INV-KILL-BEFORE', 'acme', 'overdue', 1200, NULL, '2026-07-12T12:00:00Z'),
75
76
  ('INV-KILL-DURING', 'acme', 'overdue', 1500, NULL, '2026-07-12T12:00:00Z'),
76
- ('INV-KILL-AFTER', 'acme', 'overdue', 1800, NULL, '2026-07-12T12:00:00Z');
77
+ ('INV-KILL-AFTER', 'acme', 'overdue', 1800, NULL, '2026-07-12T12:00:00Z'),
78
+ ('INV-PRINCIPAL-A', 'hospital_a', 'principal_fixture', 100, NULL, '2026-07-12T12:00:00Z'),
79
+ ('INV-PRINCIPAL-B', 'hospital_a', 'principal_fixture', 200, NULL, '2026-07-12T12:00:00Z');
80
+ UPDATE public.invoices SET assigned_to = 'case_manager_b' WHERE id = 'INV-PRINCIPAL-B';
77
81
 
78
82
  CREATE FUNCTION public.synthetic_pool_delay() RETURNS integer
79
83
  LANGUAGE plpgsql VOLATILE AS $$
@@ -272,10 +272,12 @@ Real push uses your Cloud env vars:
272
272
 
273
273
  ```bash
274
274
  export SYNAPSOR_CLOUD_BASE_URL="https://api.synapsor.ai"
275
- export SYNAPSOR_CLOUD_TOKEN="<workspace-scoped-token>"
276
- export SYNAPSOR_WORKSPACE_ID="<workspace_id>"
275
+ export SYNAPSOR_API_KEY="<contracts-write-service-key>"
276
+ export SYNAPSOR_PROJECT_ID="<project_id>"
277
+ export SYNAPSOR_SOURCE_ID="<cloud_source_id>"
277
278
 
278
279
  synapsor-runner cloud push examples/support-plan-credit/synapsor.contract.json \
280
+ --workspace "$SYNAPSOR_PROJECT_ID" \
279
281
  --name support-plan-credit
280
282
  ```
281
283
 
@@ -283,11 +285,11 @@ After push, Cloud can return the registered version and a runner bundle with
283
285
  placeholder env names:
284
286
 
285
287
  ```bash
286
- curl -H "Authorization: Bearer $SYNAPSOR_CLOUD_TOKEN" \
287
- "$SYNAPSOR_CLOUD_BASE_URL/v1/control/projects/$SYNAPSOR_WORKSPACE_ID/agent-contracts/<contract_id>/versions/<version_id>"
288
+ curl -H "Authorization: Bearer $SYNAPSOR_API_KEY" \
289
+ "$SYNAPSOR_CLOUD_BASE_URL/v1/control/projects/$SYNAPSOR_PROJECT_ID/agent-contracts/<contract_id>/versions/<version_id>"
288
290
 
289
- curl -H "Authorization: Bearer $SYNAPSOR_CLOUD_TOKEN" \
290
- "$SYNAPSOR_CLOUD_BASE_URL/v1/control/projects/$SYNAPSOR_WORKSPACE_ID/agent-contracts/<contract_id>/versions/<version_id>/runner-bundle?download=1" \
291
+ curl -H "Authorization: Bearer $SYNAPSOR_API_KEY" \
292
+ "$SYNAPSOR_CLOUD_BASE_URL/v1/control/projects/$SYNAPSOR_PROJECT_ID/agent-contracts/<contract_id>/versions/<version_id>/runner-bundle?download=1&source_id=$SYNAPSOR_SOURCE_ID" \
291
293
  -o support-plan-credit-runner-bundle.zip
292
294
  ```
293
295
 
@@ -0,0 +1,190 @@
1
+ {
2
+ "spec_version": "0.1",
3
+ "kind": "SynapsorContract",
4
+ "metadata": {
5
+ "name": "capability surface fitness fixture",
6
+ "description": "Exercises advisory capability-breadth review without changing contract validity.",
7
+ "version": "1.0.0"
8
+ },
9
+ "resources": [
10
+ {
11
+ "name": "billing_invoices",
12
+ "engine": "postgres",
13
+ "schema": "public",
14
+ "table": "invoices",
15
+ "type": "table",
16
+ "primary_key": "id",
17
+ "tenant_key": "tenant_id",
18
+ "conflict_key": "updated_at"
19
+ }
20
+ ],
21
+ "contexts": [
22
+ {
23
+ "name": "billing_operator",
24
+ "bindings": [
25
+ {
26
+ "name": "tenant_id",
27
+ "source": "environment",
28
+ "key": "SYNAPSOR_TENANT_ID",
29
+ "required": true
30
+ }
31
+ ],
32
+ "tenant_binding": "tenant_id"
33
+ }
34
+ ],
35
+ "capabilities": [
36
+ {
37
+ "name": "billing.execute",
38
+ "description": "Look up one reviewed invoice reference.",
39
+ "returns_hint": "One allowlisted invoice row.",
40
+ "kind": "read",
41
+ "context": "billing_operator",
42
+ "source": "local_postgres",
43
+ "subject": { "resource": "billing_invoices" },
44
+ "args": {
45
+ "filter": { "type": "string", "required": true, "max_length": 128 }
46
+ },
47
+ "lookup": { "id_from_arg": "filter" },
48
+ "visible_fields": ["id", "tenant_id", "status"],
49
+ "kept_out_fields": ["card_token", "internal_note"],
50
+ "evidence": { "required": true, "query_audit": true },
51
+ "max_rows": 1
52
+ },
53
+ {
54
+ "name": "billing.inspect_invoice_primary",
55
+ "description": "Inspect one primary invoice.",
56
+ "returns_hint": "One allowlisted invoice row.",
57
+ "kind": "read",
58
+ "context": "billing_operator",
59
+ "source": "local_postgres",
60
+ "subject": { "resource": "billing_invoices" },
61
+ "args": {
62
+ "invoice_id": { "type": "string", "required": true, "max_length": 128 }
63
+ },
64
+ "lookup": { "id_from_arg": "invoice_id" },
65
+ "visible_fields": ["id", "tenant_id", "status"],
66
+ "kept_out_fields": ["card_token", "internal_note"],
67
+ "evidence": { "required": true, "query_audit": true },
68
+ "max_rows": 1
69
+ },
70
+ {
71
+ "name": "billing.inspect_invoice_copy",
72
+ "description": "Inspect the same reviewed invoice shape for duplicate detection.",
73
+ "returns_hint": "One allowlisted invoice row.",
74
+ "kind": "read",
75
+ "context": "billing_operator",
76
+ "source": "local_postgres",
77
+ "subject": { "resource": "billing_invoices" },
78
+ "args": {
79
+ "invoice_id": { "type": "string", "required": true, "max_length": 128 }
80
+ },
81
+ "lookup": { "id_from_arg": "invoice_id" },
82
+ "visible_fields": ["id", "tenant_id", "status"],
83
+ "kept_out_fields": ["card_token", "internal_note"],
84
+ "evidence": { "required": true, "query_audit": true },
85
+ "max_rows": 1
86
+ },
87
+ {
88
+ "name": "billing.inspect_invoice_secondary",
89
+ "description": "Inspect one secondary invoice.",
90
+ "returns_hint": "One allowlisted invoice row.",
91
+ "kind": "read",
92
+ "context": "billing_operator",
93
+ "source": "local_postgres",
94
+ "subject": { "resource": "billing_invoices" },
95
+ "args": {
96
+ "secondary_invoice_id": { "type": "string", "required": true, "max_length": 128 }
97
+ },
98
+ "lookup": { "id_from_arg": "secondary_invoice_id" },
99
+ "visible_fields": ["id", "tenant_id", "status"],
100
+ "kept_out_fields": ["card_token", "internal_note"],
101
+ "evidence": { "required": true, "query_audit": true },
102
+ "max_rows": 1
103
+ },
104
+ {
105
+ "name": "billing.inspect_invoice_archived",
106
+ "description": "Inspect one archived invoice.",
107
+ "returns_hint": "One allowlisted invoice row.",
108
+ "kind": "read",
109
+ "context": "billing_operator",
110
+ "source": "local_postgres",
111
+ "subject": { "resource": "billing_invoices" },
112
+ "args": {
113
+ "archived_invoice_id": { "type": "string", "required": true, "max_length": 128 }
114
+ },
115
+ "lookup": { "id_from_arg": "archived_invoice_id" },
116
+ "visible_fields": ["id", "tenant_id", "status"],
117
+ "kept_out_fields": ["card_token", "internal_note"],
118
+ "evidence": { "required": true, "query_audit": true },
119
+ "max_rows": 1
120
+ },
121
+ {
122
+ "name": "billing.inspect_invoice_disputed",
123
+ "description": "Inspect one disputed invoice.",
124
+ "returns_hint": "One allowlisted invoice row.",
125
+ "kind": "read",
126
+ "context": "billing_operator",
127
+ "source": "local_postgres",
128
+ "subject": { "resource": "billing_invoices" },
129
+ "args": {
130
+ "disputed_invoice_id": { "type": "string", "required": true, "max_length": 128 }
131
+ },
132
+ "lookup": { "id_from_arg": "disputed_invoice_id" },
133
+ "visible_fields": ["id", "tenant_id", "status"],
134
+ "kept_out_fields": ["card_token", "internal_note"],
135
+ "evidence": { "required": true, "query_audit": true },
136
+ "max_rows": 1
137
+ },
138
+ {
139
+ "name": "billing.inspect_invoice_overdue",
140
+ "description": "Inspect one overdue invoice.",
141
+ "returns_hint": "One allowlisted invoice row.",
142
+ "kind": "read",
143
+ "context": "billing_operator",
144
+ "source": "local_postgres",
145
+ "subject": { "resource": "billing_invoices" },
146
+ "args": {
147
+ "overdue_invoice_id": { "type": "string", "required": true, "max_length": 128 }
148
+ },
149
+ "lookup": { "id_from_arg": "overdue_invoice_id" },
150
+ "visible_fields": ["id", "tenant_id", "status"],
151
+ "kept_out_fields": ["card_token", "internal_note"],
152
+ "evidence": { "required": true, "query_audit": true },
153
+ "max_rows": 1
154
+ },
155
+ {
156
+ "name": "billing.inspect_invoice_paid",
157
+ "description": "Inspect one paid invoice.",
158
+ "returns_hint": "One allowlisted invoice row.",
159
+ "kind": "read",
160
+ "context": "billing_operator",
161
+ "source": "local_postgres",
162
+ "subject": { "resource": "billing_invoices" },
163
+ "args": {
164
+ "paid_invoice_id": { "type": "string", "required": true, "max_length": 128 }
165
+ },
166
+ "lookup": { "id_from_arg": "paid_invoice_id" },
167
+ "visible_fields": ["id", "tenant_id", "status"],
168
+ "kept_out_fields": ["card_token", "internal_note"],
169
+ "evidence": { "required": true, "query_audit": true },
170
+ "max_rows": 1
171
+ },
172
+ {
173
+ "name": "billing.inspect_invoice_voided",
174
+ "description": "Inspect one voided invoice.",
175
+ "returns_hint": "One allowlisted invoice row.",
176
+ "kind": "read",
177
+ "context": "billing_operator",
178
+ "source": "local_postgres",
179
+ "subject": { "resource": "billing_invoices" },
180
+ "args": {
181
+ "voided_invoice_id": { "type": "string", "required": true, "max_length": 128 }
182
+ },
183
+ "lookup": { "id_from_arg": "voided_invoice_id" },
184
+ "visible_fields": ["id", "tenant_id", "status"],
185
+ "kept_out_fields": ["card_token", "internal_note"],
186
+ "evidence": { "required": true, "query_audit": true },
187
+ "max_rows": 1
188
+ }
189
+ ]
190
+ }
@@ -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