@fourier-labs/harbour-contracts 0.73.0 → 0.74.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.
package/README.md CHANGED
@@ -39,7 +39,7 @@ exist here first.
39
39
  | `sanitized-binding-evidence.schema.json` | Exact-version sanitized runtime-binding allocation receipt | AI governance §5.7 / SEC-UP-02 |
40
40
  | `ai-pricing-revision`, `ai-metering-policy`, `ai-budget-ledger-event`, `ai-trace-event-v2` | Additive metering v2 records, including media dimensions and P5 trace attribution; v1 records remain readable | AI governance §9 |
41
41
  | `app-integrations-declaration.schema.json` | Committed `.isomorph/integrations.json` (`isomorph.app-integrations/2.0`): closed twelve-operation set (Slack read/post/update/delete/DM/lookup, Gmail list/thread/read/send/attachment, warehouse view read — ADR 0018) with fixed or declared identity modes; owns the shared operation/identity/environment enums | Dev kit §4 |
42
- | `integration-execute-request`, `integration-execute-result`, `integration-connect-result`, `integration-error-details` | App SDK integrations wire contract on `/_harbour/integrations/*`: per-operation input bounds (a warehouse read filters, groups, aggregates and sorts in the warehouse and says when it was `truncated` — ADR 0017; Slack and Gmail reads page, posts thread, mail carries HTML and attachments — ADR 0018), the per-operation result shapes, consent states and the closed `details.code` reason set | Dev kit §4, §8 |
42
+ | `integration-execute-request`, `integration-execute-result`, `integration-connect-result`, `integration-error-details` | App SDK integrations wire contract on `/_harbour/integrations/*`: per-operation input bounds (a warehouse read filters, groups, aggregates and sorts in the warehouse and says when it was `truncated` — ADR 0017; Slack and Gmail reads page, posts thread, mail carries HTML and attachments — ADR 0018; a development warehouse read may carry the app's `sampleRows` and the result says `sample` — ADR 0021), the per-operation result shapes, consent states and the closed `details.code` reason set | Dev kit §4, §8 |
43
43
  | `saas-connection.schema.json`, `data-grant-v2.schema.json` | Tenant SaaS connection record (one installation and channel map per tenant and provider; IT's optional `oauthClient`, `userModeApproval`, `directMessages`, `externalRecipients` — ADR 0018) and the additive `harbour.data-grant/2.0` grant record with structured resources and provisioning lease/cleanup (status machine generated; legacy `data-grant` untouched) | Dev kit §5 |
44
44
  | `kit-bundle-manifest.schema.json`, `kit-lock.schema.json` | Published kit bundle identity (SDK tarball digest plus optional `sdk.version` and https `sdk.url` download location) and the committed `.isomorph/kit.lock.json` the CodeBuild SDK-vendoring gate compares against | Dev kit §3 |
45
45
  | `deploy-response.schema.json` | 202 Accepted body for `POST /v1/tenants/{tenant}/apps/{app}/deployments`, returned only after the authoritative QUEUED version exists and its SQS message is accepted | — |
@@ -57,7 +57,7 @@ exist here first.
57
57
  | `deployment-continuation.schema.json` | Immutable reference admitting one child deployment from a parked input request; never credential values | — |
58
58
  | `security-scan-input-v2.schema.json` | Input to the Security Assurance machine (`harbour.security-scan-input/2.0`, replaces v1): one package validated at source intake, scanned in the tenant's execution account | ADR 0014 |
59
59
  | `security-verdict.schema.json` | Security Assurance decision; `BLOCKED` surfaces as the non-retryable `gate.security-scan` category | ADR 0014 |
60
- | `data-source.schema.json` | Admin-registered external data source; `credentialRef` is a secretref URI and the credential never crosses this boundary | Data-Access MVP |
60
+ | `data-source.schema.json` | Admin-registered external data source; `credentialRef` is a secretref URI and the credential never crosses this boundary; optional `development`/`preview` copies under `environments`, production is the top-level endpoint; connectors include `trino` (ADR 0021) | Data-Access MVP |
61
61
  | `data-dependency.schema.json` | One external data dependency of an app, declared or detected, classified against the tenant's data-source inventory | Data-Access MVP |
62
62
  | `secret-reference.schema.json` | Named secret slot an app depends on; only the reference and its lifecycle cross, never the value | Secret-Manager MVP |
63
63
  | `ai-callsite-inventory.schema.json` | Immutable, sanitized AI call-site discovery for one exact deployment version (detection only) | AI governance |
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schema": "harbour.contract-release/1.0",
3
- "repositoryVersion": "0.73.0.0",
4
- "publishVersion": "0.73.0",
5
- "publishTag": "v0.73.0",
6
- "schemasDigest": "sha256:c0f37c59c7c2decb4fb62d96596b787628483bdcdb09756c3ea286385d31a5f5",
3
+ "repositoryVersion": "0.74.0.0",
4
+ "publishVersion": "0.74.0",
5
+ "publishTag": "v0.74.0",
6
+ "schemasDigest": "sha256:340d7f930a6cea6292f6ec20a02ca37ed5b78b42f4f4ec6e4c15057b44c6c3ff",
7
7
  "aiFixturesDigest": "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6",
8
- "generatedGoDigest": "sha256:eb98ee80133c91e8562b95d92388c5bb637a06ae8d97a0827f528cd6decde8c9",
9
- "generatedTypeScriptDigest": "sha256:ad539efcc9e4a7521f9526dd6304215f6b764be64bd40cd598ec023f6bbb4583",
8
+ "generatedGoDigest": "sha256:639a4832d8301e197caefe8d9b0fb2bd21cf20f9b580861a04bdce8d5411624a",
9
+ "generatedTypeScriptDigest": "sha256:ab6a5dd30f09a9604d34f5a994fb8a3491a092d1fb77232b1165e02a5e217939",
10
10
  "generatedExclusions": {
11
11
  "gen-go/contract_release.go": "contains the generated aggregate digests (self-reference)",
12
12
  "gen-ts/contract-release.ts": "contains the generated aggregate digests (self-reference)"
@@ -38,7 +38,7 @@
38
38
  "schema/data-dependency.schema.json": "sha256:b4cc2b2484e982cfed64c0f56b8f1e9d1363c71854cd71f845b24fbe7d85c971",
39
39
  "schema/data-grant-v2.schema.json": "sha256:576db6f17bbd3c6d700628bb5b93dcb4ade536ab868085634d05ff06476b6272",
40
40
  "schema/data-grant.schema.json": "sha256:e640553831ecc1c01cc9a37fe388b84d0ffeaa0d0050e63712b48f65fa437c8a",
41
- "schema/data-source.schema.json": "sha256:899384f4f11b05d2737766918ab47987b11ed49f1160220a55a367fed96f6f2c",
41
+ "schema/data-source.schema.json": "sha256:46ef4ace5fce612fce055813bf4f99956423b46c929993fbbc5dfec497087bad",
42
42
  "schema/deploy-request.schema.json": "sha256:32531e21983cdfeec511f8b4cddd4dad6530cd90e4e9fe147225d78d138b6198",
43
43
  "schema/deploy-response.schema.json": "sha256:09064948abdbba7022738285d802ed44648a16ddc2a5b8c2eeb170ae19a66aeb",
44
44
  "schema/deployment-app-registration.schema.json": "sha256:eb705c38466ebd97cb76c1ce7a6177d75c2f10381df75976b40d47eeb8dd5712",
@@ -53,9 +53,9 @@
53
53
  "schema/error-category.enum.json": "sha256:d6d83000c3357805b13138d717c411248c14f1c0fde1c02e82a25691de85d68d",
54
54
  "schema/error-envelope.schema.json": "sha256:75dccda7dd16fdf8435deda53fc7dbcdc109b65114617a17f11763010c656f90",
55
55
  "schema/integration-connect-result.schema.json": "sha256:3f254b67ef739dc1fa395bdc8c4dc93359b16d481dba2a2d777aa5b6f34b7af8",
56
- "schema/integration-error-details.schema.json": "sha256:47ad7621c34b092c7e40f912a21e7ccbdc000b974cfb7fa0c9200463e029f8fb",
57
- "schema/integration-execute-request.schema.json": "sha256:03b462a721dbd41f0591922d84e66b6074d36947338240d4f548eb0179d11ac7",
58
- "schema/integration-execute-result.schema.json": "sha256:3097b25f93234bc19872fa2081be0e3df1600c375106bd8b013533aea8deb95e",
56
+ "schema/integration-error-details.schema.json": "sha256:5a290584b1542b2b8cdd4966869126180ce29c1f0dd250dca7fb68c05e76b8e9",
57
+ "schema/integration-execute-request.schema.json": "sha256:601b5a72a99bdfb8e7b1ee0948bdda4bd1d3d694c66bb013431e927cddebfa58",
58
+ "schema/integration-execute-result.schema.json": "sha256:1b625e69a805b5b9e2a99ed9c60699372ab09793874bb98149f43b6d3ba11947",
59
59
  "schema/internal-configuration-request.schema.json": "sha256:229d44a3458f68dceddfba802e5c1adcd95523a45af29b93b972706b88461a7d",
60
60
  "schema/kit-bundle-manifest.schema.json": "sha256:4a9fcdd52ba0d55718ef4dbdab112b145ef90471d9356d4d3fb41e50fe9e400d",
61
61
  "schema/kit-lock.schema.json": "sha256:a48154d9471e49a893d928f7b0e1e0bc77708a4a7edce4fdf14073203e0e8987",
@@ -113,7 +113,7 @@
113
113
  "gen-go/integration_execute_input_rules_test.go": "sha256:17830fe9bc76d55d95e4e1959363c35ac595064972ac5c6eea3c2fae1efcaff9",
114
114
  "gen-go/integration_operations.go": "sha256:e2871c9df4c07068753256f0549aebe1ca6009495cdef8dab1168efd2894b77c",
115
115
  "gen-go/schema_documents.go": "sha256:c19c0d540f602ee997a84b90e20702d9bc63ed7d27aec267e76c04655fc182e0",
116
- "gen-go/types.go": "sha256:5c18e15a00dc7e7e0296cf7a6afefb24a93d828aca3b019f95b8256dea5754f6",
116
+ "gen-go/types.go": "sha256:01f7a573b1e0ca4366ec3dc1bf74ff6fc7d66274dec5ffb36c0332251188d4cc",
117
117
  "gen-go/version_state.go": "sha256:4ef8abb126414c0025cf2c59bc9c79a74ef4a2ff89e8f316cbf05acc872371c1"
118
118
  },
119
119
  "generatedTypeScript": {
@@ -146,7 +146,7 @@
146
146
  "gen-ts/data-dependency.ts": "sha256:259f19e2759395d72ead90b8ed74a6dcef0c2641124f4f182823138cfd19bedf",
147
147
  "gen-ts/data-grant-v2.ts": "sha256:cce15e51813fd693850391f12602a4436d869c75fc286b24cca539cfc5e27789",
148
148
  "gen-ts/data-grant.ts": "sha256:2358579d8aa1e57b01a6f2b0eacce5531e7f21ef226404c9809925365e0d6155",
149
- "gen-ts/data-source.ts": "sha256:1a40c252e94ee717d287ba63647e4368668dad9ea731770f8b58deb17d18b311",
149
+ "gen-ts/data-source.ts": "sha256:f30d4c14c15afbb9530d30af9235844198765984ce1579fa25961d7f07bcfb1f",
150
150
  "gen-ts/deploy-request.ts": "sha256:0214faa8be6680f0bfc117d5ce67c1a59fcc18bd0ea4bc840e03e7d051fa9ab6",
151
151
  "gen-ts/deploy-response.ts": "sha256:364d9f1acf32e491498237cdd3f402d5f8e102f208db7748034f4ec68c6a6137",
152
152
  "gen-ts/deployment-app-registration.ts": "sha256:642db3a3f796e5d7089cdb834991277ab1aac5bff37cd5cab42aa2f1c116f0be",
@@ -163,10 +163,10 @@
163
163
  "gen-ts/error-envelope.ts": "sha256:4457309be794f9d4fc0cf62530cc2d442c007bac9ae84aff66df099cc9e40310",
164
164
  "gen-ts/index.ts": "sha256:57927b9fcddf506785671bbd44cced9f729373e4dd3e3480bc6c37fd05fb59e8",
165
165
  "gen-ts/integration-connect-result.ts": "sha256:4b10f169b6e1af364e9b418de21caa9fadbfb77b43cbff77d741cfae0b52180c",
166
- "gen-ts/integration-error-details.ts": "sha256:ad7ecec9dd4533a6e86b3508886a7d69937052f52105497285797f77106bcc6f",
166
+ "gen-ts/integration-error-details.ts": "sha256:e73cb5942373ef88b2d8e03a1de56d5a69e71069ecd14e68396f7edba1f60780",
167
167
  "gen-ts/integration-execute-input-rules.ts": "sha256:1127c321720cb9bde63a6d20dadd177c923fef1c8c8f37e0b106a2488fe079de",
168
- "gen-ts/integration-execute-request.ts": "sha256:c9904987f61b006ebcc7d8500ec255e7bd7fc1ae0f98f23cf3a096a4273e3d42",
169
- "gen-ts/integration-execute-result.ts": "sha256:f82de8f2e36bac732e015cecd7454a7cecd6ea5cf09b6efd119e252990a47c92",
168
+ "gen-ts/integration-execute-request.ts": "sha256:f95ec137982a9d774828998fcde76b2ac08a9261dc4f6988fe222f77ecdd00ba",
169
+ "gen-ts/integration-execute-result.ts": "sha256:1ac69edc6db8641c4fefe6e4ef86bc8855c95006089af279b15e8d6daf978252",
170
170
  "gen-ts/integration-operations.ts": "sha256:f4ddc2dc5776557d20dbf92a1e20cec63198c7d008426773c2e0fc3f509cd460",
171
171
  "gen-ts/internal-configuration-request.ts": "sha256:7032437797473fa1c581d134adef2f3d4b4f4132f482cd17743e9d47097886bc",
172
172
  "gen-ts/kit-bundle-manifest.ts": "sha256:2777a268aef8787b81e71d154a7d05f6f5ded09d66f378211ff5493ddb885171",
@@ -1,7 +1,7 @@
1
- export declare const ContractRepositoryVersion: "0.73.0.0";
2
- export declare const ContractPublishVersion: "0.73.0";
3
- export declare const ContractPublishTag: "v0.73.0";
4
- export declare const ContractSchemasDigest: "sha256:c0f37c59c7c2decb4fb62d96596b787628483bdcdb09756c3ea286385d31a5f5";
1
+ export declare const ContractRepositoryVersion: "0.74.0.0";
2
+ export declare const ContractPublishVersion: "0.74.0";
3
+ export declare const ContractPublishTag: "v0.74.0";
4
+ export declare const ContractSchemasDigest: "sha256:340d7f930a6cea6292f6ec20a02ca37ed5b78b42f4f4ec6e4c15057b44c6c3ff";
5
5
  export declare const ContractAiFixturesDigest: "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
6
- export declare const ContractGeneratedGoDigest: "sha256:eb98ee80133c91e8562b95d92388c5bb637a06ae8d97a0827f528cd6decde8c9";
7
- export declare const ContractGeneratedTypeScriptDigest: "sha256:ad539efcc9e4a7521f9526dd6304215f6b764be64bd40cd598ec023f6bbb4583";
6
+ export declare const ContractGeneratedGoDigest: "sha256:639a4832d8301e197caefe8d9b0fb2bd21cf20f9b580861a04bdce8d5411624a";
7
+ export declare const ContractGeneratedTypeScriptDigest: "sha256:ab6a5dd30f09a9604d34f5a994fb8a3491a092d1fb77232b1165e02a5e217939";
@@ -1,9 +1,9 @@
1
1
  /* eslint-disable */
2
2
  // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
- export const ContractRepositoryVersion = "0.73.0.0";
4
- export const ContractPublishVersion = "0.73.0";
5
- export const ContractPublishTag = "v0.73.0";
6
- export const ContractSchemasDigest = "sha256:c0f37c59c7c2decb4fb62d96596b787628483bdcdb09756c3ea286385d31a5f5";
3
+ export const ContractRepositoryVersion = "0.74.0.0";
4
+ export const ContractPublishVersion = "0.74.0";
5
+ export const ContractPublishTag = "v0.74.0";
6
+ export const ContractSchemasDigest = "sha256:340d7f930a6cea6292f6ec20a02ca37ed5b78b42f4f4ec6e4c15057b44c6c3ff";
7
7
  export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
8
- export const ContractGeneratedGoDigest = "sha256:eb98ee80133c91e8562b95d92388c5bb637a06ae8d97a0827f528cd6decde8c9";
9
- export const ContractGeneratedTypeScriptDigest = "sha256:ad539efcc9e4a7521f9526dd6304215f6b764be64bd40cd598ec023f6bbb4583";
8
+ export const ContractGeneratedGoDigest = "sha256:639a4832d8301e197caefe8d9b0fb2bd21cf20f9b580861a04bdce8d5411624a";
9
+ export const ContractGeneratedTypeScriptDigest = "sha256:ab6a5dd30f09a9604d34f5a994fb8a3491a092d1fb77232b1165e02a5e217939";
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Closed connector set for the Data-Access MVP.
2
+ * Closed connector set for the Data-Access MVP. trino (0.74.0): Trino/Presto-compatible engines (Trino, Presto, Starburst, Galaxy) over the Trino HTTP statement protocol; the endpoint reuses host, port and database (as the Trino catalog) with no connector-specific fields.
3
3
  */
4
- export type DataSourceConnector = 'postgres' | 'redshift' | 'bigquery' | 'databricks' | 'mysql';
4
+ export type DataSourceConnector = 'postgres' | 'redshift' | 'bigquery' | 'databricks' | 'mysql' | 'trino';
5
5
  /**
6
6
  * byo = admin brought existing infrastructure; harbour_managed = Isomorph provisioned it.
7
7
  */
@@ -50,6 +50,13 @@ export interface DataSource {
50
50
  */
51
51
  allowValidation?: boolean;
52
52
  status: DataSourceStatus;
53
+ /**
54
+ * 0.74.0: per-environment copies of the source. A read for an environment that has a copy here uses that copy's endpoint and credentialRef; production always uses the top-level endpoint and credentialRef. A development read with no development copy is answered from the app's sample rows (integration-execute-request.sampleRows), never from the source; a preview read with no preview copy uses the production endpoint. A laptop never reads the production endpoint.
55
+ */
56
+ environments?: {
57
+ development?: DataSourceEnvironmentCopy;
58
+ preview?: DataSourceEnvironmentCopy;
59
+ };
53
60
  }
54
61
  /**
55
62
  * Connector endpoint coordinates. host is always present; database vs project/workspace depends on the connector family.
@@ -76,3 +83,13 @@ export interface DataSourceEndpoint {
76
83
  */
77
84
  workspace?: string;
78
85
  }
86
+ /**
87
+ * One environment's copy of a source: the same endpoint and credentialRef shapes as the top-level fields, for the same connector.
88
+ */
89
+ export interface DataSourceEnvironmentCopy {
90
+ endpoint: DataSourceEndpoint;
91
+ /**
92
+ * secretref URI naming the platform-held credential for this copy. The referenced value never crosses this boundary.
93
+ */
94
+ credentialRef: string;
95
+ }
@@ -3,9 +3,9 @@
3
3
  */
4
4
  export type IntegrationErrorCategory = 'AUTH_REQUIRED' | 'FORBIDDEN' | 'NOT_FOUND' | 'CONFLICT' | 'VALIDATION_FAILED' | 'RATE_LIMITED' | 'UNAVAILABLE';
5
5
  /**
6
- * Stable reason codes. SEND_OUTCOME_UNKNOWN: dispatch happened but the outcome is unknown; never re-post automatically. SEND_KEY_REUSED: the idempotency key was seen with a different payload or subject. GRANT_DENIED: IT denied the request. TOKEN_REFRESH_PENDING: another call holds the refresh lease; retry shortly. REQUEST_PENDING: a different request for this grant is still open. INPUT_INVALID: the operation input failed its bounds. IDEMPOTENCY_KEY_REQUIRED: a send without a UUID idempotencyKey. SEND_IN_PROGRESS: a retry arrived while the first send of that key is still running. CONNECTION_NOT_FOUND / APP_NOT_FOUND / GRANT_NOT_FOUND: the named record does not exist. AUTH_REQUIRED: no verified caller. UNAVAILABLE: the governance dependency (store, provider seam) is unreachable. MODE_NOT_GRANTED (403 FORBIDDEN, 0.55.0): a slack.message.post named a mode (app or user) IT has not approved for this app; the post is never served under the other mode. MODE_REQUIRED (400 VALIDATION_FAILED, 0.55.0): the app holds approved grants for both modes and the post named neither.
6
+ * Stable reason codes. SEND_OUTCOME_UNKNOWN: dispatch happened but the outcome is unknown; never re-post automatically. SEND_KEY_REUSED: the idempotency key was seen with a different payload or subject. GRANT_DENIED: IT denied the request. TOKEN_REFRESH_PENDING: another call holds the refresh lease; retry shortly. REQUEST_PENDING: a different request for this grant is still open. INPUT_INVALID: the operation input failed its bounds. IDEMPOTENCY_KEY_REQUIRED: a send without a UUID idempotencyKey. SEND_IN_PROGRESS: a retry arrived while the first send of that key is still running. CONNECTION_NOT_FOUND / APP_NOT_FOUND / GRANT_NOT_FOUND: the named record does not exist. AUTH_REQUIRED: no verified caller. UNAVAILABLE: the governance dependency (store, provider seam) is unreachable. MODE_NOT_GRANTED (403 FORBIDDEN, 0.55.0): a slack.message.post named a mode (app or user) IT has not approved for this app; the post is never served under the other mode. MODE_REQUIRED (400 VALIDATION_FAILED, 0.55.0): the app holds approved grants for both modes and the post named neither. SAMPLE_ROWS_NOT_ALLOWED (400 VALIDATION_FAILED, 0.74.0): an execute request carried sampleRows on any route but the development one. A sample value that does not parse as the column's recorded type is INPUT_INVALID, naming the column and the row.
7
7
  */
8
- export type IntegrationErrorCode = 'USER_CONNECTION_REQUIRED' | 'USER_RECONNECT_REQUIRED' | 'GRANT_NOT_READY' | 'GRANT_EXPIRED' | 'GRANT_REVOKED' | 'CONNECTION_DISABLED' | 'OPERATION_NOT_DECLARED' | 'RESOURCE_NOT_APPROVED' | 'IDENTITY_MODE_MISMATCH' | 'SEND_OUTCOME_UNKNOWN' | 'SEND_KEY_REUSED' | 'PROVIDER_RATE_LIMITED' | 'PROVIDER_UNAVAILABLE' | 'REQUEST_TOO_LARGE' | 'RESULT_TOO_LARGE' | 'DEADLINE_EXCEEDED' | 'APP_ACCESS_REQUIRED' | 'GRANT_DENIED' | 'TOKEN_REFRESH_PENDING' | 'REQUEST_PENDING' | 'INPUT_INVALID' | 'IDEMPOTENCY_KEY_REQUIRED' | 'SEND_IN_PROGRESS' | 'CONNECTION_NOT_FOUND' | 'APP_NOT_FOUND' | 'GRANT_NOT_FOUND' | 'AUTH_REQUIRED' | 'UNAVAILABLE' | 'MODE_NOT_GRANTED' | 'MODE_REQUIRED';
8
+ export type IntegrationErrorCode = 'USER_CONNECTION_REQUIRED' | 'USER_RECONNECT_REQUIRED' | 'GRANT_NOT_READY' | 'GRANT_EXPIRED' | 'GRANT_REVOKED' | 'CONNECTION_DISABLED' | 'OPERATION_NOT_DECLARED' | 'RESOURCE_NOT_APPROVED' | 'IDENTITY_MODE_MISMATCH' | 'SEND_OUTCOME_UNKNOWN' | 'SEND_KEY_REUSED' | 'PROVIDER_RATE_LIMITED' | 'PROVIDER_UNAVAILABLE' | 'REQUEST_TOO_LARGE' | 'RESULT_TOO_LARGE' | 'DEADLINE_EXCEEDED' | 'APP_ACCESS_REQUIRED' | 'GRANT_DENIED' | 'TOKEN_REFRESH_PENDING' | 'REQUEST_PENDING' | 'INPUT_INVALID' | 'IDEMPOTENCY_KEY_REQUIRED' | 'SEND_IN_PROGRESS' | 'CONNECTION_NOT_FOUND' | 'APP_NOT_FOUND' | 'GRANT_NOT_FOUND' | 'AUTH_REQUIRED' | 'UNAVAILABLE' | 'MODE_NOT_GRANTED' | 'MODE_REQUIRED' | 'SAMPLE_ROWS_NOT_ALLOWED';
9
9
  /**
10
10
  * The `error` member of an integration failure response `{error, requestId}` on the execute/connect routes; the SDK maps it onto IsomorphError(category, message, details). `category` is the app-SDK IsomorphErrorCategory (not the deployment pipeline's dot-path error-category registry), with the HTTP status fixed per category: 401 AUTH_REQUIRED, 403 FORBIDDEN (denied/revoked/expired), 404 NOT_FOUND (unknown app/connection/resource), 409 CONFLICT (pending provisioning/consent/concurrent request), 400 or 413 VALIDATION_FAILED (invalid input / size overflow), 429 RATE_LIMITED (+ Retry-After header), 503 UNAVAILABLE. `details.code` is the closed stable reason set; `message` is human-facing and never carries provider error bodies, tokens or SQL.
11
11
  */
@@ -150,6 +150,14 @@ export type IntegrationExecuteRequest = {
150
150
  * The declared-mode Slack operations only (slack.message.post since 0.55.0; slack.channel.history, slack.message.update and slack.message.delete since 0.71.0): which of the app's IT-approved modes this call runs under — app (the company's Slack app; a post goes out under the grant's per-app name and the footer) or user (the signed-in person, after their consent; no name, no footer). Required when the app holds approved grants for both modes (else MODE_REQUIRED); may be omitted when it holds one, which is then used. A named mode without an approved grant is refused with MODE_NOT_GRANTED; governance never falls back to the other mode. Every other operation's mode is fixed, so the field is refused there.
151
151
  */
152
152
  mode?: 'app' | 'user';
153
+ /**
154
+ * Development only (0.74.0). Rows the kit dev runtime attaches for the resource being read, from the sample-row file the agent wrote into the app's .isomorph/ folder. Governance serves them for a development warehouse.view.read when the source has no development copy (data-source.environments.development): it parses each value by the resource's recorded column type and refuses a value that does not parse, naming the column and the row. The field is refused with SAMPLE_ROWS_NOT_ALLOWED on any route but the development one, so a preview or production read never carries made-up data. Not part of the per-operation input rules; the dev runtime adds it beside the app's request.
155
+ *
156
+ * @maxItems 50
157
+ */
158
+ sampleRows?: {
159
+ [k: string]: string | number | boolean | null;
160
+ }[];
153
161
  };
154
162
  /**
155
163
  * One bare email address (no display name).
@@ -43,6 +43,10 @@ export type IntegrationExecuteResult = {
43
43
  * gmail.message.read: the text was cut at 16 KiB. warehouse.view.read (0.70.0): more rows matched than the read's limit, so `rows` is incomplete.
44
44
  */
45
45
  truncated?: boolean;
46
+ /**
47
+ * warehouse.view.read (0.74.0): true when `rows` came from the app's sample rows (integration-execute-request.sampleRows) because the development read had no development copy of the source; the rows were never read from the source. Absent or false: the rows came from the source.
48
+ */
49
+ sample?: boolean;
46
50
  /**
47
51
  * gmail.message.read (0.71.0): the message's attachments, without their bytes.
48
52
  *
@@ -1,10 +1,10 @@
1
1
  /* eslint-disable */
2
2
  // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
3
 
4
- export const ContractRepositoryVersion = "0.73.0.0" as const;
5
- export const ContractPublishVersion = "0.73.0" as const;
6
- export const ContractPublishTag = "v0.73.0" as const;
7
- export const ContractSchemasDigest = "sha256:c0f37c59c7c2decb4fb62d96596b787628483bdcdb09756c3ea286385d31a5f5" as const;
4
+ export const ContractRepositoryVersion = "0.74.0.0" as const;
5
+ export const ContractPublishVersion = "0.74.0" as const;
6
+ export const ContractPublishTag = "v0.74.0" as const;
7
+ export const ContractSchemasDigest = "sha256:340d7f930a6cea6292f6ec20a02ca37ed5b78b42f4f4ec6e4c15057b44c6c3ff" as const;
8
8
  export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6" as const;
9
- export const ContractGeneratedGoDigest = "sha256:eb98ee80133c91e8562b95d92388c5bb637a06ae8d97a0827f528cd6decde8c9" as const;
10
- export const ContractGeneratedTypeScriptDigest = "sha256:ad539efcc9e4a7521f9526dd6304215f6b764be64bd40cd598ec023f6bbb4583" as const;
9
+ export const ContractGeneratedGoDigest = "sha256:639a4832d8301e197caefe8d9b0fb2bd21cf20f9b580861a04bdce8d5411624a" as const;
10
+ export const ContractGeneratedTypeScriptDigest = "sha256:ab6a5dd30f09a9604d34f5a994fb8a3491a092d1fb77232b1165e02a5e217939" as const;
@@ -2,9 +2,9 @@
2
2
  // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
3
 
4
4
  /**
5
- * Closed connector set for the Data-Access MVP.
5
+ * Closed connector set for the Data-Access MVP. trino (0.74.0): Trino/Presto-compatible engines (Trino, Presto, Starburst, Galaxy) over the Trino HTTP statement protocol; the endpoint reuses host, port and database (as the Trino catalog) with no connector-specific fields.
6
6
  */
7
- export type DataSourceConnector = 'postgres' | 'redshift' | 'bigquery' | 'databricks' | 'mysql';
7
+ export type DataSourceConnector = 'postgres' | 'redshift' | 'bigquery' | 'databricks' | 'mysql' | 'trino';
8
8
  /**
9
9
  * byo = admin brought existing infrastructure; harbour_managed = Isomorph provisioned it.
10
10
  */
@@ -54,6 +54,13 @@ export interface DataSource {
54
54
  */
55
55
  allowValidation?: boolean;
56
56
  status: DataSourceStatus;
57
+ /**
58
+ * 0.74.0: per-environment copies of the source. A read for an environment that has a copy here uses that copy's endpoint and credentialRef; production always uses the top-level endpoint and credentialRef. A development read with no development copy is answered from the app's sample rows (integration-execute-request.sampleRows), never from the source; a preview read with no preview copy uses the production endpoint. A laptop never reads the production endpoint.
59
+ */
60
+ environments?: {
61
+ development?: DataSourceEnvironmentCopy;
62
+ preview?: DataSourceEnvironmentCopy;
63
+ };
57
64
  }
58
65
  /**
59
66
  * Connector endpoint coordinates. host is always present; database vs project/workspace depends on the connector family.
@@ -80,3 +87,13 @@ export interface DataSourceEndpoint {
80
87
  */
81
88
  workspace?: string;
82
89
  }
90
+ /**
91
+ * One environment's copy of a source: the same endpoint and credentialRef shapes as the top-level fields, for the same connector.
92
+ */
93
+ export interface DataSourceEnvironmentCopy {
94
+ endpoint: DataSourceEndpoint;
95
+ /**
96
+ * secretref URI naming the platform-held credential for this copy. The referenced value never crosses this boundary.
97
+ */
98
+ credentialRef: string;
99
+ }
@@ -7,7 +7,7 @@
7
7
  export type IntegrationErrorCategory =
8
8
  'AUTH_REQUIRED' | 'FORBIDDEN' | 'NOT_FOUND' | 'CONFLICT' | 'VALIDATION_FAILED' | 'RATE_LIMITED' | 'UNAVAILABLE';
9
9
  /**
10
- * Stable reason codes. SEND_OUTCOME_UNKNOWN: dispatch happened but the outcome is unknown; never re-post automatically. SEND_KEY_REUSED: the idempotency key was seen with a different payload or subject. GRANT_DENIED: IT denied the request. TOKEN_REFRESH_PENDING: another call holds the refresh lease; retry shortly. REQUEST_PENDING: a different request for this grant is still open. INPUT_INVALID: the operation input failed its bounds. IDEMPOTENCY_KEY_REQUIRED: a send without a UUID idempotencyKey. SEND_IN_PROGRESS: a retry arrived while the first send of that key is still running. CONNECTION_NOT_FOUND / APP_NOT_FOUND / GRANT_NOT_FOUND: the named record does not exist. AUTH_REQUIRED: no verified caller. UNAVAILABLE: the governance dependency (store, provider seam) is unreachable. MODE_NOT_GRANTED (403 FORBIDDEN, 0.55.0): a slack.message.post named a mode (app or user) IT has not approved for this app; the post is never served under the other mode. MODE_REQUIRED (400 VALIDATION_FAILED, 0.55.0): the app holds approved grants for both modes and the post named neither.
10
+ * Stable reason codes. SEND_OUTCOME_UNKNOWN: dispatch happened but the outcome is unknown; never re-post automatically. SEND_KEY_REUSED: the idempotency key was seen with a different payload or subject. GRANT_DENIED: IT denied the request. TOKEN_REFRESH_PENDING: another call holds the refresh lease; retry shortly. REQUEST_PENDING: a different request for this grant is still open. INPUT_INVALID: the operation input failed its bounds. IDEMPOTENCY_KEY_REQUIRED: a send without a UUID idempotencyKey. SEND_IN_PROGRESS: a retry arrived while the first send of that key is still running. CONNECTION_NOT_FOUND / APP_NOT_FOUND / GRANT_NOT_FOUND: the named record does not exist. AUTH_REQUIRED: no verified caller. UNAVAILABLE: the governance dependency (store, provider seam) is unreachable. MODE_NOT_GRANTED (403 FORBIDDEN, 0.55.0): a slack.message.post named a mode (app or user) IT has not approved for this app; the post is never served under the other mode. MODE_REQUIRED (400 VALIDATION_FAILED, 0.55.0): the app holds approved grants for both modes and the post named neither. SAMPLE_ROWS_NOT_ALLOWED (400 VALIDATION_FAILED, 0.74.0): an execute request carried sampleRows on any route but the development one. A sample value that does not parse as the column's recorded type is INPUT_INVALID, naming the column and the row.
11
11
  */
12
12
  export type IntegrationErrorCode =
13
13
  | 'USER_CONNECTION_REQUIRED'
@@ -39,7 +39,8 @@ export type IntegrationErrorCode =
39
39
  | 'AUTH_REQUIRED'
40
40
  | 'UNAVAILABLE'
41
41
  | 'MODE_NOT_GRANTED'
42
- | 'MODE_REQUIRED';
42
+ | 'MODE_REQUIRED'
43
+ | 'SAMPLE_ROWS_NOT_ALLOWED';
43
44
 
44
45
  /**
45
46
  * The `error` member of an integration failure response `{error, requestId}` on the execute/connect routes; the SDK maps it onto IsomorphError(category, message, details). `category` is the app-SDK IsomorphErrorCategory (not the deployment pipeline's dot-path error-category registry), with the HTTP status fixed per category: 401 AUTH_REQUIRED, 403 FORBIDDEN (denied/revoked/expired), 404 NOT_FOUND (unknown app/connection/resource), 409 CONFLICT (pending provisioning/consent/concurrent request), 400 or 413 VALIDATION_FAILED (invalid input / size overflow), 429 RATE_LIMITED (+ Retry-After header), 503 UNAVAILABLE. `details.code` is the closed stable reason set; `message` is human-facing and never carries provider error bodies, tokens or SQL.
@@ -166,6 +166,14 @@ export type IntegrationExecuteRequest = {
166
166
  * The declared-mode Slack operations only (slack.message.post since 0.55.0; slack.channel.history, slack.message.update and slack.message.delete since 0.71.0): which of the app's IT-approved modes this call runs under — app (the company's Slack app; a post goes out under the grant's per-app name and the footer) or user (the signed-in person, after their consent; no name, no footer). Required when the app holds approved grants for both modes (else MODE_REQUIRED); may be omitted when it holds one, which is then used. A named mode without an approved grant is refused with MODE_NOT_GRANTED; governance never falls back to the other mode. Every other operation's mode is fixed, so the field is refused there.
167
167
  */
168
168
  mode?: 'app' | 'user';
169
+ /**
170
+ * Development only (0.74.0). Rows the kit dev runtime attaches for the resource being read, from the sample-row file the agent wrote into the app's .isomorph/ folder. Governance serves them for a development warehouse.view.read when the source has no development copy (data-source.environments.development): it parses each value by the resource's recorded column type and refuses a value that does not parse, naming the column and the row. The field is refused with SAMPLE_ROWS_NOT_ALLOWED on any route but the development one, so a preview or production read never carries made-up data. Not part of the per-operation input rules; the dev runtime adds it beside the app's request.
171
+ *
172
+ * @maxItems 50
173
+ */
174
+ sampleRows?: {
175
+ [k: string]: string | number | boolean | null;
176
+ }[];
169
177
  };
