@empire-builder-kit/nx 1.0.0-rc.44 → 1.0.0-rc.45

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 (85) hide show
  1. package/package.json +3 -3
  2. package/src/blueprints/external-sandbox.d.ts +1 -1
  3. package/src/cli/main.d.ts +11 -5
  4. package/src/cli/main.js +88 -29
  5. package/src/container-images.d.ts +1 -1
  6. package/src/container-images.js +1 -1
  7. package/src/executors/deployment-plan/deployment-plan.js +5 -1
  8. package/src/executors/deployment-plan/schema.json +1 -1
  9. package/src/executors/fleet/fleet.js +4 -0
  10. package/src/executors/fleet/schema.json +1 -1
  11. package/src/executors/local-deploy/federated-credential-transport.d.ts +4 -3
  12. package/src/executors/local-deploy/federated-credential-transport.js +15 -5
  13. package/src/foundation/cost-estimate.d.ts +8 -3
  14. package/src/foundation/cost-estimate.js +42 -19
  15. package/src/foundation/index.d.ts +1 -0
  16. package/src/foundation/index.js +1 -0
  17. package/src/foundation/local-private-smoke-access.js +6 -0
  18. package/src/foundation/logging.d.ts +3 -1
  19. package/src/foundation/logging.js +8 -1
  20. package/src/foundation/observability.d.ts +42 -0
  21. package/src/foundation/observability.js +181 -0
  22. package/src/foundation/preflight.js +1 -1
  23. package/src/generators/function/function.js +5 -2
  24. package/src/generators/observability-infrastructure.d.ts +2 -0
  25. package/src/generators/observability-infrastructure.js +31 -0
  26. package/src/generators/observability-policy.d.ts +4 -0
  27. package/src/generators/observability-policy.js +23 -0
  28. package/src/generators/preset/dependency-overrides.d.ts +17 -6
  29. package/src/generators/preset/dependency-overrides.js +17 -6
  30. package/src/generators/preset/tool-versions.d.ts +2 -2
  31. package/src/generators/preset/tool-versions.js +2 -2
  32. package/src/generators/preset/workspace-files-adopter-skills.js +28 -17
  33. package/src/generators/preset/workspace-files-agent-development.d.ts +9 -0
  34. package/src/generators/preset/workspace-files-agent-development.js +42 -0
  35. package/src/generators/preset/workspace-files-thin-deploy.js +12 -3
  36. package/src/generators/preset/workspace-files-thin-docs.js +10 -22
  37. package/src/generators/preset/workspace-files.d.ts +1 -0
  38. package/src/generators/preset/workspace-files.js +89 -7
  39. package/src/generators/slice/built-in-plan-alarm-identity.d.ts +173 -0
  40. package/src/generators/slice/built-in-plan-alarm-identity.js +202 -0
  41. package/src/generators/slice/built-in-plan-api-contract-files.js +136 -0
  42. package/src/generators/slice/built-in-plan-database-lifecycle-files.js +1 -1
  43. package/src/generators/slice/built-in-plan-event-contract-literal.d.ts +6 -0
  44. package/src/generators/slice/built-in-plan-event-contract-literal.js +52 -0
  45. package/src/generators/slice/built-in-plan-infrastructure-files.js +304 -144
  46. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +2 -2
  47. package/src/generators/slice/built-in-plan-metadata-files.js +33 -1
  48. package/src/generators/slice/built-in-plan-operations-files.js +106 -6
  49. package/src/generators/slice/built-in-plan-project-files.js +1 -0
  50. package/src/generators/slice/built-in-plan-release-files.js +2 -43
  51. package/src/generators/slice/built-in-plan-reliability-files.js +222 -236
  52. package/src/generators/slice/built-in-plan-shared.d.ts +29 -3
  53. package/src/generators/slice/built-in-plan-shared.js +29 -2
  54. package/src/generators/slice/built-in-plan-web-files.js +18 -1
  55. package/src/generators/slice/built-in-plan.js +1 -0
  56. package/src/generators/slice/slice.js +2 -0
  57. package/src/generators/upgrade/upgrade.d.ts +4 -3
  58. package/src/generators/upgrade/upgrade.js +50 -26
  59. package/src/generators/workload/frozen-container.d.ts +1 -1
  60. package/src/generators/workload/workload.js +5 -1
  61. package/src/local-dev/dev-command-core.d.ts +24 -3
  62. package/src/local-dev/dev-command-core.js +124 -12
  63. package/src/local-dev/errors.d.ts +19 -1
  64. package/src/local-dev/errors.js +18 -1
  65. package/src/local-dev/fleet-client.d.ts +64 -6
  66. package/src/local-dev/fleet-client.js +189 -15
  67. package/src/local-dev/fleet-process.d.ts +47 -2
  68. package/src/local-dev/fleet-process.js +248 -31
  69. package/src/local-dev/fleet-runtime.d.ts +37 -0
  70. package/src/local-dev/fleet-runtime.js +127 -69
  71. package/src/local-dev/fleet-session.d.ts +1 -0
  72. package/src/local-dev/fleet-session.js +42 -3
  73. package/src/local-dev/fleet-state.d.ts +5 -0
  74. package/src/local-dev/fleet-state.js +11 -2
  75. package/src/local-dev/fleet-supervisor.d.ts +6 -0
  76. package/src/local-dev/fleet-supervisor.js +49 -1
  77. package/src/local-dev/slice-runner.d.ts +19 -1
  78. package/src/local-dev/slice-runner.js +290 -91
  79. package/src/repository-policy/deployment-plan.d.ts +1 -1
  80. package/src/repository-policy/deployment-plan.js +16 -1
  81. package/src/secrets/generated-aws-control-plane.d.ts +11 -1
  82. package/src/secrets/generated-aws-control-plane.js +92 -29
  83. package/src/secrets/operator.d.ts +3 -2
  84. package/src/secrets/operator.js +27 -5
  85. package/src/supply-chain/generated-files.js +9 -0
