@fourier-labs/harbour-contracts 0.4.0 → 0.6.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.
Files changed (91) hide show
  1. package/dist/gen-ts/ai-callsite-inventory.d.ts +49 -0
  2. package/dist/gen-ts/ai-gateway-key-lookup.d.ts +20 -0
  3. package/dist/gen-ts/ai-gateway-key-token.d.ts +4 -0
  4. package/dist/gen-ts/ai-gateway-key-token.js +6 -0
  5. package/dist/gen-ts/ai-governance-decision.d.ts +74 -0
  6. package/dist/gen-ts/ai-policy-snapshot.d.ts +165 -0
  7. package/dist/gen-ts/ai-runtime-contract.d.ts +27 -0
  8. package/dist/gen-ts/ai-trace-event.d.ts +68 -0
  9. package/dist/gen-ts/ai-transform-event.d.ts +47 -0
  10. package/dist/gen-ts/ai-transform-result.d.ts +32 -0
  11. package/dist/gen-ts/ai-verification-evidence.d.ts +49 -0
  12. package/dist/gen-ts/data-dependency.d.ts +42 -0
  13. package/dist/gen-ts/data-grant.d.ts +58 -0
  14. package/dist/gen-ts/data-grant.js +12 -0
  15. package/dist/gen-ts/data-source.d.ts +74 -0
  16. package/dist/{deploy-request.d.ts → gen-ts/deploy-request.d.ts} +30 -0
  17. package/dist/gen-ts/deployment-source.js +3 -0
  18. package/dist/gen-ts/deployment-status-failure.js +3 -0
  19. package/dist/gen-ts/deployment-status-request.js +3 -0
  20. package/dist/gen-ts/deployment-status-response.js +3 -0
  21. package/dist/gen-ts/env-bindings.js +3 -0
  22. package/dist/{error-category-registry.d.ts → gen-ts/error-category-registry.d.ts} +5 -0
  23. package/dist/{error-category-registry.js → gen-ts/error-category-registry.js} +10 -0
  24. package/dist/gen-ts/error-category.js +3 -0
  25. package/dist/gen-ts/error-envelope.js +3 -0
  26. package/dist/{index.d.ts → gen-ts/index.d.ts} +15 -0
  27. package/dist/{index.js → gen-ts/index.js} +15 -0
  28. package/dist/gen-ts/kvs-routing-entry.d.ts +27 -0
  29. package/dist/gen-ts/kvs-routing-entry.js +3 -0
  30. package/dist/gen-ts/promotion-request.d.ts +40 -0
  31. package/dist/gen-ts/promotion-request.js +3 -0
  32. package/dist/gen-ts/promotion-response.js +3 -0
  33. package/dist/gen-ts/secret-reference.d.ts +36 -0
  34. package/dist/gen-ts/secret-reference.js +3 -0
  35. package/dist/gen-ts/transform-report.d.ts +102 -0
  36. package/dist/gen-ts/transform-report.js +3 -0
  37. package/dist/gen-ts/version-state.js +3 -0
  38. package/dist/resolver/index.d.ts +256 -0
  39. package/dist/resolver/index.js +679 -0
  40. package/package.json +14 -4
  41. package/schema/ai-callsite-inventory.schema.json +138 -0
  42. package/schema/ai-gateway-key-lookup.schema.json +53 -0
  43. package/schema/ai-governance-decision.schema.json +105 -0
  44. package/schema/ai-policy-snapshot.schema.json +340 -0
  45. package/schema/ai-runtime-contract.schema.json +85 -0
  46. package/schema/ai-trace-event.schema.json +178 -0
  47. package/schema/ai-transform-event.schema.json +72 -0
  48. package/schema/ai-transform-result.schema.json +46 -0
  49. package/schema/ai-verification-evidence.schema.json +57 -0
  50. package/schema/data-dependency.schema.json +89 -0
  51. package/schema/data-grant.schema.json +107 -0
  52. package/schema/data-source.schema.json +151 -0
  53. package/schema/deploy-request.schema.json +60 -0
  54. package/schema/error-category.enum.json +6 -1
  55. package/schema/kvs-routing-entry.schema.json +28 -8
  56. package/schema/promotion-request.schema.json +38 -0
  57. package/schema/secret-reference.schema.json +70 -0
  58. package/schema/transform-report.schema.json +204 -0
  59. package/dist/kvs-routing-entry.d.ts +0 -13
  60. package/dist/promotion-request.d.ts +0 -17
  61. /package/dist/{api-error-response.js → gen-ts/ai-callsite-inventory.js} +0 -0
  62. /package/dist/{artifact-contract.js → gen-ts/ai-gateway-key-lookup.js} +0 -0
  63. /package/dist/{deploy-request.js → gen-ts/ai-governance-decision.js} +0 -0
  64. /package/dist/{deploy-response.js → gen-ts/ai-policy-snapshot.js} +0 -0
  65. /package/dist/{deployment-environment.js → gen-ts/ai-runtime-contract.js} +0 -0
  66. /package/dist/{deployment-source.js → gen-ts/ai-trace-event.js} +0 -0
  67. /package/dist/{deployment-status-failure.js → gen-ts/ai-transform-event.js} +0 -0
  68. /package/dist/{deployment-status-request.js → gen-ts/ai-transform-result.js} +0 -0
  69. /package/dist/{deployment-status-response.js → gen-ts/ai-verification-evidence.js} +0 -0
  70. /package/dist/{api-error-response.d.ts → gen-ts/api-error-response.d.ts} +0 -0
  71. /package/dist/{env-bindings.js → gen-ts/api-error-response.js} +0 -0
  72. /package/dist/{artifact-contract.d.ts → gen-ts/artifact-contract.d.ts} +0 -0
  73. /package/dist/{error-category.js → gen-ts/artifact-contract.js} +0 -0
  74. /package/dist/{error-envelope.js → gen-ts/data-dependency.js} +0 -0
  75. /package/dist/{kvs-routing-entry.js → gen-ts/data-source.js} +0 -0
  76. /package/dist/{promotion-request.js → gen-ts/deploy-request.js} +0 -0
  77. /package/dist/{deploy-response.d.ts → gen-ts/deploy-response.d.ts} +0 -0
  78. /package/dist/{promotion-response.js → gen-ts/deploy-response.js} +0 -0
  79. /package/dist/{deployment-environment.d.ts → gen-ts/deployment-environment.d.ts} +0 -0
  80. /package/dist/{version-state.js → gen-ts/deployment-environment.js} +0 -0
  81. /package/dist/{deployment-source.d.ts → gen-ts/deployment-source.d.ts} +0 -0
  82. /package/dist/{deployment-status-failure.d.ts → gen-ts/deployment-status-failure.d.ts} +0 -0
  83. /package/dist/{deployment-status-request.d.ts → gen-ts/deployment-status-request.d.ts} +0 -0
  84. /package/dist/{deployment-status-response.d.ts → gen-ts/deployment-status-response.d.ts} +0 -0
  85. /package/dist/{env-bindings.d.ts → gen-ts/env-bindings.d.ts} +0 -0
  86. /package/dist/{error-category.d.ts → gen-ts/error-category.d.ts} +0 -0
  87. /package/dist/{error-envelope.d.ts → gen-ts/error-envelope.d.ts} +0 -0
  88. /package/dist/{promotion-response.d.ts → gen-ts/promotion-response.d.ts} +0 -0
  89. /package/dist/{version-state-machine.d.ts → gen-ts/version-state-machine.d.ts} +0 -0
  90. /package/dist/{version-state-machine.js → gen-ts/version-state-machine.js} +0 -0
  91. /package/dist/{version-state.d.ts → gen-ts/version-state.d.ts} +0 -0
