@fourier-labs/harbour-contracts 0.35.0 → 0.41.0

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.
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://schemas.harbour.dev/transform-report.schema.json",
4
4
  "title": "TransformReport",
5
- "description": "Structured output an agent must return from one Harbour transformation attempt. Handed to the agent as its output schema, then decoded and semantically re-validated by the control plane, which remains the authority. The enum on applied_playbooks.items is the one dynamic part: the adapter injects the names of the playbooks supplied for that invocation before writing the document, so the model is structurally prevented from naming a playbook it was not given. Application-secret handling was removed platform-wide, so this document carries no redactions and no secrets fields. A deployable report must also carry preservation_plan: the agent's own grounded smoke tests (one to three happy_path user journeys plus a boundary_check per behaviour its playbooks or edits could regress) that the validation cell executes after transformation, and an uncovered list naming every boundary it could not test and why. An unsupported report carries only application.unsupported and no plan.",
5
+ "description": "Structured output an agent must return from one Harbour transformation attempt. Handed to the agent as its output schema, then decoded and semantically re-validated by the control plane, which remains the authority. The enum on applied_playbooks.items is the one dynamic part: the adapter injects the names of the playbooks supplied for that invocation before writing the document, so the model is structurally prevented from naming a playbook it was not given. Application-secret handling was removed platform-wide, so this document carries no redactions and no secrets fields. A deployable report must also carry preservation_plan: the agent's own grounded smoke tests (one to three happy_path user journeys plus a boundary_check per behaviour its playbooks or edits could regress) that the validation cell executes after transformation, and an uncovered list naming every boundary it could not test and why. It also carries validation_fixtures: null when no external fixture is needed, or a bounded generic HTTP/WebSocket fixture declaration for that one run. An unsupported report carries only application.unsupported and no plan or fixtures.",
6
6
  "x-go-embed": true,
7
7
  "type": "object",
8
8
  "additionalProperties": false,
@@ -56,21 +56,19 @@
56
56
  "changeType",
57
57
  "requiresCodeChanges",
58
58
  "requiresSchemaMigration",
59
- "desiredSchemaPath",
60
59
  "migrationPaths",
61
60
  "changedFiles",
62
61
  "evidence",
63
62
  "unsupportedFeatures"
64
63
  ],
65
64
  "properties": {
66
- "mode": { "type": "string", "enum": ["none", "sql", "unsupported"] },
65
+ "mode": { "type": "string", "enum": ["none", "sql"] },
67
66
  "changeType": {
68
67
  "type": "string",
69
68
  "enum": ["none", "code_only", "schema_only", "code_and_schema"]
70
69
  },
71
70
  "requiresCodeChanges": { "type": "boolean" },
72
71
  "requiresSchemaMigration": { "type": "boolean" },
73
- "desiredSchemaPath": { "type": "string", "maxLength": 512 },
74
72
  "migrationPaths": {
75
73
  "type": "array",
76
74
  "maxItems": 128,
@@ -219,6 +217,86 @@
219
217
  }
220
218
  }
221
219
  },
