@fourier-labs/harbour-contracts 0.33.0 → 0.35.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.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schema": "harbour.contract-release/1.0",
3
- "repositoryVersion": "0.33.0.0",
4
- "publishVersion": "0.33.0",
5
- "publishTag": "v0.33.0",
6
- "schemasDigest": "sha256:dd106c711981c50f8ab49cf6c54bf201aa27f36a40d6c108aa0a1c2916d1d72c",
3
+ "repositoryVersion": "0.35.0.0",
4
+ "publishVersion": "0.35.0",
5
+ "publishTag": "v0.35.0",
6
+ "schemasDigest": "sha256:e41f180a01ff48eb6580bae1e18b7d03fa33abac5904513785f1d52ab2f93d49",
7
7
  "aiFixturesDigest": "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6",
8
- "generatedGoDigest": "sha256:64d1d6efa060927f70394bd1d4758ca1008c1ccff30b34c1b50c518d27155a3b",
9
- "generatedTypeScriptDigest": "sha256:dc9111b80945f2a233e382bc462140dca0de45dd29ffc824124d9b555fd769c7",
8
+ "generatedGoDigest": "sha256:97bd42f8e900e8122c92bf955194c82762e55fbfa56ede55b19a00bd60e0e8f4",
9
+ "generatedTypeScriptDigest": "sha256:5a52cca780c16ffcf0676980c47b65f6263991fb18d49d08efd60b7f0132532c",
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)"
@@ -31,7 +31,7 @@
31
31
  "schema/ai-transform-result.schema.json": "sha256:244e380a36957ab2dcb526ea22aaecb56b53664ce1fda6934d0021c084c99133",
32
32
  "schema/ai-verification-evidence.schema.json": "sha256:bdbc762df256d2a8dd0bdda60d4d13915ce40034cd756af9515871d145f10ad9",
33
33
  "schema/api-error-response.schema.json": "sha256:3d43d5bdf0b59c944e20509d832e6776c3ecdba4d6c245a9118bbe52062a8304",
34
- "schema/artifact-contract.schema.json": "sha256:dddfeb765c7ee510bc3781689bef21e12689df50f6eabb41806083d6793cbfc5",
34
+ "schema/artifact-contract.schema.json": "sha256:bc9683cd5c62c231683906c70307412839458adb6f9273003c37dbab01a1cff3",
35
35
  "schema/binding-classification-subject.schema.json": "sha256:a8825ce652e7e00a18759c4249bd5bcd0cae552dd58440513f867e87f685133c",
36
36
  "schema/data-dependency.schema.json": "sha256:89ba8481ccabc935e7dab5d13871c985839a751ad1586710d8382a15e1f3340c",
37
37
  "schema/data-grant.schema.json": "sha256:e640553831ecc1c01cc9a37fe388b84d0ffeaa0d0050e63712b48f65fa437c8a",
@@ -53,7 +53,7 @@
53
53
  "schema/secret-reference.schema.json": "sha256:966f7a6d2575b46a4b54804d06dc05c6229ebbcbd9200fe6a6c8d3b4ffb90f9f",
54
54
  "schema/security-scan-input.schema.json": "sha256:83502dffb3c07ac2e0698928e6dc123f454fc667d4aacd336bcad6944ef2ac3d",
55
55
  "schema/security-verdict.schema.json": "sha256:ec3c2d5c9df3601aeed0909b36c7f427469c7f0eeb3295c5809b96d09bf3fc26",
56
- "schema/transform-report.schema.json": "sha256:815e764445fb41b912d0c0468f00621ad61f941fb4c7b9364e3f204ad47cdf7f",
56
+ "schema/transform-report.schema.json": "sha256:c7f3f359b70101764d4e9b411c9c78bd5b31a27c7de5bda070c1287087f680f1",
57
57
  "schema/version-state.schema.json": "sha256:3b190007e2f8e3821c41ef11c6f5b226da7d5a18d34d4077f4618004846fd444",
58
58
  "schema/well-defined-app-manifest.schema.json": "sha256:d863433d7b68260a08aa0d33f897526c8f74f5f0ee8d242a6dd6c0bb23e60987"
