Laneway
Team task boards you can run from Claude or ChatGPT. Boards hold lists, lists hold cards, and every card has a short number ("card 12"), an assignee, a due date, labels, a full change history and optional "blocked by" links. The same actions work on a quiet web page (drag and drop between lists) and over MCP, so you can say "what's on my plate this week?" to your assistant and get a real answer from your boards.
Built on Dodil: data in DataK3, sign-in through an appid user pool, running on Ignite, AI through Dodil Models. Remix it and it is yours. An entry for the Dodil Boards Challenge.
What it does
- People and roles. The owner invites people as
member(can edit) orviewer(read only), changes roles and removes people. Everyone on a board can see who is on it. Nobody sees a board they are not on: it answers "not found", on the page and over MCP. - Boards → lists → cards. A new board starts with To do / Doing / Done. Lists can be added, renamed and reordered; the last list counts as done. Cards have a title, description, assignee (an owner or member of the board), due date, labels and a position, and move between lists by drag and drop or by number.
- Card numbers. Each card gets the next number on its board, assigned without database sequences. Refer to a card by number, id or title.
- History. Every change is recorded: who, when, which field, old and new value. Comments live in the same timeline.
- Overdue cards are flagged on the board, in My cards and in MCP answers.
- Blocked by links between cards, with loops refused, and "what is blocking the launch?" for a card or a whole board.
- AI (Dodil Models): a suggested checklist for a card, find similar cards
by meaning (jina-embeddings-v4 vectors in DataK3, ranked with pgvector
<=>), and a short daily board summary.
Use it from Claude or ChatGPT (MCP)
The app serves one MCP endpoint at https://<your-app-host>/mcp.
- Add that URL as a custom connector in Claude or ChatGPT.
- You are sent to your pool's sign-in page. Sign in as yourself: the assistant acts as you and sees only your boards.
- The first time, the client arrives pending. The org admin approves it
once:
dodil appid client list <repo>-users, thendodil appid client approve <repo>-users <client-id>.
Then just ask:
| You say | The assistant calls |
|---|---|
| What's on my plate this week? | my_cards(within_days=7) |
| Add a card 'Send invoice' to Doing on the Ops board | add_card(board="Ops", title="Send invoice", list_name="Doing") |
| Move card 12 to Done | move_card(card="12", to_list="Done") |
| What's overdue? | list_overdue() |
| Assign card 7 to me, due Friday | assign_card, set_due_date |
| What is blocking the launch? | what_is_blocking("launch") |
| Invite sam@acme.com to Ops as a viewer | invite_member (owner only) |
| Is there already a card like card 4? | find_similar_cards |
| Summarise the Ops board for today | board_summary |
All tools: my_boards, show_board, create_board, add_list, rename_list,
move_list, add_card, show_card, move_card, assign_card, set_due_date,
label_card, describe_card, rename_card, comment_card, card_history,
my_cards, list_overdue, invite_member, set_member_role, remove_member,
block_card, unblock_card, what_is_blocking, suggest_checklist,
find_similar_cards, board_summary. They are actions, never table access, and
they call exactly the same use cases as the web page. A refusal comes back as a
sentence a person can read ("Not allowed: viewers can look but can't change the
board; ask the owner.").
Remix it
dodil update # CLI 0.1.89 or later
dodil git remix <org>/<repo> --to-org <your-org> --name <your-repo>
Keep <your-repo> to 18 characters or less: the remix creates a DataK3
bucket <your-repo>-data (25 characters at most), a user pool
<your-repo>-users, and a service account with access to the bucket and to
Dodil Models. The bucket gets its tables from schema.sql; CI tests, builds and
deploys the copy.
After the remix: let people sign in
The one step: the app's sign-in callback
(https://<app host>/.dodil/auth/callback) must be on the pool. The deploy
registers it itself (Dodil Git 1.6.11 and later). Once the remix run has
succeeded, in the org it went into:
- If the deploy log says the callback was "not registered", run the command that line prints, once.
- Let the first people in. A new pool allows public signup by default; if it
is off, invite them:
dodil appid user create <repo>-users <email> --roles member(no--password: they set one through the forgot-password flow). - Signing in through Claude.ai or another MCP client: approve the client once
with
dodil appid client list <repo>-users, thendodil appid client approve <repo>-users <client-id>.
Then open the app, create a board, and invite your team by email from People. They see the board as soon as they sign in with that email.
Upgrading a deployment of the original starter
A bucket created by the starter already has boards, memberships, cards and
activity. Run migrations/001_board_upgrade.sql
once against it (Postgres wire; dodil data connect <repo>-data prints the
endpoint). It only adds columns and new tables. Old boards get their list rows,
and old cards their numbers, the first time the app opens them.
Run it locally
cd app
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt pytest
python -m pytest -q ../tests # no Dodil account needed: fakes for data and AI
The tests cover the rules through the use cases, both doors (web status codes and MCP tool errors), and the repository's real SQL plus the upgrade migration against an in-memory SQLite database standing in for DataK3.
To run the server against your remixed bucket, 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;
never set it in a deployment), then:
uvicorn boards.main:app --port 8080 # open http://localhost:8080
Optional env: CHAT_MODEL (default kimi-k2.6), EMBED_MODEL (default
jina-embeddings-v4), MODELS_BASE, PG_HOST.
What's inside
app/boards/
domain/model.py Board, BoardList, Card, roles and every rule
application/use_cases.py every action; visibility enforced in one place
application/ports.py what the use cases need (repository, AI assistant)
infrastructure/repository.py DataK3 tables: keyed upserts, plain-INSERT number claims
infrastructure/dodil.py service-account token, DataK3 connection, Dodil Models
doors/web.py + page.html JSON routes and the page -> call the use cases
doors/mcp.py MCP tools -> call the same use cases
doors/identity.py who is calling (the gateway's X-Dodil-User header)
main.py wires it together, /mcp, sign-in metadata, /healthz
tests/ domain, use cases, both doors, schema + SQL
schema.sql the full schema, created by the remix
migrations/001_board_upgrade.sql starter schema -> this one
.dodil/remix.yaml what a remix creates
.dodil/deploy.yaml how CI deploys every copy
DESIGN.md why it is built this way
What I'd improve
- Archive and delete for cards and lists (today a finished card stays in the last list).
- Fewer round trips: one connection per request and a repository query for "my cards" instead of walking each visible board.
- Optimistic versions on cards, so two people editing the same card get "changed since you opened it" instead of last-write-wins.
- A
POST /self-check that opens the bucket with the app's own credential and reports row counts, fordodil ignite invoke. - Notifications (assigned to you, due tomorrow) and checklists as real items instead of text in the description.
- Verify the pgvector path and embedding size against a live bucket (it is covered offline by fakes only).
Licence
MIT. See LICENSE.