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

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 (100) 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/developer-agent/generator-planning.js +10 -3
  8. package/src/developer-agent/operation-registry.js +1 -1
  9. package/src/executors/deployment-plan/deployment-plan.js +5 -1
  10. package/src/executors/deployment-plan/schema.json +1 -1
  11. package/src/executors/fleet/fleet.js +4 -0
  12. package/src/executors/fleet/schema.json +1 -1
  13. package/src/executors/github-bootstrap/github-bootstrap.d.ts +3 -0
  14. package/src/executors/github-bootstrap/github-bootstrap.js +28 -0
  15. package/src/executors/github-bootstrap/schema.json +14 -0
  16. package/src/executors/local-deploy/federated-credential-transport.d.ts +4 -3
  17. package/src/executors/local-deploy/federated-credential-transport.js +15 -5
  18. package/src/foundation/cost-estimate.d.ts +8 -3
  19. package/src/foundation/cost-estimate.js +42 -19
  20. package/src/foundation/index.d.ts +1 -0
  21. package/src/foundation/index.js +1 -0
  22. package/src/foundation/local-private-smoke-access.js +6 -0
  23. package/src/foundation/logging.d.ts +3 -1
  24. package/src/foundation/logging.js +8 -1
  25. package/src/foundation/observability.d.ts +42 -0
  26. package/src/foundation/observability.js +181 -0
  27. package/src/foundation/preflight.js +1 -1
  28. package/src/generators/function/function.js +5 -2
  29. package/src/generators/observability-infrastructure.d.ts +2 -0
  30. package/src/generators/observability-infrastructure.js +31 -0
  31. package/src/generators/observability-policy.d.ts +4 -0
  32. package/src/generators/observability-policy.js +23 -0
  33. package/src/generators/preset/dependency-overrides.d.ts +17 -6
  34. package/src/generators/preset/dependency-overrides.js +17 -6
  35. package/src/generators/preset/tool-versions.d.ts +2 -2
  36. package/src/generators/preset/tool-versions.js +2 -2
  37. package/src/generators/preset/workspace-files-adopter-skills.js +28 -17
  38. package/src/generators/preset/workspace-files-agent-development.d.ts +9 -0
  39. package/src/generators/preset/workspace-files-agent-development.js +42 -0
  40. package/src/generators/preset/workspace-files-thin-deploy.js +17 -4
  41. package/src/generators/preset/workspace-files-thin-docs.js +10 -22
  42. package/src/generators/preset/workspace-files.d.ts +1 -0
  43. package/src/generators/preset/workspace-files.js +120 -35
  44. package/src/generators/slice/built-in-plan-alarm-identity.d.ts +173 -0
  45. package/src/generators/slice/built-in-plan-alarm-identity.js +202 -0
  46. package/src/generators/slice/built-in-plan-api-contract-files.js +136 -0
  47. package/src/generators/slice/built-in-plan-data-files.js +10 -3
  48. package/src/generators/slice/built-in-plan-database-lifecycle-files.js +1 -1
  49. package/src/generators/slice/built-in-plan-event-contract-literal.d.ts +6 -0
  50. package/src/generators/slice/built-in-plan-event-contract-literal.js +52 -0
  51. package/src/generators/slice/built-in-plan-infrastructure-files.js +304 -144
  52. package/src/generators/slice/built-in-plan-key-value-retention-infrastructure.js +2 -2
  53. package/src/generators/slice/built-in-plan-metadata-files.js +37 -2
  54. package/src/generators/slice/built-in-plan-operations-files.js +106 -6
  55. package/src/generators/slice/built-in-plan-project-files.js +1 -0
  56. package/src/generators/slice/built-in-plan-release-files.js +2 -44
  57. package/src/generators/slice/built-in-plan-reliability-files.js +222 -236
  58. package/src/generators/slice/built-in-plan-shared.d.ts +29 -3
  59. package/src/generators/slice/built-in-plan-shared.js +29 -2
  60. package/src/generators/slice/built-in-plan-verification-files.js +0 -1
  61. package/src/generators/slice/built-in-plan-web-files.js +22 -6
  62. package/src/generators/slice/built-in-plan.js +2 -2
  63. package/src/generators/slice/slice.js +2 -0
  64. package/src/generators/upgrade/upgrade.d.ts +4 -3
  65. package/src/generators/upgrade/upgrade.js +139 -29
  66. package/src/generators/workload/frozen-container.d.ts +1 -1
  67. package/src/generators/workload/reliability-registration.d.ts +1 -1
  68. package/src/generators/workload/reliability-registration.js +9 -9
  69. package/src/generators/workload/workload.d.ts +1 -1
  70. package/src/generators/workload/workload.js +32 -10
  71. package/src/git-policy/github-bootstrap.d.ts +18 -0
  72. package/src/git-policy/github-bootstrap.js +99 -6
  73. package/src/local-dev/dev-command-core.d.ts +24 -3
  74. package/src/local-dev/dev-command-core.js +124 -12
  75. package/src/local-dev/errors.d.ts +19 -1
  76. package/src/local-dev/errors.js +18 -1
  77. package/src/local-dev/fleet-client.d.ts +64 -6
  78. package/src/local-dev/fleet-client.js +189 -15
  79. package/src/local-dev/fleet-process.d.ts +47 -2
  80. package/src/local-dev/fleet-process.js +248 -31
  81. package/src/local-dev/fleet-runtime.d.ts +37 -0
  82. package/src/local-dev/fleet-runtime.js +127 -69
  83. package/src/local-dev/fleet-session.d.ts +1 -0
  84. package/src/local-dev/fleet-session.js +83 -7
  85. package/src/local-dev/fleet-state.d.ts +5 -0
  86. package/src/local-dev/fleet-state.js +11 -2
  87. package/src/local-dev/fleet-supervisor-daemon.js +1 -5
  88. package/src/local-dev/fleet-supervisor.d.ts +7 -0
  89. package/src/local-dev/fleet-supervisor.js +83 -12
  90. package/src/local-dev/slice-runner.d.ts +19 -1
  91. package/src/local-dev/slice-runner.js +290 -91
  92. package/src/repository-policy/deployment-plan.d.ts +1 -1
  93. package/src/repository-policy/deployment-plan.js +16 -1
  94. package/src/secrets/generated-aws-control-plane.d.ts +11 -1
  95. package/src/secrets/generated-aws-control-plane.js +92 -29
  96. package/src/secrets/operator.d.ts +3 -2
  97. package/src/secrets/operator.js +27 -5
  98. package/src/supply-chain/blueprint-verification-authority.js +24 -3
  99. package/src/supply-chain/generated-files.js +9 -0
  100. package/src/supply-chain/sbom.js +74 -12