@@ -541,6 +541,9 @@ writeFileSync(
541
541
  function renderThinDeployWorkflow(options) {
542
542
  const { actionPins, deploymentProfile, name, toolVersions } = options;
543
543
  const trunkPromotion = options.productionApprovalMode === 'trunk-promotion';
544
+ // The starter protected pair deploys development from protected develop;
545
+ // in the trunk lane develop is unprotected and owns no stage.
546
+ const developmentFromProtectedDevelop = deploymentProfile === 'starter' && options.deliveryLane !== 'trunk';
544
547
  return `name: Deploy
545
548
  run-name: Deploy \${{ inputs.source_commit || github.sha }}
546
549
 
@@ -707,14 +710,17 @@ jobs:
707
710
  plan_action="$action"
708
711
  [ "$action" != rehearse ] || plan_action=deploy
709
712
  case "$EBK_SOURCE_BRANCH" in
710
- main) stage=production ;;
713
+ main) stage=production ;;${options.deliveryLane === 'trunk'
714
+ ? `
715
+ develop) echo 'Trunk lane protected lifecycle runs from main'; exit 1 ;;`
716
+ : `
711
717
  develop)
712
718
  if [ "$EBK_PROFILE" = starter ]; then
713
719
  stage=development
714
720
  else
715
721
  stage=staging
716
722
  fi
