@synapsor/runner 1.6.3 → 1.6.4

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 (97) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +72 -8
  3. package/CONTRIBUTING.md +10 -3
  4. package/README.md +61 -93
  5. package/SECURITY.md +7 -3
  6. package/THREAT_MODEL.md +37 -0
  7. package/dist/authoring.mjs +98 -13
  8. package/dist/cli.d.ts +12 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/local-ui.d.ts +60 -0
  11. package/dist/local-ui.d.ts.map +1 -1
  12. package/dist/runner.mjs +7321 -2000
  13. package/dist/runtime.mjs +528 -51
  14. package/docs/README.md +14 -0
  15. package/docs/agent-guided-setup.md +7 -7
  16. package/docs/aggregate-reads.md +14 -5
  17. package/docs/app-owned-executors.md +5 -6
  18. package/docs/auto-boundary-and-scoped-explore.md +58 -6
  19. package/docs/capability-authoring.md +2 -2
  20. package/docs/client-recipes.md +1 -1
  21. package/docs/cloud-mode.md +7 -7
  22. package/docs/conformance.md +6 -3
  23. package/docs/current-scope.md +13 -2
  24. package/docs/cursor-plugin.md +3 -3
  25. package/docs/database-enforced-scope.md +6 -0
  26. package/docs/dsl-reference.md +12 -1
  27. package/docs/fresh-developer-usability.md +6 -6
  28. package/docs/getting-started-own-database.md +41 -41
  29. package/docs/guided-onboarding.md +98 -30
  30. package/docs/limitations.md +34 -14
  31. package/docs/local-mode.md +20 -20
  32. package/docs/mcp-audit.md +14 -14
  33. package/docs/mcp-client-setup.md +11 -14
  34. package/docs/mcp-clients.md +1 -1
  35. package/docs/migrating-to-synapsor-spec.md +5 -1
  36. package/docs/openai-agents-sdk.md +2 -2
  37. package/docs/proposal-evidence-freshness.md +7 -7
  38. package/docs/recipes.md +6 -6
  39. package/docs/release-notes.md +86 -10
  40. package/docs/release-policy.md +20 -7
  41. package/docs/reversible-change-sets.md +1 -1
  42. package/docs/reviewed-database-views.md +132 -0
  43. package/docs/reviewed-relationships.md +245 -0
  44. package/docs/rfcs/006-reviewed-reversible-change-sets.md +1 -1
  45. package/docs/runner-bundles.md +7 -7
  46. package/docs/schema-api-candidates.md +1 -1
  47. package/docs/security-boundary.md +30 -0
  48. package/docs/troubleshooting-first-run.md +52 -12
  49. package/docs/use-your-own-database.md +6 -4
  50. package/docs/workbench-ask.md +279 -0
  51. package/docs/writeback-executors.md +7 -7
  52. package/examples/auto-boundary-churn/README.md +1 -1
  53. package/examples/community-solar-clean-room/README.md +7 -0
  54. package/examples/community-solar-clean-room/docker-compose.yml +16 -0
  55. package/examples/community-solar-clean-room/package.json +15 -0
  56. package/examples/community-solar-clean-room/prisma/schema.prisma +171 -0
  57. package/examples/community-solar-clean-room/seed/postgres.sql +571 -0
  58. package/examples/fitflow-guided-onboarding/README.md +2 -2
  59. package/examples/openai-agents-http/Makefile +1 -1
  60. package/examples/openai-agents-http/README.md +1 -1
  61. package/examples/openai-agents-stdio/Makefile +1 -1
  62. package/examples/openai-agents-stdio/README.md +2 -2
  63. package/examples/openai-agents-stdio/agent.py +1 -4
  64. package/examples/reference-support-billing-app/README.md +16 -16
  65. package/examples/retail-clean-room/README.md +7 -0
  66. package/examples/retail-clean-room/docker-compose.yml +16 -0
  67. package/examples/retail-clean-room/package.json +9 -0
  68. package/examples/retail-clean-room/prisma/schema.prisma +239 -0
  69. package/examples/retail-clean-room/seed/postgres.sql +781 -0
  70. package/examples/retail-clean-room/view-recipe/average-retained-revenue.synapsor.sql +21 -0
  71. package/examples/retail-clean-room/view-recipe/synapsor.runner.json +25 -0
  72. package/examples/support-plan-credit/mcp-client-examples/claude-code.sh +1 -4
  73. package/examples/support-plan-credit/mcp-client-examples/claude-desktop.json +1 -4
  74. package/examples/support-plan-credit/mcp-client-examples/codex.config.toml +1 -4
  75. package/examples/support-plan-credit/mcp-client-examples/cursor-global.mcp.json +1 -4
  76. package/examples/support-plan-credit/mcp-client-examples/cursor-project.mcp.json +1 -4
  77. package/examples/support-plan-credit/mcp-client-examples/generic-stdio.json +1 -4
  78. package/examples/support-plan-credit/mcp-client-examples/generic-stdio.mjs +1 -1
  79. package/examples/support-plan-credit/mcp-client-examples/google-adk.py +1 -1
  80. package/examples/support-plan-credit/mcp-client-examples/langchain.mjs +1 -1
  81. package/examples/support-plan-credit/mcp-client-examples/llamaindex.py +1 -1
  82. package/examples/support-plan-credit/mcp-client-examples/openai-agents-stdio.ts +1 -1
  83. package/examples/support-plan-credit/mcp-client-examples/vscode.mcp.json +1 -4
  84. package/fixtures/compatibility/published-1.6.3/manifest.json +76 -0
  85. package/fixtures/compatibility/published-1.6.3/sources/packages/dsl/examples/aggregate-read.synapsor.sql +21 -0
  86. package/fixtures/compatibility/published-1.6.3/sources/packages/dsl/examples/billing-late-fee.synapsor.sql +56 -0
  87. package/fixtures/compatibility/published-1.6.3/sources/packages/dsl/examples/bounded-set-multi-term.synapsor.sql +30 -0
  88. package/fixtures/compatibility/published-1.6.3/sources/packages/dsl/examples/principal-row-scope.synapsor.sql +23 -0
  89. package/fixtures/compatibility/published-1.6.3/sources/packages/spec/fixtures/conformance/aggregate-read/contract.json +119 -0
  90. package/fixtures/compatibility/published-1.6.3/sources/packages/spec/fixtures/conformance/approval-quorum/contract.json +44 -0
  91. package/fixtures/compatibility/published-1.6.3/sources/packages/spec/fixtures/conformance/bounded-set-threats/contract.json +115 -0
  92. package/fixtures/compatibility/published-1.6.3/sources/packages/spec/fixtures/conformance/principal-row-scope/contract.json +78 -0
  93. package/fixtures/compatibility/published-1.6.3/sources/packages/spec/fixtures/conformance/proposal-capability/contract.json +101 -0
  94. package/fixtures/compatibility/published-1.6.3/sources/packages/spec/fixtures/conformance/reversible-change-sets/contract.json +98 -0
  95. package/fixtures/compatibility/published-1.6.3/sources/packages/spec/fixtures/valid/basic-read.contract.json +60 -0
  96. package/llms.txt +10 -5
  97. package/package.json +8 -2