@@ -0,0 +1,72 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-transform-event.schema.json",
4
+ "title": "AiTransformEvent",
5
+ "description": "Checksum-bound deterministic AI interception task input. Entries are the complete ordered PLAN interception plan so the task can verify the governance plan digest before applying its deterministic subset; source and patches are referenced, never embedded.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "tenantId", "appId", "environment", "versionId", "sourceRevision", "workspace", "inventoryDigest", "policyDigest", "runtimeContractDigest", "interceptionPlanDigest", "entries"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-transform-event/1.0" },
11
+ "tenantId": { "$ref": "#/definitions/AiTransformIdentifier" },
12
+ "appId": { "$ref": "#/definitions/AiTransformIdentifier" },
13
+ "environment": { "$ref": "#/definitions/AiTransformIdentifier" },
14
+ "versionId": { "type": "string", "minLength": 7, "maxLength": 320, "pattern": "^[^#]+#[^#]+#[^#]+#[0-9]{6,}$" },
15
+ "sourceRevision": { "type": "string", "minLength": 7, "maxLength": 128 },
16
+ "workspace": { "$ref": "#/definitions/AiTransformWorkspace" },
17
+ "inventoryDigest": { "$ref": "#/definitions/AiTransformDigest" },
18
+ "policyDigest": { "$ref": "#/definitions/AiTransformDigest" },
19
+ "runtimeContractDigest": { "$ref": "#/definitions/AiTransformDigest" },
20
+ "interceptionPlanDigest": { "$ref": "#/definitions/AiTransformDigest" },
21
+ "entries": {
22
+ "type": "array",
23
+ "minItems": 1,
24
+ "maxItems": 1000,
25
+ "items": { "$ref": "#/definitions/AiTransformPlanEntry" }
26
+ }
27
+ },
28
+ "definitions": {
29
+ "AiTransformIdentifier": { "type": "string", "minLength": 1, "maxLength": 160, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.@/-]*$" },
30
+ "AiTransformDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
31
+ "AiTransformWorkspace": {
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "required": ["bucket", "key", "sha256", "size"],
35
+ "properties": {
36
+ "bucket": { "type": "string", "minLength": 3, "maxLength": 63 },
37
+ "key": { "type": "string", "minLength": 1, "maxLength": 1024 },
38
+ "sha256": { "$ref": "#/definitions/AiTransformDigest" },
39
+ "size": { "type": "integer", "minimum": 1, "maximum": 536870912 }
40
+ }
41
+ },
42
+ "AiTransformPlanEntry": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "required": ["callSiteFingerprint", "sourcePath", "expectedSourceFileSha256", "strategy", "allowedSyntaxKinds"],
46
+ "properties": {
47
+ "callSiteFingerprint": { "$ref": "#/definitions/AiTransformDigest" },
48
+ "sourcePath": { "type": "string", "minLength": 1, "maxLength": 512, "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))[^\\u0000]+$" },
49
+ "expectedSourceFileSha256": { "$ref": "#/definitions/AiTransformDigest" },
50
+ "strategy": { "type": "string", "enum": ["deployment_injection", "deterministic_adapter"] },
51
+ "configurationMappingId": { "$ref": "#/definitions/AiTransformIdentifier" },
52
+ "configurationMappingVersion": { "type": "string", "minLength": 1, "maxLength": 128 },
53
+ "transformId": { "$ref": "#/definitions/AiTransformIdentifier" },
54
+ "transformVersion": { "type": "string", "minLength": 1, "maxLength": 128 },
55
+ "allowedSyntaxKinds": { "type": "array", "minItems": 1, "maxItems": 32, "uniqueItems": true, "items": { "$ref": "#/definitions/AiTransformIdentifier" } },
56
+ "runtimeShimName": { "type": "string", "minLength": 1, "maxLength": 160, "pattern": "^@?[A-Za-z0-9][A-Za-z0-9_.@/-]*$" },
57
+ "runtimeShimVersion": { "type": "string", "minLength": 1, "maxLength": 128 },
58
+ "runtimeShimDigest": { "$ref": "#/definitions/AiTransformDigest" }
59
+ },
60
+ "allOf": [
61
+ {
62
+ "if": { "properties": { "strategy": { "const": "deployment_injection" } }, "required": ["strategy"] },
63
+ "then": { "required": ["configurationMappingId", "configurationMappingVersion"] }
64
+ },
65
+ {
66
+ "if": { "properties": { "strategy": { "const": "deterministic_adapter" } }, "required": ["strategy"] },
67
+ "then": { "required": ["transformId", "transformVersion", "runtimeShimName", "runtimeShimVersion", "runtimeShimDigest"] }
68
+ }
69
+ ]
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-transform-result.schema.json",
4
+ "title": "AiTransformResult",
5
+ "description": "Bounded deterministic interception result; contains descriptors and digest evidence only.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "workspace", "interceptionPlanDigest", "transformerVersion", "applied"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-transform-result/1.0" },
11
+ "workspace": { "$ref": "#/definitions/AiTransformResultWorkspace" },
12
+ "interceptionPlanDigest": { "$ref": "#/definitions/AiTransformResultDigest" },
13
+ "transformerVersion": { "type": "string", "minLength": 1, "maxLength": 128 },
14
+ "applied": { "type": "array", "minItems": 1, "maxItems": 1000, "items": { "$ref": "#/definitions/AiTransformApplied" } }
15
+ },
16
+ "definitions": {
17
+ "AiTransformResultIdentifier": { "type": "string", "minLength": 1, "maxLength": 160, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.@/-]*$" },
18
+ "AiTransformResultDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
19
+ "AiTransformResultWorkspace": {
20
+ "type": "object",
21
+ "additionalProperties": false,
22
+ "required": ["bucket", "key", "sha256", "size"],
23
+ "properties": {
24
+ "bucket": { "type": "string", "minLength": 3, "maxLength": 63 },
25
+ "key": { "type": "string", "minLength": 1, "maxLength": 1024 },
26
+ "sha256": { "$ref": "#/definitions/AiTransformResultDigest" },
27
+ "size": { "type": "integer", "minimum": 1, "maximum": 536870912 }
28
+ }
29
+ },
30
+ "AiTransformApplied": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "required": ["callSiteFingerprint", "transformId", "transformVersion", "sourcePath", "beforeSha256", "afterSha256", "governedBoundaryFingerprint", "runtimeShimDigest"],
34
+ "properties": {
35
+ "callSiteFingerprint": { "$ref": "#/definitions/AiTransformResultDigest" },
36
+ "transformId": { "$ref": "#/definitions/AiTransformResultIdentifier" },
37
+ "transformVersion": { "type": "string", "minLength": 1, "maxLength": 128 },
38
+ "sourcePath": { "type": "string", "minLength": 1, "maxLength": 512, "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))[^\\u0000]+$" },
39
+ "beforeSha256": { "$ref": "#/definitions/AiTransformResultDigest" },
40
+ "afterSha256": { "$ref": "#/definitions/AiTransformResultDigest" },
41
+ "governedBoundaryFingerprint": { "$ref": "#/definitions/AiTransformResultDigest" },
42
+ "runtimeShimDigest": { "$ref": "#/definitions/AiTransformResultDigest" }
43
+ }
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/ai-verification-evidence.schema.json",
4
+ "title": "AiVerificationEvidence",
5
+ "description": "Bounded, value-free evidence submitted by deployment verification for one governed version.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema", "tenantId", "appId", "environment", "versionId", "sourceRevision", "inventoryDigest", "interceptionPlanDigest", "runtimeContractDigest", "policyDigest", "finalWorkspaceDigest", "verifiedCallsiteFingerprints", "appliedTransforms", "endpointChecks", "providerCredentialsAbsent", "smokeTraceId", "evidenceDigest", "verifiedAt"],
9
+ "properties": {
10
+ "schema": { "type": "string", "const": "harbour.ai-verification-evidence/1.0" },
11
+ "tenantId": { "$ref": "#/definitions/AiVerificationIdentifier" },
12
+ "appId": { "$ref": "#/definitions/AiVerificationIdentifier" },
13
+ "environment": { "$ref": "#/definitions/AiVerificationIdentifier" },
14
+ "versionId": { "type": "string", "minLength": 7, "maxLength": 320, "pattern": "^[^#]+#[^#]+#[^#]+#[0-9]{6,}$" },
15
+ "sourceRevision": { "type": "string", "minLength": 7, "maxLength": 128 },
16
+ "inventoryDigest": { "$ref": "#/definitions/AiVerificationDigest" },
17
+ "interceptionPlanDigest": { "$ref": "#/definitions/AiVerificationDigest" },
18
+ "runtimeContractDigest": { "$ref": "#/definitions/AiVerificationDigest" },
19
+ "policyDigest": { "$ref": "#/definitions/AiVerificationDigest" },
20
+ "finalWorkspaceDigest": { "$ref": "#/definitions/AiVerificationDigest" },
21
+ "verifiedCallsiteFingerprints": { "type": "array", "maxItems": 1000, "uniqueItems": true, "items": { "$ref": "#/definitions/AiVerificationDigest" } },
22
+ "appliedTransforms": { "type": "array", "maxItems": 1000, "items": { "$ref": "#/definitions/AiVerificationTransform" } },
23
+ "endpointChecks": { "type": "array", "minItems": 1, "maxItems": 32, "items": { "$ref": "#/definitions/AiVerificationEndpointCheck" } },
24
+ "providerCredentialsAbsent": { "type": "boolean", "const": true },
25
+ "smokeTraceId": { "type": "string", "pattern": "^trc_[A-Za-z0-9_-]+$", "maxLength": 160 },
26
+ "evidenceDigest": { "$ref": "#/definitions/AiVerificationDigest" },
27
+ "verifiedAt": { "type": "string", "format": "date-time" }
28
+ },
29
+ "definitions": {
30
+ "AiVerificationIdentifier": { "type": "string", "minLength": 1, "maxLength": 160, "pattern": "^[A-Za-z0-9][A-Za-z0-9_.@/-]*$" },
31
+ "AiVerificationDigest": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
32
+ "AiVerificationTransform": {
33
+ "type": "object",
34
+ "additionalProperties": false,
35
+ "required": ["callSiteFingerprint", "governedBoundaryFingerprint", "transformId", "transformVersion", "beforeSha256", "afterSha256", "runtimeShimDigest"],
36
+ "properties": {
37
+ "callSiteFingerprint": { "$ref": "#/definitions/AiVerificationDigest" },
38
+ "governedBoundaryFingerprint": { "$ref": "#/definitions/AiVerificationDigest" },
39
+ "transformId": { "$ref": "#/definitions/AiVerificationIdentifier" },
40
+ "transformVersion": { "type": "string", "minLength": 1, "maxLength": 128 },
41
+ "beforeSha256": { "$ref": "#/definitions/AiVerificationDigest" },
42
+ "afterSha256": { "$ref": "#/definitions/AiVerificationDigest" },
43
+ "runtimeShimDigest": { "$ref": "#/definitions/AiVerificationDigest" }
44
+ }
45
+ },
46
+ "AiVerificationEndpointCheck": {
47
+ "type": "object",
48
+ "additionalProperties": false,
49
+ "required": ["name", "passed", "configurationMappingVersion"],
50
+ "properties": {
51
+ "name": { "$ref": "#/definitions/AiVerificationIdentifier" },
52
+ "passed": { "type": "boolean", "const": true },
53
+ "configurationMappingVersion": { "type": "string", "minLength": 1, "maxLength": 128 }
54
+ }
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,89 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/data-dependency.schema.json",
4
+ "title": "DataDependency",
5
+ "description": "A single external data dependency of an app, either declared by the builder or detected during analysis. Classification records how the dependency resolved against the tenant's registered data-source inventory; matchedSourceId is present only when classification is matched.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["kind", "identifiers", "classification", "origin"],
9
+ "properties": {
10
+ "kind": {
11
+ "$ref": "#/definitions/DataDependencyKind"
12
+ },
13
+ "identifiers": {
14
+ "$ref": "#/definitions/DataDependencyIdentifiers"
15
+ },
16
+ "classification": {
17
+ "$ref": "#/definitions/DataDependencyClassification"
18
+ },
19
+ "matchedSourceId": {
20
+ "type": "string",
21
+ "minLength": 1,
22
+ "maxLength": 255,
23
+ "description": "sourceId of the registered data source this dependency matched; present only when classification is matched."
24
+ },
25
+ "origin": {
26
+ "$ref": "#/definitions/DataDependencyOrigin"
27
+ }
28
+ },
29
+ "definitions": {
30
+ "DataDependencyKind": {
31
+ "type": "string",
32
+ "enum": ["database", "api", "app_owned_external"],
33
+ "description": "database = a SQL/warehouse connection; api = an HTTP(S) service; app_owned_external = infrastructure the app owns outside Harbour."
34
+ },
35
+ "DataDependencyIdentifiers": {
36
+ "type": "object",
37
+ "additionalProperties": false,
38
+ "required": ["hosts", "envNames"],
39
+ "properties": {
40
+ "hosts": {
41
+ "type": "array",
42
+ "items": {
43
+ "type": "string",
44
+ "minLength": 1
45
+ },
46
+ "description": "Hostnames the dependency was observed or declared against."
47
+ },
48
+ "driver": {
49
+ "type": "string",
50
+ "minLength": 1,
51
+ "maxLength": 255,
52
+ "description": "Client driver/library that surfaced the dependency (e.g. pg, snowflake-sdk)."
53
+ },
54
+ "envNames": {
55
+ "type": "array",
56
+ "items": {
57
+ "type": "string",
58
+ "minLength": 1
59
+ },
60
+ "description": "Environment-variable names the app reads to configure this dependency."
61
+ }
62
+ },
63
+ "description": "Evidence identifying the dependency; used to match it against the registered source inventory."
64
+ },
65
+ "DataDependencyClassification": {
66
+ "type": "string",
67
+ "enum": ["matched", "out_of_scope", "unknown", "inventory", "app_owned_external"],
68
+ "description": "matched = resolved to a registered source; out_of_scope = resolved but not requestable; unknown = unresolvable; inventory = registered but unused; app_owned_external = app-owned, outside Harbour's remit."
69
+ },
70
+ "DataDependencyOrigin": {
71
+ "type": "string",
72
+ "enum": ["declared", "detected"],
73
+ "description": "declared = stated by the builder; detected = found by analysis."
74
+ }
75
+ },
76
+ "examples": [
77
+ {
78
+ "kind": "database",
79
+ "identifiers": {
80
+ "hosts": ["analytics.internal.example.com"],
81
+ "driver": "pg",
82
+ "envNames": ["DATABASE_URL"]
83
+ },
84
+ "classification": "matched",
85
+ "matchedSourceId": "analytics-warehouse",
86
+ "origin": "detected"
87
+ }
88
+ ]
89
+ }
@@ -0,0 +1,107 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/data-grant.schema.json",
4
+ "title": "DataGrant",
5
+ "description": "An app's approved (or requested) read scope against a registered data source. MVP is deliberately narrow: mode is read-only and identityModel is service (one minted principal per grant, no per-end-user identity passthrough). Grant status is a tiny state machine — legal edges live in x-transitions and codegen turns them into shared CanTransitionDataGrantStatus / IsTerminalDataGrantStatus helpers so control-plane and consumers agree on legality.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["appId", "sourceId", "scope", "mode", "identityModel", "status"],
9
+ "properties": {
10
+ "appId": {
11
+ "type": "string",
12
+ "minLength": 1,
13
+ "maxLength": 255,
14
+ "description": "App the grant belongs to."
15
+ },
16
+ "sourceId": {
17
+ "type": "string",
18
+ "minLength": 1,
19
+ "maxLength": 255,
20
+ "description": "Registered data source the grant targets (data-source.schema.json)."
21
+ },
22
+ "scope": {
23
+ "type": "array",
24
+ "items": {
25
+ "type": "string",
26
+ "minLength": 1
27
+ },
28
+ "minItems": 1,
29
+ "description": "Requested/granted selectors; every selector must fall inside the source's allowedScope."
30
+ },
31
+ "mode": {
32
+ "$ref": "#/definitions/DataGrantMode"
33
+ },
34
+ "identityModel": {
35
+ "$ref": "#/definitions/DataGrantIdentityModel"
36
+ },
37
+ "status": {
38
+ "$ref": "#/definitions/DataGrantStatus"
39
+ },
40
+ "credentialRef": {
41
+ "type": "string",
42
+ "pattern": "^secretref://[A-Za-z0-9][A-Za-z0-9/_.:-]*$",
43
+ "description": "secretref URI of the minted per-grant credential; present once GRANTED."
44
+ },
45
+ "mintedPrincipal": {
46
+ "type": "string",
47
+ "minLength": 1,
48
+ "maxLength": 255,
49
+ "description": "Name of the scoped principal minted on the source for this grant; present once GRANTED."
50
+ },
51
+ "requestedByOperationId": {
52
+ "type": "string",
53
+ "minLength": 1,
54
+ "maxLength": 255,
55
+ "description": "Operation (e.g. deployment analysis) that raised the request, for audit correlation."
56
+ },
57
+ "approvedBy": {
58
+ "type": "string",
59
+ "minLength": 1,
60
+ "maxLength": 255,
61
+ "description": "Admin identity that resolved the request; present once GRANTED or DENIED."
62
+ },
63
+ "approvedAt": {
64
+ "type": "string",
65
+ "format": "date-time",
66
+ "description": "RFC 3339 instant the request was resolved; present once GRANTED or DENIED."
67
+ }
68
+ },
69
+ "definitions": {
70
+ "DataGrantMode": {
71
+ "type": "string",
72
+ "enum": ["read"],
73
+ "description": "Access mode. MVP is read-only; write is a deliberate future schema change."
74
+ },
75
+ "DataGrantIdentityModel": {
76
+ "type": "string",
77
+ "enum": ["service"],
78
+ "description": "MVP: one minted service principal per grant. Per-end-user passthrough is a future schema change."
79
+ },
80
+ "DataGrantStatus": {
81
+ "type": "string",
82
+ "enum": ["REQUESTED", "GRANTED", "DENIED"],
83
+ "description": "Grant lifecycle state. Legal edges live in x-transitions; GRANTED and DENIED are terminal."
84
+ }
85
+ },
86
+ "x-status-definition": "DataGrantStatus",
87
+ "x-terminal": ["GRANTED", "DENIED"],
88
+ "x-transitions": [
89
+ { "from": "REQUESTED", "to": "GRANTED", "trigger": "admin approves: scope within allowedScope, principal minted, credentialRef set" },
90
+ { "from": "REQUESTED", "to": "DENIED", "trigger": "admin declines the request" }
91
+ ],
92
+ "examples": [
93
+ {
94
+ "appId": "reports",
95
+ "sourceId": "analytics-warehouse",
96
+ "scope": ["public.orders"],
97
+ "mode": "read",
98
+ "identityModel": "service",
99
+ "status": "GRANTED",
100
+ "credentialRef": "secretref://tenants/fourier/grants/reports/analytics-warehouse",
101
+ "mintedPrincipal": "harbour_grant_reports_analytics",
102
+ "requestedByOperationId": "fourier#reports#preview#1000042",
103
+ "approvedBy": "admin@fourier.example",
104
+ "approvedAt": "2026-08-05T12:00:00Z"
105
+ }
106
+ ]
107
+ }
@@ -0,0 +1,151 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/data-source.schema.json",
4
+ "title": "DataSource",
5
+ "description": "An admin-registered external data source (Data-Access MVP). The credential the platform holds for the source never crosses this boundary — credentialRef is a secretref URI resolved internally. allowedScope is the admin-declared selector allowlist grants are checked against; observedScope is what discovery actually saw and is informational only.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "tenantId",
10
+ "sourceId",
11
+ "connector",
12
+ "displayName",
13
+ "endpoint",
14
+ "credentialRef",
15
+ "provisioningMode",
16
+ "allowedScope",
17
+ "requestableByBuilders",
18
+ "status"
19
+ ],
20
+ "properties": {
21
+ "tenantId": {
22
+ "type": "string",
23
+ "minLength": 1,
24
+ "maxLength": 255,
25
+ "description": "Owning tenant (org) identifier."
26
+ },
27
+ "sourceId": {
28
+ "type": "string",
29
+ "minLength": 1,
30
+ "maxLength": 255,
31
+ "description": "Tenant-unique data-source identifier."
32
+ },
33
+ "connector": {
34
+ "$ref": "#/definitions/DataSourceConnector"
35
+ },
36
+ "displayName": {
37
+ "type": "string",
38
+ "minLength": 1,
39
+ "maxLength": 255,
40
+ "description": "Human-readable name shown to admins and builders."
41
+ },
42
+ "endpoint": {
43
+ "$ref": "#/definitions/DataSourceEndpoint"
44
+ },
45
+ "credentialRef": {
46
+ "type": "string",
47
+ "pattern": "^secretref://[A-Za-z0-9][A-Za-z0-9/_.:-]*$",
48
+ "description": "secretref URI naming the platform-held credential for this source. The referenced value never crosses this boundary."
49
+ },
50
+ "provisioningMode": {
51
+ "$ref": "#/definitions/DataSourceProvisioningMode"
52
+ },
53
+ "allowedScope": {
54
+ "type": "array",
55
+ "items": {
56
+ "type": "string",
57
+ "minLength": 1
58
+ },
59
+ "description": "Admin-declared selector allowlist (e.g. schema.table patterns) that grant scopes are validated against."
60
+ },
61
+ "observedScope": {
62
+ "type": "array",
63
+ "items": {
64
+ "type": "string",
65
+ "minLength": 1
66
+ },
67
+ "description": "Selectors discovery actually observed on the source. Informational; never widens allowedScope."
68
+ },
69
+ "requestableByBuilders": {
70
+ "type": "boolean",
71
+ "description": "Whether builders may request grants against this source."
72
+ },
73
+ "status": {
74
+ "$ref": "#/definitions/DataSourceStatus"
75
+ }
76
+ },
77
+ "definitions": {
78
+ "DataSourceConnector": {
79
+ "type": "string",
80
+ "enum": ["postgres", "redshift", "bigquery", "databricks"],
81
+ "description": "Closed connector set for the Data-Access MVP."
82
+ },
83
+ "DataSourceEndpoint": {
84
+ "type": "object",
85
+ "additionalProperties": false,
86
+ "required": ["host"],
87
+ "properties": {
88
+ "host": {
89
+ "type": "string",
90
+ "minLength": 1,
91
+ "maxLength": 255,
92
+ "description": "Network host (or warehouse endpoint) of the source."
93
+ },
94
+ "port": {
95
+ "type": "integer",
96
+ "minimum": 1,
97
+ "maximum": 65535,
98
+ "description": "TCP port; absent where the connector implies it."
99
+ },
100
+ "database": {
101
+ "type": "string",
102
+ "minLength": 1,
103
+ "maxLength": 255,
104
+ "description": "Database name (postgres/redshift-style connectors)."
105
+ },
106
+ "project": {
107
+ "type": "string",
108
+ "minLength": 1,
109
+ "maxLength": 255,
110
+ "description": "Project identifier (bigquery-style connectors)."
111
+ },
112
+ "workspace": {
113
+ "type": "string",
114
+ "minLength": 1,
115
+ "maxLength": 255,
116
+ "description": "Workspace identifier (databricks-style connectors)."
117
+ }
118
+ },
119
+ "description": "Connector endpoint coordinates. host is always present; database vs project/workspace depends on the connector family."
120
+ },
121
+ "DataSourceProvisioningMode": {
122
+ "type": "string",
123
+ "enum": ["byo", "harbour_managed"],
124
+ "description": "byo = admin brought existing infrastructure; harbour_managed = Harbour provisioned it."
125
+ },
126
+ "DataSourceStatus": {
127
+ "type": "string",
128
+ "enum": ["ACTIVE", "DISABLED"],
129
+ "description": "DISABLED sources reject new grants and suspend existing access."
130
+ }
131
+ },
132
+ "examples": [
133
+ {
134
+ "tenantId": "fourier",
135
+ "sourceId": "analytics-warehouse",
136
+ "connector": "postgres",
137
+ "displayName": "Analytics Warehouse",
138
+ "endpoint": {
139
+ "host": "analytics.internal.example.com",
140
+ "port": 5432,
141
+ "database": "analytics"
142
+ },
143
+ "credentialRef": "secretref://tenants/fourier/sources/analytics-warehouse/admin",
144
+ "provisioningMode": "byo",
145
+ "allowedScope": ["public.orders", "public.customers"],
146
+ "observedScope": ["public.orders", "public.customers", "public.internal_audit"],
147
+ "requestableByBuilders": true,
148
+ "status": "ACTIVE"
149
+ }
150
+ ]
151
+ }
@@ -30,9 +30,54 @@
30
30
  "type": "string",
