paused/slug-budget-24
needs attention2c88a05 · fullpre-PRreviewed 2026-08-11 18:49 UTC3H · 7M · 11L · 14I- Purpose
- Raises the CDK preview-branch slug budget from 17 to 24 descriptive chars so feature branches with conventional type prefixes (feat/, fix/, etc.) get the full budget for the meaningful part of their name.
- Goal
- Eliminate premature CDK-preview skips caused by prefix overhead, while maintaining the 64-char AWS resource name limit via a token abbreviation pass and a synth-time enforcement aspect.
- Sub-goals
- SG-1: Raise MAX_BRANCH_NAME_LEN 17→24 and strip type prefix in boundedBranchSlug (naming.ts + preview-provision.yml)
- SG-2: Add BRANCH_TOKEN_ABBREV + BRANCH_EXACT_ABBREV abbreviation pass for branched physical names
- SG-3: Add BranchNameLengthAspect — synth-time guard that fails the build for over-budget new resource names
- SG-4: Add audit-resource-name-lengths.mjs — developer tool for inspecting name budgets from cdk.out
- SG-5: Update the platform twin (metrics-explore-core.ts) to match new boundedBranchSlug semantics
- SG-6: Update IAM wildcards in vercel-service-user.stack.ts for both helioscope and helio (abbreviated) spellings
- SG-7: Doc sweep — CLAUDE.md, infra/cdk/CLAUDE.md, deploy-cdk, branch-provisioning, loop-autofix, cfe-pipeline, infrastructure rules
- What
- naming.ts: MAX_BRANCH_NAME_LEN=24 (exported), BRANCH_TOKEN_ABBREV + BRANCH_EXACT_ABBREV maps, abbreviateForBranch(), type-prefix stripping in boundedBranchSlug. resourceName/s3BucketName apply abbreviation when branched. New BranchNameLengthAspect and audit script. Platform twin updated. GHA slug gate updated (descriptive part). IAM policy gains abbreviated helio ARNs.
- Why
- The previous 17-char hard cap counted type prefixes (feat/fix/etc.) against the budget, forcing developers to choose cryptic branch names. The new system strips the type prefix from physical names only, giving the full 24-char budget to the descriptive part.
- Areas
- infra/cdk/src/lib/naming.ts+75−13infra/cdk/src/aspects/branch-name-length.ts+75−0infra/cdk/scripts/audit-resource-name-lengths.mjs+69−0infra/cdk/src/app/main.ts+10−1apps/platform/src/api/utils/metrics-explore-core.ts+17−6apps/platform/src/api/utils/__tests__/metrics-explore-core.test.ts+22−15.github/workflows/preview-provision.yml+14−9infra/cdk/src/stacks/cicd/vercel-service-user.stack.ts+11−0.claude/skills/deploy-cdk/SKILL.md+17−12CLAUDE.md+1−1
- Blast
- 19 files, +326/−88. CDK naming logic (all branched preview stacks), platform ARN computation (metrics exploration), GHA preview-provision gate, and doc sweep.
Findings · 24
correctness4
Dead alternations in SLUG_CORE sed (chore/spike/docs/test/refactor/perf/ci — only apply on workflow_dispatch)
.github/workflows/preview-provision.yml:736
Push triggers only on feat/**, fix/**, hotfix/**, feature/**. The extra alternations are safely inert on automatic pushes; they do fire on manual dispatch. Not a bug.
Metrics-explore-core.ts hardcodes 24 (twin drift risk, documented)
apps/platform/src/api/utils/metrics-explore-core.ts:64
Overlaps with conventions finding above.
feature/** trigger branch does not get free-prefix budget treatment (consistent, but may surprise)
.github/workflows/preview-provision.yml:9
Workflow triggers on feature/** but strips only feat- not feature-. behavior is consistent with naming.ts (both exclude 'feature'). CLAUDE.md does not call this out.
BranchNameLengthAspect correctly skips auto-named CFN resources
infra/cdk/src/aspects/branch-name-length.ts:54
Auto-named resources have undefined for the name property and are silently skipped. They can never match batu-{slug}-... so this is intentionally correct.
security4
Lambda IAM ARNs use leading wildcard (*metrics-helio-*) — accepted pre-existing pattern
infra/cdk/src/stacks/cicd/vercel-service-user.stack.ts:161
Same pattern as pre-existing helioscope entries. Mitigation: add batu-*-dev- prefix to anchor ARNs to org naming convention.
S3 helioscope intake ARNs lack account suffix (pre-existing gap, this PR adds abbreviated form only)
infra/cdk/src/stacks/cicd/vercel-service-user.stack.ts:129
Bills/CSV-export S3 grants use account suffix (last 4 digits); helioscope intake does not. This PR adds the abbreviated *metrics-helio-intake* form. Recommend adding account suffix in a follow-up hardening PR.
IAM dual-spelling (full + abbreviated) is intentional and correctly documented
infra/cdk/src/stacks/cicd/vercel-service-user.stack.ts:125
Un-branched envs keep full names; preview branches get abbreviated names. Both spellings needed in the permanent policy. Sync obligation documented in comments.
powerradar has no Vercel IAM invoke grant — consistent with current invocation model
infra/cdk/src/lib/naming.ts:36
powerradar is invoked by metrics coordinator SFN, not Vercel runtime. No IAM gap today.
conventions2
feat/x + fix/x physical-name collision caveat absent from branch-provisioning SKILL.md
.claude/skills/branch-provisioning/SKILL.md:36
CLAUDE.md and infra/cdk/CLAUDE.md document the caveat: two branches differing only by type prefix share physical resource names — second CDK deploy fails loudly. branch-provisioning is the primary surface where developers pick names; the caveat is missing. Add a ⚠️ note.
Platform twin hardcodes `24` instead of a named constant (MAX_BRANCH_NAME_LEN drift risk)
apps/platform/src/api/utils/metrics-explore-core.ts:64
TWIN comment documents this. Not a current bug — one budget-change away from silent ARN mismatch. A @batu/naming-constants package with only primitives (no CDK dep) would fix this.
tests5
No tests for CDK-side boundedBranchSlug — authoritative implementation is untested
infra/cdk/src/lib/naming.ts:79
The platform twin (metrics-explore-core.ts) gained 3 unit tests; naming.ts — the source of truth — has zero. If naming.ts diverges from the twin, tests pass while deployed ARNs point at non-existent state machines. A small Vitest suite (no CDK synth needed) should cover prefix stripping, verbatim ≤24, and head18-sha4 branches.
abbreviateForBranch is untested — IAM wildcard correctness depends on it
infra/cdk/src/lib/naming.ts:52
IAM patterns in vercel-service-user.stack.ts (*metrics-helio-materialize etc.) are derived from BRANCH_TOKEN_ABBREV + BRANCH_EXACT_ABBREV. Neither abbreviateForBranch nor the exact-then-token ordering has a test. A stale BRANCH_EXACT_ABBREV key or token-map change produces the wrong Lambda name with no compile-time signal.
BranchNameLengthAspect is untested — synth-time gate has no regression coverage
infra/cdk/src/aspects/branch-name-length.ts:1
The aspect's fixedLen arithmetic (fixedLen = name.length - this.slug.length) is the heart of the enforcement gate. An off-by-one would pass synth while exceeding the real AWS cap on a 24-char slug. A unit test with a mock CfnResource would catch this.
Audit script not integrated into CI — NAME_LIMITS sync is comment-only
infra/cdk/scripts/audit-resource-name-lengths.mjs
The audit script requires a pre-synthesised cdk.out and is not called by any workflow. NAME_PROPS must stay in sync with BranchNameLengthAspect's NAME_LIMITS by convention only. A CI step or parse-comparison test would catch table drift.
Platform test suite missing edge cases (slash-form regex, degenerate strip, 25-char no-prefix, hotfix)
apps/platform/src/api/utils/__tests__/metrics-explore-core.test.ts
Missing: (1) 'feat/my-feature' with literal slash (BRANCH_TYPE_PREFIX uses [-/] but tests only use hyphen form); (2) degenerate 'feat-' → empty stripped result (fallback path); (3) no-prefix branch at exactly 25 chars (should hash); (4) 'hotfix-something' (nested 'fix' substring).
improvement9
NAME_LIMITS duplicated between BranchNameLengthAspect and audit script with no shared source
infra/cdk/src/aspects/branch-name-length.ts:22
Identical CFN type → [property, limit] table in two files, synced by comment only. Audit script gives false green budget for missing resource types while the Aspect still enforces. A compiled-to-ESM helper or CI diff check would remove the manual obligation.
BRANCH_TYPE_PREFIX regex duplicated across three locations
apps/platform/src/api/utils/metrics-explore-core.ts:58
Appears in naming.ts, metrics-explore-core.ts, and preview-provision.yml (sed variant without '/' since SLUG is pre-slugified). Adding a new type prefix requires 3 edits. A canonical JSON list imported by naming.ts and consumed by the workflow via jq would eliminate drift.
BranchNameLengthAspect uses camelCase field reflection rather than cfnProperties (official API)
infra/cdk/src/aspects/branch-name-length.ts:51
node.cfnProperties is the documented CDK introspection surface; using propertyFieldFor() relies on L1 field-naming convention being stable. Functionally correct for all types in NAME_LIMITS.
BRANCH_EXACT_ABBREV stale key risk — TypeScript cannot catch a key that stops matching
infra/cdk/src/lib/naming.ts:46
A token-map change that renames 'helioscope' would silently un-key the EXACT entry with no TypeScript error. A unit test for abbreviateForBranch covering exact-then-token ordering would make this resilient.
No CDK-side naming.ts unit tests — tests live on the wrong side of the twin
infra/cdk/src/lib/naming.ts:1
Platform twin has tests; CDK source of truth does not. abbreviateForBranch, BRANCH_EXACT_ABBREV ordering, and token map are tested only by full CDK synth. A Vitest suite for naming.ts would be CDK-synth-free and fast.
powerradar→radar abbreviation could collide with a hypothetical future 'radar' integration
infra/cdk/src/lib/naming.ts:37
Audit script duplicate-detection is the safety net. No current collision.
Deprovision workflow comment updated but correctly has no slug-length gate
.github/workflows/preview-deprovision.yml:247
Deprovision uses delete-stack by name, not re-synth. Over-budget slugs never deployed, so delete is a no-op. Comment could note this asymmetry.
Twin pattern is the right call; @batu/naming-utils extraction is low-urgency
apps/platform/src/api/utils/metrics-explore-core.ts:53
CDK cannot be imported by Next.js. A @batu/naming-constants package with only primitives (no CDK dep) would reduce the twin to a thin wrapper. Low urgency.
scripts/*.mjs eslint ignore is intentional and correct for current directory structure
infra/cdk/eslint.config.js:35
Only one .mjs file currently in infra/cdk/scripts/. Pattern covers top-level only; subdirectory .mjs files would need a separate pattern. Correct as-is.