Journey

Clankies grew up this week. What started as a functional but nameless prototype — running under the old “MiniOns” codebase — now has its own identity, a production-grade deployment pipeline, and the architectural foundation for its first major integration.

Three parallel workstreams defined this slice:

  1. Branding: A full visual identity — 20 custom SVG avatars, a new logo icon, project-wide rename to “Clankies”, and a custom Hugo blog theme.
  2. Infrastructure: A hardened deploy pipeline with CI/CD for both production and staging environments, fixing subtle but painful bugs in the deploy script that caused port conflicts and test failures.
  3. Architecture: The design and backend implementation of a Trello Power-Up integration — ADR-004, API client, webhook processor, and full behaviour module — turning Clankies agents into Trello-aware workers.

Let’s dive into each.

Login page with Clankies branding

The new Clankies login page with custom branding and the Pop-D icon.

Grid of 20 custom Clankies avatars

Twenty custom SVG avatars — each with its own personality, designed in-house.

What Changed

1. Project Rename: MiniOns → Clankies

Every module, config, migration, and template was renamed from Minions/minions_web to Clankies/clankies_web. A massive 177-file refactor that touched the entire stack — from mix.exs to the database migrations, from the Phoenix router to the Ecto schemas, from the blog theme to the Docker Compose config.

Why? The project outgrew its working title. “Clankies” carries the right vibe — mechanical, playful, team-oriented — and the rename was clean because the codebase was still young enough to absorb it without breaking dependents.

Alongside the rename, we added:

  • 20 custom SVG avatars (av-01-boxy through av-20-trap), each with its own visual identity — a visor, a wink, a stache, a spark. These live in priv/static/images/avatars/ and are selectable during agent creation.
  • A custom logo icon (pop-d.svg) — the distinctive pink/magenta diamond that now appears in the favicon, the blog header, and the login page.
  • A custom Hugo blog theme (themes/clankies/) replacing the generic MiniOns theme, with the iceberg backlog visualization carried forward.

2. Deploy Pipeline: From Manual to CI/CD

Before this slice, deploying meant SSH’ing into the server and running a fragile script with known race conditions. Now GitHub Actions handles it automatically — push to main deploys production, push to staging deploys staging.

Production workflow (.github/workflows/deploy.yml):

Push to main → SSH into server → /opt/clankies/scripts/deploy.sh

Staging workflow (.github/workflows/deploy-staging.yml):

Push to staging → SSH into server → /opt/clankies/scripts/deploy-staging.sh

Three critical fixes landed in the deploy scripts:

Fix Problem Solution
Migration ordering Old server was stopped after migrations, causing :eaddrinuse when the new release tried to bind port 6000 pull → test → build → stop → migrate → start — kill the BEAM before running migrations
Env var leak DATABASE_URL, SECRET_KEY_BASE, and PHX_HOST from prod.env leaked into the test subshell, making Ecto use a regular connection pool instead of the SQL Sandbox Full unset of all prod-only env vars before the test step
Brevo TLS Port 587 uses STARTTLS; tls: :always caused :tls_failed on every email attempt Changed to tls: :if_available — SMTP now works correctly

3. Staging Environment

A full staging instance now runs alongside production for pre-merge testing:

Aspect Production Staging
Domain clankies.party staging.clankies.party
Port 6000 6001
Database clankies_prod clankies_staging
Auth Public Authelia (2FA)
Process Mgmt pkill -f "clankies.*beam" PID file (avoids killing prod)
Trigger Push to main Push to staging branch

The staging script uses a PID file (/opt/clankies/staging/server.pid) instead of pkill, so it never accidentally terminates the production process. The database is isolated (clankies_staging), and the staging domain is behind Authelia for access control.

4. Bug Fix Squash

Four bugs were fixed that directly impacted user experience:

  • API key not configured — When a user hadn’t set up their DeepSeek API key, the agent simply failed silently. Now the AgentServer detects the missing key before calling the adapter and broadcasts a clear inline error: “No API key configured. Go to Settings → API Providers to add your DeepSeek API key.” The agent resets to idle state so the user can configure and retry immediately.
  • Avatar form field clearing — Changing an agent’s avatar caused all other form fields to empty out. Fixed by preserving the full form state on avatar selection.
  • Power-Up button inactive — The “Add Power Up” button on agent config was a no-op. The click handler wasn’t wired to the LiveView event. Fixed with proper phx-click binding.
  • Alert text color — Flash messages had a strange pinkish background making text hard to read. The .alert CSS was cleaned up against the DaisyUI v5 migration.

5. Trello Integration: Architecture & Backend

The biggest architectural investment of this slice: designing and partially implementing a full Trello Power-Up integration.

ADR-004 defines a three-layer architecture:

Trello Auth (OAuth flow + Ecto schemas)
  ↕
Trello Webhook Receiver (Phoenix endpoint + async processor)
  ↕
Trello Power-Up Module (implements PowerUp behaviour)