31
31
  "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$",
32
32
  "description": "Optional legacy request-correlation value returned as requestId. It does not make repeated HTTP POSTs idempotent; versionId is the deployment identity and SQS deduplication key."
33
+ },
34
+ "environmentBindings": {
35
+ "$ref": "#/definitions/DeployRequestEnvironmentBindings"
33
36
  }
34
37
  },
35
38
  "definitions": {
39
+ "DeployRequestEnvironmentBindings": {
40
+ "type": "object",
41
+ "additionalProperties": false,
42
+ "required": ["bindings"],
43
+ "properties": {
44
+ "bindings": {
45
+ "type": "array",
46
+ "items": {
47
+ "$ref": "#/definitions/DeployRequestEnvironmentBinding"
48
+ },
49
+ "description": "Environment-variable bindings for this deployment."
50
+ }
51
+ },
52
+ "description": "Optional runtime environment configuration (Secret-Manager MVP). Named environmentBindings, not environment — the existing required `environment` property is the deployment-target token and predates this object. Additive: requests without it remain valid."
53
+ },
54
+ "DeployRequestEnvironmentBinding": {
55
+ "type": "object",
56
+ "additionalProperties": false,
57
+ "required": ["name"],
58
+ "properties": {
59
+ "name": {
60
+ "type": "string",
61
+ "minLength": 1,
62
+ "maxLength": 255,
63
+ "description": "Environment-variable name the app reads."
64
+ },
65
+ "secretRef": {
66
+ "type": "string",
67
+ "pattern": "^secretref://[A-Za-z0-9][A-Za-z0-9/_.:-]*$",
68
+ "description": "secretref URI resolved at runtime; the value never appears in the request."
69
+ },
70
+ "value": {
71
+ "type": "string",
72
+ "description": "Plain (non-secret) literal value."
73
+ }
74
+ },
75
+ "oneOf": [
76
+ { "required": ["secretRef"] },
77
+ { "required": ["value"] }
78
+ ],
79
+ "description": "One environment-variable binding: exactly one of secretRef (secret, resolved at runtime) or value (plain literal)."
80
+ },
36
81
  "DeployRequestSource": {
37
82
  "type": "object",
38
83
  "additionalProperties": false,
@@ -63,6 +108,21 @@
63
108
  "branch": "preview-deployment",
64
109
  "commit": "abc1234567890abcdef1234567890abcdef12345",
65
110
  "idempotencyKey": "deploy-attempt-123"
111
+ },
112
+ {
113
+ "source": {
114
+ "provider": "github",
115
+ "repositoryUrl": "https://github.com/Fourier-Labs-AI/focus-list"
116
+ },
117
+ "environment": "preview",
118
+ "branch": "preview-deployment",
119
+ "commit": "abc1234567890abcdef1234567890abcdef12345",
120
+ "environmentBindings": {
121
+ "bindings": [
122
+ { "name": "STRIPE_API_KEY", "secretRef": "secretref://tenants/fourier/apps/reports/STRIPE_API_KEY" },
123
+ { "name": "LOG_LEVEL", "value": "debug" }
124
+ ]
125
+ }
66
126
  }
67
127
  ]