@@ -8,9 +8,9 @@ exports.WORKSPACE_SST_NPM_INTEGRITY = exports.WORKSPACE_PNPM_DESCRIPTOR = export
8
8
  * existing workspace explicitly adopts the exact reviewed descriptor.
9
9
  */
10
10
  exports.WORKSPACE_SST_VERSION = '4.17.1';
11
- exports.WORKSPACE_NODE_VERSION = '22.23.1';
11
+ exports.WORKSPACE_NODE_VERSION = '22.23.4';
12
12
  exports.WORKSPACE_PNPM_VERSION = '11.20.0';
13
- exports.WORKSPACE_VITEST_VERSION = '4.1.11';
13
+ exports.WORKSPACE_VITEST_VERSION = '4.1.12';
14
14
  exports.WORKSPACE_PNPM_SHA512 = '9a6f330a95b66446ea088faf1521405a8a01f07fde7124cc9958dfed52d4bb436737e65b08f85f37b46fcba375092558ac51262b816844b22f63406ed166bfee';
15
15
  exports.WORKSPACE_PNPM_DESCRIPTOR = `pnpm@${exports.WORKSPACE_PNPM_VERSION}+sha512.${exports.WORKSPACE_PNPM_SHA512}`;
16
16
  /**
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ADOPTER_SKILL_NAMES = void 0;
4
4
  exports.renderAdopterSkillIndex = renderAdopterSkillIndex;
5
5
  exports.renderAdopterSkills = renderAdopterSkills;
6
+ const workspace_files_agent_development_js_1 = require("./workspace-files-agent-development.js");
6
7
  exports.ADOPTER_SKILL_NAMES = [
7
8
  'ebk-orient-plan',
8
9
  'ebk-build-feature',
@@ -107,6 +108,21 @@ intents commit atomically. TTL is eventual cleanup, not an authorization or
107
108
  expiry decision. Events use the same stage/task; never bridge task async work
108
109
  to deployed development.
109
110
 
111
+ For a cross-Slice event, the producer declares it in its seeded
112
+ \`contracts/events.ts\` \`EventContractSchema\`; its own queue rule then also
113
+ receives it, so its seeded consumer acknowledges it. The consumer adds the
114
+ producer's \`@<scope>/<producer>/contracts\` package to its seeded
115
+ \`contracts/dependencies.json\` \`consumes\` with a \`workspace:*\` dependency,
116
+ runs \`pnpm nx run <consumer>:contract-dependencies-sync\`, declares
117
+ \`{ "source": "<producer>", "events": [{ "detailType": "<Type>", "schemaVersions": [1] }] }\`
118
+ in its seeded \`contracts/event-subscriptions.json\`, and extends its seeded
119
+ \`api/event-consumer.ts\` to accept that source, type and every declared
120
+ version. Managed infrastructure adds one rule per source; never edit
121
+ \`infra/product.ts\`. \`contract-dependencies-check\` binds the declaration to
122
+ the producer's schema. Source and detail type are delivery filters, not
123
+ authentication: authorize every effect from the consumer's own data and
124
+ policy. Never carry product data in \`DataLifecycleChanged\`.
125
+
110
126
  Add SQL only when relational constraints, transactions or joins justify it:
111
127
  \`pnpm nx g @empire-builder-kit/nx:sql billing --reason="Ledger requires relational constraints and reporting joins"\`
112
128
  reports the paired changes; review and repeat with \`--apply\`. The generator
@@ -125,18 +141,12 @@ not done merely because its generated file exists or the page loads.
125
141
  body: `# Develop and verify
126
142
 
127
143
  Inspect \`ebk dev doctor\` and \`ebk dev status\` using the account and Region
128
- fixed by committed repository policy and the profile saved by
129
- \`ebk dev configure\`. If doctor reports an expired or missing SSO session, run
130
- \`aws sso login --profile <profile>\`; the human approves the request in the
131
- browser, so the session stays the human's own identity. In a hosted or
132
- headless shell, add \`--use-device-code\` and the human approves the printed
133
- code from their own browser. Confirm \`aws sts get-caller-identity\` reports
134
- that identity, then continue. Never embed access keys, export, copy, or read
135
- credentials from the SSO cache, or assume a separate or stronger principal.
136
- Only an attended agent (local or hosted), one a human is actively directing,
137
- uses that session; an unattended or background agent never receives a human
138
- profile or SSO session. Resolve other readiness failures using the reported
139
- prerequisite; a doctor pass is not feature proof.
144
+ fixed by committed repository policy and the development profile saved by
145
+ \`ebk dev configure\`.
146
+
147
+ ${workspace_files_agent_development_js_1.AGENT_DEVELOPMENT_AUTHORITY}
148
+ Resolve other readiness failures using the reported prerequisite; a doctor pass
149
+ is not feature proof.
140
150
  Development accepts dirty source and does not require a production
141
151
  confirmation, release artifact or full workspace barrier.
142
152
 
@@ -178,7 +188,7 @@ browser check. Prefer \`ebk dev restart\` while iterating and reserve
178
188
  \`ebk dev down\` for the completion boundary.
179
189
 
180
190
  At the completion boundary, settle \`ebk dev down billing\`, then use
181
- \`ebk deploy development billing --profile=<approved-profile> --expectedRoleArn=<exact-role>\`.
191
+ \`ebk deploy development billing --profile=<development-profile> --expectedRoleArn=<exact-role>\`.
182
192
  This selects Foundation then Billing. Omit Slice selectors for complete-stage
183
193
  proof. Verify the deployed Router without hot processes or fallback serving it.
184
194
  Check migration, smoke and cleanup outcomes; failure is not acceptance.
@@ -199,11 +209,12 @@ mutation; source policy is authoritative if deliberately changed later.
199
209
 
200
210
  Use \`ebk deploy development\` for the complete active development stage, or
201
211
  \`ebk deploy development billing\` for Foundation plus one selected Slice.
202
- Pass the human's Identity Center \`--profile\` (typically the one saved by
203
- \`ebk dev configure\`) and the exact stage deployment role from
212
+ Pass the development profile saved by \`ebk dev configure\` as \`--profile\`;
213
+ it chains the human's Identity Center source profile into the exact development
214
+ deployment role. Pass that role from
204
215
  \`docs/stage-deployment-authority.md\` as \`--expectedRoleArn\`; verify
205
- account, Region, stage and current caller. Refresh an expired development
206
- session as in the develop/verify skill. No direct SST or AWS application
216
+ account, Region, stage and current caller. Refresh an expired source session
217
+ as in the develop/verify skill. No direct SST or AWS application
207
218
  mutation. A development deploy runs to completion with no prompt; add
208
219
  \`--json\` for one bounded result on stdout after the run (the receipt this run
209
220
  wrote and each project's outcome). Staging and production refuse \`--json\`.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * One canonical statement of what a coding agent may do in the development
3
+ * stage. The seeded README and Foundation runbook and the managed
4
+ * develop/verify skill interpolate it verbatim so the three copies cannot
5
+ * drift. Any agent, attended or unattended, may use the human's approved
6
+ * development profile for development-stage work; the protected stages,
7
+ * credential handling and denial handling stay fixed.
8
+ */
9
+ export declare const AGENT_DEVELOPMENT_AUTHORITY = "Any coding agent, attended or unattended, may run `ebk dev up`, `restart`,\n`down`, and `ebk deploy development` through EBK/Nx with the human's approved\ndevelopment profile, within the approved task, account, Region, ownership,\ncost, and cleanup limits, without repeated confirmation. The source profile is\nthe human's IAM Identity Center login profile. The development profile is the\none `ebk dev configure` saves and `--profile` receives; it chains the source\nprofile into the exact bounded development deployment role named in\n`docs/stage-deployment-authority.md`. That role assumption is expected; it\ndoes not authorize another role or broader access. An agent on another host\nreceives no copy of either profile or of the SSO cache.\nIf the source session is expired or missing, run\n`aws sso login --profile <source-profile>`, adding `--use-device-code` in a\nheadless shell. The login needs the human's browser approval, so an agent whose\nsource session has expired with no human available stops and reports.\nBefore mutation, run `aws sts get-caller-identity --profile <development-profile>`.\n`Account` must equal the committed development account, `Arn` must be\n`arn:<partition>:sts::<account>:assumed-role/ebk-development-deploy/<session>`,\nan assumed-role session of that exact role, and the Region saved by\n`ebk dev configure` must equal the committed Region. `ebk dev up` checks only\nthe account and Region; `ebk deploy development` also enforces the role.\nNever embed keys, export or copy credentials, read the SSO cache, or broaden\ntrust or policies. A protected observer profile, meaning a read-only staging or\nproduction profile the human may hold for inspection, is never a role source,\nand neither is any other staging or production profile.\nStaging and production stay GitHub-only or human-only, and agents and CI never\nrun `ebk secret set`. A new binding or authority expansion stops for a human\ndecision. A denial from the agent host, EBK, or AWS stops that mutation and\nevery equivalent route: report the exact denial and command, and do not\nre-route through Nx, SST, raw AWS, another profile, or another role. A\nhuman-started Fleet is the fallback.";
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AGENT_DEVELOPMENT_AUTHORITY = void 0;
4
+ /**
5
+ * One canonical statement of what a coding agent may do in the development
6
+ * stage. The seeded README and Foundation runbook and the managed
7
+ * develop/verify skill interpolate it verbatim so the three copies cannot
8
+ * drift. Any agent, attended or unattended, may use the human's approved
9
+ * development profile for development-stage work; the protected stages,
10
+ * credential handling and denial handling stay fixed.
11
+ */
12
+ exports.AGENT_DEVELOPMENT_AUTHORITY = `Any coding agent, attended or unattended, may run \`ebk dev up\`, \`restart\`,
13
+ \`down\`, and \`ebk deploy development\` through EBK/Nx with the human's approved
14
+ development profile, within the approved task, account, Region, ownership,
15
+ cost, and cleanup limits, without repeated confirmation. The source profile is
16
+ the human's IAM Identity Center login profile. The development profile is the
17
+ one \`ebk dev configure\` saves and \`--profile\` receives; it chains the source
18
+ profile into the exact bounded development deployment role named in
19
+ \`docs/stage-deployment-authority.md\`. That role assumption is expected; it
20
+ does not authorize another role or broader access. An agent on another host
21
+ receives no copy of either profile or of the SSO cache.
22
+ If the source session is expired or missing, run
23
+ \`aws sso login --profile <source-profile>\`, adding \`--use-device-code\` in a
24
+ headless shell. The login needs the human's browser approval, so an agent whose
25
+ source session has expired with no human available stops and reports.
26
+ Before mutation, run \`aws sts get-caller-identity --profile <development-profile>\`.
27
+ \`Account\` must equal the committed development account, \`Arn\` must be
28
+ \`arn:<partition>:sts::<account>:assumed-role/ebk-development-deploy/<session>\`,
29
+ an assumed-role session of that exact role, and the Region saved by
30
+ \`ebk dev configure\` must equal the committed Region. \`ebk dev up\` checks only
31
+ the account and Region; \`ebk deploy development\` also enforces the role.
32
+ Never embed keys, export or copy credentials, read the SSO cache, or broaden
33
+ trust or policies. A protected observer profile, meaning a read-only staging or
34
+ production profile the human may hold for inspection, is never a role source,
35
+ and neither is any other staging or production profile.
36
+ Staging and production stay GitHub-only or human-only, and agents and CI never
37
+ run \`ebk secret set\`. A new binding or authority expansion stops for a human
38
+ decision. A denial from the agent host, EBK, or AWS stops that mutation and
39
+ every equivalent route: report the exact denial and command, and do not
40
+ re-route through Nx, SST, raw AWS, another profile, or another role. A
41
+ human-started Fleet is the fallback.`;
42
+ //# sourceMappingURL=workspace-files-agent-development.js.map
@@ -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"
@@ -753,7 +759,11 @@ ${indentedScript(`set -euo pipefail\n${canonicalStageAuthorityDerivationScript()
753
759
  # production authority is derived here beside the staging authority.
754
760
  - id: production-account
755
761
  name: Derive canonical committed production authority for trunk promotion
756
- if: steps.route.outputs.stage == 'staging'
762
+ if: >-
763
+ steps.route.outputs.action == 'deploy' &&
764
+ steps.route.outputs.stage == 'staging' &&
765
+ steps.policy.outputs.deployment_profile != 'starter' &&
766
+ steps.policy.outputs.has_slices == 'true'
757
767
  env:
758
768
  EBK_DEVELOPMENT_ACCOUNT: \${{ steps.policy.outputs.development_account_id }}
759
769
  EBK_PRODUCTION_ACCOUNT: \${{ steps.policy.outputs.production_account_id }}
@@ -1665,7 +1675,10 @@ ${indentedScript(restoredCandidateValidationScript(), 10)}
1665
1675
  { echo 'Protected Blueprint authority must be one full Git object ID'; exit 1; }
1666
1676
  test "$(git rev-parse HEAD)" = "$EBK_PROTECTED_BLUEPRINT_COMMIT" ||
1667
1677
  { echo 'Protected Blueprint authority differs from the exact workflow checkout'; exit 1; }
1668
- case "$EBK_STAGE" in
1678
+ case "$EBK_STAGE" in${developmentFromProtectedDevelop
1679
+ ? `
1680
+ development) protected_branch=develop ;;`
1681
+ : ''}
1669
1682
  staging) protected_branch=develop ;;
1670
1683
  production) protected_branch=main ;;
1671
1684
  *) 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',
@@ -7656,6 +7659,7 @@ console.log('Installed the repository-local EBK pre-push hook at ' + hookPath +
7656
7659
  .open-next
7657
7660
  dist
7658
7661
  *.tsbuildinfo
7662
+ **/next-env.d.ts
7659
7663
  node_modules
7660
7664
  .sst
7661
7665
  **/playwright/.auth
@@ -7874,6 +7878,72 @@ convention, current only as far as the copy each actor reads.
7874
7878
  `,
7875
7879
  [workspace_files_shared_operation_claims_js_1.SHARED_OPERATION_CLAIMS_PATH]: (0, workspace_files_shared_operation_claims_js_1.renderSharedOperationClaims)(name),
7876
7880
  'ebk.repository-policy.json': json(initialRepositoryPolicy),
7881
+ [observability_js_1.OBSERVABILITY_POLICY_PATH]: json(options.observabilityPolicy === undefined
7882
+ ? (0, observability_js_1.defaultObservabilityPolicy)(activeStages, {
7883
+ productionProfile: initialRepositoryPolicy.productionFoundation.profile,
7884
+ })
7885
+ : (0, observability_js_1.parseObservabilityPolicy)(options.observabilityPolicy, activeStages)),
7886
+ 'docs/observability.md': `# Observability spending and diagnostics
7887
+
7888
+ The seeded ebk.observability.json is the stage-specific policy. New development
7889
+ uses cost-optimized, active staging uses balanced, and production gets an explicit
7890
+ profile initialized from its capacity choice. Later capacity changes never alter
7891
+ that seeded observability choice. Task diagnostics are lean, but explicit retention
7892
+ and dormant-log preservation still apply; local hot diagnostics remain complete.
7893
+ This policy never pauses work or
7894
+ changes database protection, encryption, backups, topology or deployment authority.
7895
+
7896
+ Cost-optimized retains 5% of marked neutral request-start/success diagnostics, WARN Lambda
7897
+ system logs and no dashboards. Balanced retains 25%, INFO system logs and one
7898
+ Foundation dashboard. Standard retains 100%, INFO system logs and Foundation plus
7899
+ Slice dashboards. All retain errors, warnings, security, migration/event receipts,
7900
+ required counters and core failure/backlog alarms. Custom metrics are not free;
7901
+ batching reduces ingestion bytes, not billable metric series.
7902
+
7903
+ Mixed application/security log groups retain at least 30 days (90 for new standard
7904
+ production). diagnosticRetentionDays is an extension default; no generated log
7905
+ group uses it. Shorter diagnostic retention applies only to separately declared
7906
+ diagnostic groups, never mixed groups. Insights, enhanced database monitoring and
7907
+ tracing have no control in this policy. Retain Standard class for EMF and generated
7908
+ receipt queries. WAF enforcement and existing request-log privacy are unchanged.
7909
+
7910
+ Upgrades from older workspaces seed explicit prior retention and verbosity; they
7911
+ do not silently adopt shorter retention or remove dormant log groups. Review this
7912
+ seeded file to opt in deliberately. Profile changes require ordinary deployment;
7913
+ generators never change AWS. Workspace upgrade comes before Foundation/Slices.
7914
+ When enabling a new canonical stage, declare it here as well as in repository policy.
7915
+
7916
+ Stage overrides: profile; routineSuccessSampleRate (0–1); diagnosticRetentionDays;
7917
+ protectedRetentionDays (AWS-supported, at least 30); dashboards (none, stage, slice);
7918
+ systemLogLevel (WARN, INFO); retainDormantComponentLogs (boolean). Declared optional
7919
+ components always keep their logs. Only absent provider/webhook components may
7920
+ omit their otherwise unused log groups and findings alarms.
7921
+
7922
+ Fractional sampling uses a server-owned ID, not client correlation/request headers.
7923
+ Request starts are neutral, never successful. Legacy/standard retain an entry
7924
+ breadcrumb even for handlers that never return; cheap profiles may omit routine
7925
+ starts, but failures and receipts remain unsampled.
7926
+
7927
+ Custom reliability labels belong in each Slice's seeded reliability/metric-declarations.ts.
7928
+ Declare finite component/feature/operation values, never user, tenant, request or
7929
+ resource identifiers. Keep high-cardinality context in redacted logs. Only reviewed
7930
+ routine INFO successes may use routineSuccess; never mark receipts or security events.
7931
+ Upgrades flag the new declaration requirement with a reconciliation work marker.
7932
+ Undeclared tuples and invalid observations produce bounded reason-only warnings;
7933
+ invalid latency never suppresses the valid operation counters.
7934
+
7935
+ At low traffic, fixed dashboards, alarms and metric series can dominate the bill.
7936
+ Profile savings primarily change dashboard inventory; fresh disabled adapters omit
7937
+ two dormant groups and findings alarms. Sampling/batching affect ingestion and
7938
+ scanning volume, while retention affects storage only where applied. No live
7939
+ saving or free-tier discount is implied by the list-price estimate.
7940
+
7941
+ Diagnostics are on demand. Budget CloudWatch API metric retrieval as well as
7942
+ alarms, storage, ingestion, scanning and custom series. Narrow queries to the
7943
+ exact owned stage/resources/window; account-wide repeated sweeps are not a
7944
+ default application behavior. Never reduce a safety check's coverage or cadence
7945
+ to hide its cost. Measure actual billing after deployment, not just estimates.
7946
+ `,
7877
7947
  'nx.json': json({
7878
7948
  $schema: './node_modules/nx/schemas/nx-schema.json',
7879
7949
  defaultBase: 'develop',
@@ -8222,6 +8292,7 @@ ${foundationPlan.resources.postgres ? ' foundationDatabaseEndpointHost,' : ''}
8222
8292
  foundationSsmPath,
8223
8293
  normalizeRouterWafPolicy,
8224
8294
  reconcileRouterWafBlockingRehearsal,
8295
+ resolveObservabilityPolicy,
8225
8296
  routerWafContractDigest,
8226
8297
  type FoundationSsmKey,
8227
8298
  } from '@empire-builder-kit/nx/foundation';
@@ -8242,6 +8313,7 @@ import routerWafBlockingRehearsal from '../security/router-waf-blocking-rehearsa
8242
8313
  import routerWafLiveEvidence from '../security/router-waf-live-evidence.json';
8243
8314
  import routerWafPolicy from '../security/router-waf.json';
8244
8315
  import repositoryPolicy from '../../../ebk.repository-policy.json';
8316
+ import observabilityPolicy from '../../../ebk.observability.json';
8245
8317
 
8246
8318
  function identityDomainPrefix(stage: string, accountId: string): string {
8247
8319
  if (!/^\\d{12}$/.test(accountId)) {
@@ -8277,6 +8349,11 @@ function publishFoundation(values: Partial<Record<FoundationSsmKey, any>>) {
8277
8349
 
8278
8350
  export function foundation() {
8279
8351
  const isProduction = $app.stage === 'production';
8352
+ const observability = resolveObservabilityPolicy({
8353
+ policy: observabilityPolicy,
8354
+ stage: $app.stage,
8355
+ productionProfile: process.env.EBK_PRODUCTION_FOUNDATION_PROFILE,
8356
+ });
8280
8357
  ${foundationPlan.resources.postgres ? " const isStaging = $app.stage === 'staging';" : ''}
8281
8358
  const rootDomain = process.env.EBK_ROOT_DOMAIN?.trim();
8282
8359
  const productionConfiguration = isProduction
@@ -8289,36 +8366,38 @@ ${foundationPlan.resources.network ? " const natMode = productionConfiguration?
8289
8366
  if (repositoryPolicy.deploymentProfile !== normalizedRouterWafPolicy.deploymentProfile) {
8290
8367
  throw new Error('Router WAF policy profile differs from committed repository policy.');
8291
8368
  }
8292
- const rehearsalStage = productionRehearsalStage(normalizedRouterWafPolicy.deploymentProfile);
8293
- const rehearsalAccount = configuredDeploymentAccountForStage(
8294
- normalizedRouterWafPolicy.deploymentProfile,
8295
- repositoryPolicy.deploymentAccounts,
8296
- rehearsalStage,
8297
- );
8298
- const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
8299
- const rehearsalRoleArn = createStageAuthorityIdentity({
8300
- accountId: rehearsalAccount,
8301
- partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
8302
- region: rehearsalRegion,
8303
- stage: rehearsalStage,
8304
- }).deploymentRoleArn;
8369
+ let rehearsal: ReturnType<typeof reconcileRouterWafBlockingRehearsal> = null;
8370
+ if (isProduction) {
8371
+ const rehearsalStage = productionRehearsalStage(normalizedRouterWafPolicy.deploymentProfile);
8372
+ const rehearsalAccount = configuredDeploymentAccountForStage(
8373
+ normalizedRouterWafPolicy.deploymentProfile,
8374
+ repositoryPolicy.deploymentAccounts,
8375
+ rehearsalStage,
8376
+ );
8377
+ const rehearsalRegion = configuredDeploymentRegion(repositoryPolicy.deploymentRegion);
8378
+ const rehearsalRoleArn = createStageAuthorityIdentity({
8379
+ accountId: rehearsalAccount,
8380
+ partition: rehearsalRegion.startsWith('us-gov-') ? 'aws-us-gov' : 'aws',
8381
+ region: rehearsalRegion,
8382
+ stage: rehearsalStage,
8383
+ }).deploymentRoleArn;
8384
+ rehearsal = reconcileRouterWafBlockingRehearsal({
8385
+ evidence: routerWafLiveEvidence,
8386
+ expected: {
8387
+ accountId: rehearsalAccount,
8388
+ deploymentRoleArn: rehearsalRoleArn,
8389
+ region: rehearsalRegion,
8390
+ stage: rehearsalStage,
8391
+ wafContractDigest: routerWafContractDigest(normalizedRouterWafPolicy),
8392
+ workspace: '${name}',
8393
+ },
8394
+ policy: normalizedRouterWafPolicy,
8395
+ receipt: routerWafBlockingRehearsal,
8396
+ });
8397
+ }
8305
8398
  const wafPlan = createRouterWafStagePlan({
8306
8399
  policy: normalizedRouterWafPolicy,
8307
- rehearsal: isProduction
8308
- ? reconcileRouterWafBlockingRehearsal({
8309
- evidence: routerWafLiveEvidence,
8310
- expected: {
8311
- accountId: rehearsalAccount,
8312
- deploymentRoleArn: rehearsalRoleArn,
8313
- region: rehearsalRegion,
8314
- stage: rehearsalStage,
8315
- wafContractDigest: routerWafContractDigest(normalizedRouterWafPolicy),
8316
- workspace: '${name}',
8317
- },
8318
- policy: normalizedRouterWafPolicy,
8319
- receipt: routerWafBlockingRehearsal,
8320
- })
8321
- : null,
8400
+ rehearsal,
8322
8401
  stage: $app.stage,
8323
8402
  });
8324
8403
  const deploymentAccount = process.env.AWS_EXPECTED_ACCOUNT_ID?.trim() ?? '';
@@ -8753,7 +8832,7 @@ ${foundationPlan.resources.postgres
8753
8832
  { dependsOn: [alertTopicPolicy] },
8754
8833
  )
8755
8834
  : undefined;
8756
- const dashboard = new aws.cloudwatch.Dashboard('FoundationDashboard', {
8835
+ const dashboard = observability.dashboards === 'none' ? undefined : new aws.cloudwatch.Dashboard('FoundationDashboard', {
8757
8836
  dashboardName: 'ebk-' + $app.stage + '-foundation',
8758
8837
  dashboardBody: databaseInstanceId.apply((identifier: string) => JSON.stringify({
8759
8838
  widgets: [
@@ -8824,12 +8903,18 @@ ${foundationPlan.resources.postgres
8824
8903
  treatMissingData: 'notBreaching',
8825
8904
  },
8826
8905
  );`
8827
- : ` const dashboard = new aws.cloudwatch.Dashboard('FoundationDashboard', {
8906
+ : ` const dashboard = observability.dashboards === 'none' ? undefined : new aws.cloudwatch.Dashboard('FoundationDashboard', {
8828
8907
  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
- }] }),
8908
+ dashboardBody: router.distributionID.apply((distributionId: string) => JSON.stringify({ widgets: [{
8909
+ type: 'metric', x: 0, y: 0, width: 24, height: 6,
8910
+ properties: {
8911
+ title: 'Router requests and errors', region: 'us-east-1', period: 300,
8912
+ metrics: [
8913
+ ['AWS/CloudFront', 'Requests', 'DistributionId', distributionId, 'Region', 'Global', { stat: 'Sum' }],
8914
+ ['AWS/CloudFront', '5xxErrorRate', 'DistributionId', distributionId, 'Region', 'Global', { stat: 'Average', yAxis: 'right' }],
8915
+ ],
8916
+ },
8917
+ }] })),
8833
8918
  });`}
8834
8919
 
8835
8920
  if (new Set(['development', 'staging', 'production']).has($app.stage)) {
@@ -8879,7 +8964,7 @@ ${foundationPlan.resources.containerCompute ? ' clusterId: cluster.id,' : ''}
8879
8964
  contractVersion: '${foundationPlan.descriptor.contractVersion}',
8880
8965
  costAnomalySubscriptionArn: accountCostAnomalySubscription?.arn,
8881
8966
  ${foundationPlan.resources.postgres ? ' databaseCapacityAlarmName: databaseCapacityAlarm.name,' : ''}
8882
- dashboardName: dashboard.dashboardName,
8967
+ dashboardName: dashboard?.dashboardName,
8883
8968
  ${foundationPlan.resources.postgres
8884
8969
  ? ` databaseClusterId: database.id,
8885
8970
  databaseEventSubscriptionId: databaseEvents?.id,`