220
+ "validation_fixtures": {
221
+ "description": "A bounded, non-secret fixture program for this transformation's throwaway validation cell. Null means the application needs no external fixture. Bindings use application-owned logical names and explicitly inject only generated fixture URLs; fixtures describe deterministic HTTP or WebSocket exchanges without naming an application or provider and without executable code. The deployment plane semantically verifies unique fixture ids, template references, operation coverage, JSON text and safe config updates before forwarding the spec to the data plane.",
222
+ "anyOf": [
223
+ {
224
+ "type": "object",
225
+ "additionalProperties": false,
226
+ "required": ["version", "bindings", "fixtures"],
227
+ "properties": {
228
+ "version": { "type": "integer", "const": 1 },
229
+ "bindings": {
230
+ "description": "Bounded per-run fixture requirements. logical_name is an arbitrary application-owned binding name; deployment-plane semantic validation requires it to be unique within this array.",
231
+ "type": "array",
232
+ "minItems": 1,
233
+ "maxItems": 32,
234
+ "items": { "$ref": "#/definitions/ValidationFixtureBinding" }
235
+ },
236
+ "fixtures": {
237
+ "type": "array",
238
+ "minItems": 1,
239
+ "maxItems": 32,
240
+ "items": {
241
+ "type": "object",
242
+ "additionalProperties": false,
243
+ "required": ["id", "type", "http", "websocket"],
244
+ "properties": {
245
+ "id": {
246
+ "type": "string",
247
+ "pattern": "^[a-z][a-z0-9-]{0,63}$"
248
+ },
249
+ "type": { "type": "string", "enum": ["http", "websocket"] },
250
+ "http": {
251
+ "anyOf": [
252
+ { "$ref": "#/definitions/ValidationHttpFixture" },
253
+ { "type": "null" }
254
+ ]
255
+ },
256
+ "websocket": {
257
+ "anyOf": [
258
+ { "$ref": "#/definitions/ValidationWebSocketFixture" },
259
+ { "type": "null" }
260
+ ]
261
+ }
262
+ },
263
+ "oneOf": [
264
+ {
265
+ "type": "object",
266
+ "additionalProperties": false,
267
+ "required": ["id", "type", "http", "websocket"],
268
+ "properties": {
269
+ "id": {
270
+ "type": "string",
271
+ "pattern": "^[a-z][a-z0-9-]{0,63}$"
272
+ },
273
+ "type": { "const": "http" },
274
+ "http": { "$ref": "#/definitions/ValidationHttpFixture" },
275
+ "websocket": { "type": "null" }
276
+ }
277
+ },
278
+ {
279
+ "type": "object",
280
+ "additionalProperties": false,
281
+ "required": ["id", "type", "http", "websocket"],
282
+ "properties": {
283
+ "id": {
284
+ "type": "string",
285
+ "pattern": "^[a-z][a-z0-9-]{0,63}$"
286
+ },
287
+ "type": { "const": "websocket" },
288
+ "http": { "type": "null" },
289
+ "websocket": { "$ref": "#/definitions/ValidationWebSocketFixture" }
290
+ }
291
+ }
292
+ ]
293
+ }
294
+ }
295
+ }
296
+ },
297
+ { "type": "null" }
298
+ ]
299
+ },
222
300
  "application": {
223
301
  "type": "object",
224
302
  "additionalProperties": false,
@@ -226,11 +304,42 @@
226
304
  "unsupported": {
227
305
  "type": "object",
228
306
  "additionalProperties": false,
229
- "required": ["capability", "constraint", "reason"],
307
+ "required": ["category", "capability", "operation", "constraint", "reason", "evidence"],
230
308
  "properties": {
309
+ "category": {
310
+ "type": "string",
311
+ "enum": [
312
+ "auth.validation-seed-unsupported",
313
+ "auth.identity-entity-unresolvable",
314
+ "capability.unsupported"
315
+ ],
316
+ "description": "Closed machine category selected independently from the human explanation."
317
+ },
231
318
  "capability": { "type": "string", "minLength": 1, "maxLength": 512 },
319
+ "operation": {
320
+ "description": "The exact source operation that cannot be preserved, such as a cross-document transactional write. Name a concrete operation, not an engine or product.",
321
+ "type": "string",
322
+ "minLength": 1,
323
+ "maxLength": 512
324
+ },
232
325
  "constraint": { "type": "string", "minLength": 1, "maxLength": 512 },
233
- "reason": { "type": "string", "minLength": 1, "maxLength": 2048 }
326
+ "reason": { "type": "string", "minLength": 1, "maxLength": 2048 },
327
+ "evidence": {
328
+ "description": "Exact source locations proving the required operation and the unsupported platform capability. Environment failures and unfinished conversion work are not evidence.",
329
+ "type": "array",
330
+ "minItems": 1,
331
+ "maxItems": 32,
332
+ "items": {
333
+ "type": "object",
334
+ "additionalProperties": false,
335
+ "required": ["path", "line", "reason"],
336
+ "properties": {
337
+ "path": { "type": "string", "minLength": 1, "maxLength": 512 },
338
+ "line": { "type": "integer", "minimum": 1 },
339
+ "reason": { "type": "string", "minLength": 1, "maxLength": 1024 }
340
+ }
341
+ }
342
+ }
234
343
  }
235
344
  },
236
345
  "runtime": {
@@ -262,23 +371,18 @@
262
371
  ]
263
372
  },