68
128
  }
@@ -16,7 +16,9 @@
16
16
  { "id": "intake.unsupported-runtime", "retryable": false, "description": "Detected runtime has no Harbour build recipe." },
17
17
  { "id": "intake.attestation-missing", "retryable": false, "description": "Required auth attestation absent." },
18
18
  { "id": "intake.multi-app-repo", "retryable": false, "description": "Repo contains more than one deployable app." },
19
- { "id": "secrets.unresolvable", "retryable": false, "description": "A referenced secret has no value (prod promotion blocks at GATING until a human sets it)." },
19
+ { "id": "secrets.unresolvable", "retryable": false, "retired": true, "description": "RETIRED 2026-08-08 and no longer emittable: the secret-materializer stack, its reconciler and the secret-reference playbook were all removed, so nothing can reach this state. Kept rather than deleted because historical failure records still carry it and a dropped id would decode to nothing. Do not emit in new code." },
20
+ { "id": "secrets.reference-unresolvable", "retryable": false, "description": "A governance secret reference supplied on the deploy request could not be resolved to a stored value — the secret does not exist, the read was denied, or the secretref:// URI is malformed. Not retryable: no amount of waiting produces the value, a human must set it (or fix the reference) before the deploy can proceed." },
21
+ { "id": "secrets.placeholder-unresolved", "retryable": false, "description": "The built workspace still contains a ${HARBOUR_REDACTED_<KEY>} substitution placeholder after transformation, meaning the transform never converted it into a runtime secret lookup and the artifact would ship a literal placeholder where a secret belongs. Not retryable: the workspace is already built, so the same input yields the same placeholder until the transform is corrected." },
20
22
  { "id": "secrets.leaked-in-artifact", "retryable": false, "description": "Artifact secret scan found a plaintext secret in the built image (belt-and-suspenders)." },
