@synapsor/runner 1.4.123 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +144 -19
  2. package/README.md +132 -135
  3. package/dist/authoring.d.ts +23 -0
  4. package/dist/authoring.d.ts.map +1 -0
  5. package/dist/authoring.mjs +1318 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +4 -5
  8. package/dist/local-ui.d.ts +14 -0
  9. package/dist/local-ui.d.ts.map +1 -1
  10. package/dist/runner.mjs +18427 -7084
  11. package/dist/runtime.d.ts +50 -0
  12. package/dist/runtime.d.ts.map +1 -0
  13. package/dist/runtime.mjs +15094 -0
  14. package/dist/shadow.d.ts +36 -0
  15. package/dist/shadow.d.ts.map +1 -0
  16. package/dist/shadow.mjs +5262 -0
  17. package/docs/README.md +44 -5
  18. package/docs/alternatives.md +122 -0
  19. package/docs/capability-authoring.md +44 -1
  20. package/docs/client-recipes.md +218 -0
  21. package/docs/contract-testing.md +9 -7
  22. package/docs/cursor-plugin.md +78 -0
  23. package/docs/database-enforced-scope.md +273 -0
  24. package/docs/dependency-license-inventory.md +6 -2
  25. package/docs/doctor.md +17 -0
  26. package/docs/effect-regression.md +179 -0
  27. package/docs/fresh-developer-usability.md +110 -0
  28. package/docs/getting-started-own-database.md +103 -2
  29. package/docs/host-compatibility.md +59 -0
  30. package/docs/local-mode.md +12 -7
  31. package/docs/mcp-apps.md +188 -0
  32. package/docs/mcp-audit.md +220 -2
  33. package/docs/mcp-client-setup.md +20 -4
  34. package/docs/mcp-clients.md +41 -4
  35. package/docs/oss-vs-cloud.md +3 -0
  36. package/docs/production.md +8 -0
  37. package/docs/release-notes.md +138 -14
  38. package/docs/release-policy.md +9 -5
  39. package/docs/runner-config-reference.md +25 -0
  40. package/docs/schema-api-candidates.md +68 -0
  41. package/docs/security-boundary.md +30 -5
  42. package/docs/shadow-studies.md +241 -0
  43. package/docs/troubleshooting-first-run.md +46 -0
  44. package/examples/mcp-postgres-billing-app-handler/app-handler.mjs +9 -1
  45. package/examples/mcp-postgres-billing-app-handler/schema.sql +11 -0
  46. package/examples/mcp-postgres-billing-app-handler/synapsor-handler.mjs +3 -0
  47. package/examples/raw-sql-vs-synapsor/Makefile +2 -2
  48. package/examples/raw-sql-vs-synapsor/README.md +6 -4
  49. package/examples/support-billing-agent/Makefile +5 -1
  50. package/examples/support-billing-agent/README.md +57 -5
  51. package/examples/support-billing-agent/app/README.md +6 -0
  52. package/examples/support-billing-agent/app/contract.ts +28 -0
  53. package/examples/support-billing-agent/app/effect-adapter.mjs +23 -0
  54. package/examples/support-billing-agent/app/record-shadow-outcomes.mjs +44 -0
  55. package/examples/support-billing-agent/db/schema.sql +88 -1
  56. package/examples/support-billing-agent/db/seed.sql +15 -15
  57. package/examples/support-billing-agent/scripts/run-demo.sh +4 -0
  58. package/examples/support-billing-agent/scripts/run-evaluation.sh +63 -0
  59. package/examples/support-billing-agent/shadow-study/cases.jsonl +6 -0
  60. package/examples/support-billing-agent/shadow-study/outcomes.jsonl +2 -0
  61. package/examples/support-billing-agent/synapsor.runner.json +17 -5
  62. package/examples/support-plan-credit/README.md +48 -2
  63. package/examples/support-plan-credit/mcp-client-examples/README.md +23 -0
  64. package/examples/support-plan-credit/mcp-client-examples/claude-code.sh +34 -0
  65. package/examples/support-plan-credit/mcp-client-examples/codex.config.toml +24 -0
  66. package/examples/support-plan-credit/mcp-client-examples/generic-stdio.mjs +35 -0
  67. package/examples/support-plan-credit/mcp-client-examples/generic-streamable-http.mjs +31 -0
  68. package/examples/support-plan-credit/mcp-client-examples/google-adk.py +40 -0
  69. package/examples/support-plan-credit/mcp-client-examples/langchain.mjs +29 -0
  70. package/examples/support-plan-credit/mcp-client-examples/llamaindex.py +36 -0
  71. package/examples/support-plan-credit/mcp-client-examples/openai-agents-stdio.ts +1 -1
  72. package/examples/support-plan-credit/mcp-client-examples/openai-agents-streamable-http.ts +1 -1
  73. package/examples/support-plan-credit/mcp-client-examples/vscode.mcp.json +20 -0
  74. package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.contract-tests.generated.json +221 -0
  75. package/examples/support-plan-credit/synapsor/actions/support.propose_plan_credit.ts +34 -0
  76. package/fixtures/effects/changed/eff_support_late_fee_v1.result.json +58 -0
  77. package/fixtures/effects/dataset.json +8 -0
  78. package/fixtures/effects/results/eff_support_late_fee_v1.result.json +59 -0
  79. package/fixtures/effects/support-late-fee.effect.json +71 -0
  80. package/fixtures/generators/drizzle/malicious.ts +11 -0
  81. package/fixtures/generators/drizzle/schema.ts +12 -0
  82. package/fixtures/generators/openapi/external-ref.yaml +15 -0
  83. package/fixtures/generators/openapi/openapi.yaml +81 -0
  84. package/fixtures/generators/prisma/schema.prisma +34 -0
  85. package/fixtures/mcp-audit/README.md +14 -0
  86. package/fixtures/mcp-audit/cursor-bypass-config.json +38 -0
  87. package/fixtures/mcp-audit/dangerous-tools-list.json +33 -0
  88. package/fixtures/mcp-audit/reviewed-proposal-tools-list.json +60 -0
  89. package/package.json +29 -3
  90. package/schemas/effect-dataset.schema.json +19 -0
  91. package/schemas/effect-fixture.schema.json +191 -0
  92. package/schemas/effect-result.schema.json +52 -0
  93. package/schemas/mcp-audit-candidates.schema.json +41 -0
  94. package/schemas/mcp-audit-report.schema.json +203 -0
  95. package/schemas/schema-candidate-review.schema.json +167 -0
  96. package/schemas/schema-candidates.schema.json +49 -0
  97. package/schemas/synapsor.contract-tests.schema.json +1 -1
  98. package/schemas/synapsor.runner.schema.json +51 -0
