chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"layers": {
|
||||
"domain": {
|
||||
"root": "src/domain",
|
||||
"mayImport": ["src/domain"]
|
||||
},
|
||||
"application": {
|
||||
"root": "src/application",
|
||||
"mayImport": ["src/application", "src/domain", "src/contracts"]
|
||||
},
|
||||
"presentation": {
|
||||
"root": "src/presentation",
|
||||
"mayImport": ["src/presentation", "src/application", "src/domain", "src/contracts"]
|
||||
},
|
||||
"adapters": {
|
||||
"root": "src/adapters",
|
||||
"mayImport": ["src/adapters", "src/application", "src/domain", "src/contracts"]
|
||||
},
|
||||
"bootstrap": {
|
||||
"root": "src/bootstrap",
|
||||
"mayImport": ["src"]
|
||||
}
|
||||
},
|
||||
"forbidden": [
|
||||
["domain", "application"],
|
||||
["domain", "presentation"],
|
||||
["domain", "adapters"],
|
||||
["domain", "bootstrap"],
|
||||
["application", "presentation"],
|
||||
["application", "adapters"],
|
||||
["application", "bootstrap"],
|
||||
["presentation", "adapters"],
|
||||
["presentation", "bootstrap"],
|
||||
["adapters", "presentation"],
|
||||
["adapters", "bootstrap"]
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"families": {
|
||||
"api": {
|
||||
"additive": {
|
||||
"before": { "required": ["id"], "properties": { "id": {} } },
|
||||
"after": {
|
||||
"required": ["id"],
|
||||
"properties": { "id": {}, "displayName": {} }
|
||||
}
|
||||
},
|
||||
"breaking": {
|
||||
"before": { "required": ["id"], "properties": { "id": {} } },
|
||||
"after": {
|
||||
"required": ["id", "name"],
|
||||
"properties": { "id": {}, "name": {} }
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"additive": {
|
||||
"before": { "required": ["APP_ENV"], "properties": { "APP_ENV": {} } },
|
||||
"after": {
|
||||
"required": ["APP_ENV"],
|
||||
"properties": { "APP_ENV": {}, "OPTIONAL_FLAG": {} }
|
||||
}
|
||||
},
|
||||
"breaking": {
|
||||
"before": { "required": ["APP_ENV"], "properties": { "APP_ENV": {} } },
|
||||
"after": {
|
||||
"required": ["APP_ENV", "NEW_REQUIRED"],
|
||||
"properties": { "APP_ENV": {}, "NEW_REQUIRED": {} }
|
||||
}
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
"additive": {
|
||||
"before": { "properties": { "theme": {} } },
|
||||
"after": { "properties": { "theme": {}, "contrast": {} } }
|
||||
},
|
||||
"breaking": {
|
||||
"before": { "properties": { "theme": {} } },
|
||||
"after": { "properties": {} }
|
||||
}
|
||||
},
|
||||
"release": {
|
||||
"additive": {
|
||||
"before": { "required": ["buildId"], "properties": { "buildId": {} } },
|
||||
"after": {
|
||||
"required": ["buildId"],
|
||||
"properties": { "buildId": {}, "builtAt": {} }
|
||||
}
|
||||
},
|
||||
"breaking": {
|
||||
"before": { "required": ["buildId"], "properties": { "buildId": {} } },
|
||||
"after": {
|
||||
"required": ["buildId", "assetManifestHash"],
|
||||
"properties": { "buildId": {}, "assetManifestHash": {} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"snapshotDigest": "96b95ef1d50cce36e9fca8a98776a9e2e9e3a5dca24a6288ad83bf29c94aebd8",
|
||||
"owner": "frontend-platform",
|
||||
"reason": "Baseline canonical invalidation graph and topic-version contracts after FE-REG-QUERY retirement",
|
||||
"approvedAt": "2026-08-01T15:15:45.537Z"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"changes": [
|
||||
{
|
||||
"changeId": "FE-REG-ENV:API_CONTRACT_VERSION:*:removed",
|
||||
"versionBump": "Runtime Config V2 (CONFIG_SCHEMA_VERSION 2.0) removes the scalar API contract version.",
|
||||
"migration": "Release Manifest V2 contractSet replaces the scalar: the compiled external contract package set is canonicalized and digested, and boot compares it against the manifest.",
|
||||
"compatibilityWindow": "The V1 config and V1 manifest readers stay in place for one release. A V1 document may still carry the scalar; a V2 document is rejected if it does.",
|
||||
"rollback": "Restore the V1 writer in scripts/generate-build-manifest.ts and the scalar key in public/config.json; the V1 reader is still present.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-QUERY:*:*:removed",
|
||||
"versionBump": "Query invalidation composition moves from the legacy flat query registry to the bounded many-to-many invalidation graph.",
|
||||
"migration": "Installed feature contracts now contribute topics, namespace identities, edges, and separate wire versions; bootstrap validates and indexes those contributions before constructing coordinators.",
|
||||
"compatibilityWindow": "Cross-context envelopes remain opaque topic/version pairs and release cache epochs isolate mixed releases; no query keys or cached values cross contexts.",
|
||||
"rollback": "Restore the flat QUERY_REGISTRY composition and its coordinator adapter together with the prior governance entry.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-QUERY:$contract:allowedValues:contract-field-changed",
|
||||
"versionBump": "Cross-context invalidation wire protocol starts at version 1.",
|
||||
"migration": "Every installed query row declares invalidate-only; older tabs remain local-only.",
|
||||
"compatibilityWindow": "Mixed releases are isolated by release cacheEpoch and never exchange query keys or values.",
|
||||
"rollback": "Remove the coordinator composition and the two query registry fields.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-QUERY:$contract:breakingFields:contract-field-changed",
|
||||
"versionBump": "Cross-context invalidation wire protocol starts at version 1.",
|
||||
"migration": "Every installed query row declares its opaque topic and transport policy.",
|
||||
"compatibilityWindow": "A release cacheEpoch rejects messages from a different deployed contract.",
|
||||
"rollback": "Remove the coordinator composition and restore the prior query registry contract.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-QUERY:$contract:fieldTypes:contract-field-changed",
|
||||
"versionBump": "Cross-context invalidation wire protocol starts at version 1.",
|
||||
"migration": "The installed reference query row and all consumers were updated atomically.",
|
||||
"compatibilityWindow": "Old clients do not consume the new fields; new clients validate them before boot.",
|
||||
"rollback": "Restore the previous query registry field types and local-only invalidation.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-QUERY:$contract:requiredFields:contract-field-changed",
|
||||
"versionBump": "Cross-context invalidation wire protocol starts at version 1.",
|
||||
"migration": "Missing topics now fail registry validation instead of silently degrading at runtime.",
|
||||
"compatibilityWindow": "Only a fully built release consumes its own installed registry snapshot.",
|
||||
"rollback": "Remove the newly required fields and cross-context composition together.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-QUERY:$contract:uniqueFields:contract-field-changed",
|
||||
"versionBump": "Cross-context invalidation wire protocol starts at version 1.",
|
||||
"migration": "Existing query namespaces received unique registry-issued opaque topics.",
|
||||
"compatibilityWindow": "Topics are scoped by release cacheEpoch, so mixed releases cannot collide.",
|
||||
"rollback": "Drop invalidationTopic uniqueness after removing the transport consumer.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-RELEASE:apiContractVersion:compatibilityRole:field-changed",
|
||||
"versionBump": "The release token registry adds contractSetDigest and demotes apiContractVersion to a legacy V1 scalar.",
|
||||
"migration": "compareReleaseToRuntime no longer sources the scalar from Runtime Config; when neither side declares it there is nothing to compare and contractSet verification owns contract coherence.",
|
||||
"compatibilityWindow": "A V1 manifest still supplies apiContractVersion and is still compared against a V1 config that declares one.",
|
||||
"rollback": "Restore the previous compatibilityRole text and remove the contractSetDigest token together with the V2 manifest writer.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-STORAGE:$contract:allowedValues:contract-field-changed",
|
||||
"versionBump": "Web Storage value codec contracts start at version 1.",
|
||||
"migration": "Existing keys retain their physical schema version; values outside the selected codec are discarded.",
|
||||
"compatibilityWindow": "Valid existing color-scheme and opaque-string records remain readable.",
|
||||
"rollback": "Restore the previous registry contract; no physical key deletion is required.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-STORAGE:$contract:breakingFields:contract-field-changed",
|
||||
"versionBump": "Web Storage value codec contracts start at version 1.",
|
||||
"migration": "Each existing row received an explicit codec; executable migration was never consumed and is now forbidden.",
|
||||
"compatibilityWindow": "Schema-versioned physical keys and the envelope shape remain unchanged.",
|
||||
"rollback": "Remove valueCodec enforcement and restore the prior metadata declaration.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-STORAGE:$contract:fieldTypes:contract-field-changed",
|
||||
"versionBump": "Web Storage value codec contracts start at version 1.",
|
||||
"migration": "The dead function migration union was narrowed to the only implemented discard policy.",
|
||||
"compatibilityWindow": "All installed definitions already used discard before this contract change.",
|
||||
"rollback": "Restore the former type metadata without rewriting persisted records.",
|
||||
"owner": "frontend-platform"
|
||||
},
|
||||
{
|
||||
"changeId": "FE-REG-STORAGE:$contract:requiredFields:contract-field-changed",
|
||||
"versionBump": "Web Storage value codec contracts start at version 1.",
|
||||
"migration": "All installed storage rows now declare their closed value codec.",
|
||||
"compatibilityWindow": "Existing valid envelopes continue to decode; invalid values fail closed.",
|
||||
"rollback": "Remove the required codec field and runtime codec dispatch together.",
|
||||
"owner": "frontend-platform"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,513 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"sourceDirectories": [
|
||||
"src/application",
|
||||
"src/presentation",
|
||||
"src/domain"
|
||||
],
|
||||
"registries": [
|
||||
{
|
||||
"registryId": "FE-REG-ROUTE",
|
||||
"path": "src/features/installed-feature-contracts.ts",
|
||||
"exportName": "ROUTE_REGISTRY",
|
||||
"owner": "feature-frontend-routing-release-recovery-runtime",
|
||||
"keyField": "routeId",
|
||||
"requiredFields": [
|
||||
"routeId",
|
||||
"path",
|
||||
"paramsSchema",
|
||||
"searchSchema",
|
||||
"access",
|
||||
"loadingSurface",
|
||||
"errorSurface",
|
||||
"chunkId",
|
||||
"title",
|
||||
"navigationLabel",
|
||||
"navigationOrder"
|
||||
],
|
||||
"fieldTypes": {
|
||||
"routeId": "string",
|
||||
"path": "string",
|
||||
"paramsSchema": "string|null",
|
||||
"searchSchema": "string|null",
|
||||
"access": "string",
|
||||
"loadingSurface": "string",
|
||||
"errorSurface": "string",
|
||||
"chunkId": "string",
|
||||
"title": "string",
|
||||
"navigationLabel": "string|null",
|
||||
"navigationOrder": "integer|null"
|
||||
},
|
||||
"uniqueFields": ["routeId", "path", "chunkId"],
|
||||
"allowedValues": {
|
||||
"access": ["public", "session-required"],
|
||||
"paramsSchema": [null, "NotFoundSplat", "ReferenceResourceParams"],
|
||||
"searchSchema": [null, "ReferenceResourceListQuery"],
|
||||
"loadingSurface": [
|
||||
"app-shell",
|
||||
"example-page",
|
||||
"reference-resource-list",
|
||||
"reference-resource-detail",
|
||||
"reference-resource-form",
|
||||
"reference-resource-status",
|
||||
"none"
|
||||
],
|
||||
"errorSurface": [
|
||||
"route-boundary",
|
||||
"feature-boundary",
|
||||
"not-found"
|
||||
]
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"field": "routeId",
|
||||
"registryId": "FE-REG-ROUTE-RUNTIME",
|
||||
"targetField": "routeId"
|
||||
},
|
||||
{
|
||||
"field": "paramsSchema",
|
||||
"registryId": "FE-REG-SCHEMA",
|
||||
"targetField": "schemaId"
|
||||
},
|
||||
{
|
||||
"field": "searchSchema",
|
||||
"registryId": "FE-REG-SCHEMA",
|
||||
"targetField": "schemaId"
|
||||
}
|
||||
],
|
||||
"consumers": [
|
||||
{
|
||||
"path": "src/presentation/routes/app-router.tsx",
|
||||
"token": "ROUTE_REGISTRY"
|
||||
}
|
||||
],
|
||||
"breakingFields": [
|
||||
"routeId",
|
||||
"path",
|
||||
"paramsSchema",
|
||||
"searchSchema",
|
||||
"access",
|
||||
"chunkId"
|
||||
]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-ROUTE-RUNTIME",
|
||||
"path": "src/features/installed-feature-contracts.ts",
|
||||
"exportName": "ROUTE_RUNTIME_CONTRACT",
|
||||
"owner": "feature-frontend-routing-release-recovery-runtime",
|
||||
"keyField": "routeId",
|
||||
"requiredFields": [
|
||||
"routeId",
|
||||
"moduleId",
|
||||
"paramsCodec",
|
||||
"searchCodec"
|
||||
],
|
||||
"fieldTypes": {
|
||||
"routeId": "string",
|
||||
"moduleId": "string",
|
||||
"paramsCodec": "string",
|
||||
"searchCodec": "string"
|
||||
},
|
||||
"uniqueFields": ["routeId", "moduleId"],
|
||||
"references": [
|
||||
{
|
||||
"field": "routeId",
|
||||
"registryId": "FE-REG-ROUTE",
|
||||
"targetField": "routeId"
|
||||
},
|
||||
{
|
||||
"field": "paramsCodec",
|
||||
"registryId": "FE-REG-SCHEMA",
|
||||
"targetField": "schemaId"
|
||||
},
|
||||
{
|
||||
"field": "searchCodec",
|
||||
"registryId": "FE-REG-SCHEMA",
|
||||
"targetField": "schemaId"
|
||||
}
|
||||
],
|
||||
"consumers": [
|
||||
{
|
||||
"path": "src/presentation/routes/route-codecs.ts",
|
||||
"token": "ROUTE_RUNTIME_CONTRACT"
|
||||
}
|
||||
],
|
||||
"breakingFields": [
|
||||
"routeId",
|
||||
"moduleId",
|
||||
"paramsCodec",
|
||||
"searchCodec"
|
||||
]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-API",
|
||||
"path": "src/features/installed-feature-contracts.ts",
|
||||
"exportName": "API_OPERATIONS",
|
||||
"owner": "feature-frontend-api-client-response-envelope-contract",
|
||||
"keyField": "operationId",
|
||||
"requiredFields": [
|
||||
"method",
|
||||
"path",
|
||||
"operationId",
|
||||
"auth",
|
||||
"timeoutMs",
|
||||
"idempotency",
|
||||
"retry",
|
||||
"requestSource",
|
||||
"requestSchema",
|
||||
"responseSchema",
|
||||
"owner"
|
||||
],
|
||||
"fieldTypes": {
|
||||
"method": "string",
|
||||
"path": "string",
|
||||
"operationId": "string",
|
||||
"auth": "string",
|
||||
"timeoutMs": "integer|null",
|
||||
"idempotency": "string",
|
||||
"retry": "string",
|
||||
"requestSource": "string",
|
||||
"requestSchema": "string",
|
||||
"responseSchema": "string",
|
||||
"owner": "string"
|
||||
},
|
||||
"uniqueFields": ["operationId"],
|
||||
"allowedValues": {
|
||||
"method": ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
||||
"auth": ["none", "external-session"],
|
||||
"idempotency": ["safe", "keyed", "none"],
|
||||
"retry": ["runtime", "never"],
|
||||
"requestSource": ["none", "search", "body"]
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"field": "requestSchema",
|
||||
"registryId": "FE-REG-SCHEMA",
|
||||
"targetField": "schemaId"
|
||||
},
|
||||
{
|
||||
"field": "responseSchema",
|
||||
"registryId": "FE-REG-SCHEMA",
|
||||
"targetField": "schemaId"
|
||||
}
|
||||
],
|
||||
"consumerIdentityField": "operationId",
|
||||
"consumerDirectories": [
|
||||
"src/features/reference-feature/adapters",
|
||||
"src/features/reference-feature/application"
|
||||
],
|
||||
"breakingFields": [
|
||||
"method",
|
||||
"path",
|
||||
"operationId",
|
||||
"auth",
|
||||
"idempotency",
|
||||
"requestSource",
|
||||
"requestSchema",
|
||||
"responseSchema"
|
||||
]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-SCHEMA",
|
||||
"path": "src/features/installed-feature-contracts.ts",
|
||||
"exportName": "SCHEMA_REGISTRY",
|
||||
"owner": "feature-frontend-contract-schema-registry",
|
||||
"keyField": "schemaId",
|
||||
"requiredFields": ["schemaId", "boundary", "owner", "runtime"],
|
||||
"fieldTypes": {
|
||||
"schemaId": "string",
|
||||
"boundary": "string",
|
||||
"owner": "string",
|
||||
"runtime": "string"
|
||||
},
|
||||
"uniqueFields": ["schemaId"],
|
||||
"allowedValues": {
|
||||
"boundary": [
|
||||
"route-params",
|
||||
"route-search",
|
||||
"route-search-api-request",
|
||||
"api-request",
|
||||
"api-response"
|
||||
],
|
||||
"runtime": ["zod"]
|
||||
},
|
||||
"consumerIdentityField": "schemaId",
|
||||
"consumerDirectories": [
|
||||
"src/presentation/routes",
|
||||
"src/features/reference-feature/presentation",
|
||||
"src/features/reference-feature/contracts"
|
||||
],
|
||||
"breakingFields": ["schemaId", "boundary", "runtime"]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-ENV",
|
||||
"path": "src/contracts/env.ts",
|
||||
"exportName": "ENV_REGISTRY",
|
||||
"owner": "feature-frontend-env-runtime-config-contract",
|
||||
"requiredFields": ["phase", "classification", "required", "defaultValue"],
|
||||
"fieldTypes": {
|
||||
"phase": "string",
|
||||
"classification": "string",
|
||||
"required": "boolean",
|
||||
"defaultValue": "string|integer|boolean|null"
|
||||
},
|
||||
"allowedValues": {
|
||||
"phase": ["build", "runtime"],
|
||||
"classification": [
|
||||
"public",
|
||||
"public-sensitive",
|
||||
"public-metadata",
|
||||
"compile-time"
|
||||
]
|
||||
},
|
||||
"consumers": [
|
||||
{
|
||||
"path": "src/bootstrap/runtime-config-schema.ts",
|
||||
"token": "APP_ENV"
|
||||
},
|
||||
{
|
||||
"path": "src/contracts/env.ts",
|
||||
"token": "getBuildConfig"
|
||||
}
|
||||
],
|
||||
"breakingFields": ["phase", "classification", "required"]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-STORAGE",
|
||||
"path": "src/contracts/storage-keys.ts",
|
||||
"exportName": "STORAGE_REGISTRY",
|
||||
"owner": "feature-frontend-storage-registry-contract",
|
||||
"keyField": "logicalName",
|
||||
"requiredFields": [
|
||||
"logicalName",
|
||||
"physicalKey",
|
||||
"backend",
|
||||
"classification",
|
||||
"schemaVersion",
|
||||
"valueCodec",
|
||||
"ttl",
|
||||
"migration",
|
||||
"quotaFallback"
|
||||
],
|
||||
"fieldTypes": {
|
||||
"logicalName": "string",
|
||||
"physicalKey": "string",
|
||||
"backend": "string",
|
||||
"classification": "string",
|
||||
"schemaVersion": "integer",
|
||||
"valueCodec": "string",
|
||||
"ttl": "integer|string|null",
|
||||
"migration": "string",
|
||||
"quotaFallback": "string"
|
||||
},
|
||||
"uniqueFields": ["logicalName", "physicalKey"],
|
||||
"allowedValues": {
|
||||
"backend": [
|
||||
"memory",
|
||||
"sessionStorage",
|
||||
"localStorage",
|
||||
"disabled",
|
||||
"forbidden"
|
||||
],
|
||||
"classification": [
|
||||
"public-preference",
|
||||
"opaque-cache",
|
||||
"sensitive-forbidden"
|
||||
],
|
||||
"valueCodec": [
|
||||
"color-scheme-v1",
|
||||
"opaque-string-v1",
|
||||
"none"
|
||||
],
|
||||
"migration": ["discard"],
|
||||
"quotaFallback": ["memory", "no-persist", "feature-disable"]
|
||||
},
|
||||
"consumerIdentityField": "logicalName",
|
||||
"consumerDirectories": ["src", "tests"],
|
||||
"orphanExemptRows": ["QUERY_PERSISTENCE", "AUTH_TOKEN"],
|
||||
"breakingFields": [
|
||||
"logicalName",
|
||||
"physicalKey",
|
||||
"backend",
|
||||
"classification",
|
||||
"schemaVersion",
|
||||
"valueCodec",
|
||||
"migration"
|
||||
]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-ERROR",
|
||||
"path": "src/contracts/errors.ts",
|
||||
"exportName": "ERROR_REGISTRY",
|
||||
"owner": "feature-frontend-error-classification-boundary-contract",
|
||||
"keyField": "kind",
|
||||
"requiredFields": [
|
||||
"kind",
|
||||
"defaultRetryable",
|
||||
"severity",
|
||||
"userMessageKey",
|
||||
"action",
|
||||
"telemetryEvent",
|
||||
"redaction"
|
||||
],
|
||||
"fieldTypes": {
|
||||
"kind": "string",
|
||||
"defaultRetryable": "boolean",
|
||||
"severity": "string",
|
||||
"userMessageKey": "string",
|
||||
"action": "string",
|
||||
"telemetryEvent": "string",
|
||||
"redaction": "array"
|
||||
},
|
||||
"uniqueFields": ["kind"],
|
||||
"allowedValues": {
|
||||
"severity": ["info", "warning", "error"],
|
||||
"action": [
|
||||
"retry",
|
||||
"reauth",
|
||||
"navigate",
|
||||
"reload-once",
|
||||
"contact-support",
|
||||
"none"
|
||||
]
|
||||
},
|
||||
"consumers": [
|
||||
{
|
||||
"path": "src/adapters/http/client.ts",
|
||||
"token": "failure("
|
||||
}
|
||||
],
|
||||
"breakingFields": ["kind", "userMessageKey", "action", "telemetryEvent"]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-QUERY-INVALIDATION",
|
||||
"path": "src/features/installed-feature-contracts.ts",
|
||||
"exportName": "INVALIDATION_REGISTRY",
|
||||
"rowsPath": "edges",
|
||||
"rowKeyFields": [
|
||||
"topicId",
|
||||
"namespace.namespaceId",
|
||||
"namespace.namespaceVersion"
|
||||
],
|
||||
"owner": "feature-frontend-server-state-caching-contract",
|
||||
"requiredFields": [
|
||||
"topicId",
|
||||
"namespace.namespaceId",
|
||||
"namespace.namespaceVersion"
|
||||
],
|
||||
"fieldTypes": {
|
||||
"topicId": "string",
|
||||
"namespace.namespaceId": "string",
|
||||
"namespace.namespaceVersion": "integer"
|
||||
},
|
||||
"uniqueFieldSets": [
|
||||
[
|
||||
"topicId",
|
||||
"namespace.namespaceId",
|
||||
"namespace.namespaceVersion"
|
||||
]
|
||||
],
|
||||
"snapshotProjection": {
|
||||
"singletonRowKey": "invalidation-graph",
|
||||
"canonicalArrayKeyFields": {
|
||||
"topics": ["$value"],
|
||||
"namespaces": ["namespaceId", "namespaceVersion"],
|
||||
"edges": [
|
||||
"topicId",
|
||||
"namespace.namespaceId",
|
||||
"namespace.namespaceVersion"
|
||||
]
|
||||
}
|
||||
},
|
||||
"consumers": [
|
||||
{
|
||||
"path": "src/bootstrap/runtime-adapters.ts",
|
||||
"token": "indexInvalidationRegistry(INVALIDATION_REGISTRY)"
|
||||
}
|
||||
],
|
||||
"breakingFields": ["topics", "namespaces", "edges"]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-QUERY-INVALIDATION-TOPIC-VERSION",
|
||||
"path": "src/features/installed-feature-contracts.ts",
|
||||
"exportName": "INVALIDATION_TOPIC_VERSIONS",
|
||||
"rowKeyFields": ["topicId"],
|
||||
"owner": "feature-frontend-server-state-caching-contract",
|
||||
"requiredFields": ["topicId", "topicVersion"],
|
||||
"fieldTypes": {
|
||||
"topicId": "string",
|
||||
"topicVersion": "integer"
|
||||
},
|
||||
"uniqueFields": ["topicId"],
|
||||
"consumers": [
|
||||
{
|
||||
"path": "src/bootstrap/runtime-adapters.ts",
|
||||
"token": "indexInvalidationTopicVersions("
|
||||
}
|
||||
],
|
||||
"breakingFields": ["topicId", "topicVersion"]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-TELEMETRY",
|
||||
"path": "src/contracts/telemetry.ts",
|
||||
"exportName": "TELEMETRY_REGISTRY",
|
||||
"owner": "feature-frontend-diagnostics-telemetry-runtime",
|
||||
"keyField": "eventName",
|
||||
"requiredFields": [
|
||||
"eventName",
|
||||
"trigger",
|
||||
"requiredAttributes",
|
||||
"optionalAttributes",
|
||||
"forbiddenAttributes",
|
||||
"sampling",
|
||||
"delivery"
|
||||
],
|
||||
"fieldTypes": {
|
||||
"eventName": "string",
|
||||
"trigger": "string",
|
||||
"requiredAttributes": "array",
|
||||
"optionalAttributes": "array",
|
||||
"forbiddenAttributes": "array",
|
||||
"sampling": "string",
|
||||
"delivery": "string"
|
||||
},
|
||||
"uniqueFields": ["eventName"],
|
||||
"allowedValues": {
|
||||
"delivery": ["best-effort"]
|
||||
},
|
||||
"consumers": [
|
||||
{
|
||||
"path": "scripts/check-diagnostics.ts",
|
||||
"token": "TELEMETRY_REGISTRY"
|
||||
}
|
||||
],
|
||||
"breakingFields": [
|
||||
"eventName",
|
||||
"requiredAttributes",
|
||||
"forbiddenAttributes",
|
||||
"delivery"
|
||||
]
|
||||
},
|
||||
{
|
||||
"registryId": "FE-REG-RELEASE",
|
||||
"path": "src/contracts/release-tokens.ts",
|
||||
"exportName": "RELEASE_TOKEN_REGISTRY",
|
||||
"owner": "feature-frontend-release-cache-rollback-contract",
|
||||
"keyField": "token",
|
||||
"requiredFields": ["token", "source", "compatibilityRole"],
|
||||
"fieldTypes": {
|
||||
"token": "string",
|
||||
"source": "string",
|
||||
"compatibilityRole": "string"
|
||||
},
|
||||
"uniqueFields": ["token"],
|
||||
"consumers": [
|
||||
{
|
||||
"path": "src/bootstrap/load-release-manifest.ts",
|
||||
"token": "assetManifestHash"
|
||||
}
|
||||
],
|
||||
"breakingFields": ["token", "source", "compatibilityRole"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"surfaces": {
|
||||
"index": {
|
||||
"path": "/",
|
||||
"cacheControl": "no-cache",
|
||||
"contentTypes": ["text/html"],
|
||||
"securityHeaders": true
|
||||
},
|
||||
"runtimeConfig": {
|
||||
"path": "/config.json",
|
||||
"cacheControl": "no-store",
|
||||
"contentTypes": ["application/json"],
|
||||
"securityHeaders": true
|
||||
},
|
||||
"releaseManifest": {
|
||||
"path": "/release-manifest.json",
|
||||
"cacheControl": "no-store",
|
||||
"contentTypes": ["application/json"],
|
||||
"securityHeaders": true
|
||||
},
|
||||
"hashedAsset": {
|
||||
"pathPattern": "/assets/*",
|
||||
"cacheControl": "public, max-age=31536000, immutable",
|
||||
"contentTypes": ["text/javascript", "application/javascript"],
|
||||
"securityHeaders": false
|
||||
},
|
||||
"sourceMap": {
|
||||
"public": false
|
||||
},
|
||||
"serviceWorker": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"responses": {
|
||||
"index": {
|
||||
"cache-control": "no-cache",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-security-policy": "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self' https:; font-src 'self'; upgrade-insecure-requests",
|
||||
"strict-transport-security": "max-age=31536000; includeSubDomains",
|
||||
"x-frame-options": "DENY",
|
||||
"referrer-policy": "strict-origin-when-cross-origin",
|
||||
"x-content-type-options": "nosniff",
|
||||
"permissions-policy": "camera=(), microphone=(), geolocation=()"
|
||||
},
|
||||
"runtimeConfig": {
|
||||
"cache-control": "no-store",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-security-policy": "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self' https:; font-src 'self'; upgrade-insecure-requests",
|
||||
"strict-transport-security": "max-age=31536000; includeSubDomains",
|
||||
"x-frame-options": "DENY",
|
||||
"referrer-policy": "strict-origin-when-cross-origin",
|
||||
"x-content-type-options": "nosniff",
|
||||
"permissions-policy": "camera=(), microphone=(), geolocation=()"
|
||||
},
|
||||
"releaseManifest": {
|
||||
"cache-control": "no-store",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-security-policy": "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self' https:; font-src 'self'; upgrade-insecure-requests",
|
||||
"strict-transport-security": "max-age=31536000; includeSubDomains",
|
||||
"x-frame-options": "DENY",
|
||||
"referrer-policy": "strict-origin-when-cross-origin",
|
||||
"x-content-type-options": "nosniff",
|
||||
"permissions-policy": "camera=(), microphone=(), geolocation=()"
|
||||
},
|
||||
"hashedAsset": {
|
||||
"cache-control": "public, max-age=31536000, immutable",
|
||||
"content-type": "text/javascript; charset=utf-8"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"headers": {
|
||||
"Content-Security-Policy": "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self' https:; font-src 'self'; upgrade-insecure-requests",
|
||||
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
|
||||
"X-Frame-Options": "DENY",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"Permissions-Policy": "camera=(), microphone=(), geolocation=()"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"bundle": {
|
||||
"initialJsGzipBytes": 204800,
|
||||
"lazyChunkGzipBytes": 122880
|
||||
},
|
||||
"lab": {
|
||||
"lcpMs": 2500,
|
||||
"cls": 0.1,
|
||||
"namedInteractionMs": 200
|
||||
},
|
||||
"field": {
|
||||
"p75LcpMs": 2500,
|
||||
"p75Cls": 0.1,
|
||||
"p75InpMs": 200,
|
||||
"minimumEligibleSamples": null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"releaseId": "local-release",
|
||||
"environment": "replace-with-production",
|
||||
"source": {
|
||||
"system": "",
|
||||
"exportId": ""
|
||||
},
|
||||
"privacy": {
|
||||
"approved": false,
|
||||
"approvalRef": ""
|
||||
},
|
||||
"window": {
|
||||
"start": "2026-06-01T00:00:00Z",
|
||||
"end": "2026-06-29T00:00:00Z"
|
||||
},
|
||||
"thresholdDecision": {
|
||||
"status": "pending",
|
||||
"minimumEligibleSamples": null,
|
||||
"owner": "",
|
||||
"reviewedAt": "",
|
||||
"evidenceRef": ""
|
||||
},
|
||||
"samples": []
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
{
|
||||
"$schema": "../../schemas/config/frontend-capability-recipes.schema.json",
|
||||
"schemaVersion": 1,
|
||||
"decisionId": "VD-10",
|
||||
"defaultStatus": "NOT_INSTALLED",
|
||||
"productionRuntimeDependencies": [],
|
||||
"catalogOwner": "frontend-platform",
|
||||
"reviewOn": "project-capability-selection",
|
||||
"vendorPackagePatterns": [
|
||||
"@launchdarkly/*",
|
||||
"@sentry/*",
|
||||
"@opentelemetry/*",
|
||||
"@openapitools/openapi-generator-cli",
|
||||
"@reduxjs/toolkit",
|
||||
"@tanstack/react-virtual",
|
||||
"@uppy/*",
|
||||
"firebase",
|
||||
"idb",
|
||||
"react-window",
|
||||
"redux",
|
||||
"socket.io-client",
|
||||
"tus-js-client",
|
||||
"workbox-window",
|
||||
"xstate",
|
||||
"zustand"
|
||||
],
|
||||
"recipes": [
|
||||
{
|
||||
"id": "realtime",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"referenceRuntime": {
|
||||
"status": "AVAILABLE_NOT_COMPOSED",
|
||||
"coveredCapabilities": [
|
||||
"transport-independent event authority and recovery",
|
||||
"bounded reconnect ownership",
|
||||
"fetch-stream SSE",
|
||||
"bounded polling",
|
||||
"single-writer live and polling handoff",
|
||||
"WebSocket closed protocol",
|
||||
"Web Push window and Service Worker control"
|
||||
],
|
||||
"sourceRoots": [
|
||||
"src/application/ports/realtime",
|
||||
"src/application/ports/out/web-push-control.ts",
|
||||
"src/application/policies/bounded-polling.ts",
|
||||
"src/contracts/realtime-events.ts",
|
||||
"src/contracts/realtime-streams.ts",
|
||||
"src/contracts/web-push.ts",
|
||||
"src/adapters/realtime",
|
||||
"src/adapters/web-push"
|
||||
],
|
||||
"conformanceScripts": [
|
||||
"test:unit",
|
||||
"check:realtime-boundaries",
|
||||
"check:realtime-boundaries:fixture",
|
||||
"check:optional-recipes",
|
||||
"test:realtime-removal"
|
||||
],
|
||||
"productionComposition": false
|
||||
},
|
||||
"trigger": "The backend exposes ordered push events with a documented resume and authorization protocol.",
|
||||
"forbiddenWhen": ["Polling satisfies the measured freshness requirement.", "Event ordering and reconnect ownership are undefined."],
|
||||
"boundary": "transport-independent event authority plus separately owned SSE, WebSocket, bounded polling and Web Push adapters",
|
||||
"port": "RealtimeEventAuthority / WebPushControlPort / transport-specific connection and polling factories",
|
||||
"fake": "Deterministic event authority, transport facade, clock, repository and Service Worker test doubles",
|
||||
"failureKinds": ["abort", "disconnect-or-timeout", "protocol-or-mapping-mismatch", "duplicate-or-stale", "sequence-gap-or-cursor-expiry", "queue-overflow", "scope-fenced", "poll-budget-exhausted", "push-permission-or-subscription-failure"],
|
||||
"lifecycleMethods": ["close-or-dispose", "unsubscribe", "cancel-via-AbortSignal", "bounded-poll-lease", "revoke-push-association"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Validate every event envelope and closed transport frame before application effects.", "Bind stream state to the current opaque scope generation and advance checkpoints only after committed effects or authoritative recovery.", "Use fixed same-origin endpoints and an exact WebSocket subprotocol; never place credentials, cursors, subscription material or scope bindings in URLs or telemetry.", "Treat Web Push as a notification hint, fence registration and revocation with one durable compare-and-swap control record, and allow only registry-owned notification and route intents.", "Keep every queue, parser, reconnect, poll, notification and storage operation bounded and abortable."],
|
||||
"bundleBudgetGzipBytes": 40000,
|
||||
"fallback": "Bounded polling or explicitly stale UI.",
|
||||
"removal": ["Disable admission and close active readers, sockets, poll leases and worker handlers.", "Revoke and purge only the owned Web Push association and notification state.", "Remove composition, registries, adapters and any selected vendor dependency.", "Run realtime boundary, runtime-removal and production-bundle gates."],
|
||||
"serverStatePolicy": "query-cache-owned"
|
||||
},
|
||||
{
|
||||
"id": "offline-indexeddb",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"referenceRuntime": {
|
||||
"status": "AVAILABLE_NOT_COMPOSED",
|
||||
"coveredCapabilities": [
|
||||
"IndexedDB",
|
||||
"OPFS",
|
||||
"StorageManager estimate/persistence"
|
||||
],
|
||||
"sourceRoots": [
|
||||
"src/application/ports/browser-file-storage/indexeddb-port.ts",
|
||||
"src/application/ports/browser-file-storage/opfs-ports.ts",
|
||||
"src/application/ports/browser-file-storage/storage-durability-port.ts",
|
||||
"src/adapters/browser-file-storage",
|
||||
"src/adapters/storage/indexeddb",
|
||||
"src/adapters/storage/opfs"
|
||||
],
|
||||
"conformanceScripts": [
|
||||
"test:unit",
|
||||
"test:browser-capabilities",
|
||||
"verify:browser-capability-evidence",
|
||||
"check:browser-file-storage-boundaries",
|
||||
"check:optional-recipes",
|
||||
"test:browser-file-storage-removal"
|
||||
],
|
||||
"productionComposition": false
|
||||
},
|
||||
"trigger": "A product requirement needs indexed offline records, an unsynced command queue, or a large local binary sidecar beyond small public preferences.",
|
||||
"forbiddenWhen": ["The data contains credentials.", "The browser would connect directly to a server database or object store.", "A normal HTTP cache is sufficient.", "Partition, retention, quota and recovery ownership are undefined."],
|
||||
"boundary": "feature-specific async repository with registry-issued opaque dataset scope and immutable full-policy binding, plus an OPFS large-object sidecar whose logical commit authority and bidirectional scope binding are owned by an IndexedDB journal",
|
||||
"port": "IndexedDbRepositoryPort / IndexedDbMaintenancePort / DurableObjectStorePort / DurableObjectMaintenancePort / StorageDurabilityPort",
|
||||
"fake": "MemoryStructuredOfflineStore / MemoryDurableObjectStore / MemoryStorageDurabilityAdapter",
|
||||
"failureKinds": ["open-blocked", "versionchange", "quota", "corruption", "migration-rollback", "revision-conflict", "storage-eviction", "partial-object-write", "dataset-binding-mismatch", "dataset-budget-exceeded", "lifecycle-authorization-denied", "expired-resource"],
|
||||
"lifecycleMethods": ["close", "cancel-via-AbortSignal", "enforce-bounded-lifecycle-batch", "prune-expired-receipts", "reconcile", "enforcePolicies-with-composition-authority"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Classify every persisted field and binary namespace.", "Encrypting in the same client is not a credential protection boundary.", "Keep database schema and record codec versions separate.", "Derive the physical IndexedDB name only from registry-issued authority, namespace and partition tokens; readable namespace, business and account IDs are forbidden.", "Persist and revalidate an immutable scope plus full BrowserStoragePolicy binding during upgrade, post-open and maintenance; missing or mismatched existing bindings fail closed.", "Use the actual IndexedDB wire split: StoredRecord contains only key, codecVersion, revision and payload; writtenAtEpochMs, synchronization, measuredBytes and eligibleAtEpochMs belong to the retention sidecar, while idempotency receipts and governance binding/budget use separate stores.", "Include every store registered in lifecycleMetadataStores in bounded full-partition purge while retaining immutable governance identity.", "Measure conservative logical bytes through the codec and atomically enforce dataset usedBytes plus receiptCount with record, lifecycle and migration writes.", "Enforce TTL before sweep visibility, delete UNTIL_SYNCED only after explicit confirmation, and require a composition-authorized short-lived proof for every deleting IndexedDB lifecycle batch.", "Bound idempotency receipt retention to 31 days and configured count to the implementation ceiling of 1000000; bound migration to old-writer-drained batches no larger than 500 rows or 30000ms.", "Bind OPFS readable and physical scopes in both directions and use only /ca-frontend-opfs-v1/authorities/<authorityToken>/<namespaceToken>/<partitionToken>/ for physical dataset layout.", "For OPFS LOGOUT, UNTIL_SYNCED and ACCOUNT_DELETION maintenance, composition must provide both requestMaintenanceAuthority and consumeMaintenanceAuthority; issue a fresh proof bound to the exact frozen reason, scope and policy for at most five minutes, then atomically consume it to reject replay.", "Never expose an OPFS authority proof through the application request, persistence, diagnostics or telemetry.", "Never place user file names or identifiers in OPFS paths or diagnostics."],
|
||||
"bundleBudgetGzipBytes": 36000,
|
||||
"fallback": "Read-only or online-only query path; OPFS may degrade to a size-capped IndexedDB Blob only when the product policy approves it.",
|
||||
"removal": ["Stop writes and background migration.", "Reconcile or export unsynced data, then purge only governance-bound owned partitions and OPFS namespaces through authorized bounded lifecycle operations.", "Close all database, channel, worker and file handles.", "Remove repository composition and dependency."],
|
||||
"serverStatePolicy": "reference-or-command-only"
|
||||
},
|
||||
{
|
||||
"id": "service-worker-pwa",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"referenceRuntime": {
|
||||
"status": "AVAILABLE_NOT_COMPOSED",
|
||||
"coveredCapabilities": [
|
||||
"Cache Storage public-response administration"
|
||||
],
|
||||
"sourceRoots": [
|
||||
"src/application/ports/browser-file-storage/cache-storage-ports.ts",
|
||||
"src/adapters/browser-file-storage",
|
||||
"src/adapters/cache-storage"
|
||||
],
|
||||
"conformanceScripts": [
|
||||
"test:unit",
|
||||
"test:browser-capabilities",
|
||||
"verify:browser-capability-evidence",
|
||||
"check:browser-file-storage-boundaries",
|
||||
"check:optional-recipes",
|
||||
"test:browser-file-storage-removal"
|
||||
],
|
||||
"productionComposition": false
|
||||
},
|
||||
"trigger": "Installability, a measured offline-shell requirement, or an explicitly owned public HTTP representation cache is approved.",
|
||||
"forbiddenWhen": ["Hosting cache and worker cache ownership conflict.", "Update and rollback UX is undefined.", "Authenticated, private, opaque or personal responses would be cached.", "Cache freshness, byte and entry limits are undefined."],
|
||||
"boundary": "bootstrap update controller plus platform-local public Request/Response cache administration",
|
||||
"port": "ServiceWorkerUpdatePort / PublicResponseCacheAdmin recipe / PublicResponseCachePort / PublicResponseCacheAdminPort reference runtime",
|
||||
"fake": "FakeServiceWorkerUpdateAdapter / MemoryPublicResponseCache",
|
||||
"failureKinds": ["stale-worker", "update-loop", "offline-fallback", "incomplete-candidate", "integrity-mismatch", "cache-policy-rejection", "quota"],
|
||||
"lifecycleMethods": ["unregister", "rollback", "delete-owned-caches"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Cache only explicit same-origin public GET representations.", "Never cache authenticated, cookie-dependent, private, no-store, opaque or personal responses.", "Bind candidate cache names to release identity and a canonical manifest digest that includes normalized expectedContentType, exact request identity, expected byte length and integrity digest.", "Reject a response whose normalized Content-Type differs from manifest expectedContentType even when body integrity matches.", "Derive cleanup retention only from the verified active pointer and composition retainedPreviousReleaseCount; cleanup callers cannot submit cache names, release registry IDs or any retain set.", "Read active-pointer and release-marker control JSON through a strict UTF-8 stream capped at exactly 2 MiB (2097152 bytes), cancel on overflow and fail closed before parsing oversized metadata.", "Keep exact query and Vary semantics; ignoreSearch and ignoreVary are forbidden.", "Fail closed on malformed update metadata or integrity mismatch."],
|
||||
"bundleBudgetGzipBytes": 10000,
|
||||
"fallback": "Normal network application with hosting cache headers.",
|
||||
"removal": ["Deploy an unregister migration.", "Delete only parsed, owned cache namespaces after old controlled clients drain.", "Remove worker registration, cache metadata and manifest."],
|
||||
"serverStatePolicy": "network-cache-policy-only"
|
||||
},
|
||||
{
|
||||
"id": "file-transfer",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"referenceRuntime": {
|
||||
"status": "AVAILABLE_NOT_COMPOSED",
|
||||
"coveredCapabilities": [
|
||||
"File",
|
||||
"Blob",
|
||||
"native file input",
|
||||
"system file picker",
|
||||
"object URL preview",
|
||||
"download delivery",
|
||||
"presigned URL capability",
|
||||
"bounded streaming download",
|
||||
"multipart/resumable upload",
|
||||
"durable non-secret upload checkpoint",
|
||||
"Image CDN responsive delivery"
|
||||
],
|
||||
"sourceRoots": [
|
||||
"src/application/ports/browser-file-storage/file.ts",
|
||||
"src/application/ports/browser-transfer",
|
||||
"src/adapters/browser-file-storage",
|
||||
"src/adapters/browser-files",
|
||||
"src/adapters/browser-transfer"
|
||||
],
|
||||
"conformanceScripts": [
|
||||
"test:unit",
|
||||
"test:browser-capabilities",
|
||||
"verify:browser-capability-evidence",
|
||||
"check:browser-file-storage-boundaries",
|
||||
"check:optional-recipes",
|
||||
"test:browser-file-storage-removal"
|
||||
],
|
||||
"productionComposition": false
|
||||
},
|
||||
"trigger": "The product selects, inspects, previews, uploads, downloads or delivers image renditions with bounded memory, resumability, cancellation, expiry and integrity requirements.",
|
||||
"forbiddenWhen": ["Allowed count, byte, extension, MIME and content-signature policy is missing.", "The BFF does not own authorization, short-lived capability issuance, upload session reconciliation, quarantine and orphan cleanup.", "Long-lived credentials, presigned URLs or signed headers would enter persistence, application state or telemetry.", "Native File, Blob, object URL or file-system handles would cross into domain state or persistence.", "Large downloads would be returned as one in-memory byte array or Blob.", "Image callers could submit arbitrary CDN source URLs or transform parameters."],
|
||||
"boundary": "BFF-owned transfer control plane plus adapter-owned browser/object-storage data plane; presentation receives opaque file/image references, registered policies and bounded result streams only",
|
||||
"port": "FilePickerPort / FileContentPort / TransientPreviewPort / DownloadDeliveryPort / PresignedDownloadSourcePort / PresignedUploadPartPort / ResumableUploadPort / ImageCdnPresentationPort",
|
||||
"fake": "Memory file/preview/download adapters plus injected deterministic capability, upload-control-plane, part-executor and image-verifier test doubles",
|
||||
"failureKinds": ["dismissed", "permission-denied", "count-or-size-rejection", "type-or-signature-rejection", "file-changed", "abort", "integrity-failure", "partial-save", "expired-or-revoked-capability", "part-or-session-conflict", "checkpoint-conflict", "quarantined", "image-policy-rejection"],
|
||||
"lifecycleMethods": ["release-file-ref", "release-or-dispose-preview-leases", "cancel-via-AbortSignal", "reconcile-or-explicitly-abort-upload", "close-checkpoint-store", "dispose-capability-and-image-runtime"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Treat file name, extension, MIME and lastModified as untrusted metadata.", "Resolve only exact composition-issued file and image policy object identities; callers cannot raise byte, candidate, pixel, quality, format, lifetime or origin ceilings.", "Use opaque file references and verification receipts bound to an inspected immutable file snapshot and the exact registered profile; reject replay through another profile even when an inspection rule ID matches.", "Treat presigned URLs as bearer capabilities; bind exact method, resource or upload part, offset, length, media type, checksum, origin, path, query, headers and expiry in an in-memory identity vault.", "Use credentials omit, redirect error, no-referrer and no-store for direct data-plane fetch; never persist or observe URL, query, signed header, capability, file name or raw backend message, and never emit digest, raw ETag or receipt values to diagnostics or telemetry.", "A strict account-partitioned upload checkpoint may persist only the protocol-defined SHA-256 file fingerprint, per-part checksum and bounded opaque non-authorizing part receipt token required for server reconciliation; no bearer token or raw signed capability is allowed.", "Persist only strict non-authorizing upload checkpoints and reconcile them with server-authoritative status and re-hashed local parts before completion.", "Require a synchronous server-issued browser-managed download capability whose receipt exactly equals the caller's branded capability receipt and whose resource, media type, safe extension, maximum bytes, optional digest and expiry all match before handoff.", "Expose File, OPFS, Cache and transfer byte streams only as chunk-level closed Results; stop after the first failure, cancel native readers and never throw a raw native exception across the port.", "Accept Image CDN assets only through immutable allowlisted or signature-verified descriptors and registered preset identities; reject active formats, arbitrary transforms, pixel/decode-budget overflow and unsafe cache policy.", "Upload completion remains QUARANTINED until backend scan and promotion; client capability checks are not an authorization boundary.", "Active content preview requires isolation or download-only treatment."],
|
||||
"bundleBudgetGzipBytes": 52000,
|
||||
"fallback": "Accessible native file input, same-origin authorized server upload/download and a single bounded server-selected image rendition; generated artifacts above the buffer budget move to server-side generation.",
|
||||
"removal": ["Stop new capability and upload-session issuance, then cancel active reads and transfers.", "Reconcile or explicitly abort active multipart sessions and let backend TTL cleanup remove ambiguous orphans.", "Remove non-secret checkpoints according to account and retention policy.", "Release file references, revoke preview object-URL leases and dispose file, capability and image runtimes.", "Remove transfer/image feature facades and composition, then prove browser-transfer sources are absent from the production module inventory."],
|
||||
"serverStatePolicy": "query-cache-metadata-only"
|
||||
},
|
||||
{
|
||||
"id": "generated-api",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"trigger": "A versioned backend contract justifies generated transport code.",
|
||||
"forbiddenWhen": ["Generated DTOs would escape into domain or presentation.", "Contract drift cannot block CI."],
|
||||
"boundary": "generated client wrapped by a feature gateway facade and mapper",
|
||||
"port": "GeneratedApiFacade",
|
||||
"fake": "FakeGeneratedApiAdapter",
|
||||
"failureKinds": ["contract-drift", "unsupported-field"],
|
||||
"lifecycleMethods": ["cancel-via-AbortSignal"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Generate from an authenticated source.", "Review generator execution and output.", "Do not log request bodies."],
|
||||
"bundleBudgetGzipBytes": 16000,
|
||||
"fallback": "Existing typed request builder and runtime response schema.",
|
||||
"removal": ["Restore handwritten gateway.", "Remove generated output and generator.", "Verify DTOs do not remain in public types."],
|
||||
"serverStatePolicy": "query-cache-owned"
|
||||
},
|
||||
{
|
||||
"id": "feature-flag",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"trigger": "A staged rollout or kill switch has a named owner, default and stale policy.",
|
||||
"forbiddenWhen": ["A flag is used as authorization.", "Unknown and unavailable behavior is undefined."],
|
||||
"boundary": "application feature policy output port",
|
||||
"port": "FeatureFlagPort",
|
||||
"fake": "FakeFeatureFlagAdapter",
|
||||
"failureKinds": ["provider-unavailable", "unknown-flag", "stale-value"],
|
||||
"lifecycleMethods": ["dispose-provider-if-installed"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Flags are hints, never access control.", "Minimize targeting attributes.", "Apply consent rules to personal attributes."],
|
||||
"bundleBudgetGzipBytes": 10000,
|
||||
"fallback": "Typed local default with an explicit stale decision.",
|
||||
"removal": ["Resolve the rollout permanently.", "Delete flag key and branches.", "Remove provider composition and dependency."],
|
||||
"serverStatePolicy": "policy-cache-only"
|
||||
},
|
||||
{
|
||||
"id": "web-worker",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"trigger": "Profiling shows CPU work blocking the main thread beyond the performance budget.",
|
||||
"forbiddenWhen": ["The task is primarily network I/O.", "Cancellation and stale-result ownership are undefined."],
|
||||
"boundary": "request/result/cancel output port with a validated message adapter",
|
||||
"port": "WorkerTaskPort",
|
||||
"fake": "FakeWorkerTaskAdapter",
|
||||
"failureKinds": ["crash", "stale-result", "transfer-failure"],
|
||||
"lifecycleMethods": ["cancel", "dispose"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Validate worker messages.", "Do not send credentials.", "Bound transferred data and worker count."],
|
||||
"bundleBudgetGzipBytes": 14000,
|
||||
"fallback": "Chunked or deferred main-thread execution within a measured limit.",
|
||||
"removal": ["Stop and dispose workers.", "Restore synchronous facade implementation.", "Remove worker entry and chunk."],
|
||||
"serverStatePolicy": "no-server-state"
|
||||
},
|
||||
{
|
||||
"id": "multi-tab",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"trigger": "A documented workflow must synchronize non-sensitive events across tabs.",
|
||||
"forbiddenWhen": ["The server is the correct conflict authority.", "Event version and source identity are undefined."],
|
||||
"boundary": "versioned browser event output/input adapter",
|
||||
"port": "MultiTabPort",
|
||||
"fake": "FakeMultiTabAdapter",
|
||||
"failureKinds": ["self-echo", "duplicate", "conflict"],
|
||||
"lifecycleMethods": ["unsubscribe", "close"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Broadcast no credentials or personal payload.", "Validate versions.", "Treat events as hints rather than authorization."],
|
||||
"bundleBudgetGzipBytes": 4000,
|
||||
"fallback": "Refresh from the authoritative server on focus.",
|
||||
"removal": ["Close channels.", "Remove event registry entries.", "Restore focus-based refresh."],
|
||||
"serverStatePolicy": "invalidation-only"
|
||||
},
|
||||
{
|
||||
"id": "browser-permission",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"trigger": "A user-initiated flow requires clipboard, notification or media access.",
|
||||
"forbiddenWhen": ["Permission would be requested at boot.", "Denied, dismissed and unsupported UX are not designed."],
|
||||
"boundary": "presentation input action through a browser capability output port",
|
||||
"port": "BrowserPermissionPort",
|
||||
"fake": "FakeBrowserPermissionAdapter",
|
||||
"failureKinds": ["denied", "dismissed", "unsupported"],
|
||||
"lifecycleMethods": ["stop-media-tracks-if-opened"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Require an explicit user gesture.", "Minimize requested scope.", "Do not persist permission as authorization."],
|
||||
"bundleBudgetGzipBytes": 3000,
|
||||
"fallback": "Manual input or copy/download instruction.",
|
||||
"removal": ["Stop acquired resources.", "Remove permission action and adapter.", "Retest denied-path accessibility."],
|
||||
"serverStatePolicy": "no-server-state"
|
||||
},
|
||||
{
|
||||
"id": "client-workflow",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"trigger": "A measured cross-page client-only workflow cannot be represented by URL, local state, context or query cache.",
|
||||
"forbiddenWhen": ["The store would duplicate server response collections.", "A library is selected before state ownership is documented.", "Zustand and Redux Toolkit would both be installed."],
|
||||
"boundary": "workflow-specific local facade; vendor types remain in its adapter",
|
||||
"port": "ClientWorkflowPort",
|
||||
"fake": "FakeClientWorkflowAdapter",
|
||||
"failureKinds": ["reset", "version-mismatch", "server-state-duplication"],
|
||||
"lifecycleMethods": ["unsubscribe", "reset"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Persist only explicitly classified workflow fields.", "Never persist credentials.", "Define logout and version reset."],
|
||||
"bundleBudgetGzipBytes": 9000,
|
||||
"fallback": "URL, component state, context and TanStack Query ownership.",
|
||||
"removal": ["Move remaining state to its natural owner.", "Remove facade and one selected store dependency.", "Verify logout/reset."],
|
||||
"serverStatePolicy": "reference-only"
|
||||
},
|
||||
{
|
||||
"id": "large-data-ui",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"trigger": "Production-like profiling proves a list or grid exceeds interaction and rendering budgets.",
|
||||
"forbiddenWhen": ["Pagination solves the scale requirement.", "Keyboard and screen-reader focus behavior is undefined."],
|
||||
"boundary": "presentation facade around virtualizer or data-grid behavior",
|
||||
"port": "LargeDataUiFacade",
|
||||
"fake": "FakeLargeDataUiAdapter",
|
||||
"failureKinds": ["focus-loss", "stale-row", "scale-limit"],
|
||||
"lifecycleMethods": ["dispose-observers-if-installed"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Render only authorized rows.", "Do not expose hidden row data to telemetry.", "Preserve accessible row identity."],
|
||||
"bundleBudgetGzipBytes": 30000,
|
||||
"fallback": "Accessible pagination and bounded result sets.",
|
||||
"removal": ["Restore paginated primitive.", "Remove facade adapter and dependency.", "Run keyboard and performance evidence."],
|
||||
"serverStatePolicy": "query-cache-owned"
|
||||
},
|
||||
{
|
||||
"id": "analytics-error-sink",
|
||||
"status": "RECIPE_AVAILABLE",
|
||||
"trigger": "A production provider, consent policy, retention owner and event registry are approved.",
|
||||
"forbiddenWhen": ["Consent and essential diagnostics are not separated.", "Arbitrary message or attribute keys can bypass redaction."],
|
||||
"boundary": "closed diagnostics/analytics port with provider adapter",
|
||||
"port": "AnalyticsErrorSink",
|
||||
"fake": "RecordingAnalyticsAdapter",
|
||||
"failureKinds": ["consent-denied", "queue-full", "provider-unavailable"],
|
||||
"lifecycleMethods": ["flush", "dispose"],
|
||||
"owner": "project-owner-required",
|
||||
"securityPrivacy": ["Allowlist events and attributes.", "Redact before queueing.", "Apply consent, sampling and retention policy."],
|
||||
"bundleBudgetGzipBytes": 25000,
|
||||
"fallback": "Existing bounded local diagnostics and best-effort telemetry port.",
|
||||
"removal": ["Disable provider delivery.", "Flush or discard by policy.", "Remove adapter, runtime config and dependency."],
|
||||
"serverStatePolicy": "no-server-state"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"fixtures": [
|
||||
{
|
||||
"name": "coherent-release",
|
||||
"expectedCompatible": true,
|
||||
"frontend": {
|
||||
"buildId": "build-a",
|
||||
"configSchemaVersion": "1.0",
|
||||
"apiContractVersion": "1.0",
|
||||
"assetManifestHash": "assets-a",
|
||||
"releaseId": "release-a"
|
||||
},
|
||||
"runtime": {
|
||||
"buildId": "build-a",
|
||||
"configSchemaVersion": "1.1",
|
||||
"apiContractVersion": "1.2",
|
||||
"assetManifestHash": "assets-a",
|
||||
"releaseId": "release-a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "mixed-html-and-assets",
|
||||
"expectedCompatible": false,
|
||||
"frontend": {
|
||||
"buildId": "build-a",
|
||||
"configSchemaVersion": "1.0",
|
||||
"apiContractVersion": "1.0",
|
||||
"assetManifestHash": "assets-a",
|
||||
"releaseId": "release-a"
|
||||
},
|
||||
"runtime": {
|
||||
"buildId": "build-b",
|
||||
"configSchemaVersion": "1.0",
|
||||
"apiContractVersion": "1.0",
|
||||
"assetManifestHash": "assets-b",
|
||||
"releaseId": "release-b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "incompatible-runtime-config",
|
||||
"expectedCompatible": false,
|
||||
"frontend": {
|
||||
"buildId": "build-a",
|
||||
"configSchemaVersion": "1.0",
|
||||
"apiContractVersion": "1.0",
|
||||
"assetManifestHash": "assets-a",
|
||||
"releaseId": "release-a"
|
||||
},
|
||||
"runtime": {
|
||||
"buildId": "build-a",
|
||||
"configSchemaVersion": "2.0",
|
||||
"apiContractVersion": "1.0",
|
||||
"assetManifestHash": "assets-a",
|
||||
"releaseId": "release-a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "incompatible-api-contract",
|
||||
"expectedCompatible": false,
|
||||
"frontend": {
|
||||
"buildId": "build-a",
|
||||
"configSchemaVersion": "1.0",
|
||||
"apiContractVersion": "1.0",
|
||||
"assetManifestHash": "assets-a",
|
||||
"releaseId": "release-a"
|
||||
},
|
||||
"runtime": {
|
||||
"buildId": "build-a",
|
||||
"configSchemaVersion": "1.0",
|
||||
"apiContractVersion": "2.0",
|
||||
"assetManifestHash": "assets-a",
|
||||
"releaseId": "release-a"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"runbooks": {
|
||||
"FE-RB-001": {
|
||||
"title": "Boot configuration failure",
|
||||
"gateId": "FE-GATE-021",
|
||||
"triggerKinds": ["BOOT_CONFIG_FAILURE"],
|
||||
"containment": "stop product route mount, show the safe support shell, and refetch at most once",
|
||||
"window": "owner triage planned-default 5m",
|
||||
"escalation": ["env-config owner", "release owner"],
|
||||
"recoveryEvidence": [
|
||||
"clean-session boot",
|
||||
"product root mount",
|
||||
"config validation",
|
||||
"no repeated boot error"
|
||||
],
|
||||
"negativeFixture": "a valid config followed by an injected mount failure must fail recovery"
|
||||
},
|
||||
"FE-RB-002": {
|
||||
"title": "Chunk, manifest, or deployment mismatch",
|
||||
"gateId": "FE-GATE-022",
|
||||
"triggerKinds": [
|
||||
"CHUNK_LOAD_FAILURE",
|
||||
"RELEASE_MANIFEST_FAILURE",
|
||||
"DEPLOY_MISMATCH"
|
||||
],
|
||||
"containment": "warn for dirty state, fetch manifest no-store once, and allow one guarded reload",
|
||||
"window": "release owner triage planned-default 5m",
|
||||
"escalation": ["release-cache owner", "hosting/CDN owner"],
|
||||
"recoveryEvidence": [
|
||||
"entry and lazy assets reachable",
|
||||
"release tuple coherent",
|
||||
"second reload blocked",
|
||||
"critical route smoke"
|
||||
],
|
||||
"negativeFixture": "a second failure for the same release pair must not reload"
|
||||
},
|
||||
"FE-RB-003": {
|
||||
"title": "Backend API degradation",
|
||||
"gateId": "FE-GATE-023",
|
||||
"triggerKinds": [
|
||||
"TERMINAL_NETWORK_RATE",
|
||||
"REQUEST_TIMEOUT_RATE",
|
||||
"SERVER_FAILURE_RATE",
|
||||
"SCHEMA_MISMATCH"
|
||||
],
|
||||
"containment": "do not expand retry caps, serve safe stale reads, and never retry an unkeyed mutation",
|
||||
"window": "rolling 5m trigger; first classification planned-default 10m",
|
||||
"escalation": [
|
||||
"api-client owner",
|
||||
"backend operation owner",
|
||||
"release compatibility owner"
|
||||
],
|
||||
"recoveryEvidence": [
|
||||
"terminal failure rate at baseline",
|
||||
"no retry amplification",
|
||||
"critical read/write smoke",
|
||||
"schema fixtures"
|
||||
],
|
||||
"negativeFixture": "an unkeyed POST receiving 503 must not retry"
|
||||
},
|
||||
"FE-RB-004": {
|
||||
"title": "Telemetry sink failure",
|
||||
"gateId": "FE-GATE-024",
|
||||
"triggerKinds": ["TELEMETRY_FAILURE"],
|
||||
"containment": "keep product flow available, bound the queue, and never report recursively to the failing sink",
|
||||
"window": "platform triage planned-default 15m",
|
||||
"escalation": ["observability owner", "telemetry platform owner"],
|
||||
"recoveryEvidence": [
|
||||
"product flow unaffected",
|
||||
"delivery self-check",
|
||||
"queue drained within bound",
|
||||
"forbidden attributes absent"
|
||||
],
|
||||
"negativeFixture": "raw URL and query data must be removed from telemetry"
|
||||
},
|
||||
"FE-RB-005": {
|
||||
"title": "Coherent release rollback",
|
||||
"gateId": "FE-GATE-025",
|
||||
"triggerKinds": ["RELEASE_BLOCKING_DEFECT"],
|
||||
"containment": "select a prior immutable tuple, verify asset/config/API compatibility, atomically switch, and smoke",
|
||||
"window": "provider recovery target TBD",
|
||||
"escalation": ["release-cache owner", "release approver/hosting owner"],
|
||||
"recoveryEvidence": [
|
||||
"compatibility gate",
|
||||
"release coherence gate",
|
||||
"critical smoke",
|
||||
"release ID in incident timeline"
|
||||
],
|
||||
"negativeFixture": "HTML build A with asset manifest B must be rejected"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "ART-FE-FIELD-WEB-VITALS@1",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"generatedAt",
|
||||
"window",
|
||||
"context",
|
||||
"metrics",
|
||||
"thresholds",
|
||||
"eligibility",
|
||||
"status",
|
||||
"passed"
|
||||
],
|
||||
"properties": {
|
||||
"schemaVersion": { "const": 1 },
|
||||
"generatedAt": { "type": "string", "format": "date-time" },
|
||||
"window": { "type": "object", "required": ["days", "start", "end"] },
|
||||
"context": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"source",
|
||||
"sourceSystem",
|
||||
"exportId",
|
||||
"network",
|
||||
"routeAggregation",
|
||||
"releaseId",
|
||||
"privacyApprovalRef",
|
||||
"thresholdDecisionRef",
|
||||
"validationFailures"
|
||||
],
|
||||
"properties": {
|
||||
"source": { "type": "string" },
|
||||
"sourceSystem": { "type": ["string", "null"] },
|
||||
"exportId": { "type": ["string", "null"] },
|
||||
"network": { "const": "production-real-user" },
|
||||
"routeAggregation": { "const": "route-id-only" },
|
||||
"releaseId": { "type": ["string", "null"] },
|
||||
"privacyApprovalRef": { "type": ["string", "null"] },
|
||||
"thresholdDecisionRef": { "type": ["string", "null"] },
|
||||
"validationFailures": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"thresholds": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"p75LcpMs",
|
||||
"p75Cls",
|
||||
"p75InpMs",
|
||||
"minimumEligibleSamples"
|
||||
]
|
||||
},
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"required": ["p75LcpMs", "p75Cls", "p75InpMs"]
|
||||
},
|
||||
"eligibility": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"consentRequired",
|
||||
"totalSamples",
|
||||
"eligibleSamples",
|
||||
"minimumEligibleSamples",
|
||||
"routeSamples"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"enum": ["PASS", "FAIL_THRESHOLD", "FAIL_UNVERIFIED"]
|
||||
},
|
||||
"passed": { "type": "boolean" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "ART-FE-LAB@1",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"generatedAt",
|
||||
"context",
|
||||
"metrics",
|
||||
"thresholds",
|
||||
"fixtures",
|
||||
"passed"
|
||||
],
|
||||
"properties": {
|
||||
"schemaVersion": { "const": 1 },
|
||||
"generatedAt": { "type": "string", "format": "date-time" },
|
||||
"context": {
|
||||
"type": "object",
|
||||
"required": ["runner", "browser", "viewport", "network", "cpu", "cache", "build"]
|
||||
},
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"required": ["lcpMs", "cls", "namedInteractionMs"]
|
||||
},
|
||||
"thresholds": { "type": "object" },
|
||||
"fixtures": { "type": "array", "minItems": 2 },
|
||||
"passed": { "type": "boolean" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "ART-FE-003@1",
|
||||
"type": "object",
|
||||
"required": ["schemaVersion", "generatedAt", "artifact", "fixtures", "passed"],
|
||||
"properties": {
|
||||
"schemaVersion": { "const": 1 },
|
||||
"generatedAt": { "type": "string", "format": "date-time" },
|
||||
"artifact": {
|
||||
"type": "object",
|
||||
"required": ["checked", "compatible", "mismatches"]
|
||||
},
|
||||
"fixtures": { "type": "array", "minItems": 2 },
|
||||
"passed": { "type": "boolean" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "ART-FE-RUNBOOK-DRILL@1",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"runbookId",
|
||||
"releaseId",
|
||||
"drillTimestamp",
|
||||
"triggerInjected",
|
||||
"triggerAsserted",
|
||||
"containmentAsserted",
|
||||
"escalationPathAsserted",
|
||||
"recoveryAssertions",
|
||||
"negativeFixtureFailedAsExpected",
|
||||
"windowObservedBucket",
|
||||
"passed"
|
||||
],
|
||||
"properties": {
|
||||
"schemaVersion": { "const": 1 },
|
||||
"runbookId": { "pattern": "^FE-RB-00[1-5]$" },
|
||||
"releaseId": { "type": "string", "minLength": 1 },
|
||||
"drillTimestamp": { "type": "string", "format": "date-time" },
|
||||
"triggerInjected": { "type": "string" },
|
||||
"triggerAsserted": { "type": "boolean" },
|
||||
"containmentAsserted": { "type": "boolean" },
|
||||
"escalationPathAsserted": { "type": "boolean" },
|
||||
"recoveryAssertions": {
|
||||
"type": "array",
|
||||
"minItems": 4,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["assertion", "evidence", "passed"]
|
||||
}
|
||||
},
|
||||
"negativeFixtureFailedAsExpected": { "type": "boolean" },
|
||||
"windowObservedBucket": { "type": "string" },
|
||||
"providerVerificationRequired": { "type": "boolean" },
|
||||
"passed": { "type": "boolean" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"snapshotDigest": "ce4fa9b7944f27553067228bd6c9e73e7dc05875c283255b50d7eb3ad2923f6d",
|
||||
"owner": "frontend-platform",
|
||||
"reason": "RP-11-initial-transitive-inventory",
|
||||
"approvedAt": "2026-07-26T08:27:17.874Z"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"changes": []
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"allowedLicenses": [
|
||||
"(MIT OR CC0-1.0)",
|
||||
"0BSD",
|
||||
"Apache-2.0",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"BlueOak-1.0.0",
|
||||
"CC-BY-4.0",
|
||||
"CC0-1.0",
|
||||
"ISC",
|
||||
"MIT",
|
||||
"MIT-0",
|
||||
"MPL-2.0"
|
||||
],
|
||||
"deniedLicensePatterns": [
|
||||
"(^|\\s)AGPL",
|
||||
"(^|\\s)GPL",
|
||||
"SSPL",
|
||||
"BUSL"
|
||||
],
|
||||
"unknownLicensePolicy": "allow-only-unmaterialized-platform-optional"
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"trackedRoots": [
|
||||
"src",
|
||||
"recipes",
|
||||
"scripts",
|
||||
"tests",
|
||||
"config",
|
||||
"public",
|
||||
"schemas",
|
||||
".storybook",
|
||||
".gitea/workflows/quality-gates.yml",
|
||||
".dependency-cruiser.json",
|
||||
".nvmrc",
|
||||
".npmrc",
|
||||
"eslint.config.ts",
|
||||
"index.html",
|
||||
"package.json",
|
||||
"pnpm-lock.yaml",
|
||||
"pnpm-workspace.yaml",
|
||||
"tsconfig.json",
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.base.json",
|
||||
"tsconfig.node.json",
|
||||
"tsconfig.recipes.json",
|
||||
"tsconfig.service-worker.json",
|
||||
"tsconfig.test.json",
|
||||
"tsconfig.web-worker.json",
|
||||
"vite.config.ts",
|
||||
"vite.service-worker.config.ts",
|
||||
"vitest.config.ts",
|
||||
"playwright.config.ts",
|
||||
"playwright.capabilities.config.ts",
|
||||
"playwright.dev.config.ts",
|
||||
"playwright.storybook.config.ts",
|
||||
"playwright.visual.config.ts"
|
||||
],
|
||||
"generatedRoots": ["dist", "artifacts/release"],
|
||||
"optionalRoots": ["artifacts/release"],
|
||||
"excludedPaths": [
|
||||
"tests/fixtures/security/secret-detection/forbidden"
|
||||
],
|
||||
"allowlist": [
|
||||
{
|
||||
"path": "tests/fixtures/security/secret-detection/allowed/test-credentials.ts",
|
||||
"ruleId": "assigned-secret",
|
||||
"owner": "frontend-platform",
|
||||
"reason": "Synthetic credential verifies the scoped test-only allowlist.",
|
||||
"expiresAt": "2027-07-26T00:00:00.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"exceptions": []
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"providerMode": "external-file",
|
||||
"inputEnvironment": "VULNERABILITY_REPORT_PATH",
|
||||
"blockAtSeverity": "high",
|
||||
"allowedSeverities": ["unknown", "low", "moderate", "high", "critical"],
|
||||
"missingProviderStatus": "FAIL_UNVERIFIED"
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"repositoryBaseline": 285,
|
||||
"generatedPaths": [],
|
||||
"summary": {
|
||||
"lines": 75,
|
||||
"statements": 73,
|
||||
"functions": 80,
|
||||
"branches": 68
|
||||
},
|
||||
"criticalModules": [
|
||||
{
|
||||
"path": "src/adapters/http/bounded-body-reader.ts",
|
||||
"owner": "http-runtime",
|
||||
"minimum": { "lines": 95, "statements": 95, "functions": 95, "branches": 90 }
|
||||
},
|
||||
{
|
||||
"path": "src/adapters/http/bounded-json.ts",
|
||||
"owner": "http-runtime",
|
||||
"minimum": { "lines": 85, "statements": 84, "functions": 95, "branches": 78 }
|
||||
},
|
||||
{
|
||||
"path": "src/adapters/http/http-execution-v3.ts",
|
||||
"owner": "http-runtime",
|
||||
"minimum": { "lines": 75, "statements": 73, "functions": 70, "branches": 52 }
|
||||
},
|
||||
{
|
||||
"path": "src/adapters/http/request-builder.ts",
|
||||
"owner": "http-runtime",
|
||||
"minimum": { "lines": 85, "statements": 85, "functions": 95, "branches": 82 }
|
||||
},
|
||||
{
|
||||
"path": "src/adapters/http/retry-policy.ts",
|
||||
"owner": "http-runtime",
|
||||
"minimum": { "lines": 80, "statements": 78, "functions": 95, "branches": 78 }
|
||||
},
|
||||
{
|
||||
"path": "src/adapters/query-cache/server-state-scope-runtime.ts",
|
||||
"owner": "server-state-runtime",
|
||||
"minimum": { "lines": 85, "statements": 85, "functions": 85, "branches": 75 }
|
||||
},
|
||||
{
|
||||
"path": "src/adapters/service-worker/service-worker-lifecycle.ts",
|
||||
"owner": "service-worker-runtime",
|
||||
"minimum": { "lines": 64, "statements": 60, "functions": 65, "branches": 43 }
|
||||
},
|
||||
{
|
||||
"path": "src/adapters/storage/browser-storage-adapter.ts",
|
||||
"owner": "storage-runtime",
|
||||
"minimum": { "lines": 60, "statements": 60, "functions": 70, "branches": 60 }
|
||||
},
|
||||
{
|
||||
"path": "src/adapters/telemetry/best-effort-telemetry.ts",
|
||||
"owner": "telemetry-runtime",
|
||||
"minimum": { "lines": 85, "statements": 85, "functions": 70, "branches": 75 }
|
||||
},
|
||||
{
|
||||
"path": "src/application/create-application.ts",
|
||||
"owner": "application-runtime",
|
||||
"minimum": { "lines": 90, "statements": 90, "functions": 80, "branches": 68 }
|
||||
},
|
||||
{
|
||||
"path": "src/application/policies/compatibility.ts",
|
||||
"owner": "application-policy",
|
||||
"minimum": { "lines": 95, "statements": 95, "functions": 95, "branches": 75 }
|
||||
},
|
||||
{
|
||||
"path": "src/application/policies/performance-budgets.ts",
|
||||
"owner": "application-policy",
|
||||
"minimum": { "lines": 80, "statements": 80, "functions": 80, "branches": 40 }
|
||||
},
|
||||
{
|
||||
"path": "src/application/policies/promotion-readiness.ts",
|
||||
"owner": "release-runtime",
|
||||
"minimum": { "lines": 95, "statements": 95, "functions": 95, "branches": 95 }
|
||||
},
|
||||
{
|
||||
"path": "src/application/use-cases/decide-chunk-recovery.ts",
|
||||
"owner": "application-runtime",
|
||||
"minimum": { "lines": 90, "statements": 90, "functions": 95, "branches": 85 }
|
||||
},
|
||||
{
|
||||
"path": "src/bootstrap/load-release-manifest.ts",
|
||||
"owner": "release-runtime",
|
||||
"minimum": { "lines": 90, "statements": 90, "functions": 90, "branches": 80 }
|
||||
},
|
||||
{
|
||||
"path": "src/bootstrap/read-bounded-boot-json.ts",
|
||||
"owner": "bootstrap-runtime",
|
||||
"minimum": { "lines": 71, "statements": 66, "functions": 48, "branches": 57 }
|
||||
},
|
||||
{
|
||||
"path": "src/contracts/diagnostics.ts",
|
||||
"owner": "diagnostics-contracts",
|
||||
"minimum": { "lines": 68, "statements": 68, "functions": 95, "branches": 58 }
|
||||
},
|
||||
{
|
||||
"path": "src/features/reference-feature/adapters/reference-http-gateway.ts",
|
||||
"owner": "reference-feature",
|
||||
"minimum": { "lines": 90, "statements": 90, "functions": 90, "branches": 90 }
|
||||
},
|
||||
{
|
||||
"path": "src/presentation/adapters/query/application-query.ts",
|
||||
"owner": "presentation-runtime",
|
||||
"minimum": { "lines": 90, "statements": 90, "functions": 90, "branches": 80 }
|
||||
}
|
||||
],
|
||||
"highRiskPaths": [
|
||||
"src/adapters/http/bounded-body-reader.ts",
|
||||
"src/adapters/http/bounded-json.ts",
|
||||
"src/adapters/http/http-execution-v3.ts",
|
||||
"src/adapters/http/request-builder.ts",
|
||||
"src/adapters/http/retry-policy.ts",
|
||||
"src/adapters/query-cache/server-state-scope-runtime.ts",
|
||||
"src/adapters/service-worker/service-worker-lifecycle.ts",
|
||||
"src/adapters/storage/browser-storage-adapter.ts",
|
||||
"src/adapters/telemetry/best-effort-telemetry.ts",
|
||||
"src/application/create-application.ts",
|
||||
"src/application/policies/compatibility.ts",
|
||||
"src/application/policies/performance-budgets.ts",
|
||||
"src/application/policies/promotion-readiness.ts",
|
||||
"src/application/use-cases/decide-chunk-recovery.ts",
|
||||
"src/bootstrap/load-release-manifest.ts",
|
||||
"src/bootstrap/read-bounded-boot-json.ts",
|
||||
"src/contracts/diagnostics.ts",
|
||||
"src/features/reference-feature/adapters/reference-http-gateway.ts",
|
||||
"src/presentation/adapters/query/application-query.ts"
|
||||
],
|
||||
"waivers": []
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"scenarioCatalogs": [
|
||||
{
|
||||
"owner": "reference-feature",
|
||||
"path": "tests/mocks/scenarios/catalog.ts",
|
||||
"expectationExport": "HTTP_SCENARIO_EXPECTATIONS",
|
||||
"receiptPath": "artifacts/tests/http-scenario-executions.json",
|
||||
"receiptSchemaVersion": 1
|
||||
}
|
||||
],
|
||||
"sourceContracts": [
|
||||
{
|
||||
"owner": "reference-feature",
|
||||
"path": "tests/mocks/handlers/reference-resources.ts",
|
||||
"requiredTokens": [
|
||||
"assertOperationScenario",
|
||||
"../scenarios/catalog.ts"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user