@@ -0,0 +1,571 @@
1
+ DO $$
2
+ BEGIN
3
+ CREATE ROLE solar_technician_reader LOGIN PASSWORD 'solar_technician_reader_password';
4
+ EXCEPTION WHEN duplicate_object THEN NULL;
5
+ END
6
+ $$;
7
+
8
+ DO $$
9
+ BEGIN
10
+ CREATE ROLE solar_writer LOGIN PASSWORD 'solar_writer_password';
11
+ EXCEPTION WHEN duplicate_object THEN NULL;
12
+ END
13
+ $$;
14
+
15
+ DO $$
16
+ BEGIN
17
+ CREATE ROLE solar_setup LOGIN PASSWORD 'solar_setup_password';
18
+ EXCEPTION WHEN duplicate_object THEN NULL;
19
+ END
20
+ $$;
21
+
22
+ ALTER ROLE solar_technician_reader SET default_transaction_read_only = on;
23
+
24
+ CREATE TABLE public.cooperatives (
25
+ id text PRIMARY KEY,
26
+ name text NOT NULL
27
+ );
28
+
29
+ CREATE TABLE public.regions (
30
+ id text PRIMARY KEY,
31
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
32
+ name text NOT NULL,
33
+ climate_zone text NOT NULL,
34
+ UNIQUE (cooperative_id, name)
35
+ );
36
+
37
+ CREATE TABLE public.technician_teams (
38
+ id text PRIMARY KEY,
39
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
40
+ name text NOT NULL,
41
+ UNIQUE (cooperative_id, name)
42
+ );
43
+
44
+ CREATE TABLE public.technicians (
45
+ id text PRIMARY KEY,
46
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
47
+ team_id text NOT NULL REFERENCES public.technician_teams(id) ON DELETE RESTRICT,
48
+ display_name text NOT NULL,
49
+ private_technician_notes text NOT NULL
50
+ );
51
+
52
+ CREATE TABLE public.solar_sites (
53
+ id text PRIMARY KEY,
54
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
55
+ region_id text NOT NULL REFERENCES public.regions(id) ON DELETE RESTRICT,
56
+ name text NOT NULL,
57
+ site_group text NOT NULL,
58
+ accounting_period text NOT NULL,
59
+ UNIQUE (cooperative_id, name)
60
+ );
61
+
62
+ CREATE TABLE public.inverter_models (
63
+ id text PRIMARY KEY,
64
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
65
+ manufacturer text NOT NULL,
66
+ model_name text NOT NULL,
67
+ panel_position text NOT NULL,
68
+ UNIQUE (cooperative_id, manufacturer, model_name)
69
+ );
70
+
71
+ CREATE TABLE public.inverters (
72
+ id text PRIMARY KEY,
73
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
74
+ site_id text NOT NULL REFERENCES public.solar_sites(id) ON DELETE RESTRICT,
75
+ inverter_model_id text NOT NULL REFERENCES public.inverter_models(id) ON DELETE RESTRICT,
76
+ serial_number text NOT NULL,
77
+ status text NOT NULL CHECK (status IN ('online', 'degraded', 'offline')),
78
+ access_token text NOT NULL,
79
+ panel_position text NOT NULL,
80
+ version integer NOT NULL DEFAULT 1,
81
+ UNIQUE (cooperative_id, serial_number)
82
+ );
83
+
84
+ CREATE TABLE public.fault_categories (
85
+ id text PRIMARY KEY,
86
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
87
+ name text NOT NULL,
88
+ severity text NOT NULL CHECK (severity IN ('low', 'medium', 'high')),
89
+ UNIQUE (cooperative_id, name)
90
+ );
91
+
92
+ CREATE TABLE public.work_orders (
93
+ id text PRIMARY KEY,
94
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
95
+ site_id text NOT NULL REFERENCES public.solar_sites(id) ON DELETE RESTRICT,
96
+ region_id text NOT NULL REFERENCES public.regions(id) ON DELETE RESTRICT,
97
+ inverter_model_id text NOT NULL REFERENCES public.inverter_models(id) ON DELETE RESTRICT,
98
+ fault_category_id text NOT NULL REFERENCES public.fault_categories(id) ON DELETE RESTRICT,
99
+ assigned_technician_id text NOT NULL REFERENCES public.technicians(id) ON DELETE RESTRICT,
100
+ technician_team_id text NOT NULL REFERENCES public.technician_teams(id) ON DELETE RESTRICT,
101
+ status text NOT NULL CHECK (status IN ('open', 'in_progress', 'resolved', 'cancelled')),
102
+ opened_at timestamptz NOT NULL,
103
+ completed_at timestamptz,
104
+ downtime_minutes integer NOT NULL CHECK (downtime_minutes BETWEEN 0 AND 10080),
105
+ repair_duration_minutes integer NOT NULL CHECK (repair_duration_minutes BETWEEN 0 AND 10080),
106
+ estimated_energy_loss_wh bigint NOT NULL CHECK (estimated_energy_loss_wh >= 0),
107
+ lost_revenue_cents integer NOT NULL CHECK (lost_revenue_cents >= 0),
108
+ inspection_failed boolean NOT NULL,
109
+ payment_status text NOT NULL CHECK (payment_status IN ('not_applicable', 'pending', 'settled')),
110
+ version integer NOT NULL DEFAULT 1,
111
+ private_technician_notes text NOT NULL
112
+ );
113
+
114
+ CREATE INDEX work_orders_trusted_scope_idx
115
+ ON public.work_orders(cooperative_id, assigned_technician_id, opened_at);
116
+
117
+ CREATE TABLE public.incidents (
118
+ id text PRIMARY KEY,
119
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
120
+ site_id text NOT NULL REFERENCES public.solar_sites(id) ON DELETE RESTRICT,
121
+ region_id text NOT NULL REFERENCES public.regions(id) ON DELETE RESTRICT,
122
+ inverter_model_id text NOT NULL REFERENCES public.inverter_models(id) ON DELETE RESTRICT,
123
+ fault_category_id text NOT NULL REFERENCES public.fault_categories(id) ON DELETE RESTRICT,
124
+ status text NOT NULL CHECK (status IN ('open', 'monitoring', 'resolved')),
125
+ occurred_at timestamptz NOT NULL,
126
+ resolved_at timestamptz,
127
+ downtime_minutes integer NOT NULL CHECK (downtime_minutes >= 0),
128
+ estimated_energy_loss_wh bigint NOT NULL CHECK (estimated_energy_loss_wh >= 0)
129
+ );
130
+
131
+ CREATE TABLE public.meter_readings (
132
+ id text PRIMARY KEY,
133
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
134
+ site_id text NOT NULL REFERENCES public.solar_sites(id) ON DELETE RESTRICT,
135
+ recorded_at timestamptz NOT NULL,
136
+ energy_amount_wh bigint NOT NULL CHECK (energy_amount_wh >= 0),
137
+ quality_status text NOT NULL CHECK (quality_status IN ('measured', 'estimated', 'rejected'))
138
+ );
139
+
140
+ CREATE TABLE public.inspections (
141
+ id text PRIMARY KEY,
142
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
143
+ site_id text NOT NULL REFERENCES public.solar_sites(id) ON DELETE RESTRICT,
144
+ assigned_technician_id text NOT NULL REFERENCES public.technicians(id) ON DELETE RESTRICT,
145
+ inspected_at timestamptz NOT NULL,
146
+ outcome text NOT NULL CHECK (outcome IN ('passed', 'failed', 'follow_up')),
147
+ failure_count integer NOT NULL CHECK (failure_count >= 0),
148
+ private_technician_notes text NOT NULL
149
+ );
150
+
151
+ CREATE TABLE public.parts (
152
+ id text PRIMARY KEY,
153
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
154
+ sku text NOT NULL,
155
+ category text NOT NULL,
156
+ stock_quantity integer NOT NULL CHECK (stock_quantity >= 0),
157
+ unit_cost_cents integer NOT NULL CHECK (unit_cost_cents >= 0),
158
+ UNIQUE (cooperative_id, sku)
159
+ );
160
+
161
+ CREATE TABLE public.parts_reservations (
162
+ id text PRIMARY KEY,
163
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
164
+ work_order_id text NOT NULL REFERENCES public.work_orders(id) ON DELETE RESTRICT,
165
+ part_id text NOT NULL REFERENCES public.parts(id) ON DELETE RESTRICT,
166
+ quantity integer NOT NULL CHECK (quantity BETWEEN 1 AND 100),
167
+ reserved_at timestamptz NOT NULL
168
+ );
169
+
170
+ CREATE TABLE public.members (
171
+ id text PRIMARY KEY,
172
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
173
+ display_name text NOT NULL,
174
+ tax_id text NOT NULL,
175
+ home_address text NOT NULL,
176
+ membership_status text NOT NULL CHECK (membership_status IN ('active', 'paused', 'closed'))
177
+ );
178
+
179
+ CREATE TABLE public.payout_accounts (
180
+ id text PRIMARY KEY,
181
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
182
+ member_id text NOT NULL REFERENCES public.members(id) ON DELETE RESTRICT,
183
+ bank_account_number text NOT NULL,
184
+ routing_number text NOT NULL,
185
+ card_on_file text NOT NULL,
186
+ payment_status text NOT NULL CHECK (payment_status IN ('verified', 'pending', 'blocked'))
187
+ );
188
+
189
+ CREATE TABLE public.work_order_assignments (
190
+ work_order_id text NOT NULL REFERENCES public.work_orders(id) ON DELETE CASCADE,
191
+ technician_id text NOT NULL REFERENCES public.technicians(id) ON DELETE CASCADE,
192
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
193
+ assigned_at timestamptz NOT NULL,
194
+ PRIMARY KEY (work_order_id, technician_id)
195
+ );
196
+
197
+ CREATE TABLE public.site_groups (
198
+ id text PRIMARY KEY,
199
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
200
+ name text NOT NULL
201
+ );
202
+
203
+ CREATE TABLE public.site_group_memberships (
204
+ site_group_id text NOT NULL REFERENCES public.site_groups(id) ON DELETE CASCADE,
205
+ site_id text NOT NULL REFERENCES public.solar_sites(id) ON DELETE CASCADE,
206
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
207
+ PRIMARY KEY (site_group_id, site_id)
208
+ );
209
+
210
+ CREATE TABLE public.energy_forecasts (
211
+ id text PRIMARY KEY,
212
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
213
+ site_id text NOT NULL REFERENCES public.solar_sites(id) ON DELETE RESTRICT,
214
+ forecast_at timestamptz NOT NULL,
215
+ energy_amount_wh bigint NOT NULL CHECK (energy_amount_wh >= 0),
216
+ confidence_band text NOT NULL CHECK (confidence_band IN ('low', 'medium', 'high'))
217
+ );
218
+
219
+ DO $$
220
+ DECLARE
221
+ relation_name text;
222
+ BEGIN
223
+ FOREACH relation_name IN ARRAY ARRAY[
224
+ 'maintenance_windows',
225
+ 'weather_observations',
226
+ 'grid_connections',
227
+ 'curtailment_events',
228
+ 'battery_assets',
229
+ 'battery_cycles',
230
+ 'panel_arrays',
231
+ 'warranty_claims',
232
+ 'supplier_orders',
233
+ 'supplier_invoices',
234
+ 'member_subscriptions',
235
+ 'member_credits',
236
+ 'payout_runs',
237
+ 'payout_line_items',
238
+ 'compliance_checks',
239
+ 'safety_training',
240
+ 'technician_schedules',
241
+ 'incident_comments',
242
+ 'notification_preferences',
243
+ 'audit_events'
244
+ ]
245
+ LOOP
246
+ EXECUTE format(
247
+ 'CREATE TABLE public.%I (
248
+ id text PRIMARY KEY,
249
+ cooperative_id text NOT NULL REFERENCES public.cooperatives(id) ON DELETE RESTRICT,
250
+ status text NOT NULL,
251
+ created_at timestamptz NOT NULL DEFAULT clock_timestamp()
252
+ )',
253
+ relation_name
254
+ );
255
+ END LOOP;
256
+ END
257
+ $$;
258
+
259
+ INSERT INTO public.cooperatives (id, name) VALUES
260
+ ('coop-sunward', 'Sunward Community Energy'),
261
+ ('coop-riverbend', 'Riverbend Solar Cooperative');
262
+
263
+ INSERT INTO public.regions (id, cooperative_id, name, climate_zone) VALUES
264
+ ('region-north', 'coop-sunward', 'North Valley', 'cool'),
265
+ ('region-coast', 'coop-sunward', 'Coastal Plain', 'marine'),
266
+ ('region-desert', 'coop-sunward', 'High Desert', 'arid'),
267
+ ('region-other', 'coop-riverbend', 'River District', 'humid');
268
+
269
+ INSERT INTO public.technician_teams (id, cooperative_id, name) VALUES
270
+ ('team-field-a', 'coop-sunward', 'Field Reliability A'),
271
+ ('team-field-b', 'coop-sunward', 'Field Reliability B'),
272
+ ('team-other', 'coop-riverbend', 'River Team');
273
+
274
+ INSERT INTO public.technicians (
275
+ id, cooperative_id, team_id, display_name, private_technician_notes
276
+ ) VALUES
277
+ ('tech-alex', 'coop-sunward', 'team-field-a', 'Alex', 'private synthetic personnel note'),
278
+ ('tech-jordan', 'coop-sunward', 'team-field-b', 'Jordan', 'private synthetic personnel note'),
279
+ ('tech-other', 'coop-riverbend', 'team-other', 'Other Technician', 'other tenant private note');
280
+
281
+ INSERT INTO public.solar_sites (
282
+ id, cooperative_id, region_id, name, site_group, accounting_period
283
+ ) VALUES
284
+ ('site-north-1', 'coop-sunward', 'region-north', 'North Orchard', 'orchard', '2026-Q3'),
285
+ ('site-coast-1', 'coop-sunward', 'region-coast', 'Coastal School', 'public', '2026-Q3'),
286
+ ('site-desert-1', 'coop-sunward', 'region-desert', 'Desert Commons', 'residential', '2026-Q3'),
287
+ ('site-other', 'coop-riverbend', 'region-other', 'River Array', 'public', '2026-Q3');
288
+
289
+ INSERT INTO public.inverter_models (
290
+ id, cooperative_id, manufacturer, model_name, panel_position
291
+ ) VALUES
292
+ ('model-a', 'coop-sunward', 'HelioWorks', 'HX-400', 'north rack'),
293
+ ('model-b', 'coop-sunward', 'GridBright', 'GB-22', 'central rack'),
294
+ ('model-other', 'coop-riverbend', 'Other Energy', 'OE-9', 'east rack');
295
+
296
+ INSERT INTO public.inverters (
297
+ id, cooperative_id, site_id, inverter_model_id, serial_number, status,
298
+ access_token, panel_position, version
299
+ ) VALUES
300
+ ('inv-north', 'coop-sunward', 'site-north-1', 'model-a', 'SYN-N-001', 'online', 'synthetic-access-token-north', 'north rack', 1),
301
+ ('inv-coast', 'coop-sunward', 'site-coast-1', 'model-b', 'SYN-C-001', 'degraded', 'synthetic-access-token-coast', 'central rack', 1),
302
+ ('inv-desert', 'coop-sunward', 'site-desert-1', 'model-a', 'SYN-D-001', 'online', 'synthetic-access-token-desert', 'west rack', 1),
303
+ ('inv-other', 'coop-riverbend', 'site-other', 'model-other', 'OTH-001', 'online', 'other-secret-token', 'east rack', 1);
304
+
305
+ INSERT INTO public.fault_categories (id, cooperative_id, name, severity) VALUES
306
+ ('fault-cooling', 'coop-sunward', 'Cooling', 'medium'),
307
+ ('fault-grid', 'coop-sunward', 'Grid Sync', 'high'),
308
+ ('fault-sensor', 'coop-sunward', 'Sensor', 'low'),
309
+ ('fault-other', 'coop-riverbend', 'Other Fault', 'high');
310
+
311
+ WITH generated AS (
312
+ SELECT
313
+ item,
314
+ CASE item % 3 WHEN 0 THEN 'site-north-1' WHEN 1 THEN 'site-coast-1' ELSE 'site-desert-1' END AS site_id,
315
+ CASE item % 3 WHEN 0 THEN 'region-north' WHEN 1 THEN 'region-coast' ELSE 'region-desert' END AS region_id,
316
+ CASE item % 2 WHEN 0 THEN 'model-a' ELSE 'model-b' END AS model_id,
317
+ CASE item % 3 WHEN 0 THEN 'fault-cooling' WHEN 1 THEN 'fault-grid' ELSE 'fault-sensor' END AS fault_id,
318
+ CASE WHEN item <= 27 THEN 'tech-alex' ELSE 'tech-jordan' END AS technician_id,
319
+ CASE WHEN item <= 27 THEN 'team-field-a' ELSE 'team-field-b' END AS team_id,
320
+ CASE WHEN item % 5 = 0 THEN 'open' WHEN item % 2 = 0 THEN 'resolved' ELSE 'in_progress' END AS work_status,
321
+ ('2026-06-29T09:00:00Z'::timestamptz + (item % 21) * interval '1 day') AS opened_at
322
+ FROM generate_series(1, 36) AS item
323
+ )
324
+ INSERT INTO public.work_orders (
325
+ id, cooperative_id, site_id, region_id, inverter_model_id, fault_category_id,
326
+ assigned_technician_id, technician_team_id, status, opened_at, completed_at,
327
+ downtime_minutes, repair_duration_minutes, estimated_energy_loss_wh,
328
+ lost_revenue_cents, inspection_failed, payment_status, version,
329
+ private_technician_notes
330
+ )
331
+ SELECT
332
+ 'wo-' || lpad(item::text, 3, '0'),
333
+ 'coop-sunward',
334
+ site_id,
335
+ region_id,
336
+ model_id,
337
+ fault_id,
338
+ technician_id,
339
+ team_id,
340
+ work_status,
341
+ opened_at,
342
+ CASE WHEN work_status = 'resolved' THEN opened_at + interval '3 hours' ELSE NULL END,
343
+ item * 7,
344
+ item * 5,
345
+ item * 1100,
346
+ item * 75,
347
+ item % 7 = 0,
348
+ CASE WHEN item % 4 = 0 THEN 'pending' ELSE 'not_applicable' END,
349
+ 1,
350
+ 'synthetic private work note ' || item
351
+ FROM generated;
352
+
353
+ INSERT INTO public.work_orders (
354
+ id, cooperative_id, site_id, region_id, inverter_model_id, fault_category_id,
355
+ assigned_technician_id, technician_team_id, status, opened_at, completed_at,
356
+ downtime_minutes, repair_duration_minutes, estimated_energy_loss_wh,
357
+ lost_revenue_cents, inspection_failed, payment_status, version,
358
+ private_technician_notes
359
+ ) VALUES (
360
+ 'wo-other-001', 'coop-riverbend', 'site-other', 'region-other', 'model-other',
361
+ 'fault-other', 'tech-other', 'team-other', 'open', '2026-07-14T09:00:00Z',
362
+ NULL, 999, 999, 999999, 99999, true, 'pending', 1,
363
+ 'other tenant private work note'
364
+ );
365
+
366
+ INSERT INTO public.incidents (
367
+ id, cooperative_id, site_id, region_id, inverter_model_id, fault_category_id,
368
+ status, occurred_at, resolved_at, downtime_minutes, estimated_energy_loss_wh
369
+ )
370
+ SELECT
371
+ 'incident-' || lpad(item::text, 3, '0'),
372
+ 'coop-sunward',
373
+ CASE item % 3 WHEN 0 THEN 'site-north-1' WHEN 1 THEN 'site-coast-1' ELSE 'site-desert-1' END,
374
+ CASE item % 3 WHEN 0 THEN 'region-north' WHEN 1 THEN 'region-coast' ELSE 'region-desert' END,
375
+ CASE item % 2 WHEN 0 THEN 'model-a' ELSE 'model-b' END,
376
+ CASE item % 3 WHEN 0 THEN 'fault-cooling' WHEN 1 THEN 'fault-grid' ELSE 'fault-sensor' END,
377
+ CASE WHEN item % 4 = 0 THEN 'open' ELSE 'resolved' END,
378
+ '2026-06-29T08:00:00Z'::timestamptz + (item % 21) * interval '1 day',
379
+ CASE WHEN item % 4 = 0 THEN NULL ELSE '2026-06-29T10:00:00Z'::timestamptz + (item % 21) * interval '1 day' END,
380
+ item * 8,
381
+ item * 900
382
+ FROM generate_series(1, 36) AS item;
383
+
384
+ INSERT INTO public.meter_readings (
385
+ id, cooperative_id, site_id, recorded_at, energy_amount_wh, quality_status
386
+ )
387
+ SELECT
388
+ 'reading-' || item,
389
+ 'coop-sunward',
390
+ CASE item % 3 WHEN 0 THEN 'site-north-1' WHEN 1 THEN 'site-coast-1' ELSE 'site-desert-1' END,
391
+ '2026-07-01T00:00:00Z'::timestamptz + item * interval '6 hours',
392
+ 100000 + item * 250,
393
+ CASE WHEN item % 11 = 0 THEN 'estimated' ELSE 'measured' END
394
+ FROM generate_series(1, 60) AS item;
395
+
396
+ INSERT INTO public.inspections (
397
+ id, cooperative_id, site_id, assigned_technician_id, inspected_at, outcome,
398
+ failure_count, private_technician_notes
399
+ )
400
+ SELECT
401
+ 'inspection-' || item,
402
+ 'coop-sunward',
403
+ CASE item % 3 WHEN 0 THEN 'site-north-1' WHEN 1 THEN 'site-coast-1' ELSE 'site-desert-1' END,
404
+ CASE WHEN item <= 15 THEN 'tech-alex' ELSE 'tech-jordan' END,
405
+ '2026-07-01T11:00:00Z'::timestamptz + item * interval '1 day',
406
+ CASE WHEN item % 5 = 0 THEN 'failed' ELSE 'passed' END,
407
+ CASE WHEN item % 5 = 0 THEN 1 ELSE 0 END,
408
+ 'synthetic private inspection note ' || item
409
+ FROM generate_series(1, 18) AS item;
410
+
411
+ INSERT INTO public.parts (id, cooperative_id, sku, category, stock_quantity, unit_cost_cents) VALUES
412
+ ('part-fan', 'coop-sunward', 'FAN-01', 'cooling', 20, 4500),
413
+ ('part-sensor', 'coop-sunward', 'SNS-02', 'sensor', 35, 1800),
414
+ ('part-other', 'coop-riverbend', 'OTH-01', 'other', 5, 9999);
415
+
416
+ INSERT INTO public.parts_reservations (
417
+ id, cooperative_id, work_order_id, part_id, quantity, reserved_at
418
+ ) VALUES
419
+ ('reserve-1', 'coop-sunward', 'wo-001', 'part-fan', 1, '2026-07-01T12:00:00Z'),
420
+ ('reserve-2', 'coop-sunward', 'wo-002', 'part-sensor', 2, '2026-07-02T12:00:00Z');
421
+
422
+ INSERT INTO public.members (
423
+ id, cooperative_id, display_name, tax_id, home_address, membership_status
424
+ ) VALUES
425
+ ('member-1', 'coop-sunward', 'Synthetic Member 1', 'SYN-TAX-001', '1 Synthetic Solar Way', 'active'),
426
+ ('member-2', 'coop-sunward', 'Synthetic Member 2', 'SYN-TAX-002', '2 Synthetic Solar Way', 'active'),
427
+ ('member-other', 'coop-riverbend', 'Other Member', 'OTHER-TAX', 'Other Tenant Address', 'active');
428
+
429
+ INSERT INTO public.payout_accounts (
430
+ id, cooperative_id, member_id, bank_account_number, routing_number,
431
+ card_on_file, payment_status
432
+ ) VALUES
433
+ ('payout-1', 'coop-sunward', 'member-1', 'SYNTHETIC-BANK-001', 'SYNTHETIC-ROUTING-001', 'SYNTHETIC-CARD-001', 'verified'),
434
+ ('payout-2', 'coop-sunward', 'member-2', 'SYNTHETIC-BANK-002', 'SYNTHETIC-ROUTING-002', 'SYNTHETIC-CARD-002', 'pending'),
435
+ ('payout-other', 'coop-riverbend', 'member-other', 'OTHER-BANK', 'OTHER-ROUTING', 'OTHER-CARD', 'verified');
436
+
437
+ INSERT INTO public.work_order_assignments (
438
+ work_order_id, technician_id, cooperative_id, assigned_at
439
+ ) VALUES
440
+ ('wo-001', 'tech-alex', 'coop-sunward', '2026-07-01T09:00:00Z'),
441
+ ('wo-001', 'tech-jordan', 'coop-sunward', '2026-07-01T09:05:00Z');
442
+
443
+ INSERT INTO public.site_groups (id, cooperative_id, name) VALUES
444
+ ('group-a', 'coop-sunward', 'Priority Sites'),
445
+ ('group-b', 'coop-sunward', 'School Sites');
446
+
447
+ INSERT INTO public.site_group_memberships (
448
+ site_group_id, site_id, cooperative_id
449
+ ) VALUES
450
+ ('group-a', 'site-north-1', 'coop-sunward'),
451
+ ('group-a', 'site-coast-1', 'coop-sunward'),
452
+ ('group-b', 'site-coast-1', 'coop-sunward');
453
+
454
+ INSERT INTO public.energy_forecasts (
455
+ id, cooperative_id, site_id, forecast_at, energy_amount_wh, confidence_band
456
+ ) VALUES
457
+ ('forecast-1', 'coop-sunward', 'site-north-1', '2026-07-21T00:00:00Z', 250000, 'high'),
458
+ ('forecast-2', 'coop-sunward', 'site-coast-1', '2026-07-21T00:00:00Z', 180000, 'medium');
459
+
460
+ ALTER TABLE public.cooperatives ENABLE ROW LEVEL SECURITY;
461
+ ALTER TABLE public.cooperatives FORCE ROW LEVEL SECURITY;
462
+
463
+ CREATE POLICY cooperatives_scope ON public.cooperatives
464
+ FOR SELECT TO solar_technician_reader, solar_writer
465
+ USING (id = current_setting('app.tenant_id', true));
466
+
467
+ DO $$
468
+ DECLARE
469
+ relation_name text;
470
+ BEGIN
471
+ FOREACH relation_name IN ARRAY ARRAY[
472
+ 'regions',
473
+ 'technician_teams',
474
+ 'technicians',
475
+ 'solar_sites',
476
+ 'inverter_models',
477
+ 'inverters',
478
+ 'fault_categories',
479
+ 'incidents',
480
+ 'meter_readings',
481
+ 'parts',
482
+ 'parts_reservations',
483
+ 'members',
484
+ 'payout_accounts',
485
+ 'work_order_assignments',
486
+ 'site_groups',
487
+ 'site_group_memberships',
488
+ 'energy_forecasts',
489
+ 'maintenance_windows',
490
+ 'weather_observations',
491
+ 'grid_connections',
492
+ 'curtailment_events',
493
+ 'battery_assets',
494
+ 'battery_cycles',
495
+ 'panel_arrays',
496
+ 'warranty_claims',
497
+ 'supplier_orders',
498
+ 'supplier_invoices',
499
+ 'member_subscriptions',
500
+ 'member_credits',
501
+ 'payout_runs',
502
+ 'payout_line_items',
503
+ 'compliance_checks',
504
+ 'safety_training',
505
+ 'technician_schedules',
506
+ 'incident_comments',
507
+ 'notification_preferences',
508
+ 'audit_events'
509
+ ]
510
+ LOOP
511
+ EXECUTE format('ALTER TABLE public.%I ENABLE ROW LEVEL SECURITY', relation_name);
512
+ EXECUTE format('ALTER TABLE public.%I FORCE ROW LEVEL SECURITY', relation_name);
513
+ EXECUTE format(
514
+ 'CREATE POLICY %I ON public.%I
515
+ FOR SELECT TO solar_technician_reader, solar_writer
516
+ USING (cooperative_id = current_setting(''app.tenant_id'', true))',
517
+ relation_name || '_cooperative_scope',
518
+ relation_name
519
+ );
520
+ END LOOP;
521
+ END
522
+ $$;
523
+
524
+ ALTER TABLE public.work_orders ENABLE ROW LEVEL SECURITY;
525
+ ALTER TABLE public.work_orders FORCE ROW LEVEL SECURITY;
526
+ CREATE POLICY work_orders_technician_read ON public.work_orders
527
+ FOR SELECT TO solar_technician_reader, solar_writer
528
+ USING (
529
+ cooperative_id = current_setting('app.tenant_id', true)
530
+ AND assigned_technician_id = current_setting('app.principal', true)
531
+ );
532
+ CREATE POLICY work_orders_guarded_update ON public.work_orders
533
+ FOR UPDATE TO solar_writer
534
+ USING (
535
+ cooperative_id = current_setting('app.tenant_id', true)
536
+ AND assigned_technician_id = current_setting('app.principal', true)
537
+ )
538
+ WITH CHECK (
539
+ cooperative_id = current_setting('app.tenant_id', true)
540
+ AND assigned_technician_id = current_setting('app.principal', true)
541
+ );
542
+
543
+ ALTER TABLE public.inspections ENABLE ROW LEVEL SECURITY;
544
+ ALTER TABLE public.inspections FORCE ROW LEVEL SECURITY;
545
+ CREATE POLICY inspections_technician_read ON public.inspections
546
+ FOR SELECT TO solar_technician_reader, solar_writer
547
+ USING (
548
+ cooperative_id = current_setting('app.tenant_id', true)
549
+ AND assigned_technician_id = current_setting('app.principal', true)
550
+ );
551
+
552
+ GRANT CONNECT ON DATABASE community_solar TO solar_technician_reader, solar_writer, solar_setup;
553
+ GRANT USAGE ON SCHEMA public TO solar_technician_reader, solar_writer, solar_setup;
554
+ GRANT SELECT ON ALL TABLES IN SCHEMA public TO solar_technician_reader;
555
+ GRANT SELECT ON ALL TABLES IN SCHEMA public TO solar_writer;
556
+ GRANT UPDATE (
557
+ status,
558
+ downtime_minutes,
559
+ repair_duration_minutes,
560
+ estimated_energy_loss_wh,
561
+ lost_revenue_cents,
562
+ inspection_failed,
563
+ payment_status,
564
+ version
565
+ ) ON public.work_orders TO solar_writer;
566
+ GRANT CREATE ON SCHEMA public TO solar_setup;
567
+
568
+ CREATE TABLE public.synapsor_fixture_ready (
569
+ initialized_at timestamptz NOT NULL
570
+ );
571
+ INSERT INTO public.synapsor_fixture_ready (initialized_at) VALUES (clock_timestamp());
@@ -1,7 +1,7 @@
1
1
  # FitFlow Guided Onboarding Fixture