170
178
  /**
171
179
  * One bare email address (no display name).
@@ -46,6 +46,10 @@ export type IntegrationExecuteResult = {
46
46
  * gmail.message.read: the text was cut at 16 KiB. warehouse.view.read (0.70.0): more rows matched than the read's limit, so `rows` is incomplete.
47
47
  */
48
48
  truncated?: boolean;
49
+ /**
50
+ * warehouse.view.read (0.74.0): true when `rows` came from the app's sample rows (integration-execute-request.sampleRows) because the development read had no development copy of the source; the rows were never read from the source. Absent or false: the rows came from the source.
51
+ */
52
+ sample?: boolean;
49
53
  /**
50
54
  * gmail.message.read (0.71.0): the message's attachments, without their bytes.
51
55
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fourier-labs/harbour-contracts",
3
- "version": "0.73.0",
3
+ "version": "0.74.0",
4
4
  "description": "Single source of truth for every data shape that crosses a boundary between Isomorph components.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -77,13 +77,42 @@
77
77
  },
78
78
  "status": {
79
79
  "$ref": "#/definitions/DataSourceStatus"
80
+ },
81
+ "environments": {
82
+ "type": "object",
83
+ "additionalProperties": false,
84
+ "properties": {
85
+ "development": {
86
+ "$ref": "#/definitions/DataSourceEnvironmentCopy"
87
+ },
88
+ "preview": {
89
+ "$ref": "#/definitions/DataSourceEnvironmentCopy"
90
+ }
91
+ },
92
+ "description": "0.74.0: per-environment copies of the source. A read for an environment that has a copy here uses that copy's endpoint and credentialRef; production always uses the top-level endpoint and credentialRef. A development read with no development copy is answered from the app's sample rows (integration-execute-request.sampleRows), never from the source; a preview read with no preview copy uses the production endpoint. A laptop never reads the production endpoint."
80
93
  }
81
94
  },
82
95
  "definitions": {
83
96
  "DataSourceConnector": {
84
97
  "type": "string",
85
- "enum": ["postgres", "redshift", "bigquery", "databricks", "mysql"],
86
- "description": "Closed connector set for the Data-Access MVP."
98
+ "enum": ["postgres", "redshift", "bigquery", "databricks", "mysql", "trino"],
99
+ "description": "Closed connector set for the Data-Access MVP. trino (0.74.0): Trino/Presto-compatible engines (Trino, Presto, Starburst, Galaxy) over the Trino HTTP statement protocol; the endpoint reuses host, port and database (as the Trino catalog) with no connector-specific fields."
100
+ },
101
+ "DataSourceEnvironmentCopy": {
102
+ "type": "object",
103
+ "additionalProperties": false,
104
+ "required": ["endpoint", "credentialRef"],
105
+ "properties": {
106
+ "endpoint": {
107
+ "$ref": "#/definitions/DataSourceEndpoint"
108
+ },
109
+ "credentialRef": {
110
+ "type": "string",
111
+ "pattern": "^secretref://[A-Za-z0-9][A-Za-z0-9/_.:-]*$",
112
+ "description": "secretref URI naming the platform-held credential for this copy. The referenced value never crosses this boundary."
113
+ }
114
+ },
115
+ "description": "One environment's copy of a source: the same endpoint and credentialRef shapes as the top-level fields, for the same connector."
87
116
  },
88
117
  "DataSourceEndpoint": {
89
118
  "type": "object",
@@ -152,6 +181,32 @@
152
181
  "requestableByBuilders": true,
153
182
  "allowValidation": false,
154
183
  "status": "ACTIVE"
184
+ },
185
+ {
186
+ "tenantId": "pocketfm",
187
+ "sourceId": "lakehouse",
188
+ "connector": "trino",
189
+ "displayName": "Lakehouse (Trino)",
190
+ "endpoint": {
191
+ "host": "trino.internal.example.com",
192
+ "port": 8443,
193
+ "database": "hive"
194
+ },
195
+ "credentialRef": "secretref://tenants/pocketfm/sources/lakehouse/reader",
196
+ "environments": {
197
+ "development": {
198
+ "endpoint": {
199
+ "host": "trino-dev.internal.example.com",
200
+ "port": 8443,
201
+ "database": "hive_dev"
202
+ },
203
+ "credentialRef": "secretref://tenants/pocketfm/sources/lakehouse/reader-dev"
204
+ }
205
+ },
206
+ "provisioningMode": "byo",
207
+ "allowedScope": ["analytics.episode_plays"],
208
+ "requestableByBuilders": true,
209
+ "status": "ACTIVE"
155
210
  }
156
211
  ]
157
212
  }
@@ -62,9 +62,10 @@
62
62
  "AUTH_REQUIRED",
63
63
  "UNAVAILABLE",
64
64
  "MODE_NOT_GRANTED",
65
- "MODE_REQUIRED"
65
+ "MODE_REQUIRED",
66
+ "SAMPLE_ROWS_NOT_ALLOWED"
66
67
  ],
67
- "description": "Stable reason codes. SEND_OUTCOME_UNKNOWN: dispatch happened but the outcome is unknown; never re-post automatically. SEND_KEY_REUSED: the idempotency key was seen with a different payload or subject. GRANT_DENIED: IT denied the request. TOKEN_REFRESH_PENDING: another call holds the refresh lease; retry shortly. REQUEST_PENDING: a different request for this grant is still open. INPUT_INVALID: the operation input failed its bounds. IDEMPOTENCY_KEY_REQUIRED: a send without a UUID idempotencyKey. SEND_IN_PROGRESS: a retry arrived while the first send of that key is still running. CONNECTION_NOT_FOUND / APP_NOT_FOUND / GRANT_NOT_FOUND: the named record does not exist. AUTH_REQUIRED: no verified caller. UNAVAILABLE: the governance dependency (store, provider seam) is unreachable. MODE_NOT_GRANTED (403 FORBIDDEN, 0.55.0): a slack.message.post named a mode (app or user) IT has not approved for this app; the post is never served under the other mode. MODE_REQUIRED (400 VALIDATION_FAILED, 0.55.0): the app holds approved grants for both modes and the post named neither."
68
+ "description": "Stable reason codes. SEND_OUTCOME_UNKNOWN: dispatch happened but the outcome is unknown; never re-post automatically. SEND_KEY_REUSED: the idempotency key was seen with a different payload or subject. GRANT_DENIED: IT denied the request. TOKEN_REFRESH_PENDING: another call holds the refresh lease; retry shortly. REQUEST_PENDING: a different request for this grant is still open. INPUT_INVALID: the operation input failed its bounds. IDEMPOTENCY_KEY_REQUIRED: a send without a UUID idempotencyKey. SEND_IN_PROGRESS: a retry arrived while the first send of that key is still running. CONNECTION_NOT_FOUND / APP_NOT_FOUND / GRANT_NOT_FOUND: the named record does not exist. AUTH_REQUIRED: no verified caller. UNAVAILABLE: the governance dependency (store, provider seam) is unreachable. MODE_NOT_GRANTED (403 FORBIDDEN, 0.55.0): a slack.message.post named a mode (app or user) IT has not approved for this app; the post is never served under the other mode. MODE_REQUIRED (400 VALIDATION_FAILED, 0.55.0): the app holds approved grants for both modes and the post named neither. SAMPLE_ROWS_NOT_ALLOWED (400 VALIDATION_FAILED, 0.74.0): an execute request carried sampleRows on any route but the development one. A sample value that does not parse as the column's recorded type is INPUT_INVALID, naming the column and the row."
68
69
  }
69
70
  },
70
71
  "examples": [
@@ -244,6 +244,18 @@
244
244
  "mode": {
245
245
  "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationIdentityMode",
246
246
  "description": "The declared-mode Slack operations only (slack.message.post since 0.55.0; slack.channel.history, slack.message.update and slack.message.delete since 0.71.0): which of the app's IT-approved modes this call runs under \u2014 app (the company's Slack app; a post goes out under the grant's per-app name and the footer) or user (the signed-in person, after their consent; no name, no footer). Required when the app holds approved grants for both modes (else MODE_REQUIRED); may be omitted when it holds one, which is then used. A named mode without an approved grant is refused with MODE_NOT_GRANTED; governance never falls back to the other mode. Every other operation's mode is fixed, so the field is refused there."
247
+ },
248
+ "sampleRows": {
249
+ "type": "array",
250
+ "maxItems": 50,
251
+ "items": {
252
+ "type": "object",
253
+ "additionalProperties": {
254
+ "type": ["string", "number", "boolean", "null"]
255
+ },
256
+ "description": "One sample row keyed by column name; each value is a scalar that governance parses by the column's recorded type."
257
+ },
258
+ "description": "Development only (0.74.0). Rows the kit dev runtime attaches for the resource being read, from the sample-row file the agent wrote into the app's .isomorph/ folder. Governance serves them for a development warehouse.view.read when the source has no development copy (data-source.environments.development): it parses each value by the resource's recorded column type and refuses a value that does not parse, naming the column and the row. The field is refused with SAMPLE_ROWS_NOT_ALLOWED on any route but the development one, so a preview or production read never carries made-up data. Not part of the per-operation input rules; the dev runtime adds it beside the app's request."
247
259
  }
248
260
  },
249
261
  "allOf": [
@@ -1144,6 +1156,28 @@
1144
1156
  "text": "approved"
1145
1157
  }
1146
1158
  }
1159
+ },
1160
+ {
1161
+ "connection": "sales-warehouse",
1162
+ "operation": "warehouse.view.read",
1163
+ "resource": "weekly_sales",
1164
+ "input": {
1165
+ "columns": [
1166
+ "week",
1167
+ "total"
1168
+ ],
1169
+ "limit": 100
1170
+ },
1171
+ "sampleRows": [
1172
+ {
1173
+ "week": "2026-W36",
1174
+ "total": "1234.50"
1175
+ },
1176
+ {
1177
+ "week": "2026-W37",
1178
+ "total": null
1179
+ }
1180
+ ]
1147
1181
  }
1148
1182
  ]
1149
1183
  }
@@ -90,6 +90,10 @@
90
90
  "type": "boolean",
91
91
  "description": "gmail.message.read: the text was cut at 16 KiB. warehouse.view.read (0.70.0): more rows matched than the read's limit, so `rows` is incomplete."
92
92
  },
93
+ "sample": {
94
+ "type": "boolean",
95
+ "description": "warehouse.view.read (0.74.0): true when `rows` came from the app's sample rows (integration-execute-request.sampleRows) because the development read had no development copy of the source; the rows were never read from the source. Absent or false: the rows came from the source."
96
+ },
93
97
  "attachments": {
94
98
  "type": "array",
95
99
  "maxItems": 100,
@@ -248,7 +252,8 @@
248
252
  "propertyNames": {
249
253
  "enum": [
250
254
  "rows",
251
- "truncated"
255
+ "truncated",
256
+ "sample"
252
257
  ]
253
258
  }
254
259
  }
@@ -579,6 +584,16 @@
579
584
  "mimeType": "application/pdf",
580
585
  "size": 20,
581
586
  "dataBase64": "JVBERi0xLjQKJcOkw7zDtsOfCg=="
587
+ },
588
+ {
589
+ "rows": [
590
+ {
591
+ "week": "2026-W36",
592
+ "total": "1234.50"
593
+ }
594
+ ],
595
+ "truncated": false,
596
+ "sample": true
582
597
  }
583
598
  ]
584
599
  }