fix(mcp): credit the agent on the board, let it tidy up, and survive a restart - #57
Merged
Merged
Conversation
…a restart
Three things found by connecting a real client and using it.
THE BOARD SAID A HUMAN DID IT
Cards filed over MCP were credited to the person reading the board. Credentials
now carry a display label — "Claude Code" by default, overridable so Codex users
do not see the wrong tool — and every mutation passes it as the author, editor
or comment actor. An unlabelled key falls back to its own name rather than to
"human": a key identifier on a card is ugly, crediting the wrong party is worse.
A COMMENT THAT SILENTLY VANISHED
comment_on_task sent {comment, author}. The Library API reads {text, actor}. So
comments were accepted, reported as successful, and arrived EMPTY and
unattributed — a silent wrong result, which is worse than an error because
nothing tells you to look.
There was a test asserting the broken shape. It checked what our code sent
rather than what the API reads, so it passed for as long as the feature was
broken. Rewritten against the contract, with a note saying so, because that is
the more useful lesson than the bug.
delete_task added: an agent that files a card should be able to withdraw it.
Requires task.write, respects space scope, refreshes the board like any other
mutation, and its description says to only remove cards it created or was asked
to — a tool the agent can see is a tool it will try.
A TAB THAT NEVER CAME BACK
Reported as a WebSocket 502. The 502 itself was transient (tailscale serve
answers 502 while the upstream restarts, which is every deploy) — but the reason
a tab stayed dead is ours: backoff was 1000 * 2^attempts with NO ceiling and a
hard stop at 10. Attempt 7 waits 2 minutes, attempt 9 waits 8.5, then it gives
up permanently and tells you to refresh.
- Capped at 30s with jitter. Backing off protects the server; waiting minutes to
notice it came back protects nobody, and lockstep retries from several tabs
hit a server that is still coming up.
- Never gives up. A server down now is usually back inside a minute, and a tab
left open overnight should be alive in the morning. "Refresh the page" is the
app admitting it stopped trying.
- Refetch on a reconnect that followed a drop. Nothing replays missed events, so
a recovered tab was showing pre-gap state — the dangerous kind of stale,
because it looks fine. This is the same class as the no-polling Kanban.
294 backend + 37 frontend tests green, tsc clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three things found by connecting a real MCP client and using it.
The board said a human did it
Cards filed over MCP were credited to the person reading the board — which defeats most of what a shared board is for.
Credentials now carry a display label — "Claude Code" by default, overridable so Codex users don't see the wrong tool — and every mutation passes it as the author, editor or comment actor. An unlabelled key falls back to its own name rather than to "human": a key identifier on a card is ugly; crediting the wrong party is worse.
A comment that silently vanished
comment_on_tasksent{comment, author}. The Library API reads{text, actor}. Comments were accepted, reported successful, and arrived empty and unattributed — a silent wrong result, worse than an error because nothing tells you to look.There was a test asserting the broken shape. It checked what our code sent rather than what the API reads, so it passed for as long as the feature was broken. Rewritten against the contract with a note saying so — that's the more useful lesson than the bug.
delete_taskadded: an agent that files a card should be able to withdraw it. Requirestask.write, respects space scope, refreshes the board, and its description says to only remove cards it created or was asked to — a tool the agent can see is a tool it will try.A tab that never came back
Reported as a WebSocket 502. The 502 was transient —
tailscale serveanswers 502 while the upstream restarts, which is every deploy. But the reason a tab stayed dead is ours: backoff was1000 × 2^attemptswith no ceiling and a hard stop at 10 attempts.Verification
294 backend + 37 frontend tests green,
tscclean. Prax half is praxagent/prax#186.