@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.
Files changed (50) hide show
  1. package/README.md +102 -0
  2. package/dist/api-error-response.d.ts +13 -0
  3. package/dist/api-error-response.js +3 -0
  4. package/dist/artifact-contract.d.ts +78 -0
  5. package/dist/artifact-contract.js +3 -0
  6. package/dist/deploy-request.d.ts +32 -0
  7. package/dist/deploy-request.js +3 -0
  8. package/dist/deploy-response.d.ts +22 -0
  9. package/dist/deploy-response.js +3 -0
  10. package/dist/deployment-environment.d.ts +4 -0
  11. package/dist/deployment-environment.js +3 -0
  12. package/dist/deployment-source.d.ts +13 -0
  13. package/dist/deployment-source.js +3 -0
  14. package/dist/deployment-status-failure.d.ts +9 -0
  15. package/dist/deployment-status-failure.js +3 -0
  16. package/dist/deployment-status-request.d.ts +8 -0
  17. package/dist/deployment-status-request.js +3 -0
  18. package/dist/deployment-status-response.d.ts +24 -0
  19. package/dist/deployment-status-response.js +3 -0
  20. package/dist/env-bindings.d.ts +61 -0
  21. package/dist/env-bindings.js +3 -0
  22. package/dist/error-category-registry.d.ts +33 -0
  23. package/dist/error-category-registry.js +85 -0
  24. package/dist/error-category.d.ts +4 -0
  25. package/dist/error-category.js +3 -0
  26. package/dist/error-envelope.d.ts +46 -0
  27. package/dist/error-envelope.js +3 -0
  28. package/dist/index.d.ts +16 -0
  29. package/dist/index.js +18 -0
  30. package/dist/kvs-routing-entry.d.ts +13 -0
  31. package/dist/kvs-routing-entry.js +3 -0
  32. package/dist/version-state-machine.d.ts +19 -0
  33. package/dist/version-state-machine.js +35 -0
  34. package/dist/version-state.d.ts +4 -0
  35. package/dist/version-state.js +3 -0
  36. package/package.json +30 -0
  37. package/schema/api-error-response.schema.json +33 -0
  38. package/schema/artifact-contract.schema.json +126 -0
  39. package/schema/deploy-request.schema.json +68 -0
  40. package/schema/deploy-response.schema.json +49 -0
  41. package/schema/deployment-environment.schema.json +9 -0
  42. package/schema/deployment-source.schema.json +29 -0
  43. package/schema/deployment-status-failure.schema.json +28 -0
  44. package/schema/deployment-status-request.schema.json +25 -0
  45. package/schema/deployment-status-response.schema.json +79 -0
  46. package/schema/env-bindings.schema.json +62 -0
  47. package/schema/error-category.enum.json +49 -0
  48. package/schema/error-envelope.schema.json +85 -0
  49. package/schema/kvs-routing-entry.schema.json +21 -0
  50. package/schema/version-state.schema.json +66 -0
