@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
@@ -0,0 +1,119 @@
1
+ {
2
+ "spec_version": "0.1",
3
+ "kind": "SynapsorContract",
4
+ "metadata": {
5
+ "name": "aggregate read and enum conformance",
6
+ "version": "1.0.0"
7
+ },
8
+ "resources": [
9
+ {
10
+ "name": "billing_invoices",
11
+ "engine": "postgres",
12
+ "schema": "public",
13
+ "table": "invoices",
14
+ "primary_key": "id",
15
+ "tenant_key": "tenant_id"
16
+ }
17
+ ],
18
+ "contexts": [
19
+ {
20
+ "name": "billing_context",
21
+ "bindings": [
22
+ {
23
+ "name": "tenant_id",
24
+ "source": "environment",
25
+ "key": "SYNAPSOR_TENANT_ID"
26
+ },
27
+ {
28
+ "name": "principal",
29
+ "source": "environment",
30
+ "key": "SYNAPSOR_PRINCIPAL"
31
+ }
32
+ ],
33
+ "tenant_binding": "tenant_id",
34
+ "principal_binding": "principal"
35
+ }
36
+ ],
37
+ "capabilities": [
38
+ {
39
+ "name": "billing.sum_overdue_balance",
40
+ "description": "Return one privacy-suppressed aggregate for the trusted tenant.",
41
+ "returns_hint": "Returns one scalar or a suppression result; never member rows.",
42
+ "kind": "aggregate_read",
43
+ "context": "billing_context",
44
+ "source": "local_postgres",
45
+ "subject": {
46
+ "resource": "billing_invoices"
47
+ },
48
+ "args": {},
49
+ "visible_fields": [],
50
+ "kept_out_fields": [
51
+ "customer_email",
52
+ "private_notes"
53
+ ],
54
+ "evidence": {
55
+ "required": true,
56
+ "query_audit": true
57
+ },
58
+ "aggregate": {
59
+ "function": "sum",
60
+ "column": "balance_cents",
61
+ "selection": {
62
+ "all": [
63
+ {
64
+ "column": "status",
65
+ "operator": "eq",
66
+ "value": "overdue"
67
+ }
68
+ ]
69
+ },
70
+ "minimum_group_size": 5
71
+ }
72
+ },
73
+ {
74
+ "name": "billing.inspect_invoice",
75
+ "description": "Inspect one tenant-scoped invoice using a reviewed risk enum.",
76
+ "kind": "read",
77
+ "context": "billing_context",
78
+ "source": "local_postgres",
79
+ "subject": {
80
+ "resource": "billing_invoices"
81
+ },
82
+ "args": {
83
+ "invoice_id": {
84
+ "type": "string",
85
+ "required": true,
86
+ "max_length": 128
87
+ },
88
+ "risk_level": {
89
+ "type": "string",
90
+ "required": true,
91
+ "enum": [
92
+ "low",
93
+ "medium",
94
+ "high"
95
+ ]
96
+ }
97
+ },
98
+ "lookup": {
99
+ "id_from_arg": "invoice_id"
100
+ },
101
+ "visible_fields": [
102
+ "id",
103
+ "tenant_id",
104
+ "status",
105
+ "balance_cents"
106
+ ],
107
+ "kept_out_fields": [
108
+ "customer_email",
109
+ "private_notes"
110
+ ],
111
+ "evidence": {
112
+ "required": true,
113
+ "query_audit": true
114
+ },
115
+ "max_rows": 1
116
+ }
117
+ ]
118
+ }
119
+
@@ -0,0 +1,44 @@
1
+ {
2
+ "spec_version": "0.1",
3
+ "kind": "SynapsorContract",
4
+ "contexts": [
5
+ {
6
+ "name": "local_operator",
7
+ "bindings": [
8
+ { "name": "tenant_id", "source": "environment", "key": "SYNAPSOR_TENANT_ID" },
9
+ { "name": "principal", "source": "environment", "key": "SYNAPSOR_PRINCIPAL" }
10
+ ],
11
+ "tenant_binding": "tenant_id",
12
+ "principal_binding": "principal"
13
+ }
14
+ ],
15
+ "capabilities": [
16
+ {
17
+ "name": "billing.propose_late_fee_waiver",
18
+ "kind": "proposal",
19
+ "context": "local_operator",
20
+ "subject": {
21
+ "schema": "public",
22
+ "table": "invoices",
23
+ "primary_key": "id",
24
+ "tenant_key": "tenant_id",
25
+ "conflict_key": "updated_at"
26
+ },
27
+ "args": { "invoice_id": { "type": "string" } },
28
+ "lookup": { "id_from_arg": "invoice_id" },
29
+ "visible_fields": ["id", "tenant_id", "late_fee_cents", "updated_at"],
30
+ "proposal": {
31
+ "action": "billing.waive_late_fee",
32
+ "allowed_fields": ["late_fee_cents"],
33
+ "patch": { "late_fee_cents": { "fixed": 0 } },
34
+ "conflict_guard": { "column": "updated_at" },
35
+ "approval": {
36
+ "mode": "human",
37
+ "required_role": "billing_lead",
38
+ "required_approvals": 2
39
+ },
40
+ "writeback": { "mode": "direct_sql" }
41
+ }
42
+ }
43
+ ]
44
+ }
@@ -0,0 +1,115 @@
1
+ {
2
+ "spec_version": "0.1",
3
+ "kind": "SynapsorContract",
4
+ "metadata": { "name": "bounded-set-threats", "version": "1.0.0" },
5
+ "contexts": [
6
+ {
7
+ "name": "trusted_operator",
8
+ "bindings": [
9
+ { "name": "tenant_id", "source": "environment", "key": "SYNAPSOR_TENANT_ID", "required": true },
10
+ { "name": "principal", "source": "environment", "key": "SYNAPSOR_PRINCIPAL", "required": true }
11
+ ],
12
+ "tenant_binding": "tenant_id",
13
+ "principal_binding": "principal"
14
+ }
15
+ ],
16
+ "capabilities": [
17
+ {
18
+ "name": "billing.close_overdue_invoices",
19
+ "kind": "proposal",
20
+ "context": "trusted_operator",
21
+ "source": "local_postgres",
22
+ "subject": { "schema": "public", "table": "invoices", "primary_key": "id", "tenant_key": "tenant_id", "conflict_key": "version" },
23
+ "args": { "reason": { "type": "string", "required": true, "max_length": 500 } },
24
+ "visible_fields": ["id", "tenant_id", "status", "balance_cents", "version"],
25
+ "kept_out_fields": ["internal_risk_score"],
26
+ "evidence": { "required": true, "query_audit": true },
27
+ "proposal": {
28
+ "action": "close_overdue",
29
+ "operation": {
30
+ "kind": "update",
31
+ "cardinality": "set",
32
+ "selection": { "all": [{ "column": "status", "operator": "eq", "value": "overdue" }] },
33
+ "max_rows": 10,
34
+ "aggregate_bounds": [{ "column": "balance_cents", "measure": "before", "maximum": 50000 }],
35
+ "version_advance": { "column": "version", "strategy": "integer_increment" }
36
+ },
37
+ "allowed_fields": ["status", "close_reason"],
38
+ "patch": { "status": { "fixed": "closed" }, "close_reason": { "from_arg": "reason" } },
39
+ "conflict_guard": { "column": "version" },
40
+ "approval": { "mode": "human", "required_role": "billing_reviewer" },
41
+ "writeback": { "mode": "direct_sql" }
42
+ }
43
+ },
44
+ {
45
+ "name": "billing.delete_expired_drafts",
46
+ "kind": "proposal",
47
+ "context": "trusted_operator",
48
+ "source": "local_postgres",
49
+ "subject": { "schema": "public", "table": "invoice_drafts", "primary_key": "id", "tenant_key": "tenant_id", "conflict_key": "version" },
50
+ "args": { "reason": { "type": "string", "required": true, "max_length": 500 } },
51
+ "visible_fields": ["id", "tenant_id", "status", "estimated_cents", "version"],
52
+ "evidence": { "required": true, "query_audit": true },
53
+ "proposal": {
54
+ "action": "delete_expired_drafts",
55
+ "operation": {
56
+ "kind": "delete",
57
+ "cardinality": "set",
58
+ "selection": { "all": [{ "column": "status", "operator": "eq", "value": "expired" }] },
59
+ "max_rows": 5,
60
+ "aggregate_bounds": [{ "column": "estimated_cents", "measure": "before", "maximum": 10000 }]
61
+ },
62
+ "allowed_fields": [],
63
+ "patch": {},
64
+ "conflict_guard": { "column": "version" },
65
+ "approval": { "mode": "human", "required_role": "billing_reviewer", "required_approvals": 2 },
66
+ "writeback": { "mode": "direct_sql" }
67
+ }
68
+ },
69
+ {
70
+ "name": "billing.create_credits",
71
+ "kind": "proposal",
72
+ "context": "trusted_operator",
73
+ "source": "local_postgres",
74
+ "subject": { "schema": "public", "table": "account_credits", "primary_key": "id", "tenant_key": "tenant_id" },
75
+ "args": {
76
+ "items": {
77
+ "type": "object_array",
78
+ "required": true,
79
+ "max_items": 10,
80
+ "fields": {
81
+ "id": { "type": "string", "required": true, "max_length": 128 },
82
+ "external_id": { "type": "string", "required": true, "max_length": 128 },
83
+ "amount_cents": { "type": "number", "required": true, "minimum": 1, "maximum": 2500 },
84
+ "reason": { "type": "string", "required": true, "max_length": 500 }
85
+ }
86
+ }
87
+ },
88
+ "visible_fields": ["id", "tenant_id", "external_id", "amount_cents", "reason", "version"],
89
+ "kept_out_fields": ["internal_note"],
90
+ "evidence": { "required": true, "query_audit": true },
91
+ "proposal": {
92
+ "action": "create_credits",
93
+ "operation": {
94
+ "kind": "insert",
95
+ "cardinality": "set",
96
+ "batch": { "items_from_arg": "items" },
97
+ "max_rows": 10,
98
+ "aggregate_bounds": [{ "column": "amount_cents", "measure": "after", "maximum": 25000 }],
99
+ "deduplication": {
100
+ "components": [
101
+ { "column": "tenant_id", "source": "trusted_tenant" },
102
+ { "column": "id", "source": "item_field", "item_field": "id" },
103
+ { "column": "external_id", "source": "item_field", "item_field": "external_id" }
104
+ ]
105
+ }
106
+ },
107
+ "allowed_fields": ["amount_cents", "reason"],
108
+ "patch": { "amount_cents": { "from_item": "amount_cents" }, "reason": { "from_item": "reason" } },
109
+ "numeric_bounds": { "amount_cents": { "minimum": 1, "maximum": 2500 } },
110
+ "approval": { "mode": "human", "required_role": "billing_reviewer" },
111
+ "writeback": { "mode": "direct_sql" }
112
+ }
113
+ }
114
+ ]
115
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "spec_version": "0.1",
3
+ "kind": "SynapsorContract",
4
+ "metadata": {
5
+ "name": "trusted principal row scope",
6
+ "description": "Tenant-additive row authority for two case managers in one hospital."
7
+ },
8
+ "contexts": [
9
+ {
10
+ "name": "care_session",
11
+ "bindings": [
12
+ {
13
+ "name": "hospital_id",
14
+ "source": "http_claim",
15
+ "key": "hospital_id",
16
+ "required": true
17
+ },
18
+ {
19
+ "name": "principal",
20
+ "source": "http_claim",
21
+ "key": "sub",
22
+ "required": true
23
+ }
24
+ ],
25
+ "tenant_binding": "hospital_id",
26
+ "principal_binding": "principal"
27
+ }
28
+ ],
29
+ "resources": [],
30
+ "capabilities": [
31
+ {
32
+ "name": "care.inspect_assigned_patient",
33
+ "description": "Inspect one patient assigned to the authenticated case manager within the trusted hospital.",
34
+ "returns_hint": "Returns reviewed patient fields and evidence only when both tenant and assignee locks match.",
35
+ "kind": "read",
36
+ "context": "care_session",
37
+ "source": "local_postgres",
38
+ "subject": {
39
+ "schema": "public",
40
+ "table": "patients",
41
+ "primary_key": "id",
42
+ "tenant_key": "hospital_id",
43
+ "principal_scope_key": "assigned_to",
44
+ "conflict_key": "updated_at"
45
+ },
46
+ "args": {
47
+ "patient_id": {
48
+ "type": "string",
49
+ "required": true,
50
+ "description": "Reviewed patient identifier.",
51
+ "max_length": 128
52
+ }
53
+ },
54
+ "lookup": {
55
+ "id_from_arg": "patient_id"
56
+ },
57
+ "visible_fields": [
58
+ "id",
59
+ "hospital_id",
60
+ "display_name",
61
+ "care_status",
62
+ "updated_at"
63
+ ],
64
+ "kept_out_fields": [
65
+ "assigned_to",
66
+ "diagnosis_notes",
67
+ "insurance_member_id"
68
+ ],
69
+ "evidence": {
70
+ "required": true,
71
+ "query_audit": true
72
+ },
73
+ "max_rows": 1
74
+ }
75
+ ],
76
+ "workflows": [],
77
+ "policies": []
78
+ }
@@ -0,0 +1,101 @@
1
+ {
2
+ "spec_version": "0.1",
3
+ "kind": "SynapsorContract",
4
+ "metadata": {
5
+ "name": "proposal capability conformance"
6
+ },
7
+ "resources": [
8
+ {
9
+ "name": "billing_invoices",
10
+ "engine": "postgres",
11
+ "schema": "public",
12
+ "table": "invoices",
13
+ "primary_key": "id",
14
+ "tenant_key": "tenant_id",
15
+ "conflict_key": "updated_at"
16
+ }
17
+ ],
18
+ "contexts": [
19
+ {
20
+ "name": "local_operator",
21
+ "bindings": [
22
+ {
23
+ "name": "tenant_id",
24
+ "source": "environment",
25
+ "key": "SYNAPSOR_TENANT_ID"
26
+ },
27
+ {
28
+ "name": "principal",
29
+ "source": "environment",
30
+ "key": "SYNAPSOR_PRINCIPAL"
31
+ }
32
+ ],
33
+ "tenant_binding": "tenant_id",
34
+ "principal_binding": "principal"
35
+ }
36
+ ],
37
+ "capabilities": [
38
+ {
39
+ "name": "billing.propose_late_fee_waiver",
40
+ "kind": "proposal",
41
+ "context": "local_operator",
42
+ "source": "local_postgres",
43
+ "subject": {
44
+ "resource": "billing_invoices"
45
+ },
46
+ "args": {
47
+ "invoice_id": {
48
+ "type": "string",
49
+ "required": true
50
+ },
51
+ "waiver_reason": {
52
+ "type": "string",
53
+ "required": true
54
+ }
55
+ },
56
+ "lookup": {
57
+ "id_from_arg": "invoice_id"
58
+ },
59
+ "visible_fields": [
60
+ "id",
61
+ "tenant_id",
62
+ "late_fee_cents",
63
+ "waiver_reason",
64
+ "updated_at"
65
+ ],
66
+ "kept_out_fields": [
67
+ "card_token"
68
+ ],
69
+ "evidence": {
70
+ "required": true,
71
+ "query_audit": true
72
+ },
73
+ "max_rows": 1,
74
+ "proposal": {
75
+ "action": "billing.waive_late_fee",
76
+ "allowed_fields": [
77
+ "late_fee_cents",
78
+ "waiver_reason"
79
+ ],
80
+ "patch": {
81
+ "late_fee_cents": {
82
+ "fixed": 0
83
+ },
84
+ "waiver_reason": {
85
+ "from_arg": "waiver_reason"
86
+ }
87
+ },
88
+ "conflict_guard": {
89
+ "column": "updated_at"
90
+ },
91
+ "approval": {
92
+ "mode": "human",
93
+ "required_role": "billing_lead"
94
+ },
95
+ "writeback": {
96
+ "mode": "direct_sql"
97
+ }
98
+ }
99
+ }
100
+ ]
101
+ }
@@ -0,0 +1,98 @@
1
+ {
2
+ "spec_version": "0.1",
3
+ "kind": "SynapsorContract",
4
+ "metadata": {
5
+ "name": "reviewed-reversible-change-sets",
6
+ "version": "1.4.0"
7
+ },
8
+ "contexts": [
9
+ {
10
+ "name": "trusted_operator",
11
+ "bindings": [
12
+ { "name": "tenant_id", "source": "environment", "key": "SYNAPSOR_TENANT_ID", "required": true },
13
+ { "name": "principal", "source": "environment", "key": "SYNAPSOR_PRINCIPAL", "required": true }
14
+ ],
15
+ "tenant_binding": "tenant_id",
16
+ "principal_binding": "principal"
17
+ }
18
+ ],
19
+ "capabilities": [
20
+ {
21
+ "name": "billing.adjust_credit",
22
+ "kind": "proposal",
23
+ "context": "trusted_operator",
24
+ "source": "local_postgres",
25
+ "subject": {
26
+ "schema": "public",
27
+ "table": "account_credits",
28
+ "primary_key": "id",
29
+ "tenant_key": "tenant_id",
30
+ "conflict_key": "version"
31
+ },
32
+ "args": {
33
+ "credit_id": { "type": "string", "required": true, "max_length": 128 },
34
+ "amount_cents": { "type": "number", "required": true, "minimum": 0, "maximum": 2500 }
35
+ },
36
+ "lookup": { "id_from_arg": "credit_id" },
37
+ "visible_fields": ["id", "tenant_id", "amount_cents", "version"],
38
+ "kept_out_fields": ["internal_note"],
39
+ "evidence": { "required": true, "query_audit": true },
40
+ "proposal": {
41
+ "action": "adjust_credit",
42
+ "operation": {
43
+ "kind": "update",
44
+ "cardinality": "single",
45
+ "version_advance": { "column": "version", "strategy": "integer_increment" }
46
+ },
47
+ "allowed_fields": ["amount_cents"],
48
+ "patch": { "amount_cents": { "from_arg": "amount_cents" } },
49
+ "numeric_bounds": { "amount_cents": { "minimum": 0, "maximum": 2500 } },
50
+ "reversibility": { "mode": "reviewed_inverse" },
51
+ "conflict_guard": { "column": "version" },
52
+ "approval": { "mode": "human", "required_role": "billing_reviewer", "required_approvals": 2 },
53
+ "writeback": { "mode": "direct_sql" }
54
+ }
55
+ },
56
+ {
57
+ "name": "billing.create_credit",
58
+ "kind": "proposal",
59
+ "context": "trusted_operator",
60
+ "source": "local_postgres",
61
+ "subject": {
62
+ "schema": "public",
63
+ "table": "account_credits",
64
+ "primary_key": "id",
65
+ "tenant_key": "tenant_id"
66
+ },
67
+ "args": {
68
+ "amount_cents": { "type": "number", "required": true, "minimum": 1, "maximum": 2500 },
69
+ "reason": { "type": "string", "required": true, "max_length": 500 }
70
+ },
71
+ "visible_fields": ["id", "tenant_id", "amount_cents", "reason"],
72
+ "kept_out_fields": ["internal_note"],
73
+ "evidence": { "required": true, "query_audit": true },
74
+ "proposal": {
75
+ "action": "create_credit",
76
+ "operation": {
77
+ "kind": "insert",
78
+ "cardinality": "single",
79
+ "deduplication": {
80
+ "components": [
81
+ { "column": "tenant_id", "source": "trusted_tenant" },
82
+ { "column": "id", "source": "proposal_id" }
83
+ ]
84
+ }
85
+ },
86
+ "allowed_fields": ["amount_cents", "reason"],
87
+ "patch": {
88
+ "amount_cents": { "from_arg": "amount_cents" },
89
+ "reason": { "from_arg": "reason" }
90
+ },
91
+ "numeric_bounds": { "amount_cents": { "minimum": 1, "maximum": 2500 } },
92
+ "reversibility": { "mode": "reviewed_inverse" },
93
+ "approval": { "mode": "human", "required_role": "billing_reviewer" },
94
+ "writeback": { "mode": "direct_sql" }
95
+ }
96
+ }
97
+ ]
98
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "spec_version": "0.1",
3
+ "kind": "SynapsorContract",
4
+ "contexts": [
5
+ {
6
+ "name": "local_operator",
7
+ "bindings": [
8
+ {
9
+ "name": "tenant_id",
10
+ "source": "environment",
11
+ "key": "SYNAPSOR_TENANT_ID"
12
+ },
13
+ {
14
+ "name": "principal",
15
+ "source": "environment",
16
+ "key": "SYNAPSOR_PRINCIPAL"
17
+ }
18
+ ],
19
+ "tenant_binding": "tenant_id",
20
+ "principal_binding": "principal"
21
+ }
22
+ ],
23
+ "capabilities": [
24
+ {
25
+ "name": "billing.inspect_invoice",
26
+ "kind": "read",
27
+ "context": "local_operator",
28
+ "subject": {
29
+ "schema": "public",
30
+ "table": "invoices",
31
+ "primary_key": "id",
32
+ "tenant_key": "tenant_id",
33
+ "conflict_key": "updated_at"
34
+ },
35
+ "args": {
36
+ "invoice_id": {
37
+ "type": "string",
38
+ "required": true
39
+ }
40
+ },
41
+ "lookup": {
42
+ "id_from_arg": "invoice_id"
43
+ },
44
+ "visible_fields": [
45
+ "id",
46
+ "tenant_id",
47
+ "late_fee_cents",
48
+ "updated_at"
49
+ ],
50
+ "kept_out_fields": [
51
+ "card_token"
52
+ ],
53
+ "evidence": {
54
+ "required": true,
55
+ "query_audit": true
56
+ },
57
+ "max_rows": 1
58
+ }
59
+ ]
60
+ }