21
23
  { "id": "storage.sqlite-misclassified", "retryable": false, "description": "SQLite usage not resolvable to a managed binding." },
22
24
  { "id": "storage.unsupported-engine", "retryable": false, "family": true, "description": "Storage engine with no Harbour mapping; concrete token names the engine, e.g. storage.unsupported-engine.mongodb." },
@@ -27,6 +29,8 @@
27
29
  { "id": "frontend.env-inlined-unfixable", "retryable": false, "description": "Client code inlined build-time env/self-URLs in a way transformation cannot rewrite." },
28
30
  { "id": "build.recipe-failed", "retryable": false, "description": "The Harbour build recipe failed for the detected runtime." },
29
31
  { "id": "build.image-too-large", "retryable": false, "description": "Built image exceeded the size limit." },
32
+ { "id": "browser.probe-failed", "retryable": true, "description": "The app built and produced its declared outputs, but the browser probe found it does not serve them — typically assets emitted at the root instead of under the app base path. Retryable because it is fed back to the transform agent, which is the component that can fix it; the merged job retries in-loop and the state machine retries the job. Distinct from build.recipe-failed, which means the build itself did not succeed: here it did." },
33
+ { "id": "browser.probe-unavailable", "retryable": false, "description": "The browser probe could not reach a verdict — Chromium crashed or disconnected, or it wrote no result document. Says nothing about the app, so it is NOT fed back to the agent: retrying would spend agent budget on something the agent cannot influence, and that budget is currently the only cost control in the system (control-plane-infra ADR 0007). Replaces the uncategorized.browser-probe token the merged job emitted while this entry did not exist." },
30
34
  { "id": "verify.smoke-failed", "retryable": false, "description": "Candidate smoke/health check failed (boot, routes, storage, secret refs, response shape)." },