59
59
  },
@@ -93,8 +93,8 @@
93
93
  "gen-go/contracts_test.go": "sha256:56376287cb1548be1b35ab4c65d6f0b558c5d34885f7a47cb3d9e8d786607bf5",
94
94
  "gen-go/doc.go": "sha256:c17bc2d39ed175ce7105c68305f8ae7152f659eb7cd187ecceee7698eb3a65dd",
95
95
  "gen-go/error_category.go": "sha256:dbbc54e04b988a23b08cc5f6f368555e1b8e9c3633261f593fa9fda8b29a537e",
96
- "gen-go/schema_documents.go": "sha256:4b9094aaf0acc4bae4b65784954853f0888572a53fa1a1dc354f97c88ded805f",
97
- "gen-go/types.go": "sha256:3e61602b15af478141ec368bc2208b4f496d686004bbcbb4910bdc93a0901b08",
96
+ "gen-go/schema_documents.go": "sha256:df06963579bc6d5d711d38512c2885add3be6909c6cafa12caeefbc34d840e7e",
97
+ "gen-go/types.go": "sha256:4f1b5374277b99678d70724740afd2cac81bbcf25d0da11e05eb13d8894f9467",
98
98
  "gen-go/version_state.go": "sha256:4ef8abb126414c0025cf2c59bc9c79a74ef4a2ff89e8f316cbf05acc872371c1"
99
99
  },
