@fourier-labs/harbour-contracts 0.3.1
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 +102 -0
- package/dist/api-error-response.d.ts +13 -0
- package/dist/api-error-response.js +3 -0
- package/dist/artifact-contract.d.ts +78 -0
- package/dist/artifact-contract.js +3 -0
- package/dist/deploy-request.d.ts +32 -0
- package/dist/deploy-request.js +3 -0
- package/dist/deploy-response.d.ts +22 -0
- package/dist/deploy-response.js +3 -0
- package/dist/deployment-environment.d.ts +4 -0
- package/dist/deployment-environment.js +3 -0
- package/dist/deployment-source.d.ts +13 -0
- package/dist/deployment-source.js +3 -0
- package/dist/deployment-status-failure.d.ts +9 -0
- package/dist/deployment-status-failure.js +3 -0
- package/dist/deployment-status-request.d.ts +8 -0
- package/dist/deployment-status-request.js +3 -0
- package/dist/deployment-status-response.d.ts +24 -0
- package/dist/deployment-status-response.js +3 -0
- package/dist/env-bindings.d.ts +61 -0
- package/dist/env-bindings.js +3 -0
- package/dist/error-category-registry.d.ts +33 -0
- package/dist/error-category-registry.js +85 -0
- package/dist/error-category.d.ts +4 -0
- package/dist/error-category.js +3 -0
- package/dist/error-envelope.d.ts +46 -0
- package/dist/error-envelope.js +3 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +18 -0
- package/dist/kvs-routing-entry.d.ts +13 -0
- package/dist/kvs-routing-entry.js +3 -0
- package/dist/version-state-machine.d.ts +19 -0
- package/dist/version-state-machine.js +35 -0
- package/dist/version-state.d.ts +4 -0
- package/dist/version-state.js +3 -0
- package/package.json +30 -0
- package/schema/api-error-response.schema.json +33 -0
- package/schema/artifact-contract.schema.json +126 -0
- package/schema/deploy-request.schema.json +68 -0
- package/schema/deploy-response.schema.json +49 -0
- package/schema/deployment-environment.schema.json +9 -0
- package/schema/deployment-source.schema.json +29 -0
- package/schema/deployment-status-failure.schema.json +28 -0
- package/schema/deployment-status-request.schema.json +25 -0
- package/schema/deployment-status-response.schema.json +79 -0
- package/schema/env-bindings.schema.json +62 -0
- package/schema/error-category.enum.json +49 -0
- package/schema/error-envelope.schema.json +85 -0
- package/schema/kvs-routing-entry.schema.json +21 -0
- package/schema/version-state.schema.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# harbour-contracts
|
|
2
|
+
|
|
3
|
+
The single source of truth for every data shape that crosses a boundary between Harbour
|
|
4
|
+
components. **Schema-first:** hand-written JSON Schema in `schema/` is the source; the Go
|
|
5
|
+
package (`gen-go/`) and TypeScript package (`gen-ts/` → `@fourier-labs/harbour-contracts`) are **generated**
|
|
6
|
+
and committed. No handler, infra stack, or MCP op is built against a shape that does not
|
|
7
|
+
exist here first.
|
|
8
|
+
|
|
9
|
+
## Layout
|
|
10
|
+
|
|
11
|
+
| Path | What |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `schema/*.json` | JSON Schema (draft-07). The only files you hand-edit. |
|
|
14
|
+
| `gen-go/` | Generated Go package `contracts` (import `github.com/Fourier-Labs-AI/harbour-contracts/gen-go`). |
|
|
15
|
+
| `gen-ts/` | Generated TS sources; published as `@fourier-labs/harbour-contracts`. |
|
|
16
|
+
| `tools/gen/gen.mjs` | The single codegen driver (Node). Validates every schema, then emits Go + TS. |
|
|
17
|
+
| `docs/adr/` | Decision records (e.g. `0001-t1-ratified.md`). |
|
|
18
|
+
|
|
19
|
+
## The schemas
|
|
20
|
+
|
|
21
|
+
| File | Shape | LLD |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `deploy-request.schema.json` | MCP `deploy(app, {commit, branch})` payload | §7 |
|
|
24
|
+
| `error-envelope.schema.json` | The failure envelope emitted once per FAILED transition | §2.1 |
|
|
25
|
+
| `error-category.enum.json` | Closed failure-category enum + per-category `retryable` registry | §2.2 |
|
|
26
|
+
| `artifact-contract.schema.json` | Build output contract (the provider-portability seam) | §3 |
|
|
27
|
+
| `version-state.schema.json` | State machine T1a — states, legal transitions, versionId format | §1 |
|
|
28
|
+
| `env-bindings.schema.json` | `EnvBindings` / `Target` / `Binding` (BYOC-complete) | §4.2.2 |
|
|
29
|
+
| `kvs-routing-entry.schema.json` | CloudFront KVS routing value (`{static, api}`) | §8 |
|
|
30
|
+
|
|
31
|
+
Two schemas carry logic beyond structural types, generated into helpers:
|
|
32
|
+
|
|
33
|
+
- **`error-category.enum.json`** → `IsRetryable(category)` (Go) / `isRetryable(category)` (TS),
|
|
34
|
+
handling both concrete categories and family prefixes (`storage.unsupported-engine.<engine>`).
|
|
35
|
+
Unknown categories fail closed (non-retryable).
|
|
36
|
+
- **`version-state.schema.json`** → `CanTransition(from, to)` / `IsTerminal(state)` (Go) and
|
|
37
|
+
`canTransition` / `isTerminal` (TS), driven by the `x-transitions` edge list — so control-plane
|
|
38
|
+
and any consumer agree on legality from one source.
|
|
39
|
+
|
|
40
|
+
## Workflow
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
make gen # regenerate gen-go/ + gen-ts/ from schema/ (Node only — no Go needed)
|
|
44
|
+
make verify # regenerate, then fail if git shows a diff ← the staleness gate (CI runs this)
|
|
45
|
+
make build # compile-check the generated Go + TS (needs go + local tsc)
|
|
46
|
+
make test # generator determinism + contract-logic tests (Node; Go when installed)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Codegen is **Node-only** so `make gen` is reproducible anywhere; a Go toolchain and `tsc` are
|
|
50
|
+
used solely to *compile-check* the output (`make build`), not to generate it.
|
|
51
|
+
|
|
52
|
+
### Changing a shape
|
|
53
|
+
|
|
54
|
+
1. Edit the schema in `schema/`. The category enum and state machine are **closed** — extend
|
|
55
|
+
them **only by PR to this repo** (that PR is where consumers are notified).
|
|
56
|
+
2. `make gen` and commit `gen-go/` + `gen-ts/` alongside the schema change.
|
|
57
|
+
3. CI (`make verify`) fails any PR whose committed generated code does not match its schema.
|
|
58
|
+
4. A shape change is a `(contracts)` change: it lands via `/review` + `/code-review ultra` → `/ship`.
|
|
59
|
+
|
|
60
|
+
## Consuming
|
|
61
|
+
|
|
62
|
+
- **Go:** `go get github.com/Fourier-Labs-AI/harbour-contracts/gen-go@v0.1.0`
|
|
63
|
+
|
|
64
|
+
Two things make this work, and the second is not the obvious one.
|
|
65
|
+
|
|
66
|
+
**The tag must be 3-part semver.** Go requires exactly `vMAJOR.MINOR.PATCH`, so the
|
|
67
|
+
repo's 4-digit `VERSION` (`0.1.0.0`) cannot be a Go tag — the proxy rejects it. Each
|
|
68
|
+
release carries **two** tags on the same commit: `v0.1.0.0` (repo version, what
|
|
69
|
+
`/ship` manages) and `v0.1.0` (Go module). Keep the first three components identical.
|
|
70
|
+
|
|
71
|
+
**The repo is private, and Go fetches it from a global cache.** Set this once per
|
|
72
|
+
machine:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
go env -w GOPRIVATE=github.com/Fourier-Labs-AI/*
|
|
76
|
+
git config --global credential."https://github.com/Fourier-Labs-AI".helper '!gh auth git-credential'
|
|
77
|
+
git config --global credential."https://github.com/Fourier-Labs-AI".useHttpPath true
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The credential config has to be **global and URL-scoped**, and that combination is
|
|
81
|
+
not incidental:
|
|
82
|
+
|
|
83
|
+
- It cannot be folder-scoped (`includeIf gitdir:…`). Go runs `git ls-remote` inside
|
|
84
|
+
`~/go/pkg/mod/cache/vcs/…`, not inside your repo, so a per-directory helper never
|
|
85
|
+
applies. This fails identically from `/tmp` and from inside a Harbour repo, which
|
|
86
|
+
makes it look like a bad tag rather than an auth problem.
|
|
87
|
+
- Scoping by URL prefix keeps it off your other GitHub accounts: only
|
|
88
|
+
`github.com/Fourier-Labs-AI/*` uses this credential.
|
|
89
|
+
- An SSH `insteadOf` rewrite is the usual advice and is **wrong here** unless your
|
|
90
|
+
SSH key belongs to an account with Fourier org access. Check with
|
|
91
|
+
`ssh -T git@github.com` before reaching for it.
|
|
92
|
+
|
|
93
|
+
Without the above, `go get` fails with `Repository not found` — which reads as a
|
|
94
|
+
missing tag, not as missing access.
|
|
95
|
+
|
|
96
|
+
- **TS:** `npm i @fourier-labs/harbour-contracts` — published from the tag by CI.
|
|
97
|
+
|
|
98
|
+
## Notes
|
|
99
|
+
|
|
100
|
+
`gen.mjs` produces both languages deterministically from Node. Go **struct field alignment** is
|
|
101
|
+
not gofmt-normalized by the generator (gofmt is optional hygiene via `make fmt`); this never
|
|
102
|
+
affects compilation, and the staleness gate compares raw generator output, so it stays green.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe JSON error body returned by the Deploy and Status Lambdas for API-level 4xx and 5xx responses. It is distinct from the internal pipeline ErrorEnvelope, which requires a version and evidence reference.
|
|
3
|
+
*/
|
|
4
|
+
export interface ApiErrorResponse {
|
|
5
|
+
code: string;
|
|
6
|
+
message: string;
|
|
7
|
+
correlationId: string;
|
|
8
|
+
requestId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Present when a version was minted before the API-level failure.
|
|
11
|
+
*/
|
|
12
|
+
versionId?: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The build output contract (LLD §3) — the provider-portability seam. Emitted by the build (`artifact.json`) and consumed by every deploy stage below build. Deploy stages read ONLY this document plus the blobs it references. Secret REFERENCES only — never values.
|
|
3
|
+
*/
|
|
4
|
+
export interface ArtifactContract {
|
|
5
|
+
contract_version: 1;
|
|
6
|
+
app_id: string;
|
|
7
|
+
commit: string;
|
|
8
|
+
/**
|
|
9
|
+
* sha256 of the canonical bundle; the immutable identity that promotion pins to (invariant §1.3.2).
|
|
10
|
+
*/
|
|
11
|
+
artifact_digest: string;
|
|
12
|
+
backend: {
|
|
13
|
+
type: 'oci-image' | 'zip';
|
|
14
|
+
/**
|
|
15
|
+
* ECR uri@digest (oci-image) or S3 key (zip).
|
|
16
|
+
*/
|
|
17
|
+
ref: string;
|
|
18
|
+
entrypoint: string;
|
|
19
|
+
runtime: string;
|
|
20
|
+
};
|
|
21
|
+
static: {
|
|
22
|
+
/**
|
|
23
|
+
* e.g. builds/<digest>/
|
|
24
|
+
*/
|
|
25
|
+
path: string;
|
|
26
|
+
/**
|
|
27
|
+
* true when static assets carry no env-specific values (no static rebuild needed on promotion).
|
|
28
|
+
*/
|
|
29
|
+
env_agnostic: boolean;
|
|
30
|
+
};
|
|
31
|
+
routes: {
|
|
32
|
+
path: string;
|
|
33
|
+
target: 'backend' | 'static';
|
|
34
|
+
}[];
|
|
35
|
+
env: {
|
|
36
|
+
/**
|
|
37
|
+
* Non-secret env vars, e.g. {"PORT":"8080"}.
|
|
38
|
+
*/
|
|
39
|
+
vars: {
|
|
40
|
+
[k: string]: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Secrets Manager references only, e.g. harbour/<org>/<app>/<env>/STRIPE_KEY. Never values.
|
|
44
|
+
*/
|
|
45
|
+
secret_refs: string[];
|
|
46
|
+
};
|
|
47
|
+
storage_bindings: {
|
|
48
|
+
kind: 'sql' | 'kv' | 'files';
|
|
49
|
+
/**
|
|
50
|
+
* Logical name, e.g. "default".
|
|
51
|
+
*/
|
|
52
|
+
logical?: string;
|
|
53
|
+
/**
|
|
54
|
+
* e.g. aurora-sv2.
|
|
55
|
+
*/
|
|
56
|
+
provider: string;
|
|
57
|
+
}[];
|
|
58
|
+
/**
|
|
59
|
+
* Post-MVP (migration tooling is post-MVP, LLD §4.6); MVP emits an empty array.
|
|
60
|
+
*/
|
|
61
|
+
migrations: {
|
|
62
|
+
seq: number;
|
|
63
|
+
sha: string;
|
|
64
|
+
destructive: boolean;
|
|
65
|
+
path: string;
|
|
66
|
+
}[];
|
|
67
|
+
/**
|
|
68
|
+
* MVP emits an empty array.
|
|
69
|
+
*/
|
|
70
|
+
scheduled_tasks: {}[];
|
|
71
|
+
transform_report: {
|
|
72
|
+
/**
|
|
73
|
+
* Transformed surfaces, e.g. ["secrets","storage.sql"].
|
|
74
|
+
*/
|
|
75
|
+
surfaces: string[];
|
|
76
|
+
static_rebuild_required: boolean;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON body for POST /v1/tenants/{tenant}/apps/{app}/deployments. tenant and app come from path parameters and are not body fields. The preview MVP requires environment=preview. Every accepted POST is a new deployment intent; idempotencyKey is retained only as request correlation.
|
|
3
|
+
*/
|
|
4
|
+
export interface DeployRequest {
|
|
5
|
+
source: DeployRequestSource;
|
|
6
|
+
/**
|
|
7
|
+
* Deployment target. The API rejects production; production remains human-merge gated.
|
|
8
|
+
*/
|
|
9
|
+
environment: 'preview';
|
|
10
|
+
/**
|
|
11
|
+
* Git branch that must contain the exact requested commit.
|
|
12
|
+
*/
|
|
13
|
+
branch: string;
|
|
14
|
+
/**
|
|
15
|
+
* Exact lowercase hexadecimal Git commit to materialize and deploy.
|
|
16
|
+
*/
|
|
17
|
+
commit: string;
|
|
18
|
+
/**
|
|
19
|
+
* 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.
|
|
20
|
+
*/
|
|
21
|
+
idempotencyKey?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface DeployRequestSource {
|
|
24
|
+
/**
|
|
25
|
+
* Source provider. The preview MVP accepts only github.
|
|
26
|
+
*/
|
|
27
|
+
provider: 'github';
|
|
28
|
+
/**
|
|
29
|
+
* Credential-free HTTPS GitHub repository URL.
|
|
30
|
+
*/
|
|
31
|
+
repositoryUrl: string;
|
|
32
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 202 Accepted body returned by POST /v1/tenants/{tenant}/apps/{app}/deployments only after the authoritative QUEUED version exists and its version-qualified SQS message is accepted.
|
|
3
|
+
*/
|
|
4
|
+
export interface DeployResponse {
|
|
5
|
+
/**
|
|
6
|
+
* Caller correlation value. This is not the deployment identity.
|
|
7
|
+
*/
|
|
8
|
+
requestId: string;
|
|
9
|
+
/**
|
|
10
|
+
* Immutable identity of the admitted preview version.
|
|
11
|
+
*/
|
|
12
|
+
versionId: string;
|
|
13
|
+
tenant: string;
|
|
14
|
+
app: string;
|
|
15
|
+
environment: 'preview';
|
|
16
|
+
status: 'QUEUED';
|
|
17
|
+
acceptedAt: string;
|
|
18
|
+
/**
|
|
19
|
+
* Relative URL for exact-version Status. The versionId path segment is URL encoded.
|
|
20
|
+
*/
|
|
21
|
+
statusUrl: string;
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential-free GitHub source supplied in the DeployRequest body. Credentials are resolved internally by MaterializeSource and never cross the API boundary.
|
|
3
|
+
*/
|
|
4
|
+
export interface DeploymentSource {
|
|
5
|
+
/**
|
|
6
|
+
* Source provider. The preview MVP accepts only GitHub.
|
|
7
|
+
*/
|
|
8
|
+
provider: 'github';
|
|
9
|
+
/**
|
|
10
|
+
* Credential-free HTTPS GitHub repository URL. Query strings, fragments, embedded credentials and non-github.com hosts are rejected.
|
|
11
|
+
*/
|
|
12
|
+
repositoryUrl: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frontend-safe failure projection nested in a failed DeploymentStatusResponse. Internal evidence references, AWS identifiers and raw provider errors are excluded.
|
|
3
|
+
*/
|
|
4
|
+
export interface DeploymentStatusFailure {
|
|
5
|
+
category: string;
|
|
6
|
+
retryable: boolean;
|
|
7
|
+
message: string;
|
|
8
|
+
remediationHint: string;
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validated path parameters for GET /v1/tenants/{tenant}/apps/{app}/deployments/{versionId}. The Status Lambda constructs this object from API Gateway path parameters; it is not a GET request body.
|
|
3
|
+
*/
|
|
4
|
+
export interface DeploymentStatusRequest {
|
|
5
|
+
tenant: string;
|
|
6
|
+
app: string;
|
|
7
|
+
versionId: string;
|
|
8
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 200 OK body returned by GET /v1/tenants/{tenant}/apps/{app}/deployments/{versionId}. It describes that exact immutable version; newer deployments cannot change which record this response represents.
|
|
3
|
+
*/
|
|
4
|
+
export interface DeploymentStatusResponse {
|
|
5
|
+
requestId: string;
|
|
6
|
+
versionId: string;
|
|
7
|
+
tenant: string;
|
|
8
|
+
app: string;
|
|
9
|
+
environment: 'preview';
|
|
10
|
+
status: 'QUEUED' | 'ANALYZING' | 'TRANSFORMING' | 'VALIDATING' | 'BUILDING' | 'VERIFYING' | 'GATING' | 'LIVE' | 'FAILED' | 'STALE';
|
|
11
|
+
message: string;
|
|
12
|
+
branch: string;
|
|
13
|
+
commit: string;
|
|
14
|
+
requestedAt: string;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
url?: string;
|
|
17
|
+
failure?: StatusFailureProjection;
|
|
18
|
+
}
|
|
19
|
+
export interface StatusFailureProjection {
|
|
20
|
+
category: string;
|
|
21
|
+
retryable: boolean;
|
|
22
|
+
message: string;
|
|
23
|
+
remediationHint: string;
|
|
24
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolved environment bindings (LLD §4.2.2). Deliberately COMPLETE — including BYOC/multi-cloud fields MVP does not use (roleArn/externalId absent = act in own account). Resolved once at admission by EnvResolver, stamped onto the version record; every downstream stage reads it from the record and never re-resolves. Adding a field bumps bindingsVersion and touches every consumer — that is the point. `executionCell` is where source-touching stages run (MVP: provider 'harbour').
|
|
3
|
+
*/
|
|
4
|
+
export interface EnvBindings {
|
|
5
|
+
/**
|
|
6
|
+
* e.g. preview | prod.
|
|
7
|
+
*/
|
|
8
|
+
env: string;
|
|
9
|
+
executor: Binding;
|
|
10
|
+
sql: Binding;
|
|
11
|
+
kv: Binding;
|
|
12
|
+
files: Binding;
|
|
13
|
+
secrets: Binding;
|
|
14
|
+
serving: Binding;
|
|
15
|
+
executionCell: Binding;
|
|
16
|
+
/**
|
|
17
|
+
* Ordered gate names evaluated at GATING.
|
|
18
|
+
*/
|
|
19
|
+
gates: string[];
|
|
20
|
+
/**
|
|
21
|
+
* MVP: absent. Future: per-target constraints (region pins, size caps, …).
|
|
22
|
+
*/
|
|
23
|
+
constraints?: {
|
|
24
|
+
[k: string]: unknown;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Stamped onto the version record for audit; bumps when the binding shape changes.
|
|
28
|
+
*/
|
|
29
|
+
bindingsVersion: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* provider + target + provider-specific `config` (LLD §4.2.2). The binding object itself is closed: a new first-class field is a deliberate schema change plus a bindingsVersion bump, never an ad-hoc key.
|
|
33
|
+
*/
|
|
34
|
+
export interface Binding {
|
|
35
|
+
provider: string;
|
|
36
|
+
target: Target;
|
|
37
|
+
/**
|
|
38
|
+
* Provider-specific values, resolved at admission and stamped on the version record. MVP (StaticEnvResolver, from CDK stack outputs) e.g. sql: {clusterArn, proxyEndpoint, databaseName}; serving: {distributionId, bucket}. Kept as a named open map rather than loose top-level keys so BOTH generated languages preserve it faithfully — a Go struct silently drops unrecognized top-level keys, a map does not.
|
|
39
|
+
*/
|
|
40
|
+
config?: {
|
|
41
|
+
[k: string]: unknown;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface Target {
|
|
45
|
+
/**
|
|
46
|
+
* MVP: own account id.
|
|
47
|
+
*/
|
|
48
|
+
account: string;
|
|
49
|
+
/**
|
|
50
|
+
* MVP: us-east-1.
|
|
51
|
+
*/
|
|
52
|
+
region: string;
|
|
53
|
+
/**
|
|
54
|
+
* BYOC cross-account role; absent in MVP = act locally.
|
|
55
|
+
*/
|
|
56
|
+
roleArn?: string;
|
|
57
|
+
/**
|
|
58
|
+
* BYOC assume-role external id; absent in MVP.
|
|
59
|
+
*/
|
|
60
|
+
externalId?: string;
|
|
61
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const ErrorCategories: {
|
|
2
|
+
readonly IntakeNoEntrypoint: "intake.no-entrypoint";
|
|
3
|
+
readonly IntakeUnsupportedRuntime: "intake.unsupported-runtime";
|
|
4
|
+
readonly IntakeAttestationMissing: "intake.attestation-missing";
|
|
5
|
+
readonly IntakeMultiAppRepo: "intake.multi-app-repo";
|
|
6
|
+
readonly SecretsUnresolvable: "secrets.unresolvable";
|
|
7
|
+
readonly SecretsLeakedInArtifact: "secrets.leaked-in-artifact";
|
|
8
|
+
readonly StorageSqliteMisclassified: "storage.sqlite-misclassified";
|
|
9
|
+
readonly StorageMappingDrift: "storage.mapping-drift";
|
|
10
|
+
readonly TransformDeadlineExceeded: "transform.deadline-exceeded";
|
|
11
|
+
readonly TransformValidationFailed: "transform.validation-failed";
|
|
12
|
+
readonly FrontendEnvInlinedUnfixable: "frontend.env-inlined-unfixable";
|
|
13
|
+
readonly BuildRecipeFailed: "build.recipe-failed";
|
|
14
|
+
readonly BuildImageTooLarge: "build.image-too-large";
|
|
15
|
+
readonly VerifySmokeFailed: "verify.smoke-failed";
|
|
16
|
+
readonly VerifyCharacterizationDiverged: "verify.characterization-diverged";
|
|
17
|
+
readonly VerifyMigrationFromZeroFailed: "verify.migration-from-zero-failed";
|
|
18
|
+
readonly VerifyMigrationSeededFailed: "verify.migration-seeded-failed";
|
|
19
|
+
readonly AuthAppPubliclyInvocable: "auth.app-publicly-invocable";
|
|
20
|
+
readonly AuthIdentityHeaderContractViolated: "auth.identity-header-contract-violated";
|
|
21
|
+
readonly GateSecurityScan: "gate.security-scan";
|
|
22
|
+
readonly GateOrgApproverDeclined: "gate.org-approver-declined";
|
|
23
|
+
readonly PromoteMigrationFailed: "promote.migration-failed";
|
|
24
|
+
readonly PromoteStaticRebuildFailed: "promote.static-rebuild-failed";
|
|
25
|
+
readonly RuntimePersistentComputeRequired: "runtime.persistent-compute-required";
|
|
26
|
+
readonly RuntimeEgressBlocked: "runtime.egress-blocked";
|
|
27
|
+
readonly AdmissionAppNotRegistered: "admission.app-not-registered";
|
|
28
|
+
readonly DeployCommitNotOnBranch: "deploy.commit-not-on-branch";
|
|
29
|
+
readonly DeployQueuePublicationFailed: "deploy.queue-publication-failed";
|
|
30
|
+
readonly CutoverFlipFailed: "cutover.flip-failed";
|
|
31
|
+
};
|
|
32
|
+
/** Retryable lookup for a concrete or family-suffixed category. Unknown → false (fail closed). */
|
|
33
|
+
export declare function isRetryable(category: string): boolean;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
export const ErrorCategories = {
|
|
4
|
+
IntakeNoEntrypoint: "intake.no-entrypoint",
|
|
5
|
+
IntakeUnsupportedRuntime: "intake.unsupported-runtime",
|
|
6
|
+
IntakeAttestationMissing: "intake.attestation-missing",
|
|
7
|
+
IntakeMultiAppRepo: "intake.multi-app-repo",
|
|
8
|
+
SecretsUnresolvable: "secrets.unresolvable",
|
|
9
|
+
SecretsLeakedInArtifact: "secrets.leaked-in-artifact",
|
|
10
|
+
StorageSqliteMisclassified: "storage.sqlite-misclassified",
|
|
11
|
+
StorageMappingDrift: "storage.mapping-drift",
|
|
12
|
+
TransformDeadlineExceeded: "transform.deadline-exceeded",
|
|
13
|
+
TransformValidationFailed: "transform.validation-failed",
|
|
14
|
+
FrontendEnvInlinedUnfixable: "frontend.env-inlined-unfixable",
|
|
15
|
+
BuildRecipeFailed: "build.recipe-failed",
|
|
16
|
+
BuildImageTooLarge: "build.image-too-large",
|
|
17
|
+
VerifySmokeFailed: "verify.smoke-failed",
|
|
18
|
+
VerifyCharacterizationDiverged: "verify.characterization-diverged",
|
|
19
|
+
VerifyMigrationFromZeroFailed: "verify.migration-from-zero-failed",
|
|
20
|
+
VerifyMigrationSeededFailed: "verify.migration-seeded-failed",
|
|
21
|
+
AuthAppPubliclyInvocable: "auth.app-publicly-invocable",
|
|
22
|
+
AuthIdentityHeaderContractViolated: "auth.identity-header-contract-violated",
|
|
23
|
+
GateSecurityScan: "gate.security-scan",
|
|
24
|
+
GateOrgApproverDeclined: "gate.org-approver-declined",
|
|
25
|
+
PromoteMigrationFailed: "promote.migration-failed",
|
|
26
|
+
PromoteStaticRebuildFailed: "promote.static-rebuild-failed",
|
|
27
|
+
RuntimePersistentComputeRequired: "runtime.persistent-compute-required",
|
|
28
|
+
RuntimeEgressBlocked: "runtime.egress-blocked",
|
|
29
|
+
AdmissionAppNotRegistered: "admission.app-not-registered",
|
|
30
|
+
DeployCommitNotOnBranch: "deploy.commit-not-on-branch",
|
|
31
|
+
DeployQueuePublicationFailed: "deploy.queue-publication-failed",
|
|
32
|
+
CutoverFlipFailed: "cutover.flip-failed",
|
|
33
|
+
};
|
|
34
|
+
const retryableExact = {
|
|
35
|
+
"intake.no-entrypoint": false,
|
|
36
|
+
"intake.unsupported-runtime": false,
|
|
37
|
+
"intake.attestation-missing": false,
|
|
38
|
+
"intake.multi-app-repo": false,
|
|
39
|
+
"secrets.unresolvable": false,
|
|
40
|
+
"secrets.leaked-in-artifact": false,
|
|
41
|
+
"storage.sqlite-misclassified": false,
|
|
42
|
+
"storage.mapping-drift": false,
|
|
43
|
+
"transform.deadline-exceeded": false,
|
|
44
|
+
"transform.validation-failed": true,
|
|
45
|
+
"frontend.env-inlined-unfixable": false,
|
|
46
|
+
"build.recipe-failed": false,
|
|
47
|
+
"build.image-too-large": false,
|
|
48
|
+
"verify.smoke-failed": false,
|
|
49
|
+
"verify.characterization-diverged": false,
|
|
50
|
+
"verify.migration-from-zero-failed": false,
|
|
51
|
+
"verify.migration-seeded-failed": false,
|
|
52
|
+
"auth.app-publicly-invocable": false,
|
|
53
|
+
"auth.identity-header-contract-violated": false,
|
|
54
|
+
"gate.security-scan": false,
|
|
55
|
+
"gate.org-approver-declined": false,
|
|
56
|
+
"promote.migration-failed": false,
|
|
57
|
+
"promote.static-rebuild-failed": false,
|
|
58
|
+
"runtime.persistent-compute-required": false,
|
|
59
|
+
"runtime.egress-blocked": false,
|
|
60
|
+
"admission.app-not-registered": false,
|
|
61
|
+
"deploy.commit-not-on-branch": false,
|
|
62
|
+
"deploy.queue-publication-failed": true,
|
|
63
|
+
"cutover.flip-failed": false,
|
|
64
|
+
};
|
|
65
|
+
const retryableFamilies = {
|
|
66
|
+
"storage.unsupported-engine": false,
|
|
67
|
+
"transform.non-retryable": false,
|
|
68
|
+
"quota.exceeded": false,
|
|
69
|
+
"uncategorized": false,
|
|
70
|
+
};
|
|
71
|
+
/** Retryable lookup for a concrete or family-suffixed category. Unknown → false (fail closed). */
|
|
72
|
+
export function isRetryable(category) {
|
|
73
|
+
if (category in retryableExact)
|
|
74
|
+
return retryableExact[category];
|
|
75
|
+
let best = false, bestLen = -1;
|
|
76
|
+
for (const prefix of Object.keys(retryableFamilies)) {
|
|
77
|
+
if (category === prefix || category.startsWith(prefix + '.')) {
|
|
78
|
+
if (prefix.length > bestLen) {
|
|
79
|
+
best = retryableFamilies[prefix];
|
|
80
|
+
bestLen = prefix.length;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return best;
|
|
85
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The closed failure-category enum T1b (LLD §2.2). Categories are hierarchical lowercase dot-paths; the histogram groups on any prefix. Some entries are FAMILIES (variable last segment, e.g. storage.unsupported-engine.<engine>) — at runtime a concrete token replaces the placeholder. Extended ONLY by PR to this file. Root validates any well-formed category string; x-registry is the source of truth for the per-category `retryable` flag and codegen constants.
|
|
3
|
+
*/
|
|
4
|
+
export type ErrorCategory = string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emitted exactly once per FAILED transition (LLD §2.1). Consumed identically by MCP (maker), alarms (platform), and the failure-log histogram. `maker_message` is the only human-facing text; everything else is data.
|
|
3
|
+
*/
|
|
4
|
+
export interface ErrorEnvelope {
|
|
5
|
+
/**
|
|
6
|
+
* Schema version of this envelope. Bumped only by PR to this file.
|
|
7
|
+
*/
|
|
8
|
+
envelope_version: 1;
|
|
9
|
+
/**
|
|
10
|
+
* Immutable app slug.
|
|
11
|
+
*/
|
|
12
|
+
app_id: string;
|
|
13
|
+
/**
|
|
14
|
+
* `<org>#<app>#<env>#<zero-padded ordinal>` (LLD §4.2.1).
|
|
15
|
+
*/
|
|
16
|
+
version_id: string;
|
|
17
|
+
env: 'preview' | 'prod';
|
|
18
|
+
/**
|
|
19
|
+
* Pipeline stage that produced the failure (frozen T1b stage enum).
|
|
20
|
+
*/
|
|
21
|
+
stage: 'admission' | 'intake' | 'transform' | 'validate' | 'build' | 'verify' | 'gate' | 'cutover' | 'promote';
|
|
22
|
+
/**
|
|
23
|
+
* A category from the closed enum in error-category.enum.json (families carry a concrete last segment at runtime).
|
|
24
|
+
*/
|
|
25
|
+
category: string;
|
|
26
|
+
/**
|
|
27
|
+
* Echoed from the category registry (LLD §2.2). The maker's tool renders "retrying…" vs "this app can't be converted because…".
|
|
28
|
+
*/
|
|
29
|
+
retryable: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Plain English, no AWS terms. What happened + what next. The ONLY human-facing field.
|
|
32
|
+
*/
|
|
33
|
+
maker_message: string;
|
|
34
|
+
/**
|
|
35
|
+
* Actionable for the maker's AI tool (machine-usable).
|
|
36
|
+
*/
|
|
37
|
+
remediation_hint: string;
|
|
38
|
+
/**
|
|
39
|
+
* `s3://…/evidence/<org>/<app>/<version_id>/…` — reference to the evidence bundle, never inline.
|
|
40
|
+
*/
|
|
41
|
+
evidence_ref: string;
|
|
42
|
+
/**
|
|
43
|
+
* ISO-8601 UTC.
|
|
44
|
+
*/
|
|
45
|
+
ts: string;
|
|
46
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from './api-error-response.js';
|
|
2
|
+
export * from './artifact-contract.js';
|
|
3
|
+
export * from './deploy-request.js';
|
|
4
|
+
export * from './deploy-response.js';
|
|
5
|
+
export * from './deployment-environment.js';
|
|
6
|
+
export * from './deployment-source.js';
|
|
7
|
+
export * from './deployment-status-failure.js';
|
|
8
|
+
export * from './deployment-status-request.js';
|
|
9
|
+
export * from './deployment-status-response.js';
|
|
10
|
+
export * from './env-bindings.js';
|
|
11
|
+
export * from './error-category-registry.js';
|
|
12
|
+
export * from './error-category.js';
|
|
13
|
+
export * from './error-envelope.js';
|
|
14
|
+
export * from './kvs-routing-entry.js';
|
|
15
|
+
export * from './version-state-machine.js';
|
|
16
|
+
export * from './version-state.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
export * from './api-error-response.js';
|
|
4
|
+
export * from './artifact-contract.js';
|
|
5
|
+
export * from './deploy-request.js';
|
|
6
|
+
export * from './deploy-response.js';
|
|
7
|
+
export * from './deployment-environment.js';
|
|
8
|
+
export * from './deployment-source.js';
|
|
9
|
+
export * from './deployment-status-failure.js';
|
|
10
|
+
export * from './deployment-status-request.js';
|
|
11
|
+
export * from './deployment-status-response.js';
|
|
12
|
+
export * from './env-bindings.js';
|
|
13
|
+
export * from './error-category-registry.js';
|
|
14
|
+
export * from './error-category.js';
|
|
15
|
+
export * from './error-envelope.js';
|
|
16
|
+
export * from './kvs-routing-entry.js';
|
|
17
|
+
export * from './version-state-machine.js';
|
|
18
|
+
export * from './version-state.js';
|