@@ -0,0 +1,71 @@
1
+ {
2
+ "$schema": "../../schemas/effect-fixture.schema.json",
3
+ "schema_version": "synapsor.effect-fixture.v1",
4
+ "fixture_id": "eff_support_late_fee_v1",
5
+ "fixture_digest": "sha256:d9f1b57bcac0afcc7e29215d52aa998fee4ed1eb3be3d203ecd7366d9925284f",
6
+ "name": "support late-fee waiver",
7
+ "business_request": "Waive the $55 late fee on invoice INV-3001 after reviewing support evidence.",
8
+ "source": {
9
+ "kind": "shadow_case",
10
+ "reference_id": "shc_reference_late_fee_v1",
11
+ "captured_digest": "sha256:998d3f9939fae03b6b3607d954014f11380b5fc4421376a807aaa9c48521ab36"
12
+ },
13
+ "evidence": {
14
+ "mode": "replay_snapshot",
15
+ "bundle_ids": [
16
+ "ev_reference_late_fee_v1"
17
+ ],
18
+ "query_fingerprints": [
19
+ "sha256:f9e33e7580abaedf9c6d7b6a8fa829ad821a39b48a2687b14af2954ef68cd79e"
20
+ ],
21
+ "snapshot": [
22
+ {
23
+ "facts": {
24
+ "support_ticket": "SUP-184",
25
+ "courtesy_waiver_eligible": true,
26
+ "invoice_status": "past_due"
27
+ }
28
+ }
29
+ ],
30
+ "source_reads_during_evaluation": false
31
+ },
32
+ "expected": {
33
+ "capability_calls": [
34
+ "billing.inspect_invoice",
35
+ "billing.propose_late_fee_waiver"
36
+ ],
37
+ "trusted_context": {
38
+ "tenant_id": "acme",
39
+ "principal": "support-agent-reference",
40
+ "provenance": "static_dev"
41
+ },
42
+ "proposal": {
43
+ "result_category": "proposal_created",
44
+ "capability": "billing.propose_late_fee_waiver",
45
+ "contract_version": "0.1.0",
46
+ "target": {
47
+ "business_object": "invoice",
48
+ "object_id": "INV-3001"
49
+ },
50
+ "diff": {
51
+ "late_fee_cents": {
52
+ "before": 5500,
53
+ "proposed": 0
54
+ },
55
+ "waiver_reason": {
56
+ "before": null,
57
+ "proposed": "Courtesy waiver supported by SUP-184"
58
+ }
59
+ },
60
+ "policy": {
61
+ "decision": "pending_review",
62
+ "required_role": "billing_lead"
63
+ }
64
+ },
65
+ "hidden_fields": [
66
+ "card_token",
67
+ "internal_risk_note"
68
+ ],
69
+ "source_database_changed": false
70
+ }
71
+ }
@@ -0,0 +1,11 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { pgTable, text } from "drizzle-orm/pg-core";
3
+
4
+ writeFileSync(process.env.SYNAPSOR_GENERATOR_MARKER!, "executed");
5
+ const unrelatedSecret = process.env.SYNAPSOR_GENERATOR_UNRELATED_SECRET;
6
+
7
+ export const invoices = pgTable("invoices", {
8
+ id: text("id").primaryKey(),
9
+ tenantId: text("tenant_id").notNull(),
10
+ status: text("status").notNull().default(unrelatedSecret),
11
+ });
@@ -0,0 +1,12 @@
1
+ import { integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
2
+
3
+ export const supportTickets = pgTable("support_tickets", {
4
+ id: text("id").primaryKey(),
5
+ tenantId: text("tenant_id").notNull(),
6
+ customerId: text("customer_id").notNull(),
7
+ subject: text("subject").notNull(),
8
+ status: text("status").notNull(),
9
+ internalNote: text("internal_note"),
10
+ priority: integer("priority").notNull(),
11
+ updatedAt: timestamp("updated_at").notNull().defaultNow(),
12
+ });
@@ -0,0 +1,15 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: Forbidden external reference
4
+ version: 1.0.0
5
+ paths:
6
+ /customers/{customer_id}:
7
+ get:
8
+ operationId: getCustomer
9
+ responses:
10
+ "200":
11
+ description: Customer
12
+ content:
13
+ application/json:
14
+ schema:
15
+ $ref: "https://attacker.invalid/customer.yaml"
@@ -0,0 +1,81 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: Support billing API
4
+ version: 1.0.0
5
+ servers:
6
+ - url: https://user:server-secret@example.invalid/api
7
+ paths:
8
+ /invoices/{invoice_id}:
9
+ get:
10
+ operationId: getInvoice
11
+ tags: [billing]
12
+ parameters:
13
+ - name: invoice_id
14
+ in: path
15
+ required: true
16
+ schema:
17
+ type: string
18
+ example: INV-SECRET-EXAMPLE
19
+ responses:
20
+ "200":
21
+ description: Invoice response with server-secret-description
22
+ content:
23
+ application/json:
24
+ schema:
25
+ $ref: "#/components/schemas/Invoice"
26
+ patch:
27
+ operationId: waiveLateFee
28
+ tags: [billing]
29
+ parameters:
30
+ - name: invoice_id
31
+ in: path
32
+ required: true
33
+ schema:
34
+ type: string
35
+ requestBody:
36
+ required: true
37
+ content:
38
+ application/json:
39
+ schema:
40
+ $ref: "#/components/schemas/WaiverRequest"
41
+ responses:
42
+ "200":
43
+ description: Updated invoice
44
+ content:
45
+ application/json:
46
+ schema:
47
+ $ref: "#/components/schemas/Invoice"
48
+ components:
49
+ schemas:
50
+ Invoice:
51
+ type: object
52
+ required: [id, tenant_id, status, balance_cents, updated_at]
53
+ properties:
54
+ id:
55
+ type: string
56
+ readOnly: true
57
+ tenant_id:
58
+ type: string
59
+ status:
60
+ type: string
61
+ balance_cents:
62
+ type: integer
63
+ card_token:
64
+ type: string
65
+ writeOnly: true
66
+ default: card-secret-must-not-escape
67
+ updated_at:
68
+ type: string
69
+ format: date-time
70
+ readOnly: true
71
+ WaiverRequest:
72
+ type: object
73
+ required: [reason]
74
+ properties:
75
+ reason:
76
+ type: string
77
+ example: secret-business-example
78
+ amount_cents:
79
+ type: integer
80
+ minimum: 0
81
+ maximum: 5000
@@ -0,0 +1,34 @@
1
+ datasource db {
2
+ provider = "postgresql"
3
+ url = env("GENERATOR_FIXTURE_DATABASE_URL")
4
+ }
5
+
6
+ generator client {
7
+ provider = "prisma-client-js"
8
+ }
9
+
10
+ model Customer {
11
+ id String @id @default(uuid())
12
+ tenantId String @map("tenant_id")
13
+ email String
14
+ displayName String @map("display_name")
15
+ apiToken String @default("fixture-secret-must-not-escape") @map("api_token")
16
+ updatedAt DateTime @updatedAt @map("updated_at")
17
+ invoices Invoice[]
18
+
19
+ @@map("customers")
20
+ }
21
+
22
+ model Invoice {
23
+ id String @id
24
+ tenantId String @map("tenant_id")
25
+ customerId String @map("customer_id")
26
+ status String
27
+ balanceCents Int @map("balance_cents")
28
+ lateFeeCents Int @map("late_fee_cents")
29
+ internalNote String? @map("internal_note")
30
+ updatedAt DateTime @updatedAt @map("updated_at")
31
+ customer Customer @relation(fields: [customerId], references: [id])
32
+
33
+ @@map("invoices")
34
+ }
@@ -0,0 +1,14 @@
1
+ # MCP audit fixtures
2
+
3
+ These deterministic fixtures exercise the public `synapsor-runner audit`
4
+ contract without a database, account, telemetry, or business-tool call.
5
+
6
+ - `dangerous-tools-list.json` contains raw SQL and model-callable commit
7
+ authority.
8
+ - `reviewed-proposal-tools-list.json` contains a semantic read and an exact
9
+ proposal boundary.
10
+ - `cursor-bypass-config.json` proves that a reviewed Synapsor server does not
11
+ govern a second model-visible server that still exposes `execute_sql`.
12
+
13
+ Static config auditing never launches either command in the Cursor fixture.
14
+ Live inspection requires one exact `--live-server` name and explicit `--yes`.
@@ -0,0 +1,38 @@
1
+ {
2
+ "mcpServers": {
3
+ "synapsor": {
4
+ "type": "stdio",
5
+ "command": "npx",
6
+ "args": [
7
+ "-y",
8
+ "-p",
9
+ "@synapsor/runner@1.5.3",
10
+ "synapsor-runner",
11
+ "mcp",
12
+ "serve",
13
+ "--config",
14
+ "${workspaceFolder}/synapsor.runner.json",
15
+ "--store",
16
+ "${workspaceFolder}/.synapsor/local.db"
17
+ ]
18
+ },
19
+ "legacy-database": {
20
+ "type": "stdio",
21
+ "command": "legacy-database-mcp",
22
+ "args": [],
23
+ "tools": [
24
+ {
25
+ "name": "execute_sql",
26
+ "description": "Execute arbitrary SQL.",
27
+ "inputSchema": {
28
+ "type": "object",
29
+ "properties": {
30
+ "sql": { "type": "string" }
31
+ },
32
+ "required": ["sql"]
33
+ }
34
+ }
35
+ ]
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "jsonrpc": "2.0",
3
+ "id": 1,
4
+ "result": {
5
+ "tools": [
6
+ {
7
+ "name": "execute_sql",
8
+ "description": "Execute arbitrary SQL against the application database.",
9
+ "inputSchema": {
10
+ "type": "object",
11
+ "properties": {
12
+ "sql": { "type": "string" },
13
+ "tenant_id": { "type": "string" },
14
+ "table_name": { "type": "string" },
15
+ "where": { "type": "string" }
16
+ },
17
+ "required": ["sql"]
18
+ }
19
+ },
20
+ {
21
+ "name": "approve_and_apply_change",
22
+ "description": "Approve and apply a database change immediately.",
23
+ "inputSchema": {
24
+ "type": "object",
25
+ "properties": {
26
+ "change_id": { "type": "string" }
27
+ },
28
+ "required": ["change_id"]
29
+ }
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "jsonrpc": "2.0",
3
+ "id": 1,
4
+ "result": {
5
+ "tools": [
6
+ {
7
+ "name": "billing.inspect_invoice",
8
+ "description": "Inspect one tenant-scoped invoice through reviewed visible fields.",
9
+ "inputSchema": {
10
+ "type": "object",
11
+ "properties": {
12
+ "invoice_id": { "type": "string", "maxLength": 128 }
13
+ },
14
+ "required": ["invoice_id"]
15
+ },
16
+ "outputSchema": {
17
+ "type": "object",
18
+ "properties": {
19
+ "status": { "type": "string" },
20
+ "evidence_bundle_id": { "type": "string" }
21
+ },
22
+ "required": ["status", "evidence_bundle_id"]
23
+ },
24
+ "annotations": {
25
+ "readOnlyHint": true,
26
+ "destructiveHint": false
27
+ },
28
+ "x_synapsor_test_fixture": "billing-invoice-safe"
29
+ },
30
+ {
31
+ "name": "billing.propose_late_fee_waiver",
32
+ "description": "Create an exact evidence-backed proposal for review before trusted writeback.",
33
+ "inputSchema": {
34
+ "type": "object",
35
+ "properties": {
36
+ "invoice_id": { "type": "string", "maxLength": 128 },
37
+ "reason": { "type": "string", "maxLength": 500 }
38
+ },
39
+ "required": ["invoice_id", "reason"]
40
+ },
41
+ "outputSchema": {
42
+ "type": "object",
43
+ "properties": {
44
+ "status": { "type": "string" },
45
+ "proposal_id": { "type": "string" },
46
+ "receipt_hash": { "type": "string" },
47
+ "retryable": { "type": "boolean" },
48
+ "source_database_changed": { "type": "boolean" }
49
+ },
50
+ "required": ["status", "proposal_id", "source_database_changed"]
51
+ },
52
+ "annotations": {
53
+ "readOnlyHint": false,
54
+ "destructiveHint": false
55
+ },
56
+ "x_synapsor_test_fixture": "billing-waiver-proposal-safe"
57
+ }
58
+ ]
59
+ }
60
+ }
package/package.json CHANGED
@@ -1,12 +1,26 @@
1
1
  {
2
2
  "name": "@synapsor/runner",
3
- "version": "1.4.123",
3
+ "version": "1.5.3",
4
4
  "description": "Stop giving AI agents execute_sql; expose reviewed Postgres/MySQL MCP actions with proposals, approval, writeback, and replay.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "synapsor-runner": "dist/cli.js"
9
9
  },
10
+ "exports": {
11
+ "./authoring": {
12
+ "types": "./dist/authoring.d.ts",
13
+ "import": "./dist/authoring.mjs"
14
+ },
15
+ "./shadow": {
16
+ "types": "./dist/shadow.d.ts",
17
+ "import": "./dist/shadow.mjs"
18
+ },
19
+ "./runtime": {
20
+ "types": "./dist/runtime.d.ts",
21
+ "import": "./dist/runtime.mjs"
22
+ }
23
+ },
10
24
  "files": [
11
25
  "dist/cli.js",
12
26
  "dist/cli.d.ts",
@@ -14,6 +28,15 @@
14
28
  "dist/local-ui.d.ts",
15
29
  "dist/local-ui.d.ts.map",
16
30
  "dist/runner.mjs",
31
+ "dist/runtime.mjs",
32
+ "dist/runtime.d.ts",
33
+ "dist/runtime.d.ts.map",
34
+ "dist/authoring.mjs",
35
+ "dist/authoring.d.ts",
36
+ "dist/authoring.d.ts.map",
37
+ "dist/shadow.mjs",
38
+ "dist/shadow.d.ts",
39
+ "dist/shadow.d.ts.map",
17
40
  "docs/**/*.md",
18
41
  "examples/dangerous-mcp-tools.json",
19
42
  "examples/app-owned-writeback/**",
@@ -56,20 +79,23 @@
56
79
  "model-context-protocol"
57
80
  ],
58
81
  "engines": {
59
- "node": ">=22.5.0"
82
+ "node": ">=22.13.0"
60
83
  },
61
84
  "dependencies": {
62
85
  "@modelcontextprotocol/sdk": "1.29.0",
86
+ "@synapsor/spec": "^1.4.2",
63
87
  "jose": "6.2.3",
64
88
  "mysql2": "^3.11.0",
65
89
  "pdf-lib": "1.17.1",
66
90
  "pg": "^8.13.0",
91
+ "typescript": "5.9.3",
67
92
  "vscode-languageserver": "9.0.1",
68
93
  "vscode-languageserver-textdocument": "1.0.12",
94
+ "yaml": "2.8.3",
69
95
  "zod": "^3.25.0"
70
96
  },
71
97
  "devDependencies": {
72
- "typescript": "^5.7.0",
98
+ "ajv": "^8.17.1",
73
99
  "vitest": "^3.2.0"
74
100
  },
75
101
  "homepage": "https://synapsor.ai",
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://synapsor.ai/schemas/effect-dataset.schema.json",
4
+ "title": "Synapsor effect regression dataset",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "name", "fixtures"],
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "schema_version": { "const": "synapsor.effect-dataset.v1" },
11
+ "name": { "type": "string", "minLength": 1, "maxLength": 256 },
12
+ "fixtures": {
13
+ "type": "array",
14
+ "minItems": 1,
15
+ "maxItems": 1000,
16
+ "items": { "type": "string", "minLength": 1, "maxLength": 2048 }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,191 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://synapsor.ai/schemas/effect-fixture.schema.json",
4
+ "title": "Synapsor effect regression fixture",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version",
9
+ "fixture_id",
10
+ "fixture_digest",
11
+ "name",
12
+ "business_request",
13
+ "source",
14
+ "evidence",
15
+ "expected"
16
+ ],
17
+ "properties": {
18
+ "$schema": { "type": "string" },
19
+ "schema_version": { "const": "synapsor.effect-fixture.v1" },
20
+ "fixture_id": { "type": "string", "minLength": 1, "maxLength": 256 },
21
+ "fixture_digest": { "$ref": "#/$defs/sha256" },
22
+ "name": { "type": "string", "minLength": 1, "maxLength": 256 },
23
+ "business_request": { "type": "string", "minLength": 1, "maxLength": 2000 },
24
+ "source": {
25
+ "type": "object",
26
+ "additionalProperties": false,
27
+ "required": ["kind", "reference_id", "captured_digest"],
28
+ "properties": {
29
+ "kind": { "enum": ["replay", "proposal", "shadow_case"] },
30
+ "reference_id": { "type": "string", "minLength": 1, "maxLength": 256 },
31
+ "proposal_id": { "type": "string", "minLength": 1, "maxLength": 256 },
32
+ "captured_digest": { "$ref": "#/$defs/sha256" }
33
+ }
34
+ },
35
+ "evidence": {
36
+ "type": "object",
37
+ "additionalProperties": false,
38
+ "required": [
39
+ "mode",
40
+ "bundle_ids",
41
+ "query_fingerprints",
42
+ "snapshot",
43
+ "source_reads_during_evaluation"
44
+ ],
45
+ "properties": {
46
+ "mode": { "const": "replay_snapshot" },
47
+ "bundle_ids": {
48
+ "type": "array",
49
+ "maxItems": 100,
50
+ "items": { "type": "string", "minLength": 1, "maxLength": 256 }
51
+ },
52
+ "query_fingerprints": {
53
+ "type": "array",
54
+ "maxItems": 100,
55
+ "items": { "$ref": "#/$defs/sha256" }
56
+ },
57
+ "snapshot": { "type": "array", "maxItems": 1000 },
58
+ "source_reads_during_evaluation": { "const": false }
59
+ }
60
+ },
61
+ "expected": { "$ref": "#/$defs/expected" },
62
+ "baseline_history": {
63
+ "type": "array",
64
+ "maxItems": 50,
65
+ "items": {
66
+ "type": "object",
67
+ "additionalProperties": false,
68
+ "required": [
69
+ "actor",
70
+ "reason",
71
+ "accepted_at",
72
+ "previous_expected_digest",
73
+ "accepted_expected_digest"
74
+ ],
75
+ "properties": {
76
+ "actor": { "type": "string", "minLength": 1, "maxLength": 256 },
77
+ "reason": { "type": "string", "minLength": 1, "maxLength": 2000 },
78
+ "accepted_at": { "type": "string", "format": "date-time" },
79
+ "previous_expected_digest": { "$ref": "#/$defs/sha256" },
80
+ "accepted_expected_digest": { "$ref": "#/$defs/sha256" }
81
+ }
82
+ }
83
+ }
84
+ },
85
+ "$defs": {
86
+ "sha256": {
87
+ "type": "string",
88
+ "pattern": "^sha256:[a-f0-9]{64}$"
89
+ },
90
+ "trusted_context": {
91
+ "type": "object",
92
+ "additionalProperties": false,
93
+ "required": ["tenant_id", "principal"],
94
+ "properties": {
95
+ "tenant_id": { "type": "string", "minLength": 1, "maxLength": 256 },
96
+ "principal": { "type": "string", "minLength": 1, "maxLength": 256 },
97
+ "provenance": {
98
+ "enum": [
99
+ "environment",
100
+ "static_dev",
101
+ "http_claims",
102
+ "cloud_session",
103
+ "trusted_session"
104
+ ]
105
+ }
106
+ }
107
+ },
108
+ "target": {
109
+ "type": "object",
110
+ "additionalProperties": false,
111
+ "required": ["business_object", "object_id"],
112
+ "properties": {
113
+ "business_object": { "type": "string", "minLength": 1, "maxLength": 256 },
114
+ "object_id": { "type": "string", "minLength": 1, "maxLength": 256 }
115
+ }
116
+ },
117
+ "diff_value": {
118
+ "type": "object",
119
+ "additionalProperties": false,
120
+ "properties": {
121
+ "before": { "type": ["string", "number", "boolean", "null"] },
122
+ "proposed": { "type": ["string", "number", "boolean", "null"] }
123
+ }
124
+ },
125
+ "policy": {
126
+ "type": "object",
127
+ "additionalProperties": false,
128
+ "required": ["decision"],
129
+ "properties": {
130
+ "decision": {
131
+ "enum": ["pending_review", "auto_approved", "denied", "not_applicable"]
132
+ },
133
+ "required_role": { "type": "string", "minLength": 1, "maxLength": 256 },
134
+ "required_approvals": { "type": "integer", "minimum": 1, "maximum": 10 },
135
+ "reason_code": { "type": "string", "minLength": 1, "maxLength": 256 }
136
+ }
137
+ },
138
+ "proposal": {
139
+ "type": "object",
140
+ "additionalProperties": false,
141
+ "required": ["result_category", "policy"],
142
+ "properties": {
143
+ "result_category": {
144
+ "enum": [
145
+ "proposal_created",
146
+ "policy_denied",
147
+ "unable_to_propose",
148
+ "stale_conflict",
149
+ "invalid_unsafe_scope_attempt"
150
+ ]
151
+ },
152
+ "capability": { "type": "string", "minLength": 1, "maxLength": 256 },
153
+ "contract_version": { "type": "string", "minLength": 1, "maxLength": 256 },
154
+ "target": { "$ref": "#/$defs/target" },
155
+ "diff": {
156
+ "type": "object",
157
+ "maxProperties": 256,
158
+ "additionalProperties": { "$ref": "#/$defs/diff_value" }
159
+ },
160
+ "policy": { "$ref": "#/$defs/policy" },
161
+ "error_code": { "type": "string", "minLength": 1, "maxLength": 256 }
162
+ }
163
+ },
164
+ "expected": {
165
+ "type": "object",
166
+ "additionalProperties": false,
167
+ "required": [
168
+ "capability_calls",
169
+ "trusted_context",
170
+ "proposal",
171
+ "hidden_fields",
172
+ "source_database_changed"
173
+ ],
174
+ "properties": {
175
+ "capability_calls": {
176
+ "type": "array",
177
+ "maxItems": 100,
178
+ "items": { "type": "string", "minLength": 1, "maxLength": 256 }
179
+ },
180
+ "trusted_context": { "$ref": "#/$defs/trusted_context" },
181
+ "proposal": { "$ref": "#/$defs/proposal" },
182
+ "hidden_fields": {
183
+ "type": "array",
184
+ "maxItems": 256,
185
+ "items": { "type": "string", "minLength": 1, "maxLength": 256 }
186
+ },
187
+ "source_database_changed": { "const": false }
188
+ }
189
+ }
190
+ }
191
+ }