100
100
  "generatedTypeScript": {
@@ -119,7 +119,7 @@
119
119
  "gen-ts/ai-transform-result.ts": "sha256:208c66b2aeb425c0cbcc18b7191f6e8989443f2b2c1690648e0e06cc1448ce25",
120
120
  "gen-ts/ai-verification-evidence.ts": "sha256:979439d77f8b47631d1eb3b6c3c54ea6fec41089d2a51dc815fd370b1c4917fc",
121
121
  "gen-ts/api-error-response.ts": "sha256:0fd73be282e7a5d72d6c343a4e64d09c232556b16161167f0cffd6278ed7e3d3",
122
- "gen-ts/artifact-contract.ts": "sha256:e14be765244303e311bf97004d70d8d40d37da5accfcc084765257f69b5ca15c",
122
+ "gen-ts/artifact-contract.ts": "sha256:ad39ac5a2bcc9462e206101c9792e92fd11ef721e26de9a40043a4ba00dd7350",
123
123
  "gen-ts/binding-classification-digest-helper.ts": "sha256:40fc7d85b4c3f3ffc5caf7d7950c62d013c83ee42058c7453b2100d95817f167",
124
124
  "gen-ts/binding-classification-subject.ts": "sha256:c409a3fd7b942b50419632c51d9a79474fb30d6374bdc79c0e25c73618e800c3",
125
125
  "gen-ts/data-dependency.ts": "sha256:0572c31d7ae9088eccc104eee1a205bf176d1d6dacfaf0f290d666024e284966",
@@ -145,7 +145,7 @@
145
145
  "gen-ts/secret-reference.ts": "sha256:a8b65de72705b6f51f57cd8ca58881483108005b1fa4d19106c3e9cf1fa877e7",
146
146
  "gen-ts/security-scan-input.ts": "sha256:b31f048be04c34b40f49d3b3ba53277e473cd435ae9216ba3a3b4dac69fb5685",
147
147
  "gen-ts/security-verdict.ts": "sha256:fea950cddbd12fe0218e2573c9a29c6748d56612a464559f1e6d688fa97f68c7",
148
- "gen-ts/transform-report.ts": "sha256:6c54b579884d98135d73945d00980820b136f269622db27fa56a1c4c0e57d3b6",
148
+ "gen-ts/transform-report.ts": "sha256:666b7d202be851c2f605fc4db175046c2eabbed454c64e9f8796617a1fd8d518",
149
149
  "gen-ts/version-state-machine.ts": "sha256:c67af3e86ac306743345b850dfd3ae5201c19d10c40e2c149cf8c38258320d5f",
150
150
  "gen-ts/version-state.ts": "sha256:a835112930299e7b593a1b8810b7414f6c9e809da2092306c743128170a3b702",
151
151
  "gen-ts/well-defined-app-manifest.ts": "sha256:0c5c2fed747ad7f4c1034104c1acd9bb2e316f2c0b08a4ff3e2fab486f576a2f"
@@ -22,6 +22,10 @@ export interface ArtifactContract {
22
22
  */
23
23
  runtime_features?: 'chromium'[];
24
24
  };
25
+ /**
26
+ * Optional immutable OCI image used only by auxiliary workloads of a statically served SPA.
27
+ */
28
+ workload_image_ref?: string;
25
29
  static: {
26
30
  /**
27
31
  * e.g. builds/<digest>/
@@ -93,6 +97,14 @@ export interface ArtifactContract {
93
97
  path: '.harbour/app-manifest.json';
94
98
  sha256: string;
95
99
  };
100
+ /**
101
+ * Optional digest-pinned browser capability declaration published only after LIVE cutover.
102
+ */
103
+ app_capabilities?: {
104
+ version: 1;
105
+ path: '.harbour/app-capabilities.json';
106
+ sha256: string;
107
+ };
96
108
  /**
97
109
  * Optional method/path authentication policy compiled by the transformation. The default is SSO; listed routes preserve the application's own token, signature, or callback authorization.
98
110
  */
@@ -1,7 +1,7 @@
1
- export declare const ContractRepositoryVersion: "0.33.0.0";
2
- export declare const ContractPublishVersion: "0.33.0";
3
- export declare const ContractPublishTag: "v0.33.0";
4
- export declare const ContractSchemasDigest: "sha256:dd106c711981c50f8ab49cf6c54bf201aa27f36a40d6c108aa0a1c2916d1d72c";
1
+ export declare const ContractRepositoryVersion: "0.35.0.0";
2
+ export declare const ContractPublishVersion: "0.35.0";
3
+ export declare const ContractPublishTag: "v0.35.0";
4
+ export declare const ContractSchemasDigest: "sha256:e41f180a01ff48eb6580bae1e18b7d03fa33abac5904513785f1d52ab2f93d49";
5
5
  export declare const ContractAiFixturesDigest: "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
6
- export declare const ContractGeneratedGoDigest: "sha256:64d1d6efa060927f70394bd1d4758ca1008c1ccff30b34c1b50c518d27155a3b";
7
- export declare const ContractGeneratedTypeScriptDigest: "sha256:dc9111b80945f2a233e382bc462140dca0de45dd29ffc824124d9b555fd769c7";
6
+ export declare const ContractGeneratedGoDigest: "sha256:97bd42f8e900e8122c92bf955194c82762e55fbfa56ede55b19a00bd60e0e8f4";
7
+ export declare const ContractGeneratedTypeScriptDigest: "sha256:5a52cca780c16ffcf0676980c47b65f6263991fb18d49d08efd60b7f0132532c";
@@ -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.33.0.0";
4
- export const ContractPublishVersion = "0.33.0";
5
- export const ContractPublishTag = "v0.33.0";
6
- export const ContractSchemasDigest = "sha256:dd106c711981c50f8ab49cf6c54bf201aa27f36a40d6c108aa0a1c2916d1d72c";
3
+ export const ContractRepositoryVersion = "0.35.0.0";
4
+ export const ContractPublishVersion = "0.35.0";
5
+ export const ContractPublishTag = "v0.35.0";
6
+ export const ContractSchemasDigest = "sha256:e41f180a01ff48eb6580bae1e18b7d03fa33abac5904513785f1d52ab2f93d49";
7
7
  export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6";
8
- export const ContractGeneratedGoDigest = "sha256:64d1d6efa060927f70394bd1d4758ca1008c1ccff30b34c1b50c518d27155a3b";
9
- export const ContractGeneratedTypeScriptDigest = "sha256:dc9111b80945f2a233e382bc462140dca0de45dd29ffc824124d9b555fd769c7";
8
+ export const ContractGeneratedGoDigest = "sha256:97bd42f8e900e8122c92bf955194c82762e55fbfa56ede55b19a00bd60e0e8f4";
9
+ export const ContractGeneratedTypeScriptDigest = "sha256:5a52cca780c16ffcf0676980c47b65f6263991fb18d49d08efd60b7f0132532c";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 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.
2
+ * 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.
3
3
  */
4
4
  export type TransformReport = {
5
5
  /**
@@ -55,6 +55,102 @@ export type TransformReport = {
55
55
  */
56
56
  unsupportedFeatures: string[];
57
57
  };
58
+ /**
59
+ * Agent-authored preservation (smoke) plan for this attempt. Required whenever the report is deployable; forbidden on an unsupported report. Every test must be grounded in files the agent actually read or changed (evidence) and expressed as tenant-relative browser or HTTP steps the validation cell can execute without secrets. Structural bounds live here; per-action required fields, at least one assert* step per test, one to three happy_path tests, unique ids, the {{runId}} substitution and path safety are enforced semantically by the deployment plane.
60
+ */
61
+ preservation_plan?: {
62
+ version: 1;
63
+ /**
64
+ * Ordered smoke tests. happy_path tests cover primary user journeys inferred from routes, APIs, forms and focused tests; boundary_check tests pin the smallest observable behaviour each applied playbook or changed file could regress.
65
+ *
66
+ * @minItems 1
67
+ * @maxItems 8
68
+ */
69
+ tests: {
70
+ id: string;
71
+ kind: 'happy_path' | 'boundary_check';
72
+ name: string;
73
+ /**
74
+ * Transformation boundaries this test claims to cover.
75
+ *
76
+ * @minItems 1
77
+ * @maxItems 8
78
+ */
79
+ boundaries: ('application' | 'routing' | 'database' | 'authentication' | 'runtime_dependency' | 'files' | 'provider' | 'background_work')[];
80
+ /**
81
+ * Files (and optionally lines) in the transformed workspace that ground this test. Paths must exist after transformation.
82
+ *
83
+ * @minItems 1
84
+ * @maxItems 8
85
+ */
86
+ evidence: {
87
+ path: string;
88
+ line?: number;
89
+ reason: string;
90
+ }[];
91
+ confidence: 'high' | 'medium' | 'low';
92
+ /**
93
+ * Executed in order inside the validation cell's authenticated browser context; the test stops at its first failed step. Which optional fields apply depends on action: navigate/request/assertURL use path; click/fill/assertVisible use target; fill uses value; assertVisible uses text; request uses method, body and name; assertResponse uses request, status, jsonKey and jsonValue.
94
+ *
95
+ * @minItems 1
96
+ * @maxItems 20
97
+ */
98
+ steps: {
99
+ action: 'navigate' | 'click' | 'fill' | 'reload' | 'request' | 'assertVisible' | 'assertURL' | 'assertResponse';
100
+ /**
101
+ * Tenant-relative path starting with '/'; never a scheme or host.
102
+ */
103
+ path?: string;
104
+ target?: {
105
+ by: 'role' | 'label' | 'placeholder' | 'text' | 'testId' | 'css';
106
+ role?: string;
107
+ /**
108
+ * Accessible name when by is role.
109
+ */
110
+ name?: string;
111
+ /**
112
+ * Locator value for every by other than role.
113
+ */
114
+ value?: string;
115
+ };
116
+ value?: string;
117
+ text?: string;
118
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
119
+ /**
120
+ * JSON text sent as the request body.
121
+ */
122
+ body?: string;
123
+ /**
124
+ * Names a request's response for a later assertResponse in the same test.
125
+ */
126
+ name?: string;
127
+ /**
128
+ * The named preceding request an assertResponse inspects.
129
+ */
130
+ request?: string;
131
+ status?: number;
132
+ /**
133
+ * Top-level key or dotted path into the JSON response; no brackets.
134
+ */
135
+ jsonKey?: string;
136
+ /**
137
+ * Expected value, compared as a string.
138
+ */
139
+ jsonValue?: string;
140
+ timeoutMs?: number;
141
+ }[];
142
+ }[];
143
+ /**
144
+ * Boundaries the agent deliberately left untested, each with a reviewed reason. An empty array claims full coverage.
145
+ *
146
+ * @maxItems 8
147
+ */
148
+ uncovered: {
149
+ boundary: 'application' | 'routing' | 'database' | 'authentication' | 'runtime_dependency' | 'files' | 'provider' | 'background_work';
150
+ reason: 'not-observable-in-validation-cell' | 'no-safe-fixture' | 'no-grounded-surface';
151
+ detail?: string;
152
+ }[];
153
+ };
58
154
  application: {
59
155
  unsupported?: {
60
156
  capability: string;
@@ -25,6 +25,10 @@ export interface ArtifactContract {
25
25
  */
26
26
  runtime_features?: 'chromium'[];
27
27
  };
28
+ /**
29
+ * Optional immutable OCI image used only by auxiliary workloads of a statically served SPA.
30
+ */
31
+ workload_image_ref?: string;
28
32
  static: {
29
33
  /**
30
34
  * e.g. builds/<digest>/
@@ -96,6 +100,14 @@ export interface ArtifactContract {
96
100
  path: '.harbour/app-manifest.json';
97
101
  sha256: string;
98
102
  };
103
+ /**
104
+ * Optional digest-pinned browser capability declaration published only after LIVE cutover.
105
+ */
106
+ app_capabilities?: {
107
+ version: 1;
108
+ path: '.harbour/app-capabilities.json';
109
+ sha256: string;
110
+ };
99
111
  /**
100
112
  * Optional method/path authentication policy compiled by the transformation. The default is SSO; listed routes preserve the application's own token, signature, or callback authorization.
101
113
  */
@@ -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.33.0.0" as const;
5
- export const ContractPublishVersion = "0.33.0" as const;
6
- export const ContractPublishTag = "v0.33.0" as const;
7
- export const ContractSchemasDigest = "sha256:dd106c711981c50f8ab49cf6c54bf201aa27f36a40d6c108aa0a1c2916d1d72c" as const;
4
+ export const ContractRepositoryVersion = "0.35.0.0" as const;
5
+ export const ContractPublishVersion = "0.35.0" as const;
6
+ export const ContractPublishTag = "v0.35.0" as const;
7
+ export const ContractSchemasDigest = "sha256:e41f180a01ff48eb6580bae1e18b7d03fa33abac5904513785f1d52ab2f93d49" as const;
8
8
  export const ContractAiFixturesDigest = "sha256:edac31c92e146ca1c40130f2fb5649db82e24b8cd1ac758153b90612052fddb6" as const;
9
- export const ContractGeneratedGoDigest = "sha256:64d1d6efa060927f70394bd1d4758ca1008c1ccff30b34c1b50c518d27155a3b" as const;
10
- export const ContractGeneratedTypeScriptDigest = "sha256:dc9111b80945f2a233e382bc462140dca0de45dd29ffc824124d9b555fd769c7" as const;
9
+ export const ContractGeneratedGoDigest = "sha256:97bd42f8e900e8122c92bf955194c82762e55fbfa56ede55b19a00bd60e0e8f4" as const;
10
+ export const ContractGeneratedTypeScriptDigest = "sha256:5a52cca780c16ffcf0676980c47b65f6263991fb18d49d08efd60b7f0132532c" as const;
@@ -2,7 +2,7 @@
2
2
  // Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
3
3
 
4
4
  /**
5
- * 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.
5
+ * 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.
6
6
  */
7
7
  export type TransformReport = {
8
8
  /**
@@ -58,6 +58,119 @@ export type TransformReport = {
58
58
  */
59
59
  unsupportedFeatures: string[];
60
60
  };
61
+ /**
62
+ * Agent-authored preservation (smoke) plan for this attempt. Required whenever the report is deployable; forbidden on an unsupported report. Every test must be grounded in files the agent actually read or changed (evidence) and expressed as tenant-relative browser or HTTP steps the validation cell can execute without secrets. Structural bounds live here; per-action required fields, at least one assert* step per test, one to three happy_path tests, unique ids, the {{runId}} substitution and path safety are enforced semantically by the deployment plane.
63
+ */
64
+ preservation_plan?: {
65
+ version: 1;
66
+ /**
67
+ * Ordered smoke tests. happy_path tests cover primary user journeys inferred from routes, APIs, forms and focused tests; boundary_check tests pin the smallest observable behaviour each applied playbook or changed file could regress.
68
+ *
69
+ * @minItems 1
70
+ * @maxItems 8
71
+ */
72
+ tests: {
73
+ id: string;
74
+ kind: 'happy_path' | 'boundary_check';
75
+ name: string;
76
+ /**
77
+ * Transformation boundaries this test claims to cover.
78
+ *
79
+ * @minItems 1
80
+ * @maxItems 8
81
+ */
82
+ boundaries: (
83
+ | 'application'
84
+ | 'routing'
85
+ | 'database'
86
+ | 'authentication'
87
+ | 'runtime_dependency'
88
+ | 'files'
89
+ | 'provider'
90
+ | 'background_work'
91
+ )[];
92
+ /**
93
+ * Files (and optionally lines) in the transformed workspace that ground this test. Paths must exist after transformation.
94
+ *
95
+ * @minItems 1
96
+ * @maxItems 8
97
+ */
98
+ evidence: {
99
+ path: string;
100
+ line?: number;
101
+ reason: string;
102
+ }[];
103
+ confidence: 'high' | 'medium' | 'low';
104
+ /**
105
+ * Executed in order inside the validation cell's authenticated browser context; the test stops at its first failed step. Which optional fields apply depends on action: navigate/request/assertURL use path; click/fill/assertVisible use target; fill uses value; assertVisible uses text; request uses method, body and name; assertResponse uses request, status, jsonKey and jsonValue.
106
+ *
107
+ * @minItems 1
108
+ * @maxItems 20
109
+ */
110
+ steps: {
111
+ action: 'navigate' | 'click' | 'fill' | 'reload' | 'request' | 'assertVisible' | 'assertURL' | 'assertResponse';
112
+ /**
113
+ * Tenant-relative path starting with '/'; never a scheme or host.
114
+ */
115
+ path?: string;
116
+ target?: {
117
+ by: 'role' | 'label' | 'placeholder' | 'text' | 'testId' | 'css';
118
+ role?: string;
119
+ /**
120
+ * Accessible name when by is role.
121
+ */
122
+ name?: string;
123
+ /**
124
+ * Locator value for every by other than role.
125
+ */
126
+ value?: string;
127
+ };
128
+ value?: string;
129
+ text?: string;
130
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
131
+ /**
132
+ * JSON text sent as the request body.
133
+ */
134
+ body?: string;
135
+ /**
136
+ * Names a request's response for a later assertResponse in the same test.
137
+ */
138
+ name?: string;
139
+ /**
140
+ * The named preceding request an assertResponse inspects.
141
+ */
142
+ request?: string;
143
+ status?: number;
144
+ /**
145
+ * Top-level key or dotted path into the JSON response; no brackets.
146
+ */
147
+ jsonKey?: string;
148
+ /**
149
+ * Expected value, compared as a string.
150
+ */
151
+ jsonValue?: string;
152
+ timeoutMs?: number;
153
+ }[];
154
+ }[];
155
+ /**
156
+ * Boundaries the agent deliberately left untested, each with a reviewed reason. An empty array claims full coverage.
157
+ *
158
+ * @maxItems 8
159
+ */
160
+ uncovered: {
161
+ boundary:
162
+ | 'application'
163
+ | 'routing'
164
+ | 'database'
165
+ | 'authentication'
166
+ | 'runtime_dependency'
167
+ | 'files'
168
+ | 'provider'
169
+ | 'background_work';
170
+ reason: 'not-observable-in-validation-cell' | 'no-safe-fixture' | 'no-grounded-surface';
171
+ detail?: string;
172
+ }[];
173
+ };
61
174
  application: {
62
175
  unsupported?: {
63
176
  capability: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fourier-labs/harbour-contracts",
3
- "version": "0.33.0",
3
+ "version": "0.35.0",
4
4
  "description": "Single source of truth for every data shape that crosses a boundary between Harbour components.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -48,6 +48,11 @@
48
48
  }
49
49
  }
50
50
  },
51
+ "workload_image_ref": {
52
+ "type": "string",
53
+ "pattern": "^[^\\s@]+@sha256:[0-9a-f]{64}$",
54
+ "description": "Optional immutable OCI image used only by auxiliary workloads of a statically served SPA."
55
+ },
51
56
  "static": {
52
57
  "type": "object",
53
58
  "additionalProperties": false,
@@ -146,6 +151,17 @@
146
151
  },
147
152
  "description": "Optional digest-pinned P0 well-defined application manifest. Provisioning reads the referenced document without re-analyzing source."
148
153
  },
154
+ "app_capabilities": {
155
+ "type": "object",
156
+ "additionalProperties": false,
157
+ "required": ["version", "path", "sha256"],
158
+ "properties": {
159
+ "version": { "type": "integer", "const": 1 },
160
+ "path": { "type": "string", "const": ".harbour/app-capabilities.json" },
161
+ "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" }
162
+ },
163
+ "description": "Optional digest-pinned browser capability declaration published only after LIVE cutover."
164
+ },
149
165
  "route_auth": {
150
166
  "description": "Optional method/path authentication policy compiled by the transformation. The default is SSO; listed routes preserve the application's own token, signature, or callback authorization.",
151
167
  "type": "object",
@@ -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.",
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.",
6
6
  "x-go-embed": true,
7
7
  "type": "object",
8
8
  "additionalProperties": false,
@@ -102,6 +102,123 @@
102
102
  }
103
103
  }
104
104
  },
