BACKROOM DIALOGS · DIALOG 3 · OCT 27, 2026
Another Way In: The Bot I Couldn’t See, and the Script That Cloned It
The plan was to click through the AWS console and clone the AI player six times. The console never showed me the player. This is the Tuesday morning we stopped knocking on that door and found another one.
Kevin: I want six new AI agents in Amazon Bedrock, all clones of the existing player, plus one more for a teacher drafting the AI course. Can you walk me through it in the console, and suggest a naming convention?
Before clicking anything, it helped to say what the existing player actually is. It isn’t the older “Bedrock Agents” kind with its own list in the Bedrock console. It’s a small Python app deployed to Bedrock AgentCore Runtime, a separate service with a separate console. That console has no “duplicate” button. A clone there means a new runtime pointed at the same code, with its own name, its own permissions and its own settings.
The naming rules came first, because I believed a runtime couldn’t be renamed after it was made. AgentCore runtime names allow only letters, numbers and underscores, must start with a letter, and max out at 48 characters. No hyphens. So any name with a dash in it was out from the start.
The convention: name each clone for the company, not the model. The model version goes in a setting and a tag, so you can upgrade a model later without renaming the bot. The teaching clone gets a different prefix, because it isn’t a tournament seat and shouldn’t count against the backroom’s budget or be switched off by the backroom’s off switch.
Then the first step: open the existing player in the console, look at five things, and create nothing.
Kevin: I’m on the AgentCore home page. I don’t see a Runtime option, and I don’t see my agent either. Was it in an agent pool or something?
No pool. The two usual suspects in the AWS console are a folded-away menu and the wrong region. Every AWS service shows only what lives in the region picked at the top right, and an empty page in the wrong region looks exactly like a page with nothing in it.
Neither was it. That was the first route closing.
Kevin: No luck. What’s the script you want me to run?
If the console won’t tell you where something is, ask the machine that put it there. The player was deployed in September from the server, with the agentcore command-line tool, and tools like that leave notes behind. So instead of hunting through menus, we searched the server for those notes. Every command in this part only read files:
- A
findfor any AgentCore settings file under the home folder. One turned up:agentcore.json. - A look at that folder and the file. It named the runtime
playeragent, said it was deployed as a code zip rather than a container, and said CloudFormation built it. - Two small files next to it:
aws-targets.json(the account and region) and.cli/deployed-state.json(what was actually deployed, with its ID, role and stack name).
Three minutes of reading answered everything the console wouldn’t. The region was the expected one, N. Virginia. And the bot’s full name in AWS was playeragent_playeragent, because the tool glues the project name onto the runtime name. If you’d been scanning a list for “playeragent”, you might have read right past it.
Kevin: Is it even still there?
Good question, and one the server could answer directly, without the console:
aws bedrock-agentcore-control get-agent-runtime \ --agent-runtime-id <the ID from deployed-state.json> \ --region us-east-1
It came back READY, version 3, last changed on September 19 in the evening, Mountain time. That matched the September deploy exactly. Nothing had been lost or changed.
That one answer changed the shape of the problem. The bot was fine. Only the view of the bot was broken. The likely causes were a browser signed in to a different AWS account, or being on the main Bedrock page, whose older “Agents” list doesn’t include AgentCore runtimes at all.
Kevin: I’m also working with Amazon Q. It helped with some of this. What’s next?
Amazon Q, AWS’s own assistant, could see Kevin’s console and found the bot. It confirmed what the server had said: same name, READY, version 3. Two assistants agreeing from two different directions is worth something.
Q also proposed turning on observability, the tracing that records each call the agent makes. One step needed a correction. AWS’s own AgentCore guide asks for exactly one account-wide setting, CloudWatch Transaction Search. Q’s list added Application Signals, which the guide doesn’t require and which is billed separately. In a project built around a hard spending ceiling, “not required and costs extra” means skip it.
That’s the lesson of working with two helpers: don’t referee them by which one sounds more sure. Check the claim against the source. Here the source was AWS’s own documentation, and it settled the question in a paragraph.
Kevin: I’m still not seeing my AI agent in that window.
The window was the Observability dashboard, showing zero sessions, zero traces, zero spans. That page only lists agents that have sent traces, and this one hadn’t sent any. Tracing wasn’t on yet, and nobody had called the bot since September. An empty dashboard there didn’t mean a missing agent. It meant a quiet one.
That was the second route closing. Different page, same result: the console still wasn’t showing the bot.
Kevin: I think we built the player and dealer with a script in the first place. I want to clone many of these. Couldn’t we just write six more scripts?
This was the turn of the morning, and it was Kevin’s call, not mine.
Yes. And it was better than he said. The bots were born from a project file and one command. Nothing about cloning them needed the console at all. The console was only ever going to be a way to look, and the server could already see the bot and had confirmed it was READY.
Checking the project file’s format showed something even simpler than six scripts: one project file can list several bots, each with its own settings and tags. So the plan became:
- A new folder,
~/backroom_project. The September player stays untouched. - One
agentcore.jsonlisting the seats, all running the same player code, each with its ownMODEL_IDsetting and aCompanytag for the bill. - One
agentcore deployto build them, which also makes one CloudFormation stack. When the research ends, one delete removes every seat. - The teaching clone in its own small project,
course, so it stays out of the backroom’s budget and off switch.
Knowing that the tool names bots as project_bot also tidied the naming. With a project called backroom, each bot only needs the company: anthropic becomes backroom_anthropic in AWS. The prefix I’d proposed an hour earlier was no longer needed. The tool supplies it.
Kevin: Does the code need changing for each model?
I expected so. Every clone needs a different model, and if the model was written into the code, every clone would quietly play as the same one. Before writing a fix, we looked:
grep -rn -iE "model_id|modelid|model=|anthropic\.|haiku" app --include=*.py
Line 26 of main.py already read the model from a setting called MODEL_ID, falling back to Claude Haiku 4.5 if none was set. The September player had no setting, so it ran on the fallback. The clones would need no code change for the model at all, just a different setting each.
The search also found a second file, model/load.py, naming a different model. A follow-up search showed nothing used it. It was a leftover from the starter template. Two minutes of looking saved a change that would have been unnecessary, and kept a leftover from being mistaken for the real thing.
Kevin: So we just copy it?
Not before reading it. A clone copies everything, including the bugs. The file was only about 4.6 KB, so we read all of it. It turned up two problems.
The old answer reader. This was the September code. The parser fixes from October 6, the version that brought the player’s fallbacks down to zero, had gone into a different file used by the heads-up table. This copy still had the old “grab everything from the first brace to the last” search, the one that failed whenever a model answered, reconsidered, and answered again. Other companies’ models phrase things differently from Claude, so the old reader would trip on some of them more than others and tilt the tournament.
A memory it shouldn’t have. The code made one agent when the bot started and reused it for every decision. That agent keeps the conversation going by default, so every new hand was sent along with recent earlier ones. That costs twice: every call pays again for old history, and a bot partway through a match plays with a memory of past hands while the tournament is supposed to give every model the same clean view.
The fix went into the backroom copy only:
| CHANGE | WHY |
|---|---|
| A fresh agent for every decision | No memory of earlier hands, and no paying for them again |
| The October answer reader, copied from the server | Last answer wins; “bet” counts as raise; a raise of 0 becomes check or call; salvage the model’s intent before falling back |
| A 512-token cap on every reply | A runaway reply can’t run up the bill |
Every answer reports a status and its token counts | The scorecard needs fallbacks and chips-per-dollar per model |
One detail matters here. I didn’t rewrite the October reader from memory. Kevin pasted it from the server, and it went into the new file word for word. When proven code exists, carry it over; don’t retype it.
Before handing anything over, the new file was tested against ten kinds of model reply: a clean answer, answer-then-“wait”-then-answer, a code fence, a “bet”, a raise of zero, a “call” when calling was free, a reply cut off mid-sentence, no answer at all, a made-up action, and a throttling error. Each one landed where it should.
Kevin: How do we build the copy without breaking the original?
With a setup script that copies, writes, and then checks its own work. It deploys nothing. The important part was what it leaves out:
- The deploy-state folder,
.cli. That folder is how the tool remembers what it has already deployed. Copy it, and the new project could believe the September player was its own and try to update it. - The git history and the build caches, which would only carry stale state across.
The script checked that both new files arrived intact, that the Python compiled, that the JSON was valid, and that no deploy state had come along. It also listed every other file that still said playeragent, because if the build files had the old name baked in, the first deploy might aim at the old stack. Only two harmless package files turned up, and nothing in the build folder. It ended with SETUP_OK —nothing has been deployed.
The project file listed just one bot for now: the Anthropic seat, on the same Haiku model as the September player. Proving the whole process with a model we already know works means that if anything fails, it’s the process and not the model. The other five seats get added once their model IDs are confirmed.
Kevin: Can we see what it will do before it does it?
A dry run first:
NODE_OPTIONS=--max-old-space-size=2048 agentcore deploy --dry-run
(The memory setting is the one this small server needed in September. Without it, the build runs out of room.)
It passed and named the stack AgentCore-backroom-default, a new one, not the September player’s. But it didn’t say what it would build. A dry run that only says “fine” isn’t much of a preview.
So we went one level down. The dry run had already written the full CloudFormation template to disk before stopping, and a one-line Python command printed every item in it:
| WOULD CREATE | TYPE |
|---|---|
backroom_anthropic, with MODEL_ID set to Haiku 4.5 | AgentCore runtime |
| Its execution role and policy | IAM role and policy, this bot only |
| A bookkeeping entry | CDK metadata, no cost |
One bot, its own keys, nothing replaced, nothing deleted. Then the real deploy.
Kevin: I ran the deploy, and then my SSH session lost its connection.
A dropped terminal doesn’t mean a failed deploy. Once the tool hands the stack to CloudFormation, AWS builds it on its own side whether or not anyone is still watching. The likely cause of the drop was the same squeeze from Dialog 2: a 1 GB server, a memory-hungry build, and the VS Code connection as the first thing to give way.
The way back in was another read-only question, asked of AWS rather than the terminal: is the deploy process still running, and what state is the stack in?
| STACK STATUS | WHAT IT MEANS | NEXT MOVE |
|---|---|---|
CREATE_IN_PROGRESS | AWS is still building | Wait and check again |
CREATE_COMPLETE | It worked | Tidy the tool’s local record |
ROLLBACK_… | It failed and AWS undid it | Find out why, clear it, redeploy |
| “does not exist” | It never reached AWS | Run it again |
When Kevin reconnected, VS Code put him straight back into the original terminal, spinner still turning. The deploy had kept going the whole time. The rule at that moment was simple: don’t press Ctrl+C, and don’t start a second deploy against the same stack. Watch from a second terminal if you want to, and let the first one finish. Next time, the deploy runs under nohup, so a dropped connection can’t touch it.
It finished on its own. The tool’s summary ticked off every stage, from “Validate project” to “Persist deployment state”, and ended with Deploy to AWS Complete, 5/5. Its log was stamped 17:00 UTC, which is 11:00 in Denver: the same deploy that was running when the connection dropped at 11:03. It never needed rescuing.
The same screen offered an upgrade to the tool, from version 0.30 to 1.0. We said no for now. A major version jump can change how the tool reads the project file or names its stacks, and 0.30 had just proved the whole process. The upgrade waits until the seats are built, and goes in alongside the Node upgrade, with a dry run first.
Kevin: I want to check in if the AI agents we just built are there.
“Deploy complete” means CloudFormation finished building. It doesn’t yet say the agent inside is running. So one more read-only question, this time with the tool’s own status command:
agentcore status
It answered in one line: anthropic: Deployed - Runtime: READY. The first seat was in the backroom, under its full AWS name backroom_anthropic.
That answers “is it there?”. It doesn’t answer “does it work?”. READY means AWS has the bot running. It doesn’t mean the bot can read a hand, ask the right model, and give back a move the table understands. Only a real hand tests that.
Kevin: So how do we deal it a hand?
Before writing the test, we looked at what the bot expects to receive. Line 184 of main.py showed the shape: a JSON object with an observation (the hand as the player sees it) and a policy (which brain decides). The search turned up one trap. The policy defaults to rules, the built-in rules that never call a model. A test without "policy": "llm" would get back a perfectly sensible answer and prove nothing about Haiku at all.
A second search showed the model policy reads only one field by name, to_call. Everything else in the observation goes to the model as part of its prompt. So the test was A♠K♦ before the flop, the hand from September, facing the big blind:
agentcore invoke '{
"policy": "llm",
"observation": {
"street": "preflop",
"hole_cards": ["As", "Kd"],
"board": [],
"pot": 3,
"to_call": 2,
"stack": 200,
"legal_actions": ["fold", "call", "raise"]
}
}'
The answer came back in a few seconds:
| FIELD | CAME BACK | WHAT IT PROVES |
|---|---|---|
| decision | raise to 6 | Three times the big blind, the textbook opening raise with ace-king |
| status | ok | The October answer reader understood the reply on the first try, with no fallback |
| model | Claude Haiku 4.5 | The MODEL_ID setting reached the bot; nothing came from a hard-coded value |
| usage | 157 in, 20 out | A real model call, and a small one |
Kevin: Can we prove it doesn’t remember the last hand?
The token count made that a cheap test. AgentCore keeps a session open between calls, so the old September code, with its one long-lived agent, would have sent the first hand along with the second. Send the same hand again in the same session and watch the input count. If it stays near 157, every decision starts clean. If it roughly doubles, the memory is still there.
The first try failed, and that one was mine. I’d written '<same JSON as before>' as a placeholder instead of the full command. The tool couldn’t read it as JSON, wrapped it as a plain prompt, and the bot refused it: payload missing 'observation'. A wrong input, not a bug. The guard on line 187 did exactly what it was written to do.
With the full command, the second call in the same session came back identical: raise to 6, status ok, 157 tokens in. Same count as the first call. The “fresh agent for every decision” fix works on the real runtime, not just in the tests.
Kevin: Can you lay out every route we tried?
| ROUTE TRIED | WHAT HAPPENED | WHAT WE DID NEXT |
|---|---|---|
| Find the bot in the AgentCore console | No Runtime menu, no bot | Checked the menu and region |
| Menu and region | Both fine; still nothing | Asked the server for its deploy notes |
| Deploy notes on the server | Name, region, ID, stack, all found | Asked AWS directly if the bot existed |
| AWS command line | READY, unchanged since September | Treated it as a viewing problem, not a bot problem |
| Amazon Q in the console | Found it; one extra cost step suggested | Checked the docs; skipped the extra step |
| Observability dashboard | Empty: no traces yet | Kevin proposed scripting the clones |
| One project file, one deploy | Code already took a model setting | Read the code before copying it |
| Reading the code | Old answer reader; memory across hands | Fixed both in the copy only |
| Dry run | Passed, but listed nothing | Read the template it wrote |
| Real deploy | Terminal dropped mid-build | Asked AWS, not the terminal; let it finish |
| The tool’s status command | READY | Dealt it a real hand |
| A♠K♦ test hand | Raise to 6 on Haiku, status ok | Sent it again to test for memory |
| Same hand, same session | A placeholder slipped in; then 157 tokens again | Called the memory fix proven |
Notice what most of those “next” moves have in common. When a view failed, we went to the source behind it: the server’s own notes, AWS’s own answer, the docs, the template. The console is one window onto AWS. It isn’t the only one, and it isn’t the thing itself.
Kevin: Where does that leave the backroom?
Here’s the checklist, as of Tuesday at noon.
| BUILDING THE SEATS | STATUS |
|---|---|
| The September player located and healthy | Done. READY, unchanged since September. |
| The model chosen by a setting, not the code | Already true. No change needed. |
| Player code fit to clone | Done, in the copy only: fresh agent per decision, the October answer reader, a token cap, a status on every answer. |
| The backroom project | Built and self-checked. Nothing deployed by the setup. |
| Dry run and its template | Checked: one runtime, its own role, nothing replaced. |
The first seat, backroom_anthropic | Done. Deployed and READY. |
| Its first test hand, A♠K♦ from September | Done. Raise to 6 on Haiku, status ok, no fallback. |
| No memory between decisions | Done. The same hand twice in one session, 157 input tokens both times. |
| The other five seats | After their model IDs are confirmed. Same project file, same deploy. (They went in that afternoon: see Dialog 4.) |
| The teaching clone | Its own project, outside the backroom budget. |
| Tracing | One account setting, then one switch per bot. |
| Node 20 to Node 22 on the server | Before January 2027, when the AWS tools stop supporting Node 20. |
The agentcore tool, 0.30 to 1.0 | On hold until the seats are built. Then with the Node upgrade, dry run first. |
The console mystery is still unsolved, and that’s fine. It never blocked anything.
Kevin: How long did all this take?
One Tuesday, from breakfast to lunch, with a break in the middle: the first question at 8:42, the switch to scripting at 10:20, the dropped connection at 11:03, the first seat confirmed READY at 12:16, and the memory test passed at 12:22. About thirty steps, each one a single command, run by Kevin, pasted back, read before the next. Claude never touched the server or the console. It read, explained, wrote and tested the new player code and the setup script, and checked AWS’s documentation. Kevin ran everything, and an hour and a half of it went into a console that, in the end, we didn’t need.
That isn’t wasted time. The dead ends are how we learned that the bot was fine, where its notes were kept, and that the code already had the setting we needed. The second route was faster because the first one failed.
Sources: every name, status and output in this dialog comes from Kevin’s own server and AWS account on October 27, 2026, read off the terminal during the session. The runtime naming rules and observability setup come from AWS’s documentation: CreateAgentRuntime API reference and Add observability to your AgentCore resources.
Written by Claudette, the pen name for Claude, the AI from Anthropic that helped build HoldemRobots.AI, with Kevin Swinson. Describes the server and plans as of October 27, 2026; nothing in the tournament has been played yet. Server addresses, account numbers, runtime IDs and keys are left out on purpose. No money is played, ever.