@@ -0,0 +1,49 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/error-category.enum.json",
4
+ "title": "ErrorCategory",
5
+ "description": "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.",
6
+ "$ref": "#/definitions/ErrorCategory",
7
+ "definitions": {
8
+ "ErrorCategory": {
9
+ "type": "string",
10
+ "pattern": "^[a-z][a-z0-9]*(\\.[a-z0-9-]+)+$",
11
+ "description": "Well-formed category: >=2 lowercase dot-path segments; segments may contain hyphens and digits."
12
+ }
13
+ },
14
+ "x-registry": [
15
+ { "id": "intake.no-entrypoint", "retryable": false, "description": "No detectable app entrypoint in the repo." },
16
+ { "id": "intake.unsupported-runtime", "retryable": false, "description": "Detected runtime has no Harbour build recipe." },
17
+ { "id": "intake.attestation-missing", "retryable": false, "description": "Required auth attestation absent." },
18
+ { "id": "intake.multi-app-repo", "retryable": false, "description": "Repo contains more than one deployable app." },
19
+ { "id": "secrets.unresolvable", "retryable": false, "description": "A referenced secret has no value (prod promotion blocks at GATING until a human sets it)." },
20
+ { "id": "secrets.leaked-in-artifact", "retryable": false, "description": "Artifact secret scan found a plaintext secret in the built image (belt-and-suspenders)." },
21
+ { "id": "storage.sqlite-misclassified", "retryable": false, "description": "SQLite usage not resolvable to a managed binding." },
22
+ { "id": "storage.unsupported-engine", "retryable": false, "family": true, "description": "Storage engine with no Harbour mapping; concrete token names the engine, e.g. storage.unsupported-engine.mongodb." },
23
+ { "id": "storage.mapping-drift", "retryable": false, "description": "Declared storage binding diverged from detected usage." },
24
+ { "id": "transform.deadline-exceeded", "retryable": false, "description": "The 15-minute transform wall deadline was reached." },
25
+ { "id": "transform.non-retryable", "retryable": false, "family": true, "description": "Agent declared the app unconvertible or hit an unsupported construct; fails immediately without burning remaining budget. Concrete token names the reason." },
26
+ { "id": "transform.validation-failed", "retryable": true, "description": "Transform candidate failed validation with iterable feedback; re-enters TRANSFORMING while deadline remains." },
27
+ { "id": "frontend.env-inlined-unfixable", "retryable": false, "description": "Client code inlined build-time env/self-URLs in a way transformation cannot rewrite." },
28
+ { "id": "build.recipe-failed", "retryable": false, "description": "The Harbour build recipe failed for the detected runtime." },
29
+ { "id": "build.image-too-large", "retryable": false, "description": "Built image exceeded the size limit." },
30
+ { "id": "verify.smoke-failed", "retryable": false, "description": "Candidate smoke/health check failed (boot, routes, storage, secret refs, response shape)." },
31
+ { "id": "verify.characterization-diverged", "retryable": false, "description": "Post-MVP: recorded req/res replay diverged beyond thresholds." },
32
+ { "id": "verify.migration-from-zero-failed", "retryable": false, "description": "Post-MVP: from-zero migration chain failed on scratch DB." },
33
+ { "id": "verify.migration-seeded-failed", "retryable": false, "description": "Post-MVP: seeded-data migration survival test failed." },
34
+ { "id": "auth.app-publicly-invocable", "retryable": false, "description": "The unsigned Function-URL probe was NOT rejected with 403 — the app door is open (hard verify gate)." },
35
+ { "id": "auth.identity-header-contract-violated", "retryable": false, "description": "App mishandled the platform identity header contract." },
36
+ { "id": "gate.security-scan", "retryable": false, "description": "A blocking security-scan gate failed." },
37
+ { "id": "gate.org-approver-declined", "retryable": false, "description": "An org approver declined promotion." },
38
+ { "id": "promote.migration-failed", "retryable": false, "description": "Post-MVP: prod migration replay failed; prod alias never moved." },
39
+ { "id": "promote.static-rebuild-failed", "retryable": false, "description": "Static rebuild during promotion failed." },
40
+ { "id": "runtime.persistent-compute-required", "retryable": false, "description": "App needs long-lived compute Lambda cannot provide (expected day-one leader; Fargate TODO trigger watches this)." },
41
+ { "id": "runtime.egress-blocked", "retryable": false, "description": "App required outbound network egress that policy blocked (e.g. external LLM call without NAT allowance)." },
42
+ { "id": "admission.app-not-registered", "retryable": false, "description": "Admission rejected: the app directory does not know this app, so there is no sourceRef to fetch a commit from. Fails closed rather than creating the app on demand — a deploy request must not become an app-creation side effect, or app naming stops being a reviewed act. Non-retryable: the directory is authoritative, so retrying cannot make an unknown app known." },
43
+ { "id": "deploy.commit-not-on-branch", "retryable": false, "description": "Admission rejected: the deploy request's commit is not reachable from the app's branch." },
44
+ { "id": "deploy.queue-publication-failed", "retryable": true, "description": "The version was created but its version-qualified SQS message definitively could not be published. The lane is released when this version still owns it; submitting a new deployment is safe." },
45
+ { "id": "cutover.flip-failed", "retryable": false, "description": "The atomic alias flip failed at cutover; previous version keeps serving." },
46
+ { "id": "quota.exceeded", "retryable": false, "family": true, "description": "A provisioning/account limit was hit (cluster/IAM-role quota) — NOT runtime traffic quotas. Concrete token names which limit." },
47
+ { "id": "uncategorized", "retryable": false, "family": true, "description": "A failure that fit no category — this is a bug: file uncategorized.<stage> AND a spec PR to this file." }
48
+ ]
49
+ }
@@ -0,0 +1,85 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/error-envelope.schema.json",
4
+ "title": "ErrorEnvelope",
5
+ "description": "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.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "envelope_version",
10
+ "app_id",
11
+ "version_id",
12
+ "env",
13
+ "stage",
14
+ "category",
15
+ "retryable",
16
+ "maker_message",
17
+ "remediation_hint",
18
+ "evidence_ref",
19
+ "ts"
20
+ ],
21
+ "properties": {
22
+ "envelope_version": {
23
+ "type": "integer",
24
+ "const": 1,
25
+ "description": "Schema version of this envelope. Bumped only by PR to this file."
26
+ },
27
+ "app_id": {
28
+ "type": "string",
29
+ "minLength": 1,
30
+ "description": "Immutable app slug."
31
+ },
32
+ "version_id": {
33
+ "type": "string",
34
+ "pattern": "^[^#]+#[^#]+#(preview|prod)#[0-9]{6,}$",
35
+ "description": "`<org>#<app>#<env>#<zero-padded ordinal>` (LLD §4.2.1)."
36
+ },
37
+ "env": {
38
+ "type": "string",
39
+ "enum": ["preview", "prod"]
40
+ },
41
+ "stage": {
42
+ "type": "string",
43
+ "enum": [
44
+ "admission",
45
+ "intake",
46
+ "transform",
47
+ "validate",
48
+ "build",
49
+ "verify",
50
+ "gate",
51
+ "cutover",
52
+ "promote"
53
+ ],
54
+ "description": "Pipeline stage that produced the failure (frozen T1b stage enum)."
55
+ },
56
+ "category": {
57
+ "type": "string",
58
+ "pattern": "^[a-z][a-z0-9]*(\\.[a-z0-9-]+)+$",
59
+ "description": "A category from the closed enum in error-category.enum.json (families carry a concrete last segment at runtime)."
60
+ },
61
+ "retryable": {
62
+ "type": "boolean",
63
+ "description": "Echoed from the category registry (LLD §2.2). The maker's tool renders \"retrying…\" vs \"this app can't be converted because…\"."
64
+ },
65
+ "maker_message": {
66
+ "type": "string",
67
+ "minLength": 1,
68
+ "description": "Plain English, no AWS terms. What happened + what next. The ONLY human-facing field."
69
+ },
70
+ "remediation_hint": {
71
+ "type": "string",
72
+ "description": "Actionable for the maker's AI tool (machine-usable)."
73
+ },
74
+ "evidence_ref": {
75
+ "type": "string",
76
+ "pattern": "^s3://",
77
+ "description": "`s3://…/evidence/<org>/<app>/<version_id>/…` — reference to the evidence bundle, never inline."
78
+ },
79
+ "ts": {
80
+ "type": "string",
81
+ "format": "date-time",
82
+ "description": "ISO-8601 UTC."
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/kvs-routing-entry.schema.json",
4
+ "title": "KvsRoutingEntry",
5
+ "description": "Value stored in the CloudFront KeyValueStore, keyed by host header `<app>.(beta.)harbour.dev` and read by the CloudFront Function on every request (LLD §8). MVP carries exactly {static, api} and nothing else — quota/kill-switch fields are a deliberate, additive future extension consumed by the same function.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["static", "api"],
9
+ "properties": {
10
+ "static": {
11
+ "type": "string",
12
+ "minLength": 1,
13
+ "description": "S3 origin path for static assets of the LIVE version, e.g. `builds/<app>/<digest>/` (from the artifact `static.path`, LLD §3)."
14
+ },
15
+ "api": {
16
+ "type": "string",
17
+ "minLength": 1,
18
+ "description": "Backend target for `/api/*` routes — the Lambda alias reached via API Gateway / Function URL for the LIVE version."
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://schemas.harbour.dev/version-state.schema.json",
4
+ "title": "VersionState",
5
+ "description": "The app-version state machine T1a (LLD §1). Root type is the state token itself. `FAILED` carries a separate error category (see error-category.enum.json); the state token is the bare word `FAILED`. Legal edges live in x-transitions; codegen turns them into a shared transition-validation table so control-plane and any consumer agree on legality.",
6
+ "$ref": "#/definitions/State",
7
+ "definitions": {
8
+ "State": {
9
+ "type": "string",
10
+ "enum": [
11
+ "QUEUED",
12
+ "ANALYZING",
13
+ "TRANSFORMING",
14
+ "VALIDATING",
15
+ "BUILDING",
16
+ "VERIFYING",
17
+ "GATING",
18
+ "LIVE",
19
+ "AWAITING-PROMOTION",
20
+ "PROMOTING",
21
+ "FAILED",
22
+ "STALE"
23
+ ]
24
+ },
25
+ "Env": {
26
+ "type": "string",
27
+ "enum": ["preview", "prod"]
28
+ },
29
+ "VersionId": {
30
+ "type": "string",
31
+ "pattern": "^[^#]+#[^#]+#(preview|prod)#[0-9]{6,}$",
32
+ "description": "`<org>#<app>#<env>#<zero-padded ordinal>` (LLD §4.2.1). Globally unique by composition; the ordinal is human-speakable (\"deploy #124\")."
33
+ }
34
+ },
35
+ "x-terminal": ["FAILED", "STALE"],
36
+ "x-terminalStable": ["LIVE", "AWAITING-PROMOTION"],
37
+ "x-transitions": [
38
+ { "from": "QUEUED", "to": "ANALYZING", "trigger": "admission: slot free AND commit reachable from branch" },
39
+ { "from": "QUEUED", "to": "FAILED", "trigger": "admission validation fails — commit not reachable from branch" },
40
+ { "from": "ANALYZING", "to": "BUILDING", "trigger": "nothing to transform (fast path)" },
41
+ { "from": "ANALYZING", "to": "TRANSFORMING", "trigger": "transform-relevant findings; 15-min wall deadline stamped" },
42
+ { "from": "ANALYZING", "to": "FAILED", "trigger": "hard intake failure" },
43
+ { "from": "TRANSFORMING", "to": "VALIDATING", "trigger": "agent produced candidate" },
44
+ { "from": "TRANSFORMING", "to": "FAILED", "trigger": "non-retryable failure or deadline exceeded" },
45
+ { "from": "VALIDATING", "to": "BUILDING", "trigger": "validation passed" },
46
+ { "from": "VALIDATING", "to": "TRANSFORMING", "trigger": "retryable failure AND now < deadline" },
47
+ { "from": "VALIDATING", "to": "FAILED", "trigger": "non-retryable failure or deadline exceeded" },
48
+ { "from": "BUILDING", "to": "VERIFYING", "trigger": "artifact produced + artifact secret scan clean" },
49
+ { "from": "BUILDING", "to": "FAILED", "trigger": "build recipe failed or secret scan hit" },
50
+ { "from": "VERIFYING", "to": "GATING", "trigger": "all candidate checks pass" },
51
+ { "from": "VERIFYING", "to": "FAILED", "trigger": "a verification check fails (e.g. auth.app-publicly-invocable)" },
52
+ { "from": "GATING", "to": "LIVE", "trigger": "all gates pass -> atomic alias flip" },
53
+ { "from": "GATING", "to": "FAILED", "trigger": "a blocking gate fails" },
54
+ { "from": "LIVE", "to": "AWAITING-PROMOTION", "trigger": "preview only — maker confirms; platform raises PR" },
55
+ { "from": "AWAITING-PROMOTION", "to": "PROMOTING", "trigger": "human merges PR (no agent-callable path)" },
56
+ { "from": "PROMOTING", "to": "LIVE", "trigger": "prod candidate verified -> flip" },
57
+ { "from": "PROMOTING", "to": "FAILED", "trigger": "candidate verify/migration fails; prod alias never moved" },
58
+ { "from": "QUEUED", "to": "STALE", "trigger": "newer deploy request passes its guard" },
59
+ { "from": "ANALYZING", "to": "STALE", "trigger": "newer deploy request passes its guard" },
60
+ { "from": "TRANSFORMING", "to": "STALE", "trigger": "newer deploy request; survives unless new commit touches transform-relevant files" },
61
+ { "from": "VALIDATING", "to": "STALE", "trigger": "newer deploy request passes its guard" },
62
+ { "from": "BUILDING", "to": "STALE", "trigger": "newer deploy request; let finish, skip flip" },
63
+ { "from": "VERIFYING", "to": "STALE", "trigger": "newer deploy request; let finish, skip flip" },
64
+ { "from": "GATING", "to": "STALE", "trigger": "newer deploy request passes its guard" }
65
+ ]
66
+ }