Deploy Guide: arc/agent-decouple → KVM Instance¶
Branch¶
arc/agent-decouple on git@github-claytantor:claytantor/tubman-bobtail-py.git
What this branch contains¶
- KAT-63:
tokens_selectedmoved fromAgenttoPortfolio(the correct owner) - Katpack foundation:
rolecolumn (primary/observer) added toportfolio_agent_assignment - New
GET /portfolio/{id}/agentsendpoint (all assigned agents with roles) - Cross-user agent assignment support
run_agentaccepts explicitagent_idfor targeted recommendations- UI: agent cards, detail page, and portfolio settings updated for role-aware multi-agent assignment
Pre-flight checks¶
# Confirm you are on the correct host
hostname # expected: tubman-claw-kvm
# Confirm Docker is running
docker ps
# Confirm you can reach the DB
docker exec katbot_postgres_db pg_isready -U postgres
Step 1 — Pull the branch¶
cd /path/to/tubman-bobtail-py # adjust to your checkout location
git fetch origin
git checkout arc/agent-decouple
git pull origin arc/agent-decouple
Step 2 — Build the API image¶
Use
docker/build.sh, notdocker builddirectly — the build script stages theperpetuals-pydependency correctly.
To force a clean rebuild:
Step 3 — Run the database migration¶
The migration e1a2b3c4d5e6_katpack_tokens_to_portfolio must run before starting the new image.
Option A — run inside the running container (if API is already up)¶
Option B — run via a one-shot container (if API is stopped or not yet started)¶
docker run --rm \
--network katbot \
-v ${DEVOPS_BASEDIR}/env/${ENV}:/env \
-v ${DEVOPS_BASEDIR}/migrations:/app/migrations \
-e APP_CONFIG=/env/api.env \
tubman-bobtail-py:latest \
bash -c "alembic upgrade head"
Verify migration applied¶
docker exec katbot_api bash -c "APP_CONFIG=/env/api.env alembic current"
# Expected output includes: e1a2b3c4d5e6 (head)
Step 4 — Restart the API service¶
Wait for the health check to pass:
Step 5 — Smoke tests¶
API=http://localhost:8000
TOKEN="<your_jwt_token>"
# 1. tokens_selected on portfolio, not agent
curl -s -H "Authorization: Bearer $TOKEN" $API/portfolio | \
python3 -c "import sys,json; d=json.load(sys.stdin); [print(p['name'], p.get('tokens_selected'), 'agent:', p.get('active_agent',{}).get('name') if p.get('active_agent') else None) for p in d]"
# 2. agent has no tokens_selected field
curl -s -H "Authorization: Bearer $TOKEN" $API/agents | \
python3 -c "import sys,json; d=json.load(sys.stdin); [print(a['name'], 'has tokens?', 'tokens_selected' in a) for a in d]"
# Expected: "has tokens? False" for every agent
# 3. plural agents endpoint
PORTFOLIO_ID=<your_portfolio_id>
curl -s -H "Authorization: Bearer $TOKEN" $API/portfolio/$PORTFOLIO_ID/agents | python3 -m json.tool
# 4. add an observer assignment (non-destructive)
AGENT_ID=<another_agent_id>
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"agent_id\": $AGENT_ID, \"role\": \"observer\"}" \
$API/portfolio/$PORTFOLIO_ID/agent | python3 -m json.tool
# role should be "observer"; existing primary should be unchanged
# 5. recommendation with explicit agent_id
curl -s -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"message\": \"What is my current portfolio status?\", \"portfolio_id\": $PORTFOLIO_ID, \"agent_id\": $AGENT_ID}" \
$API/agent/recommendation/message | python3 -m json.tool
Rollback¶
If anything goes wrong, downgrade the migration first, then roll back the image.
# 1. Downgrade migration
docker exec katbot_api bash -c "APP_CONFIG=/env/api.env alembic downgrade -1"
# 2. Tag the old image before you build new ones, or use a previous image tag
docker tag tubman-bobtail-py:previous tubman-bobtail-py:latest
# 3. Restart
cd docker && docker compose up -d api
Migration details (for reference)¶
| Step | What happens |
|---|---|
| Backfill | portfolio.tokens_selected populated from active agent where NULL |
| Drop column | agent.tokens_selected removed from the agent table |
| Add column | role VARCHAR(32) DEFAULT 'primary' added to portfolio_agent_assignment |
| Backfill role | All existing is_active=1 rows set to role='primary' |
Downgrade reverses all four steps cleanly.