The backend implementation is substantial (~4,200 lines across 39 files):

  • Clankies.Trello.TrelloConnection — Ecto schema for user Trello auth tokens (supports both API Key + Token and future OAuth 1.0a)
  • Clankies.Trello.TrelloBoardMapping — Maps a Trello board to a Clankies agent with per-board config
  • Clankies.Trello.ApiClient — Req-based REST client that injects API key + user token into every request
  • Clankies.Trello.Auth — OAuth flow logic (token exchange, validation via GET /1/members/me)
  • Clankies.Trello.WebhookRegistrar — Registers/deregisters webhooks with Trello’s API
  • Clankies.Trello.EventProcessor — Inbound webhook events → formatted agent messages
  • Clankies.PowerUps.Trello.Trello — Implements PowerUp behaviour with 8 agent tools (trello_get_card, trello_search_cards, trello_create_card, trello_update_card, trello_add_comment, trello_move_card, trello_get_board_lists, trello_assign_member)
  • LiveView UITrelloSettingsLive for board selection and mapping
  • ControllersTrelloController (OAuth browser flow), TrelloApiController (board listing/mapping API), TrelloWebhookController (webhook receiver), PowerUpController (static iframe assets)

The Trello Power-Up module gives agents a system_prompt_fragment that teaches them about connected Trello boards, so when a card moves to “Done” or a comment is added, the agent can respond autonomously.

A self-event filter prevents agent action loops (actions triggered by Clankies itself are skipped by comparing the memberCreator ID against the connected Trello user).

Agent settings with API key configuration

The Settings page where users configure API providers and manage agent settings.

Dashboard with agent cards

The Clankies dashboard after the rename — agents are now selectable with custom avatars.

Agent chat interface

The agent chat interface, now with inline error messages when an API key is missing.

Architecture

CI/CD Deploy Pipeline Flow

sequenceDiagram
    participant Dev as Developer
    participant GH as GitHub
    participant GA as GitHub Actions
    participant Server as Server
    participant DB as PostgreSQL

    Dev->>GH: Push to main
    GH->>GA: Trigger deploy workflow
    GA->>Server: SSH: /opt/clankies/scripts/deploy.sh
    
    rect rgb(40, 40, 50)
        Server->>Server: git pull origin main
        Server->>Server: mix test (env-isolated subshell)
        Server->>Server: mix assets.deploy
        Server->>Server: mix release --overwrite
    end

    Server->>Server: pkill -f "clankies.*beam"
    Note right of Server: Kills old BEAM BEFORE migration
    
    Server->>DB: ecto.migrate (clankies_prod)
    DB-->>Server: Migrations applied

    Server->>Server: nohup start.sh &
    Server->>Server: Health check (curl :6000)
    alt Healthy
        Server->>Server: hugo build → blog output
        Server-->>GA: ✅ Status 200
    else Failed
        Server-->>GA: ❌ Rolling back...
    end

    GA-->>Dev: Deploy result (GitHub)

The key insight here is order matters. The original script ran migrations before stopping the old server, which meant the $APP_BIN eval that started the app for migrations would crash with :eaddrinuse because the old BEAM was still on port 6000. Moving pkill before the migration step fixed a recurring deploy failure.

Staging vs Production Isolation

graph TD
    subgraph "GitHub"
        Main["main branch"]
        Staging["staging branch"]
        GA["GitHub Actions"]
    end

    subgraph "Server"
        subgraph Production
            P_SCRIPT["deploy.sh<br/>(pkill + :6000)"]
            P_DB[("clankies_prod")]
            P_APP["BEAM :6000"]
            P_BLOG["Hugo → blog.party"]
        end

        subgraph Staging
            S_SCRIPT["deploy-staging.sh<br/>(PID file + :6001)"]
            S_DB[("clankies_staging")]
            S_APP["BEAM :6001"]
        end
    end

    Main -->|push| GA
    Staging -->|push| GA
    GA -->|SSH| P_SCRIPT
    GA -->|SSH| S_SCRIPT
    P_SCRIPT --> P_APP --> P_DB
    S_SCRIPT --> S_APP --> S_DB

Technical Decisions

Decision Rationale Trade-offs
PID file for staging Avoids accidentally killing production when pkill -f "clankies.*beam" matches both instances Extra complexity in script; must handle stale PID files
API Key + Token first Trello’s recommended Power-Up auth; token never expires No refresh flow needed; OAuth 1.0a support deferred
ETS dedup for webhooks Simpler than DB-backed idempotency; handles 99.9% of duplicate deliveries Not durable across restarts; 5-min TTL window
Ad-hoc Req client Consistent with existing HTTP stack; no new deps No built-in retry with exponential backoff yet
Snapshot-only blog deploy Blog builds during deploy; no GitHub Pages action needed Deploy failure also means stale blog; uncoupled via separate workflow later

What We Learned

  • Deploy ordering is hard. A subtle ordering bug (migrate → stop → start instead of stop → migrate → start) caused intermittent failures that were hard to reproduce locally because the race condition only happens when the old server is healthy and the new release is building — a cross-section of production-only timing.
  • env leaks are silent until they crash. prod.env sourced with set -a in the deploy script leaked DATABASE_URL into the test subshell. The symptom (cannot invoke sandbox operation) pointed at Ecto config, but the root cause was in the deploy script, not the app. Always unset prod vars before running tests in a deploy context.
  • Snap Chromium + file:// is a dead end. Snapped browsers can’t access local files. All screenshot workflows must use the dev server or a Python HTTP server.
  • Custom avatars are surprisingly engaging. The 20 SVG set took design effort but immediately gave the app personality. Users connect to their agent through its visual identity — it’s not cosmetic, it’s UX.

What’s Next

The Trello Power-Up backend is implemented and tested, but the Power-Up behaviour module (Slice 3) is blocked pending the test schema alignment. Next steps are to unblock the splitter, complete the behaviour module, and wire up the iframe connector so agents can interact with Trello cards in real time.