31
35
  { "id": "verify.characterization-diverged", "retryable": false, "description": "Post-MVP: recorded req/res replay diverged beyond thresholds." },
32
36
  { "id": "verify.migration-from-zero-failed", "retryable": false, "description": "Post-MVP: from-zero migration chain failed on scratch DB." },
@@ -42,6 +46,7 @@
42
46
  { "id": "admission.app-not-registered", "retryable": false, "description": "Admission rejected: the app directory does not know this app, so there is no sourceRef to fetch a commit from. Fails closed rather than creating the app on demand — a deploy request must not become an app-creation side effect, or app naming stops being a reviewed act. Non-retryable: the directory is authoritative, so retrying cannot make an unknown app known." },
43
47
  { "id": "deploy.commit-not-on-branch", "retryable": false, "description": "Admission rejected: the deploy request's commit is not reachable from the app's branch." },
44
48
  { "id": "deploy.queue-publication-failed", "retryable": true, "description": "The version was created but its version-qualified SQS message definitively could not be published. The lane is released when this version still owns it; submitting a new deployment is safe." },
49
+ { "id": "source.branch-moved", "retryable": false, "description": "The app branch head moved while the transformed commit was being published, so the publish would have raced another push. Non-retryable at this version: the deploy request named a commit and the branch now points elsewhere, so a retry would deploy something the requester did not ask for. Submitting a new deployment for the new head is the recovery." },
45
50
  { "id": "promotion.preview-not-promotable", "retryable": false, "description": "The referenced preview is missing, belongs to another route, is not LIVE, or lacks immutable artifact and source-tree evidence." },
46
51
  { "id": "promotion.production-branch-moved", "retryable": false, "description": "The requested production commit is not the current configured production branch head." },
47
52
  { "id": "promotion.source-tree-mismatch", "retryable": false, "description": "The production commit root Git tree identity differs from the referenced preview tree identity." },