Otter Boards
A team task board you can remix into your own Dodil organization and run from the web page or from Claude and ChatGPT. Boards hold lists, lists hold cards, and an owner decides who is on each board. Say "move card 12 to Done" to your assistant, and the card moves on your teammate's screen.
Built for the Dodil Boards Challenge (rules). MIT licence.
What it does
- People: an owner invites members by email. Roles are owner, member and viewer. A viewer can read but not change anything, only the owner manages people, and a board you aren't on doesn't exist for you, on the web or through MCP.
- Boards, lists, cards: real lists you can add, rename, reorder and archive. A card has a title, description, assignee, due date and labels, and a short number on its board. Drag cards between lists on the page.
- History: every change to a card records who, when and what changed.
- MCP: 30 tools that are actions, never table access. They take the names people use: a board by name, a list by name, a card by its number or title.
- Extras: an AI checklist you review before saving, "find cards like this one" by meaning, today's summary of a board, "blocked by" links with "what is blocking the launch?", and overdue cards flagged automatically. If Dodil Models is down the board keeps working: similar cards fall back to shared words, and the summary falls back to the plain facts.
Remix it
dodil update # CLI 0.1.89 or later
dodil git remix stubborn-otter/otter-boards --to-org <your-org> --name <your-repo>
The remix creates, in your org: the repo, a DataK3 bucket <repo>-data with the tables from
schema.sql, a user pool <repo>-users, and a service account with access to the bucket and to
Dodil Models. CI then tests, builds and deploys it. Keep <your-repo> to 18 characters or
less, so the bucket name fits.
After the remix: let people sign in
Add the app's callback URL to the pool, as dodil guide remix describes under "After the
remix: let people sign in". The app's host is on its page under Apps in the console:
dodil appid settings set <your-repo>-users \
--redirect-uris "https://<your-app-host>/.dodil/auth/callback"
On our own remix Dodil had already allowed that URL, so sign-in may work before you run this. If
the sign-in page says redirect_uri is not in the pool's allowlist, this is the fix. Public
sign-up is on, so anyone with the link can make an account, but they see no boards until invited.
Then open the app, create a board, and invite people from Manage people. There is no email service, so send them the link yourself: a person sees a board once they sign in with the email you invited.
Connect Claude or ChatGPT
The app serves MCP at https://<your-app-host>/mcp. Add it as a custom connector: it asks you to
sign in through your pool, then approve the client once with
dodil appid client list <repo>-users and dodil appid client approve <repo>-users <client-id>.
| You say | Tool |
|---|---|
| What's on my plate this week? | my_cards |
| What's overdue? | overdue_cards |
| Add a card 'Send invoice' to Doing on the Ops board | add_card |
| Move card 12 to Done | move_card |
| Give it to Ana, due Friday | assign_card, update_card |
| What is blocking the launch? | what_is_blocking |
| Turn this card into a checklist | suggest_checklist, then save_checklist |
| Find cards like this one | find_similar_cards |
| Summarise the Ops board for today | board_summary |
Card numbers are per board, so if 12 exists on two of your boards the tool asks which one.
Run it locally
Tests need no Dodil account: fakes stand in for the data and the AI.
cd app
python3.12 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt pytest
python -m pytest -q ../tests
The end-to-end run needs Docker. It starts a throwaway postgres:16-alpine, loads schema.sql,
runs the rules on the real repository, then starts the real server and drives it over HTTP and
with a real MCP client. A small local stand-in plays Dodil Models, so nothing calls Dodil.
tests/e2e/run.sh # run everything, then remove the container
tests/e2e/run.sh serve # leave it running to try at http://127.0.0.1:8765
AS=bo@example.com tests/e2e/run.sh serve # the same, as somebody else
To run against a real bucket instead, set BUCKET, DODIL_SERVICE_ACCOUNT_ID,
DODIL_SERVICE_ACCOUNT_SECRET and DEV_USER=you@example.com (acts as that person without the
gateway; local only), then uvicorn boards.main:app --port 8080 from app/.
What's inside
app/boards/
domain/model.py Board, BoardList, Card, roles and the rules. No database, no HTTP.
domain/similarity.py how alike two cards are: by meaning or by shared words
application/use_cases.py every action. Web and MCP call these and nothing else.
application/ports.py what the use cases need: a repository and an assistant
infrastructure/repository.py the tables in DataK3
infrastructure/dodil.py service-account token, DataK3 connection, Dodil Models
doors/web.py JSON routes and the page -> call the use cases
doors/mcp.py the MCP tools -> call the same use cases
doors/identity.py who is calling (the gateway's X-Dodil-User header)
doors/static/ the page: plain HTML, CSS and JavaScript, no build step
main.py wires it together: /mcp, sign-in metadata, /healthz
tests/ rules, roles through both doors, the Models client, the schema
tests/e2e/ the real run, with local stand-ins. Never in the production image.
schema.sql the tables, created by the remix
docs/DECISIONS.md why it's built this way
What I'd improve
- Finish proving it on Dodil. See "Not proven yet" in docs/DECISIONS.md.
- Edits that can't collide. Two people saving the same card at the same moment: the last save wins the whole row. A version column would catch that, if DataK3 allows a conditional write.
- Writes that land together. A card and its history line are two writes. If the second fails the change is saved but not logged. A transaction fixes it, once DataK3's support is known.
- Vector search in DataK3. Similar cards are scored in Python today. DataK3 advertises vector search over the same rows, which would scale better than loading a board's embeddings.
- Smaller things: restoring archived cards, moving ownership to someone else, closing a board, time zones (overdue uses UTC days today), a live feed instead of a 15-second refresh, and touch drag and drop (on phones, the card's List menu moves it).
Licence
MIT. See LICENSE.