From 9505446bea2a3fc7346ee3b871a1db83764cc23e Mon Sep 17 00:00:00 2001 From: bgagent Date: Fri, 8 May 2026 17:13:46 -0700 Subject: [PATCH 1/8] fix: allow stackName to change Signed-off-by: bgagent --- cdk/src/main.ts | 8 +++++++- cdk/src/stacks/agent.ts | 4 ++-- cdk/test/stacks/agent.test.ts | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cdk/src/main.ts b/cdk/src/main.ts index b64838e4..f3bbb15c 100644 --- a/cdk/src/main.ts +++ b/cdk/src/main.ts @@ -31,9 +31,15 @@ const app = new App(); Aspects.of(app).add(new AwsSolutionsChecks()); +const stackName = app.node.tryGetContext('stackName') ?? 'backgroundagent-dev'; + +if (stackName.length > 40) { + throw new Error(`stackName must be at most 40 characters (got ${stackName.length}). This limit ensures derived resource names (e.g. AgentCore runtime) stay within AWS service limits.`); +} + new AgentStack( app, - 'backgroundagent-dev', + stackName, { env: devEnv, description: 'ABCA Development Stack', diff --git a/cdk/src/stacks/agent.ts b/cdk/src/stacks/agent.ts index 74443e58..e78898fd 100644 --- a/cdk/src/stacks/agent.ts +++ b/cdk/src/stacks/agent.ts @@ -119,7 +119,7 @@ export class AgentStack extends Stack { }, ]); - const runtimeName = 'jean_cloude'; + const runtimeName = this.stackName.replace(/-/g, '_'); // Log groups (created before runtime so we can reference the name in env vars) const applicationLogGroup = new logs.LogGroup(this, 'RuntimeApplicationLogGroup', { @@ -618,7 +618,7 @@ export class AgentStack extends Stack { // --- Bedrock model invocation logging (account-level) --- const invocationLogGroup = new logs.LogGroup(this, 'ModelInvocationLogGroup', { - logGroupName: '/aws/bedrock/model-invocation-logs', + logGroupName: `/aws/bedrock/model-invocation-logs/${this.stackName}`, retention: logs.RetentionDays.THREE_MONTHS, removalPolicy: RemovalPolicy.DESTROY, }); diff --git a/cdk/test/stacks/agent.test.ts b/cdk/test/stacks/agent.test.ts index 8bb86218..f62d291f 100644 --- a/cdk/test/stacks/agent.test.ts +++ b/cdk/test/stacks/agent.test.ts @@ -296,7 +296,7 @@ describe('AgentStack', () => { test('creates a log group for model invocation logs', () => { template.hasResourceProperties('AWS::Logs::LogGroup', { - LogGroupName: '/aws/bedrock/model-invocation-logs', + LogGroupName: '/aws/bedrock/model-invocation-logs/TestAgentStack', RetentionInDays: 90, }); }); From d2773bd17232163d2d675fb50d0b959dcdc95871 Mon Sep 17 00:00:00 2001 From: bgagent <345885+scottschreckengaust@users.noreply.github.com> Date: Sat, 9 May 2026 02:23:04 +0000 Subject: [PATCH 2/8] fix: self-guard resource names to prevent multi-stack collisions Each resource with a name uniqueness constraint now truncates to its own API limit, removing the need for a central stackName length check: - Guardrail: ${stackName}-guardrail (.slice(0, 50)) - Memory: mem_${stackName} (.slice(0, 48)) - Runtime: sanitize + .slice(0, 48), ensure starts with letter - Model invocation logging: remove onDelete (account-level singleton) Co-Authored-By: Claude Opus 4.6 (1M context) --- cdk/src/constructs/agent-memory.ts | 4 ++-- cdk/src/main.ts | 4 ---- cdk/src/stacks/agent.ts | 12 ++++-------- cdk/test/constructs/agent-memory.test.ts | 4 ++-- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/cdk/src/constructs/agent-memory.ts b/cdk/src/constructs/agent-memory.ts index 9af312bd..96f006bd 100644 --- a/cdk/src/constructs/agent-memory.ts +++ b/cdk/src/constructs/agent-memory.ts @@ -18,7 +18,7 @@ */ import * as agentcore from '@aws-cdk/aws-bedrock-agentcore-alpha'; -import { Duration } from 'aws-cdk-lib'; +import { Duration, Stack } from 'aws-cdk-lib'; import type * as iam from 'aws-cdk-lib/aws-iam'; import { NagSuppressions } from 'cdk-nag'; import { Construct } from 'constructs'; @@ -70,7 +70,7 @@ export class AgentMemory extends Construct { super(scope, id); this.memory = new agentcore.Memory(this, 'Memory', { - memoryName: props?.memoryName ?? 'bgagent_memory', + memoryName: props?.memoryName ?? `mem_${Stack.of(this).stackName.replace(/[^a-zA-Z0-9]/g, '_')}`.slice(0, 48), description: 'Cross-task interaction memory for background coding agents', expirationDuration: props?.expirationDuration ?? Duration.days(365), memoryStrategies: [ diff --git a/cdk/src/main.ts b/cdk/src/main.ts index f3bbb15c..880dd005 100644 --- a/cdk/src/main.ts +++ b/cdk/src/main.ts @@ -33,10 +33,6 @@ Aspects.of(app).add(new AwsSolutionsChecks()); const stackName = app.node.tryGetContext('stackName') ?? 'backgroundagent-dev'; -if (stackName.length > 40) { - throw new Error(`stackName must be at most 40 characters (got ${stackName.length}). This limit ensures derived resource names (e.g. AgentCore runtime) stay within AWS service limits.`); -} - new AgentStack( app, stackName, diff --git a/cdk/src/stacks/agent.ts b/cdk/src/stacks/agent.ts index e78898fd..bdfd9253 100644 --- a/cdk/src/stacks/agent.ts +++ b/cdk/src/stacks/agent.ts @@ -119,7 +119,7 @@ export class AgentStack extends Stack { }, ]); - const runtimeName = this.stackName.replace(/-/g, '_'); + const runtimeName = this.stackName.replace(/[^a-zA-Z0-9]/g, '_').replace(/^[^a-zA-Z]/, 'r').slice(0, 48); // Log groups (created before runtime so we can reference the name in env vars) const applicationLogGroup = new logs.LogGroup(this, 'RuntimeApplicationLogGroup', { @@ -164,7 +164,7 @@ export class AgentStack extends Stack { // --- Bedrock Guardrail for prompt injection detection --- // (Declared early so TaskApi — constructed before the runtimes — can reference it.) const inputGuardrail = new bedrock.Guardrail(this, 'InputGuardrail', { - guardrailName: 'task-input-guardrail', + guardrailName: `${this.stackName}-guardrail`.slice(0, 50), description: 'Screens task submissions for prompt injection attacks', contentFilters: [ { @@ -671,12 +671,8 @@ export class AgentStack extends Stack { physicalResourceId: cr.PhysicalResourceId.of('bedrock-invocation-logging'), ignoreErrorCodesMatching: '.*', }, - onDelete: { - service: 'Bedrock', - action: 'deleteModelInvocationLoggingConfiguration', - parameters: {}, - ignoreErrorCodesMatching: '.*', - }, + // onDelete intentionally omitted — model invocation logging is account-level; + // deleting one stack should not disable logging that another stack relies on. policy: cr.AwsCustomResourcePolicy.fromStatements([ new iam.PolicyStatement({ actions: [ diff --git a/cdk/test/constructs/agent-memory.test.ts b/cdk/test/constructs/agent-memory.test.ts index 09407fae..5be12f70 100644 --- a/cdk/test/constructs/agent-memory.test.ts +++ b/cdk/test/constructs/agent-memory.test.ts @@ -42,10 +42,10 @@ describe('AgentMemory construct', () => { template.resourceCountIs('AWS::BedrockAgentCore::Memory', 1); }); - test('uses default memory name', () => { + test('uses default memory name derived from stack name', () => { const { template } = createStack(); template.hasResourceProperties('AWS::BedrockAgentCore::Memory', { - Name: 'bgagent_memory', + Name: 'mem_TestStack', }); }); From 940e23221a5564c6e2daa721888cc708d50f7dfa Mon Sep 17 00:00:00 2001 From: bgagent <345885+scottschreckengaust@users.noreply.github.com> Date: Sat, 9 May 2026 02:27:03 +0000 Subject: [PATCH 3/8] fix(security): allowlist test fixture in gitleaks The Slack verification test uses 'test-signing-secret-abc123' which triggers gitleaks generic-api-key rule. Not a real credential. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitleaks.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitleaks.toml b/.gitleaks.toml index df18debb..01251357 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -14,3 +14,7 @@ paths = [ [[allowlists]] description = "Mock workload access token in CDK handler tests (not a real credential)." stopwords = ["wat-opaque-123"] + +[[allowlists]] +description = "Test fixture signing secret in Slack verification unit test (not a real credential)." +stopwords = ["test-signing-secret-abc123"] From 8ec933495093309dc41dcbe68e90b44a763ecf36 Mon Sep 17 00:00:00 2001 From: Scott Schreckengaust Date: Fri, 8 May 2026 22:48:35 -0700 Subject: [PATCH 4/8] fix: keep name similar --- cdk/src/stacks/agent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/src/stacks/agent.ts b/cdk/src/stacks/agent.ts index bdfd9253..7f64d2e5 100644 --- a/cdk/src/stacks/agent.ts +++ b/cdk/src/stacks/agent.ts @@ -164,7 +164,7 @@ export class AgentStack extends Stack { // --- Bedrock Guardrail for prompt injection detection --- // (Declared early so TaskApi — constructed before the runtimes — can reference it.) const inputGuardrail = new bedrock.Guardrail(this, 'InputGuardrail', { - guardrailName: `${this.stackName}-guardrail`.slice(0, 50), + guardrailName: `task-input-guardrail-${this.stackName}`.slice(0, 50), description: 'Screens task submissions for prompt injection attacks', contentFilters: [ { From 00e6f4c656c6b97fa6635041b953343bd20975ad Mon Sep 17 00:00:00 2001 From: Scott Schreckengaust Date: Fri, 8 May 2026 22:52:59 -0700 Subject: [PATCH 5/8] fix: jean cloude --- cdk/src/stacks/agent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/src/stacks/agent.ts b/cdk/src/stacks/agent.ts index 7f64d2e5..e3da7de5 100644 --- a/cdk/src/stacks/agent.ts +++ b/cdk/src/stacks/agent.ts @@ -119,7 +119,7 @@ export class AgentStack extends Stack { }, ]); - const runtimeName = this.stackName.replace(/[^a-zA-Z0-9]/g, '_').replace(/^[^a-zA-Z]/, 'r').slice(0, 48); + const runtimeName = `jean_cloude_${this.stackName}`.replace(/[^a-zA-Z0-9]/g, '_').replace(/^[^a-zA-Z]/, 'r').slice(0, 48); // Log groups (created before runtime so we can reference the name in env vars) const applicationLogGroup = new logs.LogGroup(this, 'RuntimeApplicationLogGroup', { From 74987b0f82bd7fb40e29ee2526757966773ca028 Mon Sep 17 00:00:00 2001 From: Scott Schreckengaust Date: Fri, 8 May 2026 22:57:12 -0700 Subject: [PATCH 6/8] fix: bgagent_memory --- cdk/src/constructs/agent-memory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/src/constructs/agent-memory.ts b/cdk/src/constructs/agent-memory.ts index 96f006bd..394f8094 100644 --- a/cdk/src/constructs/agent-memory.ts +++ b/cdk/src/constructs/agent-memory.ts @@ -70,7 +70,7 @@ export class AgentMemory extends Construct { super(scope, id); this.memory = new agentcore.Memory(this, 'Memory', { - memoryName: props?.memoryName ?? `mem_${Stack.of(this).stackName.replace(/[^a-zA-Z0-9]/g, '_')}`.slice(0, 48), + memoryName: props?.memoryName ?? `bgagent_memory_${Stack.of(this).stackName.replace(/[^a-zA-Z0-9]/g, '_')}`.slice(0, 48), description: 'Cross-task interaction memory for background coding agents', expirationDuration: props?.expirationDuration ?? Duration.days(365), memoryStrategies: [ From a3686cf62bcb9aafc4cf881031b7389a1af89d49 Mon Sep 17 00:00:00 2001 From: bgagent <345885+scottschreckengaust@users.noreply.github.com> Date: Sat, 9 May 2026 17:13:15 +0000 Subject: [PATCH 7/8] fix(test): update memory name assertion to match new prefix The default memory name changed from `mem_` to `bgagent_memory_` prefix. Co-Authored-By: Claude Opus 4.6 (1M context) --- cdk/test/constructs/agent-memory.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdk/test/constructs/agent-memory.test.ts b/cdk/test/constructs/agent-memory.test.ts index 5be12f70..fbb49d11 100644 --- a/cdk/test/constructs/agent-memory.test.ts +++ b/cdk/test/constructs/agent-memory.test.ts @@ -45,7 +45,7 @@ describe('AgentMemory construct', () => { test('uses default memory name derived from stack name', () => { const { template } = createStack(); template.hasResourceProperties('AWS::BedrockAgentCore::Memory', { - Name: 'mem_TestStack', + Name: 'bgagent_memory_TestStack', }); }); From 8d0a110f4948dd62faffebd96b4ba37046e67782 Mon Sep 17 00:00:00 2001 From: bgagent <345885+scottschreckengaust@users.noreply.github.com> Date: Mon, 11 May 2026 19:23:12 +0000 Subject: [PATCH 8/8] fix: let CDK auto-generate Runtime and Memory names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove explicit runtimeName and memoryName — the L2 constructs auto-generate unique names via Names.uniqueResourceName() from the construct path (which includes the stack name), eliminating name collisions entirely without manual .slice() guards. Only the guardrail name remains explicit (required by the L2 API). Log group names retain stackName for operational discoverability. Co-Authored-By: Claude Opus 4.6 (1M context) --- cdk/src/constructs/agent-memory.ts | 4 ++-- cdk/src/stacks/agent.ts | 7 ++----- cdk/test/constructs/agent-memory.test.ts | 4 ++-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/cdk/src/constructs/agent-memory.ts b/cdk/src/constructs/agent-memory.ts index 394f8094..7ba4de66 100644 --- a/cdk/src/constructs/agent-memory.ts +++ b/cdk/src/constructs/agent-memory.ts @@ -18,7 +18,7 @@ */ import * as agentcore from '@aws-cdk/aws-bedrock-agentcore-alpha'; -import { Duration, Stack } from 'aws-cdk-lib'; +import { Duration } from 'aws-cdk-lib'; import type * as iam from 'aws-cdk-lib/aws-iam'; import { NagSuppressions } from 'cdk-nag'; import { Construct } from 'constructs'; @@ -70,7 +70,7 @@ export class AgentMemory extends Construct { super(scope, id); this.memory = new agentcore.Memory(this, 'Memory', { - memoryName: props?.memoryName ?? `bgagent_memory_${Stack.of(this).stackName.replace(/[^a-zA-Z0-9]/g, '_')}`.slice(0, 48), + memoryName: props?.memoryName, description: 'Cross-task interaction memory for background coding agents', expirationDuration: props?.expirationDuration ?? Duration.days(365), memoryStrategies: [ diff --git a/cdk/src/stacks/agent.ts b/cdk/src/stacks/agent.ts index e3da7de5..7d5c3d08 100644 --- a/cdk/src/stacks/agent.ts +++ b/cdk/src/stacks/agent.ts @@ -119,17 +119,15 @@ export class AgentStack extends Stack { }, ]); - const runtimeName = `jean_cloude_${this.stackName}`.replace(/[^a-zA-Z0-9]/g, '_').replace(/^[^a-zA-Z]/, 'r').slice(0, 48); - // Log groups (created before runtime so we can reference the name in env vars) const applicationLogGroup = new logs.LogGroup(this, 'RuntimeApplicationLogGroup', { - logGroupName: `/aws/vendedlogs/bedrock-agentcore/runtime/APPLICATION_LOGS/${runtimeName}`, + logGroupName: `/aws/vendedlogs/bedrock-agentcore/runtime/APPLICATION_LOGS/${this.stackName}`, retention: logs.RetentionDays.THREE_MONTHS, removalPolicy: RemovalPolicy.DESTROY, }); const usageLogGroup = new logs.LogGroup(this, 'RuntimeUsageLogGroup', { - logGroupName: `/aws/vendedlogs/bedrock-agentcore/runtime/USAGE_LOGS/${runtimeName}`, + logGroupName: `/aws/vendedlogs/bedrock-agentcore/runtime/USAGE_LOGS/${this.stackName}`, retention: logs.RetentionDays.THREE_MONTHS, removalPolicy: RemovalPolicy.DESTROY, }); @@ -284,7 +282,6 @@ export class AgentStack extends Stack { // AgentCore's account-level runtimeName uniqueness and triggering an // UPDATE_ROLLBACK. const runtime = new agentcore.Runtime(this, 'Runtime', { - runtimeName, agentRuntimeArtifact: artifact, networkConfiguration: runtimeNetworkConfig, environmentVariables: runtimeEnvironmentVariables, diff --git a/cdk/test/constructs/agent-memory.test.ts b/cdk/test/constructs/agent-memory.test.ts index fbb49d11..9512e129 100644 --- a/cdk/test/constructs/agent-memory.test.ts +++ b/cdk/test/constructs/agent-memory.test.ts @@ -42,10 +42,10 @@ describe('AgentMemory construct', () => { template.resourceCountIs('AWS::BedrockAgentCore::Memory', 1); }); - test('uses default memory name derived from stack name', () => { + test('auto-generates memory name when not provided', () => { const { template } = createStack(); template.hasResourceProperties('AWS::BedrockAgentCore::Memory', { - Name: 'bgagent_memory_TestStack', + Name: Match.stringLikeRegexp('^[a-zA-Z][a-zA-Z0-9_]*$'), }); });