717
- ;;
723
+ ;;`}
718
724
  *) echo 'Protected lifecycle must run from develop or main'; exit 1 ;;
719
725
  esac
720
726
  echo "action=$action" >> "$GITHUB_OUTPUT"
@@ -1665,7 +1671,10 @@ ${indentedScript(restoredCandidateValidationScript(), 10)}
1665
1671
  { echo 'Protected Blueprint authority must be one full Git object ID'; exit 1; }
1666
1672
  test "$(git rev-parse HEAD)" = "$EBK_PROTECTED_BLUEPRINT_COMMIT" ||
1667
1673
  { echo 'Protected Blueprint authority differs from the exact workflow checkout'; exit 1; }
1668
- case "$EBK_STAGE" in
1674
+ case "$EBK_STAGE" in${developmentFromProtectedDevelop
1675
+ ? `
1676
+ development) protected_branch=develop ;;`
1677
+ : ''}
1669
1678
  staging) protected_branch=develop ;;
1670
1679
  production) protected_branch=main ;;
1671
1680
  *) exit 1 ;;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderThinLifecycleDocumentation = renderThinLifecycleDocumentation;
4
+ const workspace_files_agent_development_js_1 = require("./workspace-files-agent-development.js");
4
5
  const index_js_1 = require("../../repository-policy/index.js");
5
6
  function renderThinLifecycleDocumentation(options) {
6
7
  const stages = (0, index_js_1.activeDeploymentStages)(options.deploymentProfile);
@@ -18,19 +19,13 @@ The committed \`${options.deploymentProfile}\` profile activates: ${stageList}.
18
19
 
19
20
  For ordinary feature work, run affected tests, typecheck, lint, and ownership
20
21
  checks. Run \`pnpm verify\` at a milestone, not before each development edit.
21
- Configure the exact temporary AWS profile and stage deployment role described
22
+ Configure the development profile and the stage deployment role described
22
23
  in \`docs/stage-deployment-authority.md\` before deploying development through
23
- \`ebk deploy development --profile=<profile> --expectedRoleArn=<role-arn>\`
24
- (the root \`deploy-local\` target). The profile is the human's IAM Identity
25
- Center profile, typically the one saved by \`ebk dev configure\`. When
26
- \`ebk dev doctor\` reports the session expired or missing, an attended coding
27
- agent (local or hosted) may run \`aws sso login --profile <profile>\`; the
28
- human approves the request in the browser, so the session stays the human's
29
- own identity. In a hosted or headless shell, add \`--use-device-code\` and the
30
- human approves the printed code from their own browser. Confirm
31
- \`aws sts get-caller-identity\` reports that identity before continuing.
32
- Never embed access keys, export, copy, or read credentials from the SSO cache,
33
- or assume a separate or stronger principal. Do not invoke SST directly.
24
+ \`ebk deploy development --profile=<development-profile> --expectedRoleArn=<role-arn>\`
25
+ (the root \`deploy-local\` target).
26
+
27
+ ${workspace_files_agent_development_js_1.AGENT_DEVELOPMENT_AUTHORITY}
28
+ Do not invoke SST directly.
34
29
 
35
30
  Slice \`verify\` runs \`test-integration\`, which needs a running Docker engine
36
31
  for DynamoDB Local (and PostgreSQL when SQL is declared).
@@ -143,7 +138,7 @@ removal, unlock, discovery, and secret resolution.
143
138
 
144
139
  ## Native lifecycle
145
140
 
146
- Use \`ebk deploy development --profile=<profile> --expectedRoleArn=<role-arn>\`.
141
+ Use \`ebk deploy development --profile=<development-profile> --expectedRoleArn=<role-arn>\`.
147
142
  Add Slice names, for example \`ebk deploy development billing\` with the same
148
143
  authority options, for focused deployment after Foundation. Other Slices are
149
144
  not redeployed; required peers must already exist. Omit Slice names for the
@@ -248,16 +243,9 @@ The resolved ${rehearsalStage} stage is rehearsed with
248
243
  Use temporary IAM Identity Center credentials and the stable stage deployment
249
244
  role. Development accepts a dirty tree and requires no acknowledgement:
250
245
 
251
- \`ebk deploy development --profile=<profile> --expectedRoleArn=<role-arn>\`
246
+ \`ebk deploy development --profile=<development-profile> --expectedRoleArn=<role-arn>\`
252
247
 
253
- For development, when the session is expired or missing, an attended coding
254
- agent (local or hosted) may run \`aws sso login --profile <profile>\`; the
255
- human approves the request in the browser, so the session stays the human's
256
- own identity. In a hosted or headless shell, add \`--use-device-code\` and the
257
- human approves the printed code from their own browser. Confirm
258
- \`aws sts get-caller-identity\` reports that identity before continuing.
259
- Never embed access keys, export, copy, or read credentials from the SSO cache,
260
- or assume a separate or stronger principal.
248
+ ${workspace_files_agent_development_js_1.AGENT_DEVELOPMENT_AUTHORITY}
261
249
 
262
250
  Local staging, when active, requires a clean commit and an interactive
263
251
  \`approve:deploy:staging:<source-commit>\` response. Starter production requires
@@ -11,6 +11,7 @@ export interface WorkspaceFileOptions {
11
11
  /** Seeded supply-chain/dependency-overrides.json data. When absent, the
12
12
  * workspace is seeded with DEFAULT_DEPENDENCY_OVERRIDES. */
13
13
  dependencyOverrides?: unknown;
14
+ observabilityPolicy?: unknown;
14
15
  deploymentAccounts?: ConfiguredDeploymentAccounts;
15
16
  deploymentProfile?: DeploymentProfile;
16
17
  deploymentRegion?: string | null;
@@ -11,6 +11,7 @@ const dependency_overrides_js_1 = require("./dependency-overrides.js");
11
11
  const index_js_1 = require("../../ownership/index.js");
12
12
  const index_js_2 = require("../../frontend-security/index.js");
13
13
  const capability_plan_js_1 = require("../../foundation/capability-plan.js");
14
+ const observability_js_1 = require("../../foundation/observability.js");
14
15
  const cost_estimate_js_1 = require("../../foundation/cost-estimate.js");
15
16
  const index_js_3 = require("../../git-policy/index.js");
16
17
  const index_js_4 = require("../../repository-policy/index.js");
@@ -1059,6 +1060,7 @@ const SUPPLY_CHAIN_WORKSPACE_FILE_PLAN = (0, index_js_6.renderSupplyChainWorkspa
1059
1060
  });
1060
1061
  const WORKSPACE_SEEDED_PATHS = new Set([
1061
1062
  dependency_overrides_js_1.DEPENDENCY_OVERRIDES_PATH,
1063
+ observability_js_1.OBSERVABILITY_POLICY_PATH,
1062
1064
  ...SUPPLY_CHAIN_WORKSPACE_FILE_PLAN.seeded,
1063
1065
  ...workspace_files_developer_agent_js_1.DEVELOPER_AGENT_WORKSPACE_SEEDED_PATHS,
1064
1066
  '.github/CODEOWNERS',
@@ -1066,6 +1068,7 @@ const WORKSPACE_SEEDED_PATHS = new Set([
1066
1068
  'CLAUDE.md',
1067
1069
  'README.md',
1068
1070
  'docs/architecture.md',
1071
+ 'docs/observability.md',
1069
1072
  'docs/git-workflow.md',
1070
1073
  'docs/repository-operating-policy.md',
1071
1074
  'docs/stage-deployment-authority.md',
@@ -7874,6 +7877,72 @@ convention, current only as far as the copy each actor reads.
7874
7877
  `,
7875
7878
  [workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIMS_PATH]: (0, workspace_files_shared_operation_claims_js_1.renderSharedOperationClaims)(name),
7876
7879
  'ebk.repository-policy.json': json(initialRepositoryPolicy),