2
2
 
3
3
  FitFlow is a synthetic multi-tenant fitness application used to verify the
4
- Runner 1.6.3 first-time developer journey.
4
+ Runner 1.6.4 first-time developer journey.
5
5
 
6
6
  Its live PostgreSQL schema contains 41 relations: six seeded core relations,
7
7
  34 realistic surrounding application subsystems, and one readiness marker.
@@ -37,7 +37,7 @@ Use the organization analytics role for the first onboarding journey:
37
37
  ```bash
38
38
  export DATABASE_URL='postgresql://fitflow_analytics_reader:fitflow_analytics_reader_password@127.0.0.1:55463/fitflow'
39
39
  export SYNAPSOR_TENANT_ID='org-fitflow'
40
- npx -y @synapsor/runner@latest start --from-env DATABASE_URL
40
+ npx -y @synapsor/runner start --from-env DATABASE_URL
41
41
  ```
42
42
 
43
43
  Use only synthetic data. The fixture is not a production deployment template.
@@ -2,5 +2,5 @@
2
2
 
3
3
  smoke:
4
4
  python3 -m py_compile agent.py
5
- npx -y -p @synapsor/runner synapsor-runner mcp serve-streamable-http --help >/dev/null
5
+ npx -y @synapsor/runner mcp serve-streamable-http --help >/dev/null
6
6
  @printf '%s\n' "OpenAI Agents Streamable HTTP example smoke passed."