105
+ "preservation_plan": {
106
+ "description": "Agent-authored preservation (smoke) plan for this attempt. Required whenever the report is deployable; forbidden on an unsupported report. Every test must be grounded in files the agent actually read or changed (evidence) and expressed as tenant-relative browser or HTTP steps the validation cell can execute without secrets. Structural bounds live here; per-action required fields, at least one assert* step per test, one to three happy_path tests, unique ids, the {{runId}} substitution and path safety are enforced semantically by the deployment plane.",
107
+ "type": "object",
108
+ "additionalProperties": false,
109
+ "required": ["version", "tests", "uncovered"],
110
+ "properties": {
111
+ "version": { "type": "integer", "const": 1 },
112
+ "tests": {
113
+ "description": "Ordered smoke tests. happy_path tests cover primary user journeys inferred from routes, APIs, forms and focused tests; boundary_check tests pin the smallest observable behaviour each applied playbook or changed file could regress.",
114
+ "type": "array",
115
+ "minItems": 1,
116
+ "maxItems": 8,
117
+ "items": {
118
+ "type": "object",
119
+ "additionalProperties": false,
120
+ "required": ["id", "kind", "name", "boundaries", "evidence", "confidence", "steps"],
121
+ "properties": {
122
+ "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" },
123
+ "kind": { "type": "string", "enum": ["happy_path", "boundary_check"] },
124
+ "name": { "type": "string", "minLength": 1, "maxLength": 120 },
125
+ "boundaries": {
126
+ "description": "Transformation boundaries this test claims to cover.",
127
+ "type": "array",
128
+ "minItems": 1,
129
+ "maxItems": 8,
130
+ "uniqueItems": true,
131
+ "items": {
132
+ "type": "string",
133
+ "enum": ["application", "routing", "database", "authentication", "runtime_dependency", "files", "provider", "background_work"]
134
+ }
135
+ },
136
+ "evidence": {
137
+ "description": "Files (and optionally lines) in the transformed workspace that ground this test. Paths must exist after transformation.",
138
+ "type": "array",
139
+ "minItems": 1,
140
+ "maxItems": 8,
141
+ "items": {
142
+ "type": "object",
143
+ "additionalProperties": false,
144
+ "required": ["path", "reason"],
145
+ "properties": {
146
+ "path": { "type": "string", "minLength": 1, "maxLength": 512 },
147
+ "line": { "type": "integer", "minimum": 1 },
148
+ "reason": { "type": "string", "minLength": 1, "maxLength": 512 }
149
+ }
150
+ }
151
+ },
152
+ "confidence": { "type": "string", "enum": ["high", "medium", "low"] },
153
+ "steps": {
154
+ "description": "Executed in order inside the validation cell's authenticated browser context; the test stops at its first failed step. Which optional fields apply depends on action: navigate/request/assertURL use path; click/fill/assertVisible use target; fill uses value; assertVisible uses text; request uses method, body and name; assertResponse uses request, status, jsonKey and jsonValue.",
155
+ "type": "array",
156
+ "minItems": 1,
157
+ "maxItems": 20,
158
+ "items": {
159
+ "type": "object",
160
+ "additionalProperties": false,
161
+ "required": ["action"],
162
+ "properties": {
163
+ "action": {
164
+ "type": "string",
165
+ "enum": ["navigate", "click", "fill", "reload", "request", "assertVisible", "assertURL", "assertResponse"]
166
+ },
167
+ "path": {
168
+ "description": "Tenant-relative path starting with '/'; never a scheme or host.",
169
+ "type": "string",
170
+ "maxLength": 512
171
+ },
172
+ "target": {
173
+ "type": "object",
174
+ "additionalProperties": false,
175
+ "required": ["by"],
176
+ "properties": {
177
+ "by": { "type": "string", "enum": ["role", "label", "placeholder", "text", "testId", "css"] },
178
+ "role": { "type": "string", "maxLength": 64 },
179
+ "name": { "description": "Accessible name when by is role.", "type": "string", "maxLength": 256 },
180
+ "value": { "description": "Locator value for every by other than role.", "type": "string", "maxLength": 512 }
181
+ }
182
+ },
183
+ "value": { "type": "string", "maxLength": 1024 },
184
+ "text": { "type": "string", "maxLength": 512 },
185
+ "method": { "type": "string", "enum": ["GET", "POST", "PUT", "PATCH", "DELETE"] },
186
+ "body": { "description": "JSON text sent as the request body.", "type": "string", "maxLength": 16384 },
187
+ "name": { "description": "Names a request's response for a later assertResponse in the same test.", "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" },
188
+ "request": { "description": "The named preceding request an assertResponse inspects.", "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" },
189
+ "status": { "type": "integer", "minimum": 100, "maximum": 599 },
190
+ "jsonKey": { "description": "Top-level key or dotted path into the JSON response; no brackets.", "type": "string", "maxLength": 128 },
191
+ "jsonValue": { "description": "Expected value, compared as a string.", "type": "string", "maxLength": 512 },
192
+ "timeoutMs": { "type": "integer", "minimum": 1000, "maximum": 30000 }
193
+ }
194
+ }
195
+ }
196
+ }
197
+ }
198
+ },
199
+ "uncovered": {
200
+ "description": "Boundaries the agent deliberately left untested, each with a reviewed reason. An empty array claims full coverage.",
201
+ "type": "array",
202
+ "maxItems": 8,
203
+ "items": {
204
+ "type": "object",
205
+ "additionalProperties": false,
206
+ "required": ["boundary", "reason"],
207
+ "properties": {
208
+ "boundary": {
209
+ "type": "string",
210
+ "enum": ["application", "routing", "database", "authentication", "runtime_dependency", "files", "provider", "background_work"]
211
+ },
212
+ "reason": {
213
+ "type": "string",
214
+ "enum": ["not-observable-in-validation-cell", "no-safe-fixture", "no-grounded-surface"]
215
+ },
216
+ "detail": { "type": "string", "maxLength": 512 }
217
+ }
218
+ }
219
+ }
220
+ }
221
+ },
105
222
  "application": {
106
223
  "type": "object",
107
224
  "additionalProperties": false,
@@ -245,7 +362,7 @@
245
362
  },
246
363
  "oneOf": [
247
364
  {
248
- "required": ["applied_playbooks", "static_rebuild_required", "analysis", "database", "application"],
365
+ "required": ["applied_playbooks", "static_rebuild_required", "analysis", "database", "preservation_plan", "application"],
249
366
  "properties": {
250
367
  "application": {
251
368
  "required": ["runtime", "buildKind", "topology", "entrypoint", "build", "basePath", "evidence", "unsupportedFeatures"],