264
373
  "validationSeed": {
265
- "description": "Build-only bootstrap for authenticated browser validation. Use null when the application does not require a local database user. When local authorization resolves Harbour identity to an application-owned user or membership row, declare an idempotent command that creates the fixed validation identity in the throwaway database. Reuse an existing seed command when available; otherwise add the smallest validation-only script using the application's existing database layer. The command is never run during production deployment.",
374
+ "description": "Build-only PostgreSQL bootstrap for authenticated browser validation. Use null when the application does not require a pre-existing database identity. The SQL file runs only in Harbour's throwaway validation database after migrations and before candidate boot. Harbour supplies the fixed probe subject and lowercased email as transaction-local settings.",
266
375
  "anyOf": [
267
376
  {
268
377
  "type": "object",
269
378
  "additionalProperties": false,
270
- "required": ["command", "identityEmailEnvironmentVariable"],
379
+ "required": ["kind", "path"],
271
380
  "properties": {
272
- "command": {
273
- "type": "array",
274
- "minItems": 1,
275
- "maxItems": 64,
276
- "items": { "type": "string", "minLength": 1, "maxLength": 1024 }
277
- },
278
- "identityEmailEnvironmentVariable": {
381
+ "kind": { "type": "string", "const": "sql" },
382
+ "path": {
279
383
  "type": "string",
280
- "pattern": "^[A-Z][A-Z0-9_]{0,127}$",
281
- "description": "Existing application-owned environment variable that receives the trusted browser probe email, for example ADMIN_EMAIL."
384
+ "pattern": "^\\.harbour/validation/(?:[^/]+/)*[^/]+\\.sql$",
385
+ "maxLength": 512
282
386
  }
283
387
  }
284
388
  },
@@ -360,9 +464,209 @@
360
464
  }
361
465
  }
362
466
  },