@@ -42,7 +42,7 @@ export SYNAPSOR_TENANT_ID="acme"
42
42
  export SYNAPSOR_PRINCIPAL="openai_agent_demo"
43
43
  export SYNAPSOR_RUNNER_HTTP_TOKEN="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("base64url"))')"
44
44
 
45
- npx -y -p @synapsor/runner synapsor-runner mcp serve-streamable-http \
45
+ npx -y @synapsor/runner mcp serve-streamable-http \
46
46
  --config ./synapsor.runner.json \
47
47
  --store ./.synapsor/local.db \
48
48
  --auth-token-env SYNAPSOR_RUNNER_HTTP_TOKEN \
@@ -2,5 +2,5 @@
2
2
 
3
3
  smoke:
4
4
  python3 -m py_compile agent.py
5
- npx -y -p @synapsor/runner synapsor-runner mcp serve --help >/dev/null
5
+ npx -y @synapsor/runner mcp serve --help >/dev/null
6
6
  @printf '%s\n' "OpenAI Agents stdio example smoke passed."
@@ -30,13 +30,13 @@ OpenAI Agents stdio example smoke passed.
30
30
  Generate `synapsor.runner.json` first:
31
31
 
32
32
  ```bash
33
- npx -y -p @synapsor/runner synapsor-runner demo
33
+ npx -y @synapsor/runner demo
34
34
  ```
35
35
 
36
36
  or connect your own staging database:
37
37
 
38
38
  ```bash
39
- npx -y -p @synapsor/runner synapsor-runner onboard db --from-env DATABASE_URL
39
+ npx -y @synapsor/runner onboard db --from-env DATABASE_URL
40
40
  ```
41
41
 
42
42
  Then install the Python dependencies:
@@ -25,10 +25,7 @@ async def main() -> None:
25
25
  params = {
26
26
  "command": "npx",
27
27
  "args": [
28
- "-y",
29
- "-p",
30
- "@synapsor/runner",
31
- "synapsor-runner",
28
+ "-y", "@synapsor/runner",
32
29
  "mcp",
33
30
  "serve",
34
31
  "--config",