Deployment

Two independent deploy paths: CDK for the AgentCore runtime, SAM for the Lambda application. Neither depends on the other at deploy time.

Order for a fresh environment

  1. Deploy the SAM stack. It creates the shared secret (agentcore-configs) as an empty resource, the queues, and the functions.

  2. Populate the secret — see the shared secret. Functions will fail their cold start until it is populated.

  3. Apply the BigQuery views from bigquery/*.sql. Handlers that query a missing view log the error and return.

  4. Deploy the CDK stack for the AgentCore runtime.

  5. Force a cold start on the Lambdas so they pick up the populated secret (redeploy, or touch the function configuration).

SAM — Lambda application

Run from lambdas/.

make deploy               # sync all requirements → sam build --no-cached → sam deploy

Per-function deploys build only that resource, then deploy the stack; CloudFormation applies only what changed:

make deploy-scraper
make deploy-scorer
make deploy-hubspot
make deploy-email-agent
make deploy-phone-agent

Each target first regenerates requirements.txt from uv.lock:

cd <function> && uv export --no-dev --no-hashes -o requirements.txt

A dependency added with uv add is not deployed until requirements.txt is re-exported. The make deploy-* targets do this for you; a bare sam build does not.

Stack settings come from lambdas/samconfig.toml: stack name morpheus-sam, CAPABILITY_IAM, resolve_s3 = true, confirm_changeset = false. Resource names are derived from the stack name, so the CRM queue is morpheus-sam-crm-sync.fifo and the CRM function is morpheus-sam-hubspot-sync.

Useful outputs:

aws cloudformation describe-stacks \
  --stack-name morpheus-sam \
  --query "Stacks[0].Outputs" --output table

AgentcoreSecretArn, CrmSyncQueueUrl, EnrichmentPipelineQueueUrl, CrmSyncDlqUrl, EnrichmentPipelineDlqUrl, and one ARN + IAM role per function.

CDK — AgentCore runtime

Run from the repository root.

make deploy-cdk           # cdk bootstrap (idempotent) + cdk deploy --all
make deploy-dev           # cdk deploy --all
make deploy-prod          # cdk deploy --all --require-approval never

Or from cdk/:

pnpm install
npx cdk diff              # review before applying
npx cdk deploy

Requires a running Docker daemon: the stack builds the agentcore/ image for linux/arm64 via DockerImageAsset and pushes it to ECR.

The secret ARN is currently hardcoded in cdk/bin/cdk.ts. Update it when the secret is recreated.

Outputs: AgentRuntimeId, AgentRuntimeArn, RuntimeRoleArn, ImageUri, LogGroupName, and SecretArn. Copy RuntimeRoleArn into agentcore/.bedrock_agentcore.yaml as execution_role so the AgentCore CLI uses the CDK-managed role rather than creating its own.

Details: CDK stack.

BigQuery views

There is no migration runner. Apply a changed view by executing its file:

bq query --use_legacy_sql=false < bigquery/v_lost_deal_reeng.sql

Views are CREATE OR REPLACE, so re-applying is safe. The handler queries are pinned to view names in lambdas/morpheus_hubspot/utils/constants.py and lambdas/phone_agent/utils/constants.py — renaming a view requires a code change and a Lambda deploy.

Verifying a deploy

# Tail a function
aws logs tail /aws/lambda/morpheus-sam-hubspot-sync --follow

# Tail the runtime
aws logs tail /aws/bedrock-agentcore/agents/agentcore --follow

# Confirm nothing landed in a DLQ
aws sqs get-queue-attributes \
  --queue-url <CrmSyncDlqUrl> \
  --attribute-names ApproximateNumberOfMessages

Trigger a scheduled handler on demand rather than waiting for its rule:

aws lambda invoke \
  --function-name morpheus-sam-hubspot-sync \
  --payload '{"detail-type":"Hubspot.ContactResolve","source":"morpheus.hubspot","detail":{}}' \
  --cli-binary-format raw-in-base64-out /dev/stdout

Enabling a disabled trigger

Several schedules and the EnrichmentPipelineQueue SQS trigger are commented out in lambdas/template.yaml — see EventBridge schedules for the current state of each. To enable one, uncomment the Events block and redeploy. Two constraints:

  • An SQS-triggered function’s Timeout must stay below the queue’s VisibilityTimeout.

  • The Input JSON detail-type must exactly match a key registered in that function’s registry.py, or the handler logs "no handler registered" and returns.

CI

.github/workflows/security-review.yml runs an automated security review of pull-request diffs using the prompt in .github/agents/security-risk-analysis.md. There is no deployment pipeline — deploys are run from a developer machine.