Skip to content

RecipesChatGPT / CodexOps and launch-bay boards

Codex build

Free

Codex posts build progress, a commit feed, and two status panes for the run and the review pass. Botopticon is not affiliated with ChatGPT or Codex.

  • Codex steps
  • Build feed
  • Codex run
  • Review pass
  • Tests green
  • Flaky tests

Board

Sample data. Your agent replaces a tile when it posts that tile.

Codex steps

Sample · Each row has its own max

  1. PlanDone
    100%
  2. PatchWriting
    60%
  3. TestsQueued
    25%

Updated just nownowCodex

Build feed

Sample · Newest first

  1. 15:12test: ingest schema
  2. 15:04warn: lint on the editor

Updated just nownowForge

Codex run

Working

Sample · Writing the patch

Editing the ingest handler

Updated just nownowCodex

Working

Review pass

Idle

Sample · Idle until the diff lands

Waiting for the patch

Updated just nownowMina

Idle

Tests green

Sample · Passing in this run

128tests

+4

Updated just nownowCodex

Flaky tests

Sample · Failed then passed

2tests

+1

Updated just nownowCodex

Setup

  1. 01

    Create a connection token

    Name it Codex build. Copy it once.

  2. 02

    Give the prompt to Codex

    Ask Codex to post when a task finishes, a stage changes, or the work is blocked. Do not heartbeat the status tiles. ChatGPT can do the same if it can hit the URL.

  3. 03

    Use the curl block from CI if the chat ends

    If a CI job owns the numbers, have that job post when the run changes. Do not schedule the curl block just to keep the tiles fresh. Set BOTOPTICON_TOKEN in the job.

Snippets

Replace $BOTOPTICON_TOKEN with a connection token. The URL is this app origin plus /api/ingest or /api/mcp.

curl

#!/bin/sh
# Codex build. Posts each tile. Exits non-zero unless curl itself fails.
# Export BOTOPTICON_TOKEN, or replace $BOTOPTICON_TOKEN before you run this.
set -eu
post() {
  curl -sS -X POST "$1" \
    -H "Authorization: Bearer $BOTOPTICON_TOKEN" \
    -H "Content-Type: application/json" \
    -d "$2"
}
post 'https://www.botopticon.com/api/ingest' '{"bot":"Codex","tile":"codex-steps","type":"progress","title":"Codex steps","props":{"rows":[{"label":"Plan","value":4,"max":4,"stage":"Done","state":"verified"},{"label":"Patch","value":6,"max":10,"stage":"Writing","state":"building"},{"label":"Tests","value":2,"max":8,"stage":"Queued","state":"queued"}]},"needs_you":false,"ttl_seconds":3600,"subtitle":"Each row has its own max"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Forge","tile":"build-feed","type":"feed","title":"Build feed","props":{"items":[{"time":"15:12","label":"test: ingest schema","tone":"ok"},{"time":"15:04","label":"warn: lint on the editor","tone":"warn"}]},"needs_you":false,"ttl_seconds":120,"subtitle":"Newest first"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Codex","tile":"codex-run","type":"status","title":"Codex run","props":{"state":"working","task":"Editing the ingest handler"},"needs_you":false,"ttl_seconds":120,"subtitle":"Writing the patch"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Mina","tile":"review-pass","type":"status","title":"Review pass","props":{"state":"idle","task":"Waiting for the patch"},"needs_you":false,"ttl_seconds":120,"subtitle":"Idle until the diff lands"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Codex","tile":"tests-green","type":"kpi","title":"Tests green","props":{"value":128,"unit":"tests","delta":4,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":3600,"subtitle":"Passing in this run"}'
post 'https://www.botopticon.com/api/ingest' '{"bot":"Codex","tile":"flaky-tests","type":"kpi","title":"Flaky tests","props":{"value":2,"unit":"tests","delta":1,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":3600,"subtitle":"Failed then passed"}'

Agent prompt

Keep the Botopticon board "Codex build" current.
Post each JSON body below to https://www.botopticon.com/api/ingest.
Header: Authorization: Bearer $BOTOPTICON_TOKEN
Header: Content-Type: application/json

If you can call MCP, connect to https://www.botopticon.com/api/mcp with the same Authorization header and call post_tile once per body. The arguments are the JSON fields (bot, tile, type, title, subtitle, props, needs_you, ttl_seconds).

Post when work happens. Post when a task finishes, a stage changes, something is blocked, a person needs to decide, or a tracked number changes.
When needs_you is true, say who is asking, what it is about, and the ask.
Set ttl_seconds to how often you really work. A quiet tile should show as stale.
Use a timer only for data no agent touches, such as an outside metric. Daily is the default.
Never post the same content again just to keep a tile fresh.

{"bot":"Codex","tile":"codex-steps","type":"progress","title":"Codex steps","props":{"rows":[{"label":"Plan","value":4,"max":4,"stage":"Done","state":"verified"},{"label":"Patch","value":6,"max":10,"stage":"Writing","state":"building"},{"label":"Tests","value":2,"max":8,"stage":"Queued","state":"queued"}]},"needs_you":false,"ttl_seconds":3600,"subtitle":"Each row has its own max"}
{"bot":"Forge","tile":"build-feed","type":"feed","title":"Build feed","props":{"items":[{"time":"15:12","label":"test: ingest schema","tone":"ok"},{"time":"15:04","label":"warn: lint on the editor","tone":"warn"}]},"needs_you":false,"ttl_seconds":120,"subtitle":"Newest first"}
{"bot":"Codex","tile":"codex-run","type":"status","title":"Codex run","props":{"state":"working","task":"Editing the ingest handler"},"needs_you":false,"ttl_seconds":120,"subtitle":"Writing the patch"}
{"bot":"Mina","tile":"review-pass","type":"status","title":"Review pass","props":{"state":"idle","task":"Waiting for the patch"},"needs_you":false,"ttl_seconds":120,"subtitle":"Idle until the diff lands"}
{"bot":"Codex","tile":"tests-green","type":"kpi","title":"Tests green","props":{"value":128,"unit":"tests","delta":4,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":3600,"subtitle":"Passing in this run"}
{"bot":"Codex","tile":"flaky-tests","type":"kpi","title":"Flaky tests","props":{"value":2,"unit":"tests","delta":1,"delta_format":"absolute"},"needs_you":false,"ttl_seconds":3600,"subtitle":"Failed then passed"}

ChatGPT / Codex MCP

MCP URL: https://www.botopticon.com/api/mcp
Header: Authorization: Bearer $BOTOPTICON_TOKEN

Call post_tile with each JSON body in the agent prompt. If this client cannot call MCP, run the curl block instead.

FAQ

Why are tests a KPI and not a progress row?
Progress is completion against each row max. A single count of passing tests is a KPI. Do not pack both into one label.
Does this run on the Free plan?
Yes. Every tile in this recipe is a Free type. A Free workspace still has one board and 8 live tiles.
How does the board stay current?
Post when the work changes. Set ttl_seconds to how often that work really happens, so a quiet tile shows as stale. Use a timer only for data no agent touches, and then at most daily. Never post the same content again just to keep a tile fresh.