467
+ "definitions": {
468
+ "ValidationFixtureBinding": {
469
+ "type": "object",
470
+ "additionalProperties": false,
471
+ "required": ["logical_name", "operations", "environment", "config"],
472
+ "properties": {
473
+ "logical_name": {
474
+ "type": "string",
475
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$"
476
+ },
477
+ "fixture_id": {
478
+ "type": "string",
479
+ "pattern": "^[a-z][a-z0-9-]{0,63}$",
480
+ "description": "Optional explicit fixture association for a binding that has no URL mapping, such as a credential-only binding."
481
+ },
482
+ "operations": {
483
+ "type": "array",
484
+ "minItems": 1,
485
+ "maxItems": 5,
486
+ "uniqueItems": true,
487
+ "items": {
488
+ "type": "string",
489
+ "enum": ["http", "websocket", "email", "webhook", "ai"]
490
+ }
491
+ },
492
+ "environment": {
493
+ "description": "Environment variables that receive a generated fixture URL. Values cannot carry arbitrary configuration or secrets.",
494
+ "type": "array",
495
+ "maxItems": 32,
496
+ "items": {
497
+ "type": "object",
498
+ "additionalProperties": false,
499
+ "required": ["name"],
500
+ "properties": {
501
+ "name": {
502
+ "type": "string",
503
+ "pattern": "^[A-Z][A-Z0-9_]{0,127}$"
504
+ },
505
+ "value": {
506
+ "type": "string",
507
+ "pattern": "^\\{\\{fixture\\.[a-z][a-z0-9-]{0,63}\\.url\\}\\}$"
508
+ },
509
+ "test_value": {
510
+ "type": "string",
511
+ "minLength": 13,
512
+ "maxLength": 256,
513
+ "pattern": "^harbour-test-[^:/\\s]{1,243}$",
514
+ "description": "Validation-only synthetic credential. It is never exported as deployed runtime configuration."
515
+ }
516
+ },
517
+ "oneOf": [
518
+ { "required": ["value"], "not": { "required": ["test_value"] } },
519
+ { "required": ["test_value"], "not": { "required": ["value"] } }
520
+ ]
521
+ }
522
+ },
523
+ "config": {
524
+ "description": "Structured JSON or YAML keys that receive a generated fixture URL in a bounded relative application config file.",
525
+ "type": "array",
526
+ "maxItems": 32,
527
+ "items": {
528
+ "type": "object",
529
+ "additionalProperties": false,
530
+ "required": ["path", "format", "key", "value"],
531
+ "properties": {
532
+ "path": {
533
+ "type": "string",
534
+ "minLength": 1,
535
+ "maxLength": 512,
536
+ "pattern": "^(?:[A-Za-z0-9_-][A-Za-z0-9._-]*/)*[A-Za-z0-9_-][A-Za-z0-9._-]*$"
537
+ },
538
+ "format": { "type": "string", "enum": ["json", "yaml"] },
539
+ "key": {
540
+ "type": "string",
541
+ "minLength": 1,
542
+ "maxLength": 256,
543
+ "pattern": "^[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*$"
544
+ },
545
+ "value": {
546
+ "type": "string",
547
+ "pattern": "^\\{\\{fixture\\.[a-z][a-z0-9-]{0,63}\\.url\\}\\}$"
548
+ }
549
+ }
550
+ }
551
+ }
552
+ }
553
+ },
554
+ "ValidationFixtureNameValuePairs": {
555
+ "type": "array",
556
+ "maxItems": 32,
557
+ "items": {
558
+ "type": "object",
559
+ "additionalProperties": false,
560
+ "required": ["name", "value"],
561
+ "properties": {
562
+ "name": { "type": "string", "minLength": 1, "maxLength": 128 },
563
+ "value": { "type": "string", "maxLength": 2048 }
564
+ }
565
+ }
566
+ },
567
+ "ValidationHttpFixture": {
568
+ "type": "object",
569
+ "additionalProperties": false,
570
+ "required": ["base_path", "routes"],
571
+ "properties": {
572
+ "base_path": {
573
+ "type": "string",
574
+ "maxLength": 512,
575
+ "pattern": "^/(?:[A-Za-z0-9._~-]+(?:/[A-Za-z0-9._~-]+)*)?$"
576
+ },
577
+ "routes": {
578
+ "type": "array",
579
+ "minItems": 1,
580
+ "maxItems": 64,
581
+ "items": {
582
+ "type": "object",
583
+ "additionalProperties": false,
584
+ "required": ["operation", "request", "response"],
585
+ "properties": {
586
+ "operation": {
587
+ "type": "string",
588
+ "enum": ["http", "email", "webhook", "ai"]
589
+ },
590
+ "request": {
591
+ "type": "object",
592
+ "additionalProperties": false,
593
+ "required": ["method", "path", "query", "headers", "body"],
594
+ "properties": {
595
+ "method": {
596
+ "type": "string",
597
+ "enum": ["GET", "POST", "PUT", "PATCH", "DELETE"]
598
+ },
599
+ "path": {
600
+ "type": "string",
601
+ "maxLength": 512,
602
+ "pattern": "^/(?:[A-Za-z0-9._~-]+(?:/[A-Za-z0-9._~-]+)*)?$"
603
+ },
604
+ "query": { "$ref": "#/definitions/ValidationFixtureNameValuePairs" },
605
+ "headers": { "$ref": "#/definitions/ValidationFixtureNameValuePairs" },
606
+ "body": { "type": "string", "maxLength": 16384 }
607
+ }
608
+ },
609
+ "response": {
610
+ "type": "object",
611
+ "additionalProperties": false,
612
+ "required": ["status", "headers", "body"],
613
+ "properties": {
614
+ "status": { "type": "integer", "minimum": 100, "maximum": 599 },
615
+ "headers": { "$ref": "#/definitions/ValidationFixtureNameValuePairs" },
616
+ "body": { "type": "string", "maxLength": 16384 }
617
+ }
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
623
+ },
624
+ "ValidationWebSocketFixture": {
625
+ "type": "object",
626
+ "additionalProperties": false,
627
+ "required": ["path", "expected_json", "send_messages", "close"],
628
+ "properties": {
629
+ "path": {
630
+ "type": "string",
631
+ "maxLength": 512,
632
+ "pattern": "^/(?:[A-Za-z0-9._~-]+(?:/[A-Za-z0-9._~-]+)*)?$"
633
+ },
634
+ "expected_json": {
635
+ "description": "Exact JSON value the fixture expects as the first client message, or null when no client message is required. JSON parsing is a semantic check.",
636
+ "anyOf": [
637
+ { "type": "string", "minLength": 1, "maxLength": 16384 },
638
+ { "type": "null" }
639
+ ]
640
+ },
641
+ "send_messages": {
642
+ "type": "array",
643
+ "minItems": 1,
644
+ "maxItems": 64,
645
+ "items": { "type": "string", "maxLength": 16384 }
646
+ },
647
+ "close": {
648
+ "anyOf": [
649
+ {
650
+ "type": "object",
651
+ "additionalProperties": false,
652
+ "required": ["code", "reason"],
653
+ "properties": {
654
+ "code": { "type": "integer", "minimum": 1000, "maximum": 4999 },
655
+ "reason": { "type": "string", "maxLength": 123 }
656
+ }
657
+ },
658
+ { "type": "null" }
659
+ ]
660
+ }
661
+ }
662
+ }
663
+ },
664
+ "x-unique-array-by": [
665
+ { "field": "validation_fixtures.bindings", "key": "logical_name" }
666
+ ],
363
667
  "oneOf": [
364
668
  {
365
- "required": ["applied_playbooks", "static_rebuild_required", "analysis", "database", "preservation_plan", "application"],
669
+ "required": ["applied_playbooks", "static_rebuild_required", "analysis", "database", "preservation_plan", "validation_fixtures", "application"],
366
670
  "properties": {
367
671
  "application": {
368
672
  "required": ["runtime", "buildKind", "topology", "entrypoint", "build", "basePath", "evidence", "unsupportedFeatures"],