25b4fecb rewrote the Stack block of docker/.env.example to say that ./up.sh
overwrites STACK_NAME and MEMORY_ID, and that you fill them in by hand only if
you drive `docker compose` directly. That newly blessed the plain-compose path
while leaving two entries whose value position is empty and whose explanation
sits after it on the same line.
Compose's env-file parser only treats a whitespace-separated `#` as starting a
comment when it follows a NON-empty value. With nothing between `=` and the
hash, leading whitespace is stripped and the rest of the line becomes the value.
Measured, not assumed — a probe env file through `docker compose config`:
C1= # explanatory comment -> '# explanatory comment' (literal)
C2=# explanatory comment -> '# explanatory comment' (literal)
C3=value # explanatory ... -> 'value' (stripped)
C4=value# explanatory comment -> 'value# explanatory ...' (literal)
C5="value" # explanatory ... -> 'value' (stripped)
C6= # explanatory comment -> '# explanatory comment' (literal)
C7="" # explanatory comment -> '' (stripped)
So the affected set is exactly the two empty-valued lines. STACK_NAME's
`# or -st for Strands` looks like the same defect but is not: it follows a
non-empty value and is stripped correctly (case C3). It moved to its own line
for consistency, not because it was broken.
Before, against the example's real docker-compose.yml:
$ docker compose --env-file .env.example -f docker-compose.yml config
AWS_SESSION_TOKEN: '# leave blank if using long-term creds'
MEMORY_ID: '# MemoryArn last segment (after final /)'
STACK_NAME: my-copilotkit-agentcore-lg
After:
$ docker compose --env-file .env.example -f docker-compose.yml config
AWS_SESSION_TOKEN: ""
MEMORY_ID: ""
STACK_NAME: my-copilotkit-agentcore-lg
Confirmed in a real container rather than only in `config`, via a busybox
service given the same two files:
== container env BEFORE ==
P_MEMORY_ID=# MemoryArn last segment (after final /)
P_AWS_SESSION_TOKEN=# leave blank if using long-term creds
== container env AFTER ==
P_MEMORY_ID=
P_AWS_SESSION_TOKEN=
Why ./up.sh never showed it: bash and Compose disagree on these lines. `source`
of the old file yields MEMORY_ID=[] and AWS_SESSION_TOKEN=[] because bash does
treat the trailing hash as a comment, and up.sh's `set -a && source` then exports
them, where the process environment outranks the env file. Running the same old
file through Compose with those exports in place gives MEMORY_ID: "" — the bug
is invisible on the up.sh path and reachable only on the path 25b4fecb
documented.
Audited the rest of the file against the same parser: no remaining inline hash
on an assignment line, no duplicate keys, no quoting, no trailing whitespace, no
CRLF, no BOM. All eleven keys now resolve to what a reader would predict.
up.sh and docker-compose.yml needed no change. up.sh's `cp .env.example .env`
hint and its "auto-fills .env with stack outputs" header stay accurate, and its
`^KEY=.*` rewrite still matches both keys in the new layout — with the added
benefit that own-line comments survive the rewrite, where the inline ones were
destroyed by it. docker-compose.yml's "use ./up.sh instead of docker compose
directly" still holds; .env.example only says what to fill in if you don't.
up.sh's own known defects (AGENT grep/cut, unguarded config read, silent source,
hardcoded region) are deferred and untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The AgentCore example states its Python-tooling and deploy contract in six
places — two READMEs, four script self-docs, terraform.tfvars.example, the
Terraform variable descriptions and .gitignore. There is only one contract, but
each of the last three review rounds corrected a single copy of it, so the
copies drifted apart and now contradict each other. This pass reconciles all of
them against measured behaviour instead of patching one more surface.
What the contract actually is, verified by running each command:
- test-agent.py imports boto3/requests/colorama, so it runs under uv with no
--project flag. `uv run` resolves the script path against the shell's cwd, not
the project root, so `--project ..` is redundant, not required: both forms load
infra-terraform/scripts/test-agent.py and both reach the same
`FileNotFoundError: 'terraform'`. The script's Usage block claimed the flag was
needed; it no longer does.
- deploy-frontend.py (Terraform) is standard-library only with a 3.8 floor, so uv
is optional. `uv run --no-project` and plain `python3` stop identically at
"terraform is not installed". Its "Requires: uv" line said otherwise.
- That same script cannot succeed at all. It requires a Terraform output named
feedback_api_url; no root or module outputs.tf declares one (only an SSM
parameter of that name). Fed the exact output set that outputs.tf does declare,
it exits 1 at "Missing required Terraform outputs: feedback_api_url" before any
build or upload. The README documented it as the working path for a Terraform
deployment; it now says what happens and points at infra-cdk. Repairing the
script or declaring the output is tracked separately.
- agents/ holds two uv projects plus agents/utils/, which both Dockerfiles COPY
in and which has no pyproject.toml or lockfile. "Each agent is its own uv
project" overstated the guarantee.
- docker mode needs Docker running but no separate build step: the apply's
docker_build_push provisioner builds and pushes ARM64 before the runtime
resource, which depends_on it. tfvars.example prescribed
apply -> build script -> apply, contradicting the build script's own header.
- .gitignore covered .venv/ but not venv/, the third and last surface of a guard
.dockerignore and the Terraform image-hash filter already cover. A real
UV_PROJECT_ENVIRONMENT=venv sync produced 2329 committable files (30MB); it is
now ignored, matching the other two.
Also corrected while auditing every command, path, prerequisite and tool version
in the same tree: the frontend is Vite, not Next.js; the CDK tester reads
config.yaml at the example root, not infra-cdk/config.yaml; the CDK frontend
deployer's floor is 3.8, not 3.11, and its usage hint named a path that does not
resolve from the example root; build-and-push-image.sh resolves region from
AWS_REGION/AWS_DEFAULT_REGION/aws-config with no us-east-1 fallback; up.sh
overwrites the STACK_NAME and MEMORY_ID that .env.example told you to fill in;
and backend_pattern's "available patterns" listed two agents this example does
not ship.
Deliberately untouched, tracked elsewhere: the missing docs/ directory and its
links, "Node.js 18+", the duplicated `cd infra-cdk` teardown, the
Memory-and-Gateway-only claim, the undeclared aws_region variable (still the one
remaining README/tfvars.example disagreement), the absent teardown section, the
duplicate deploy-frontend.sh, and every code-behaviour defect in the scripts.
Verified: py_compile on all four touched Python files, bash -n on all five shell
scripts, and every documented command run from the directory its text names.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The deploy and local-dev scripts called bare `python3` and relied on the caller
already having PyYAML, requests, boto3 and colorama importable. `scripts/requirements.txt`
listed them but nothing installed it, and `uv run scripts/test-agent.py` — the
command the script's own docstring gives — failed because there was no project
for uv to resolve against.
Route every Python entry point through `uv run --project`, backed by the
example-root project added in the previous commit, and drop the orphaned
`requirements.txt`. Preflight now checks for `uv` rather than `python3`; uv
provisions the interpreter itself, so the hand-rolled Python 3.8 version assert
goes away with it.
`test-agent.py` starts a local agent inside that agent's own uv project with
`--locked`, so a locally run agent gets the same dependency set as its image
instead of an ad-hoc resolve.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>