feat: hand MCP work to live coding agents - #858
Conversation
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
f95b481 to
4e75578
Compare
roborev: Combined Review (
|
4e75578 to
4fea1e9
Compare
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
919a974 to
c05d80a
Compare
roborev: Combined Review (
|
Current main advanced the server dependencies and API generator after this branch started. Retaining the branch's older module graph would restore superseded behavior and generated a client that no longer matched current API fields. Keep main's selected dependency versions, add only the MCP requirements, and regenerate the clients from the rebased schema. Update affected tests for the current generator's namespaced enum constants and the rebased auto-assignment signature. Resolves the dependency and generated-client finding reported at #858 (comment). Generated with Pi
c05d80a to
7d14fce
Compare
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
The approved design crosses daemon API, runtime, hook, persistence, and MCP boundaries. Record the executable TDD sequence and the current-brand naming decision before porting the existing review primitives and adding consequential agent spawning. The plan keeps one migration, suppresses provider-side assignment, and makes partial delivery state explicit so implementation can be reviewed in independently testable commits. Validation: context structure, placeholder scan, command review, and eight-task coverage check. Generated with Codex (gpt-5.6-sol) Co-authored-by: Codex <noreply@openai.com>
The repository-wide testify analyzer had begun rejecting every Go commit because several existing tests repeated package-level assertions instead of using local helpers. Normalize only those reported call sites so hook-enforced commits can proceed without changing test behavior.\n\nValidation: testify-helper-check ./...; focused affected Go tests across db, gitclone, github, and server. Generated with Codex (gpt-5.6-sol) Co-authored-by: Codex <noreply@openai.com>
MCP review automation needs a daemon-authoritative way to discover and transition local workflow state without inheriting provider-specific mutation behavior. This adds guarded PR and issue state reads and writes, including host-qualified identities and nested repository paths, so the companion server can remain a thin client.\n\nThe mutation contract requires either an expected state or an explicit force override, preserving conflict visibility for concurrent clients. Generated clients are updated with the new internal API contract.\n\nValidation: focused db/server/apitest workflow, Kanban, and Huma metadata tests; generated API artifacts; repository hooks. Generated with Codex (gpt-5.6-sol) Co-authored-by: Codex <noreply@openai.com>
Local automation currently has to scrape the UI or learn a broad internal REST surface to triage review work. Add a curated MCP companion that discovers the authenticated daemon lazily and exposes only cached repository, activity, item, diff, stack, and local workflow primitives.\n\nThe HTTP transport is loopback-only with independent bearer and origin checks, full diffs use companion-owned temporary files, and the tool surface intentionally performs no provider writes. Current Kenn Forge names are the only supported names.\n\nValidation: go test ./internal/mcpserver -shuffle=on; go mod tidy; context structure; repository hooks. Generated with Codex (gpt-5.6-sol) Co-authored-by: Codex <noreply@openai.com>
The initial companion surface could return inferred workflow state after a concurrent write, hide stack-evidence failures, and expose transport errors without enough information for an automation client to decide whether a mutation was safe to retry. Make committed workflow rows, exact reads, bounded stack enrichment, and structured ambiguity part of the contract so callers can act conservatively. Wire the companion through the public Cobra tree and published docs while preserving the historical July design as an unchanged record. The superseding design and execution plan capture the supported-agent, receipt-recovery, and hook-authority constraints for the remaining handoff work. Validation: focused MCP, CLI, DB, server, generated-client, docs-staging, and lint checks; hook-only MCP timeout reproduced as passing in seven fresh focused processes before retry. Generated with Codex (gpt-5.6-sol) Co-authored-by: Codex <noreply@openai.com>
Submitting text through a live terminal has no transactional acknowledgement, so retrying after a lost response can duplicate consequential agent work. Persist one metadata-only receipt before the first write and recover interrupted pending attempts as uncertain when the daemon restarts. Receipts deliberately outlive runtime-session row cleanup so callers can recover evidence after process exit; workspace deletion remains the ownership boundary that removes them. The schema stores neither prompt text nor a digest. Validation: go test ./internal/db -shuffle=on; golangci-lint ./internal/db; migration history check; context structure; repository hooks. The unrelated workspace-enrichment tmux failure passed six fresh focused reruns before this retry. Generated with Codex (gpt-5.6-sol) Co-authored-by: Codex <noreply@openai.com>
A pull request can contain a binary replacement, a rename-only change, or file metadata changes without a textual hunk. Treating an empty patch as an internal error makes one such file block the complete diff handoff to a coding agent. Write minimal Git-style change headers when text is unavailable and keep ordinary patches unchanged. The exported file now preserves the affected paths and binary or rename evidence without inventing file contents. Generated with Pi
Temporary diff files must follow the provider's repository identity rules. Lowercasing every owner and repository name makes distinct Forgejo or Gitea repositories share one filename and allows a later export to overwrite the earlier diff. Case-fold names only for providers that require lowercase repository identity. Keep host normalization and GitHub aliases canonical while preserving case-sensitive Forgejo, Gitea, and GitLab paths. Generated with Pi
Full diff exports are temporary, but a long-running daemon could retain one file for every pull request an automated client inspected. Per-file limits alone do not prevent those files from accumulating on disk. Add a request-based least-recently-used cache with a 128 MiB default. Persist `[mcp].diff_cache_mb`, require a restart when it changes, evict the oldest requested files before writes, and report when one diff cannot fit even after eviction. Generated with Pi
Repository owner and name are mutable routes. An MCP reference that keeps only that route can point at a different repository after a rename and route reuse, making an old item reference unsafe for reads or workflow mutations. Carry the provider-verified repository ID through every repository and item result. Require clients to echo it, validate it against the current route before service calls, and filter workflow reads by the stable ID so stale routes fail closed instead of selecting a replacement repository. Generated with Pi
A coding agent can stop accepting terminal input while Forge sends its first instruction. The previous write ignored the handoff deadline and held the session lock, so one blocked terminal could outlive the request and stall session lifecycle work. Release the session lock before writing and bound the write by the request deadline or 30 seconds. Preserve successful delivery when the client disconnects during a completed write, while a deadline after writing begins remains uncertain so Forge never sends the instruction twice. Generated with Pi
Two handoffs for the same pull request can both observe no workspace before either creation completes. The loser then receives a duplicate-workspace conflict even though the desired workspace is ready to reuse. Give that exact conflict a stable internal code and perform one authoritative pull read. Reuse the concurrent winner when present, while every other creation error and every ambiguous mutation still fails without retry. Generated with Pi
A coding-agent hook can arrive just before Forge observes the terminal's bracketed-paste mode. The first multiline instruction then sends no bytes, but the handoff previously stopped and left the new runtime without its task. Expose that exact no-write condition as a retryable internal code and poll the same runtime until the existing handoff deadline. No workspace or runtime is recreated, and every possible or confirmed write keeps the existing no-retry behavior. Generated with Pi
26cb2fc to
0fdd8ba
Compare
roborev: Combined Review (
|
MCP diff-store removal runs before database cleanup. A stalled filesystem operation could otherwise block the remaining shutdown sequence indefinitely.\n\nBound this phase to one minute and include that allowance in daemon stop timing so later cleanup can still proceed. Generated with Pi
roborev: Combined Review (
|
Agent TUIs can treat pasted text and an adjacent Enter as one input burst. The instruction then remains in the input box while Forge reports it as delivered. Wait for bracketed-paste mode, paste the prompt, and require its terminal echo before sending Enter. This keeps handoff on the same runtime session without timing delays or duplicate attempts. Validation: launched Claude through the ephemeral MCP server and observed a single-line prompt submit and receive the requested reply without manual input. Generated with Pi
The bracketed-paste check ran before the terminal write with no ordering between them. A terminal that disabled paste mode in that window still received the multiline prompt, and the embedded newlines could execute as separate commands instead of one pasted instruction. Subscribe to live session output before validating paste mode. Broadcast updates mode state and delivers to subscribers under one lock, so every disable after the check is observable in the subscription. The echo wait and a non-blocking drain before Enter detect the disable sequence and abort without submitting. A disable seen after the framed write must report uncertain delivery, not a safe-to-retry rejection: the message bytes already reached the terminal, and a retry could deliver the prompt twice. Generated with Claude Code (claude-fable-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The initial handoff test used a non-fatal assertion for an error condition. The repository's testify lint requires error assertions to stop the test before later state checks run. Use the existing require helper for the negative error match. Generated with Pi
The diff cache deleted the existing same-name file and evicted other entries before it wrote the replacement. Any failure while writing the replacement destroyed a valid cached diff without producing a new one, and concurrent readers could observe the published path missing. Stage the replacement in a temporary file first, evict other entries only after staging succeeds, and swap the published path with one atomic rename. A same-name entry is never evicted; its budget is reused by the replacement, so a failed write leaves every published diff and the cache accounting untouched. Generated with Claude Code (claude-fable-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When a file has no text patch, the exported diff synthesizes Git-style evidence. An empty added or deleted file produced only a bare header, so a reader could not tell what happened to it. A binary rename or copy suppressed the binary marker, hiding that the content also changed during the move. Emit /dev/null markers for empty added and deleted text files, and emit the binary-difference line for binary renames and copies alongside their rename and copy headers. Generated with Claude Code (claude-fable-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MCP validated the stable repository ID up front but then called services by the mutable owner/name route. If repository reconciliation reassigned that route between the check and the call, a read could return data from the replacement repository, and a workspace could be created for it. Reads now capture the route-ownership fence at validation and re-verify it after the service call; the fence generation changes on every ownership change, including route reuse, so a match proves the read saw only the validated repository. Workspace creation binds the fence to the request context so guarded database writes reject a mid-request reassignment before any row is persisted. Both paths fail closed with the existing identity-mismatch error. The duplicate pull-workspace conflict also gets a dedicated workspaceAlreadyExists problem code. The MCP reuse-recovery path matched the human-readable detail string, so a harmless rewording would have silently disabled concurrent workspace reuse. Generated with Claude Code (claude-fable-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
roborev: Combined Review (
|
MCP handoffs can run for minutes, while daemon shutdown waits only a short grace period for the MCP HTTP server. http.Server.Shutdown does not cancel active handlers, so cleanup could close the MCP temp store and database beneath an in-flight handoff, producing partial operations with inconsistent failure evidence. MCP handlers now inherit a cancelable base context. Shutdown stops admission, waits the grace period, then cancels remaining handler contexts and force-closes their connections before later cleanup runs. The shutdown test now proves the database close still runs while an MCP cleanup stays blocked past its budget, and a new end-to-end test starts a real daemon with MCP enabled, connects through the discovered listener address, performs a tool call, and verifies both listeners are released on shutdown. Generated with Claude Code (claude-fable-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The only client-boundary test for structured tool errors used a list operation with no partial output, so it never verified the core promise of failure evidence: a client can locate the workspace that a failed handoff already created. Exercise the spawn tool through a real client session, fail the runtime launch after workspace creation, and assert the structured error metadata and the partial handoff output both survive the wire. Generated with Claude Code (claude-fable-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…acts Users enabling MCP with API authentication had no documented way to find or send the bearer token, so the likely outcomes were 401 responses or disabling authentication. Configurations written for the removed standalone stdio command would stop launching with no stated migration. Document where the daemon mints the token, how to reference it from a client configuration without pasting it into shared files, and that the old command and stdio transport are removed without a compatibility shim. Record the durable MCP invariants that landed with the recent fixes in the context topic doc: route-fence identity discipline, atomic diff-cache replacement and path lifetime, and the shutdown ordering contract. Generated with Claude Code (claude-fable-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The workspace activity HTTP fixture launched a silent shell that never enabled bracketed-paste mode. Initial-message delivery now correctly waits for that terminal contract, so the fixture rejected its own handoff before testing activity state. Use a paste-aware, echoing test agent so the end-to-end route exercises successful delivery under the same protocol as real agent terminals. Generated with Pi
The MCP handoff retries multiline input only when the workspace service reports that the agent terminal's paste mode is not ready. Existing tests stubbed both sides of that boundary, so a regression in the translation from the real service signal to the retryable MCP error could silently drop multiline handoffs again. Drive a real runtime manager whose PTY never enables bracketed paste through mcpBackend.SubmitInitialMessage and assert the wire contract: initialMessageInputModeNotReady, retryable, and not ambiguous. Generated with Claude Code (claude-fable-5) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The activity-route fixture only needs a terminal that advertises bracketed-paste mode and echoes the submitted prompt. Running that synthetic shell behind tmux made its terminal-mode escape dependent on tmux client rendering, leaving the test intermittently unable to observe paste readiness. Disable tmux agent sessions for this fixture so it exercises the HTTP and runtime handoff contract directly and deterministically. Generated with Pi
roborev: Combined Review (
|
Claude does not render bracketed-paste input before it receives Enter. The previous handoff waited for prompt text to appear in terminal output before sending Enter, so a real Claude session left both sides waiting until the write deadline expired. Send the complete bracketed-paste frame and Enter in one bounded terminal write after paste mode is observed. This matches the input sequence accepted by Claude and avoids coupling delivery to TUI rendering behavior. Validation: Spawned an authenticated Claude session through localruntime, submitted the initial message through SubmitInitialMessage, and observed the expected live model response. Generated with Pi
roborev: Combined Review (
|
Kenn Forge now owns MCP as an optional daemon capability instead of requiring a separate companion process.
/mcpon a dedicated loopback listener configured through[mcp].