7880
+ [observability_js_1.OBSERVABILITY_POLICY_PATH]: json(options.observabilityPolicy === undefined
7881
+ ? (0, observability_js_1.defaultObservabilityPolicy)(activeStages, {
7882
+ productionProfile: initialRepositoryPolicy.productionFoundation.profile,
7883
+ })
7884
+ : (0, observability_js_1.parseObservabilityPolicy)(options.observabilityPolicy, activeStages)),
7885
+ 'docs/observability.md': `# Observability spending and diagnostics
7886
+
7887
+ The seeded ebk.observability.json is the stage-specific policy. New development
7888
+ uses cost-optimized, active staging uses balanced, and production gets an explicit
7889
+ profile initialized from its capacity choice. Later capacity changes never alter
7890
+ that seeded observability choice. Task diagnostics are lean, but explicit retention
7891
+ and dormant-log preservation still apply; local hot diagnostics remain complete.
7892
+ This policy never pauses work or
7893
+ changes database protection, encryption, backups, topology or deployment authority.
7894
+
7895
+ Cost-optimized retains 5% of marked neutral request-start/success diagnostics, WARN Lambda
7896
+ system logs and no dashboards. Balanced retains 25%, INFO system logs and one
7897
+ Foundation dashboard. Standard retains 100%, INFO system logs and Foundation plus
7898
+ Slice dashboards. All retain errors, warnings, security, migration/event receipts,
7899
+ required counters and core failure/backlog alarms. Custom metrics are not free;
7900
+ batching reduces ingestion bytes, not billable metric series.
7901
+
7902
+ Mixed application/security log groups retain at least 30 days (90 for new standard
7903
+ production). diagnosticRetentionDays is an extension default; no generated log
7904
+ group uses it. Shorter diagnostic retention applies only to separately declared
7905
+ diagnostic groups, never mixed groups. Insights, enhanced database monitoring and
7906
+ tracing have no control in this policy. Retain Standard class for EMF and generated
7907
+ receipt queries. WAF enforcement and existing request-log privacy are unchanged.
7908
+
7909
+ Upgrades from older workspaces seed explicit prior retention and verbosity; they
7910
+ do not silently adopt shorter retention or remove dormant log groups. Review this
7911
+ seeded file to opt in deliberately. Profile changes require ordinary deployment;
7912
+ generators never change AWS. Workspace upgrade comes before Foundation/Slices.
7913
+ When enabling a new canonical stage, declare it here as well as in repository policy.
7914
+
7915
+ Stage overrides: profile; routineSuccessSampleRate (0–1); diagnosticRetentionDays;
7916
+ protectedRetentionDays (AWS-supported, at least 30); dashboards (none, stage, slice);
7917
+ systemLogLevel (WARN, INFO); retainDormantComponentLogs (boolean). Declared optional
7918
+ components always keep their logs. Only absent provider/webhook components may
7919
+ omit their otherwise unused log groups and findings alarms.
7920
+
7921
+ Fractional sampling uses a server-owned ID, not client correlation/request headers.
7922
+ Request starts are neutral, never successful. Legacy/standard retain an entry
7923
+ breadcrumb even for handlers that never return; cheap profiles may omit routine
7924
+ starts, but failures and receipts remain unsampled.
7925
+
7926
+ Custom reliability labels belong in each Slice's seeded reliability/metric-declarations.ts.
7927
+ Declare finite component/feature/operation values, never user, tenant, request or
7928
+ resource identifiers. Keep high-cardinality context in redacted logs. Only reviewed
7929
+ routine INFO successes may use routineSuccess; never mark receipts or security events.
7930
+ Upgrades flag the new declaration requirement with a reconciliation work marker.
7931
+ Undeclared tuples and invalid observations produce bounded reason-only warnings;
7932
+ invalid latency never suppresses the valid operation counters.
7933
+
7934
+ At low traffic, fixed dashboards, alarms and metric series can dominate the bill.
7935
+ Profile savings primarily change dashboard inventory; fresh disabled adapters omit
7936
+ two dormant groups and findings alarms. Sampling/batching affect ingestion and
7937
+ scanning volume, while retention affects storage only where applied. No live
7938
+ saving or free-tier discount is implied by the list-price estimate.
7939
+
7940
+ Diagnostics are on demand. Budget CloudWatch API metric retrieval as well as
7941
+ alarms, storage, ingestion, scanning and custom series. Narrow queries to the
7942
+ exact owned stage/resources/window; account-wide repeated sweeps are not a
7943
+ default application behavior. Never reduce a safety check's coverage or cadence
7944
+ to hide its cost. Measure actual billing after deployment, not just estimates.
7945
+ `,
7877
7946
  'nx.json': json({
7878
7947
  $schema: './node_modules/nx/schemas/nx-schema.json',
7879
7948
  defaultBase: 'develop',
@@ -8222,6 +8291,7 @@ ${foundationPlan.resources.postgres ? ' foundationDatabaseEndpointHost,' : ''}
8222
8291
  foundationSsmPath,
8223
8292
  normalizeRouterWafPolicy,
8224
8293
  reconcileRouterWafBlockingRehearsal,
8294
+ resolveObservabilityPolicy,
8225
8295
  routerWafContractDigest,
8226
8296
  type FoundationSsmKey,
8227
8297
  } from '@empire-builder-kit/nx/foundation';
@@ -8242,6 +8312,7 @@ import routerWafBlockingRehearsal from '../security/router-waf-blocking-rehearsa
8242
8312
  import routerWafLiveEvidence from '../security/router-waf-live-evidence.json';
8243
8313
  import routerWafPolicy from '../security/router-waf.json';
8244
8314
  import repositoryPolicy from '../../../ebk.repository-policy.json';
8315
+ import observabilityPolicy from '../../../ebk.observability.json';
8245
8316
 
8246
8317
  function identityDomainPrefix(stage: string, accountId: string): string {
8247
8318
  if (!/^\\d{12}$/.test(accountId)) {
@@ -8277,6 +8348,11 @@ function publishFoundation(values: Partial<Record<FoundationSsmKey, any>>) {
8277
8348
 
8278
8349
  export function foundation() {
8279
8350
  const isProduction = $app.stage === 'production';
8351
+ const observability = resolveObservabilityPolicy({
8352
+ policy: observabilityPolicy,
8353
+ stage: $app.stage,
8354
+ productionProfile: process.env.EBK_PRODUCTION_FOUNDATION_PROFILE,
8355
+ });
8280
8356
  ${foundationPlan.resources.postgres ? " const isStaging = $app.stage === 'staging';" : ''}
8281
8357
  const rootDomain = process.env.EBK_ROOT_DOMAIN?.trim();
8282
8358
  const productionConfiguration = isProduction
@@ -8753,7 +8829,7 @@ ${foundationPlan.resources.postgres
8753
8829
  { dependsOn: [alertTopicPolicy] },
8754
8830
  )
8755
8831
  : undefined;
8756
- const dashboard = new aws.cloudwatch.Dashboard('FoundationDashboard', {
8832
+ const dashboard = observability.dashboards === 'none' ? undefined : new aws.cloudwatch.Dashboard('FoundationDashboard', {
8757
8833
  dashboardName: 'ebk-' + $app.stage + '-foundation',
8758
8834
  dashboardBody: databaseInstanceId.apply((identifier: string) => JSON.stringify({
8759
8835
  widgets: [
@@ -8824,12 +8900,18 @@ ${foundationPlan.resources.postgres
8824
8900
  treatMissingData: 'notBreaching',
8825
8901
  },
8826
8902
  );`
8827
- : ` const dashboard = new aws.cloudwatch.Dashboard('FoundationDashboard', {
8903
+ : ` const dashboard = observability.dashboards === 'none' ? undefined : new aws.cloudwatch.Dashboard('FoundationDashboard', {
8828
8904
  dashboardName: 'ebk-' + $app.stage + '-foundation',
8829
- dashboardBody: JSON.stringify({ widgets: [{
8830
- type: 'text', x: 0, y: 0, width: 24, height: 3,
8831
- properties: { markdown: '# Empire Builder Kit Foundation\\nStage: ' + $app.stage + '\\nPersistence: Slice-scoped DynamoDB; SQL not declared.' },
8832
- }] }),
8905
+ dashboardBody: router.distributionID.apply((distributionId: string) => JSON.stringify({ widgets: [{
8906
+ type: 'metric', x: 0, y: 0, width: 24, height: 6,
8907
+ properties: {
8908
+ title: 'Router requests and errors', region: 'us-east-1', period: 300,
8909
+ metrics: [
8910
+ ['AWS/CloudFront', 'Requests', 'DistributionId', distributionId, 'Region', 'Global', { stat: 'Sum' }],
8911
+ ['AWS/CloudFront', '5xxErrorRate', 'DistributionId', distributionId, 'Region', 'Global', { stat: 'Average', yAxis: 'right' }],
8912
+ ],
8913
+ },
8914
+ }] })),
8833
8915
  });`}
8834
8916
 
8835
8917
  if (new Set(['development', 'staging', 'production']).has($app.stage)) {
@@ -8879,7 +8961,7 @@ ${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
8879
8961
  contractVersion: '${foundationPlan.descriptor.contractVersion}',
8880
8962
  costAnomalySubscriptionArn: accountCostAnomalySubscription?.arn,
8881
8963
  ${foundationPlan.resources.postgres ? ' databaseCapacityAlarmName: databaseCapacityAlarm.name,' : ''}
8882
- dashboardName: dashboard.dashboardName,
8964
+ dashboardName: dashboard?.dashboardName,
8883
8965
  ${foundationPlan.resources.postgres
8884
8966
  ? ` databaseClusterId: database.id,
8885
8967
  databaseEventSubscriptionId: databaseEvents?.id,`
@@ -0,0 +1,173 @@
1
+ /**
2
+ * The one identity table for every alarm a Standard Slice registers.
3
+ *
4
+ * The seeded operations/reliability.json (its alarmRegistrations and
5
+ * deployedAlarmResources) and every managed MetricAlarm description
6
+ * (infra/reliability.ts, infra/product.ts, infra/key-value-retention.ts) are
7
+ * rendered from this table. The generated `scripts/verify-reliability.mjs
8
+ * --deployed` compares the inventory's owner, route and runbook with each
9
+ * deployed alarm's description, so a value written in two places can drift
10
+ * into a deployed alarm the verifier rejects (CROSS-SLICE-RETENTION-IDENTITY-003).
11
+ *
12
+ * Where the seeded and managed values disagreed, the seeded value is kept:
13
+ * an upgrade rewrites only the managed emitters, so aligning them with the
14
+ * seeded inventory repairs every existing adopter without a hand edit.
15
+ *
16
+ * For the same reason the runbook of an existing ID, and the owner and route
17
+ * from sliceAlarmOwnership, are frozen for existing adopters:
18
+ * built-in-plan-alarm-identity.spec.ts pins them, and changing one needs a
19
+ * seeded operations/reliability.json migration.
20
+ */
21
+ declare const SLICE_ALARM_IDENTITIES: {
22
+ readonly 'alert-delivery-degraded': {
23
+ readonly businessSymptom: "The primary alert route is failing delivery.";
24
+ readonly runbook: "alert-delivery";
25
+ };
26
+ readonly 'api-fast-burn': {
27
+ readonly businessSymptom: "Critical API operations are failing.";
28
+ readonly runbook: "api-recovery";
29
+ };
30
+ readonly 'api-slow-burn': {
31
+ readonly businessSymptom: "Critical API operations have sustained failures.";
32
+ readonly runbook: "api-recovery";
33
+ };
34
+ readonly 'async-target-dlq-depth': {
35
+ readonly businessSymptom: "An asynchronous target has terminal failures.";
36
+ readonly runbook: "async-recovery";
37
+ };
38
+ readonly 'data-export-cleanup-deadline': {
39
+ readonly businessSymptom: "Confidential-export residue exceeded its deletion deadline.";
40
+ readonly runbook: "database-recovery";
41
+ };
42
+ readonly 'data-export-cleanup-failure': {
43
+ readonly businessSymptom: "The confidential-export cleanup invocation is failing.";
44
+ readonly runbook: "database-recovery";
45
+ };
46
+ readonly 'data-export-cleanup-invocation-missing': {
47
+ readonly businessSymptom: "The scheduled confidential-export cleanup is not running.";
48
+ readonly runbook: "database-recovery";
49
+ };
50
+ readonly 'data-protection-findings': {
51
+ readonly businessSymptom: "A managed log group contains a protected-data finding.";
52
+ readonly runbook: "log-investigation";
53
+ };
54
+ readonly 'database-capacity': {
55
+ readonly businessSymptom: "Slice data access or durable event delivery is being throttled.";
56
+ readonly runbook: "database-recovery";
57
+ };
58
+ readonly 'event-delivery-attempts': {
59
+ readonly businessSymptom: "A domain-event intent is repeatedly failing delivery.";
60
+ readonly runbook: "async-recovery";
61
+ };
62
+ readonly 'event-dlq-depth': {
63
+ readonly businessSymptom: "Domain-event processing has terminal failures.";
64
+ readonly runbook: "async-recovery";
65
+ };
66
+ readonly 'event-intent-rejected': {
67
+ readonly businessSymptom: "A retained domain-event intent cannot be delivered.";
68
+ readonly runbook: "async-recovery";
69
+ };
70
+ readonly 'function-failure': {
71
+ readonly businessSymptom: "The generated confidential-export cleanup Function has a terminal invocation error.";
72
+ readonly runbook: "api-recovery";
73
+ };
74
+ readonly 'inbox-cleanup-backlog': {
75
+ readonly businessSymptom: "Terminal asynchronous inbox records are accumulating.";
76
+ readonly runbook: "async-recovery";
77
+ };
78
+ readonly 'inbox-cleanup-errors': {
79
+ readonly businessSymptom: "The asynchronous inbox cleanup job is failing.";
80
+ readonly runbook: "async-recovery";
81
+ };
82
+ readonly 'key-value-reconciliation-errors': {
83
+ readonly businessSymptom: "Durable domain-event recovery is failing.";
84
+ readonly runbook: "async-recovery";
85
+ };
86
+ readonly 'key-value-stream-delivery-errors': {
87
+ readonly businessSymptom: "The key-value stream delivery Function failed an invocation.";
88
+ readonly runbook: "async-recovery";
89
+ };
90
+ readonly 'key-value-stream-iterator-age': {
91
+ readonly businessSymptom: "Prompt domain-event delivery from the key-value stream is falling behind.";
92
+ readonly runbook: "async-recovery";
93
+ };
94
+ readonly 'outbox-backlog': {
95
+ readonly businessSymptom: "Durable domain-event intents remain undelivered.";
96
+ readonly runbook: "async-recovery";
97
+ };
98
+ readonly 'outbox-relay-errors': {
99
+ readonly businessSymptom: "The isolated performance-sink outbox relay is failing.";
100
+ readonly runbook: "async-recovery";
101
+ };
102
+ readonly 'performance-load-cleanup-backlog': {
103
+ readonly businessSymptom: "Expired protected performance-load control records are accumulating.";
104
+ readonly runbook: "performance-capacity-cost";
105
+ };
106
+ readonly 'performance-load-cleanup-deadline': {
107
+ readonly businessSymptom: "Protected performance-load residue exceeded its cleanup deadline.";
108
+ readonly runbook: "performance-capacity-cost";
109
+ };
110
+ readonly 'provider-dlq-depth': {
111
+ readonly businessSymptom: "Provider deliveries have terminal failures.";
112
+ readonly runbook: "async-recovery";
113
+ };
114
+ readonly 'provider-queue-age': {
115
+ readonly businessSymptom: "Accepted provider work is missing its deadline.";
116
+ readonly runbook: "async-recovery";
117
+ };
118
+ readonly 'retention-job-failure': {
119
+ readonly businessSymptom: "The built-in retention Job terminated unsuccessfully.";
120
+ readonly runbook: "database-recovery";
121
+ };
122
+ readonly 'sql-outbox-attempts': {
123
+ readonly businessSymptom: "A relational event intent is repeatedly failing delivery.";
124
+ readonly runbook: "async-recovery";
125
+ };
126
+ readonly 'sql-outbox-backlog': {
127
+ readonly businessSymptom: "Relational event intents remain undelivered.";
128
+ readonly runbook: "async-recovery";
129
+ };
130
+ readonly 'sql-outbox-relay-errors': {
131
+ readonly businessSymptom: "Relational event delivery is failing.";
132
+ readonly runbook: "async-recovery";
133
+ };
134
+ readonly 'web-latency-fast-burn': {
135
+ readonly businessSymptom: "Server-rendered routes exceed the latency objective.";
136
+ readonly runbook: "web-recovery";
137
+ };
138
+ readonly 'web-latency-slow-burn': {
139
+ readonly businessSymptom: "Server-rendered routes have sustained latency degradation.";
140
+ readonly runbook: "web-recovery";
141
+ };
142
+ };
143
+ export type SliceAlarmId = keyof typeof SLICE_ALARM_IDENTITIES;
144
+ export interface SliceAlarmIdentity {
145
+ businessSymptom: string;
146
+ id: SliceAlarmId;
147
+ owner: string;
148
+ route: string;
149
+ /** The Slice-relative runbook path that the description and inventory name. */
150
+ runbook: string;
151
+ /** The alarm registration's reference to the same runbook. */
152
+ runbookRef: string;
153
+ }
154
+ /** The owner and escalation route every built-in Slice alarm carries. */
155
+ export declare function sliceAlarmOwnership(name: string): {
156
+ owner: string;
157
+ route: string;
158
+ };
159
+ export declare function sliceAlarmIdentity(name: string, id: SliceAlarmId): SliceAlarmIdentity;
160
+ /**
161
+ * The generated description helper's argument for one alarm, as TypeScript
162
+ * source. Both generated helpers (`alarmDescription` in infra/reliability.ts
163
+ * and `operationalAlarmDescription` in infra/product.ts) add the owner,
164
+ * route, schema version and Slice from sliceAlarmOwnership.
165
+ */
166
+ export declare function renderSliceAlarmDescriptionInput(name: string, id: SliceAlarmId, indent: string): string;
167
+ /**
168
+ * The complete alarm description object for one alarm, as TypeScript source
169
+ * for an emitter without a description helper. Keys are in the helpers'
170
+ * order.
171
+ */
172
+ export declare function renderSliceAlarmDescriptionObject(name: string, id: SliceAlarmId): string;
173
+ export {};
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sliceAlarmOwnership = sliceAlarmOwnership;
4
+ exports.sliceAlarmIdentity = sliceAlarmIdentity;
5
+ exports.renderSliceAlarmDescriptionInput = renderSliceAlarmDescriptionInput;
6
+ exports.renderSliceAlarmDescriptionObject = renderSliceAlarmDescriptionObject;
7
+ /**
8
+ * The one identity table for every alarm a Standard Slice registers.
9
+ *
10
+ * The seeded operations/reliability.json (its alarmRegistrations and
11
+ * deployedAlarmResources) and every managed MetricAlarm description
12
+ * (infra/reliability.ts, infra/product.ts, infra/key-value-retention.ts) are
13
+ * rendered from this table. The generated `scripts/verify-reliability.mjs
14
+ * --deployed` compares the inventory's owner, route and runbook with each
15
+ * deployed alarm's description, so a value written in two places can drift
16
+ * into a deployed alarm the verifier rejects (CROSS-SLICE-RETENTION-IDENTITY-003).
17
+ *
18
+ * Where the seeded and managed values disagreed, the seeded value is kept:
19
+ * an upgrade rewrites only the managed emitters, so aligning them with the
20
+ * seeded inventory repairs every existing adopter without a hand edit.
21
+ *
22
+ * For the same reason the runbook of an existing ID, and the owner and route
23
+ * from sliceAlarmOwnership, are frozen for existing adopters:
24
+ * built-in-plan-alarm-identity.spec.ts pins them, and changing one needs a
25
+ * seeded operations/reliability.json migration.
26
+ */
27
+ const SLICE_ALARM_IDENTITIES = {
28
+ 'alert-delivery-degraded': {
29
+ businessSymptom: 'The primary alert route is failing delivery.',
30
+ runbook: 'alert-delivery',
31
+ },
32
+ 'api-fast-burn': {
33
+ businessSymptom: 'Critical API operations are failing.',
34
+ runbook: 'api-recovery',
35
+ },
36
+ 'api-slow-burn': {
37
+ businessSymptom: 'Critical API operations have sustained failures.',
38
+ runbook: 'api-recovery',
39
+ },
40
+ 'async-target-dlq-depth': {
41
+ businessSymptom: 'An asynchronous target has terminal failures.',
42
+ runbook: 'async-recovery',
43
+ },
44
+ 'data-export-cleanup-deadline': {
45
+ businessSymptom: 'Confidential-export residue exceeded its deletion deadline.',
46
+ runbook: 'database-recovery',
47
+ },
48
+ 'data-export-cleanup-failure': {
49
+ businessSymptom: 'The confidential-export cleanup invocation is failing.',
50
+ runbook: 'database-recovery',
51
+ },
52
+ 'data-export-cleanup-invocation-missing': {
53
+ businessSymptom: 'The scheduled confidential-export cleanup is not running.',
54
+ runbook: 'database-recovery',
55
+ },
56
+ 'data-protection-findings': {
57
+ businessSymptom: 'A managed log group contains a protected-data finding.',
58
+ runbook: 'log-investigation',
59
+ },
60
+ 'database-capacity': {
61
+ businessSymptom: 'Slice data access or durable event delivery is being throttled.',
62
+ runbook: 'database-recovery',
63
+ },
64
+ 'event-delivery-attempts': {
65
+ businessSymptom: 'A domain-event intent is repeatedly failing delivery.',
66
+ runbook: 'async-recovery',
67
+ },
68
+ 'event-dlq-depth': {
69
+ businessSymptom: 'Domain-event processing has terminal failures.',
70
+ runbook: 'async-recovery',
71
+ },
72
+ 'event-intent-rejected': {
73
+ businessSymptom: 'A retained domain-event intent cannot be delivered.',
74
+ runbook: 'async-recovery',
75
+ },
76
+ 'function-failure': {
77
+ businessSymptom: 'The generated confidential-export cleanup Function has a terminal invocation error.',
78
+ runbook: 'api-recovery',
79
+ },
80
+ 'inbox-cleanup-backlog': {
81
+ businessSymptom: 'Terminal asynchronous inbox records are accumulating.',
82
+ runbook: 'async-recovery',
83
+ },
84
+ 'inbox-cleanup-errors': {
85
+ businessSymptom: 'The asynchronous inbox cleanup job is failing.',
86
+ runbook: 'async-recovery',
87
+ },
88
+ 'key-value-reconciliation-errors': {
89
+ businessSymptom: 'Durable domain-event recovery is failing.',
90
+ runbook: 'async-recovery',
91
+ },
92
+ 'key-value-stream-delivery-errors': {
93
+ businessSymptom: 'The key-value stream delivery Function failed an invocation.',
94
+ runbook: 'async-recovery',
95
+ },
96
+ 'key-value-stream-iterator-age': {
97
+ businessSymptom: 'Prompt domain-event delivery from the key-value stream is falling behind.',
98
+ runbook: 'async-recovery',
99
+ },
100
+ 'outbox-backlog': {
101
+ businessSymptom: 'Durable domain-event intents remain undelivered.',
102
+ runbook: 'async-recovery',
103
+ },
104
+ 'outbox-relay-errors': {
105
+ businessSymptom: 'The isolated performance-sink outbox relay is failing.',
106
+ runbook: 'async-recovery',
107
+ },
108
+ 'performance-load-cleanup-backlog': {
109
+ businessSymptom: 'Expired protected performance-load control records are accumulating.',
110
+ runbook: 'performance-capacity-cost',
111
+ },
112
+ 'performance-load-cleanup-deadline': {
113
+ businessSymptom: 'Protected performance-load residue exceeded its cleanup deadline.',
114
+ runbook: 'performance-capacity-cost',
115
+ },
116
+ 'provider-dlq-depth': {
117
+ businessSymptom: 'Provider deliveries have terminal failures.',
118
+ runbook: 'async-recovery',
119
+ },
120
+ 'provider-queue-age': {
121
+ businessSymptom: 'Accepted provider work is missing its deadline.',
122
+ runbook: 'async-recovery',
123
+ },
124
+ // The Step Functions retention workflow is a database-lifecycle job: its
125
+ // recovery belongs with the other lifecycle alarms in database-recovery.md,
126
+ // which routes the operator to the Job's own README for the run procedure.
127
+ 'retention-job-failure': {
128
+ businessSymptom: 'The built-in retention Job terminated unsuccessfully.',
129
+ runbook: 'database-recovery',
130
+ },
131
+ 'sql-outbox-attempts': {
132
+ businessSymptom: 'A relational event intent is repeatedly failing delivery.',
133
+ runbook: 'async-recovery',
134
+ },
135
+ 'sql-outbox-backlog': {
136
+ businessSymptom: 'Relational event intents remain undelivered.',
137
+ runbook: 'async-recovery',
138
+ },
139
+ 'sql-outbox-relay-errors': {
140
+ businessSymptom: 'Relational event delivery is failing.',
141
+ runbook: 'async-recovery',
142
+ },
143
+ 'web-latency-fast-burn': {
144
+ businessSymptom: 'Server-rendered routes exceed the latency objective.',
145
+ runbook: 'web-recovery',
146
+ },
147
+ 'web-latency-slow-burn': {
148
+ businessSymptom: 'Server-rendered routes have sustained latency degradation.',
149
+ runbook: 'web-recovery',
150
+ },
151
+ };
152
+ /** The owner and escalation route every built-in Slice alarm carries. */
153
+ function sliceAlarmOwnership(name) {
154
+ return { owner: `${name}-product`, route: `${name}-on-call` };
155
+ }
156
+ function sliceAlarmIdentity(name, id) {
157
+ const { businessSymptom, runbook } = SLICE_ALARM_IDENTITIES[id];
158
+ return {
159
+ businessSymptom,
160
+ id,
161
+ ...sliceAlarmOwnership(name),
162
+ runbook: `docs/runbooks/${runbook}.md`,
163
+ runbookRef: `runbook:${runbook}`,
164
+ };
165
+ }
166
+ /** A TypeScript single-quoted string literal. */
167
+ function typescriptString(value) {
168
+ if (/[\\'\n\r\u2028\u2029]/u.test(value)) {
169
+ throw new Error(`Alarm identity text cannot be rendered: ${value}`);
170
+ }
171
+ return `'${value}'`;
172
+ }
173
+ /**
174
+ * The generated description helper's argument for one alarm, as TypeScript
175
+ * source. Both generated helpers (`alarmDescription` in infra/reliability.ts
176
+ * and `operationalAlarmDescription` in infra/product.ts) add the owner,
177
+ * route, schema version and Slice from sliceAlarmOwnership.
178
+ */
179
+ function renderSliceAlarmDescriptionInput(name, id, indent) {
180
+ const identity = sliceAlarmIdentity(name, id);
181
+ const symptom = `${indent} businessSymptom: ${typescriptString(identity.businessSymptom)},`;
182
+ return [
183
+ '{',
184
+ // Wrap a symptom that would not fit 80 columns, as the template did.
185
+ symptom.length <= 80
186
+ ? symptom
187
+ : `${indent} businessSymptom:\n${indent} ${typescriptString(identity.businessSymptom)},`,
188
+ `${indent} id: ${typescriptString(identity.id)},`,
189
+ `${indent} runbook: ${typescriptString(identity.runbook)},`,
190
+ `${indent}}`,
191
+ ].join('\n');
192
+ }
193
+ /**
194
+ * The complete alarm description object for one alarm, as TypeScript source
195
+ * for an emitter without a description helper. Keys are in the helpers'
196
+ * order.
197
+ */
198
+ function renderSliceAlarmDescriptionObject(name, id) {
199
+ const identity = sliceAlarmIdentity(name, id);
200
+ return `{ businessSymptom: ${typescriptString(identity.businessSymptom)}, id: ${typescriptString(identity.id)}, owner: ${typescriptString(identity.owner)}, route: ${typescriptString(identity.route)}, runbook: ${typescriptString(identity.runbook)}, schemaVersion: 1, slice: ${typescriptString(name)} }`;
201
+ }
202
+ //# sourceMappingURL=built-in-plan-alarm-identity.js.map