Skip to content

feat: hand MCP work to live coding agents - #858

Draft
mariusvniekerk wants to merge 17 commits into
mainfrom
t3code/4c6152bf
Draft

feat: hand MCP work to live coding agents#858
mariusvniekerk wants to merge 17 commits into
mainfrom
t3code/4c6152bf

Conversation

@mariusvniekerk

Copy link
Copy Markdown
Collaborator

Kenn Forge's MCP companion can identify review work, but it could not hand selected work to a coding agent without clients inferring terminal state or session identity. That made automated handoff unsafe after lost responses and left no authoritative way to discover the coding session ID.

This adds a curated, daemon-authoritative handoff for pull requests, issues, and ad-hoc workspaces. The companion launches a new configured agent runtime, waits for its hook-reported session, and submits exactly one initial message. It also lists fresh coding sessions for a workspace. Ambiguous local mutations are not retried or cleaned up, initial-message recovery is receipt-only, and provider writes remain outside the MCP boundary. Follow-up messaging to an existing session remains out of scope.

Validation
  • Full Go suite passed with shuffled tests and serialized package execution.
  • Non-mutating Go lint and NilAway passed.
  • Frontend unit suite passed: 3,507 tests, with two expected skips.
  • API generation was idempotent, and Gitleaks found no secrets across all 14 commits.

generated by a clanker

@roborev-ci

roborev-ci Bot commented Aug 7, 2026

Copy link
Copy Markdown

roborev: Combined Review (9c3a89e)

High-risk security and prompt-delivery flaws remain, alongside several medium reliability and performance issues.

High

  • Indirect prompt injection enables local process executioninternal/mcpserver/tools_agent_spawn.go:84
    Provider-controlled PR content can influence a model that can create worktrees and launch coding agents with maintainer privileges, without deterministic human approval. Require an out-of-band, human-approved capability bound to the source and target, or separate/disable handoff when provider-reading tools are enabled.

  • Owner-managed PTY delivery is falsely acknowledgedinternal/workspace/localruntime/manager.go:2463
    Socket encoding is treated as successful delivery even if the PTY write fails. A permitted 64 KiB payload may also exceed the owner’s framed input limit and be silently discarded. Add an acknowledged operation that verifies the complete write, accounts for framing, and has full-stack coverage.

Medium

  • Receipt recovery can accept an unrelated deliveryinternal/mcpserver/tools_agent_spawn.go:545
    Recovery ignores agent, session, and byte-count identity, so a concurrent receipt may falsely confirm delivery. Verify all identities and preserve ambiguous: true for uncertain or pending states.

  • Wait loops overload the daemoninternal/mcpserver/tools_agent_spawn.go:20
    Polling every 10 ms generates 100–200 requests per second during potentially long operations. Use a slower cadence or bounded backoff with a test-configurable interval.

  • Validation errors violate the stable MCP envelopeinternal/mcpserver/tools_read.go:98
    Several local failures return plain errors without kind, retryable, or ambiguous. Convert them to typed daemonError values and add a fallback wrapper for unclassified errors.


Reviewers: 2 done | Synthesis: codex, 12s | Total: 13m15s

@roborev-ci

roborev-ci Bot commented Aug 8, 2026

Copy link
Copy Markdown

roborev: Combined Review (8fb095f)

The PR has three medium-severity correctness issues; no critical or high-severity findings were identified.

Medium

  • internal/mcpserver/tools_diff.go:153 — Diff-file generation rejects empty patches, but binary and metadata-only changes such as pure renames can legitimately produce them, causing emit_diff_file to fail. Generate complete patch sections for binary, rename-only, and mode-only changes, with full-stack test coverage.

  • internal/mcpserver/tools_agent_spawn.go:168,586 — Possible writes may be reported as ambiguous:false: recovered pending/uncertain receipts become plain errors, while total-timeout handling can discard a mutation error’s ambiguity. Preserve ambiguity independently of timeout classification and return an ambiguous error for every non-delivered receipt that may represent a write.

  • internal/mcpserver/tools_candidates.go:135 — Candidate discovery filters item types only after /activity applies its 5,000-row cap, so busy repository-level activity can hide relevant PR or issue activity. Pass the selected item_types=pr and/or item_types=issue filters to /activity.


Reviewers: 2 done | Synthesis: codex, 10s | Total: 13m18s

@roborev-ci

roborev-ci Bot commented Aug 8, 2026

Copy link
Copy Markdown

roborev: Combined Review (f95b481)

Changes need fixes for two medium-severity reliability issues.

Medium

  • internal/server/workspaceapi/initial_message.go:171 — Failures occurring before any write, such as AttachSession failures, incorrectly mark the receipt uncertain and permanently block safe retries. Distinguish definitive pre-write failures from possible partial writes; release reservations for pre-write failures and mark only potentially partial writes uncertain.

  • internal/mcpserver/difftmp.go:24 — Deterministic per-item diff filenames are atomically overwritten, so concurrent snapshot requests can return summaries referencing files that now contain another request’s diff. Use content-addressed or request-unique filenames.


Reviewers: 2 done | Synthesis: codex, 8s | Total: 12m9s

@roborev-ci

roborev-ci Bot commented Aug 9, 2026

Copy link
Copy Markdown

roborev: Combined Review (4e75578)

High-severity issues prevent safe merge; three additional medium-severity reliability defects need correction.

High

  • Persistent daemon token can leak to a stale loopback listener
    Location: internal/mcpserver/daemon.go:186-225
    do indefinitely reuses the cached baseURL and sends the daemon bearer token without revalidating the runtime lock or endpoint identity. If the daemon stops and another local process binds the former port, the next MCP request can disclose the persistent token.
    Fix: Revalidate authoritative runtime identity before every credential-bearing request. Use the existing token-derived proof flow, reject stopped or changed runtime records, atomically bind the verified endpoint and token, and disable credential-bearing redirects.

  • Duplicate migration version prevents database migration and server startup
    Location: internal/db/migrations/000047_agent_initial_message_receipts.up.sql:1
    Version 000047 is already assigned to 000047_repository_route_generation, making the embedded migration source invalid.
    Fix: Renumber the new up/down migration files to the next unused sequential version and update related references.

Medium

  • Valid no-hunk changes cause diff export failure
    Location: internal/mcpserver/tools_diff.go:153
    Diff export treats an empty Patch as a daemon error, although binary files, pure renames, and mode-only changes can legitimately have no hunks. This makes emit_diff_file fail for valid handoffs.
    Fix: Preserve file metadata while omitting or synthesizing representations for legitimate no-hunk patches, with tests at the real diff-building boundary.

  • Multiline initial prompts can be silently lost during agent startup
    Location: internal/mcpserver/tools_agent_spawn.go:552
    Session startup does not guarantee bracketed-paste readiness. A proven no-write rejection is not retried, leaving a live agent and workspace without the requested prompt.
    Fix: Expose a stable paste-not-ready error reason and retry only that proven-no-write condition within the existing timeout. Add end-to-end coverage using a multiline prompt and actual terminal readiness.


Reviewers: 2 done | Synthesis: codex, 14s | Total: 19m41s

mariusvniekerk and others added 17 commits August 12, 2026 23:45
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 lost mutation response can leave committed state behind, so malformed success bodies must be treated with the same ambiguity as transport loss. The public command now also rejects flags and ephemeral HTTP addresses that cannot produce a usable execution contract, with a real stdio/Cobra/daemon/SQLite round trip protecting the entry point.

Initial-message recovery now distinguishes an exact retry from a different handoff and conflicts on changed agent, coding-session, or byte metadata. Pending rows are recovered only during daemon startup after exclusive runtime ownership, avoiding interference from ordinary database readers.

The hook-blocking workspace enrichment test now asserts the stable failed operation instead of an incidental child exit code, so host-pressure cancellation still proves last-known-good preservation.

Validation: affected DB, MCP, CLI, startup-cancellation, workspace enrichment, and command-level stdio e2e tests; golangci-lint; migration/history and context checks; repository hooks.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <noreply@openai.com>
Agent handoff needs a daemon-authoritative coding session ID without treating launch-target labels or stale hook files as identity. Project normalized hook reports only while their exact agent runtime and canonical worktree remain live, preserving same opaque IDs across supported integrations.

Initial-message receipt metadata is joined only on an exact agent and coding-session match, so clients cannot mistake evidence from another hook identity for this session.

Validation: focused agent activity, workspace API, and shared workspace integration tests; generated API contracts; context structure; repository hooks.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <noreply@openai.com>
Automation must not treat a partially framed mutation response as confirmed state, and a nominally explicit HTTP address must identify a usable fixed port. Enforce both contracts at the companion boundary.

Receipt recovery and hook-authoritative session projection now cross real daemon/server startup, SQLite, HTTP normalization, and generated-client paths. The active plan is reordered into executable red-green stages, and receipt lifetime is explicit.

Validation: go test ./... -shuffle=on; focused daemon-startup test; golangci-lint ./cmd/kenn-forge; context structure; repository hooks. Roborev jobs 10408, 10412, 10413, and 10414 were commented and closed.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <noreply@openai.com>
Terminal input has no transactional acknowledgement, so an agent handoff must correlate the exact live hook identity and reserve its sole attempt before any bytes are written. Exact retries recover metadata without resending; failed or partial writes remain durable as uncertain evidence.

Multiline prompts are framed only while tracked bracketed-paste mode is active, and input validation bounds normalized UTF-8 without retaining prompt material. MCP-originated PR and issue workspaces can suppress configured provider self-assignment while ordinary callers keep existing behavior.

Validation: full localruntime, workspaceapi, workspacetest, and server suites; focused retry after a transient package-global limiter collision; affected golangci-lint; deterministic API generation; context structure; repository hooks.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <noreply@openai.com>
Automation should not guess launch-target keys or infer coding session identity from commands and labels. Expose only configured agent targets backed by supported hook profiles, without argv, and project live workspace sessions from the daemon-authoritative API.

This read-only stage deliberately lands before spawn orchestration so the consequential mutation can validate against a stable 11-tool discovery surface.

Validation: go test ./internal/mcpserver -shuffle=on; golangci-lint ./internal/mcpserver; context structure; repository hooks.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <noreply@openai.com>
MCP clients need one bounded operation that turns a PR, issue, or ad-hoc source into a ready coding session without guessing terminal state or risking a duplicate prompt. The handoff always starts a new runtime, waits for hook-authoritative identity, and reports every resource created before a failure.

Workspace and runtime mutations are never retried or cleaned up after ambiguous results. Initial-message recovery is receipt-only, finalization survives client cancellation, and proven no-write rejection leaves room for one corrected attempt. Follow-up messaging remains outside this surface.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <noreply@openai.com>
A total handoff deadline can expire while an idempotent daemon read is in flight. The daemon client then reports its lower-level request timeout, which hides the orchestration contract and gives callers the wrong failure kind.

Let the expired outer context take precedence while preserving daemon timeouts when the total handoff budget remains active. The regression test now holds the readiness read until cancellation so this boundary is deterministic.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <noreply@openai.com>
The companion must speak the 2026-07-28 protocol instead of inheriting legacy defaults from an older Go SDK. Stateless HTTP must reject session deletion, and the static catalog must not advertise deprecated logging or change notifications that the server cannot deliver.

Use the official SDK revision that implements the new protocol and make the advertised capability surface explicit so client discovery matches actual behavior.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <noreply@openai.com>
A total handoff timeout could erase ambiguity from an in-flight mutation, while lost initial-message responses reused a canceled context and collapsed pending or uncertain evidence into a falsely safe failure. Either path could make a duplicate agent handoff appear retryable.

Preserve daemon evidence before applying the total-timeout envelope, recover only through bounded cancellation-independent receipt reads, and reduce production polling load. The complete handoff now crosses real workspace creation, runtime launch, hook correlation, PTY delivery, and SQLite receipt persistence in one integration test.

Generated with Codex (gpt-5.6-sol)
Co-authored-by: Codex <noreply@openai.com>
Main now reserves migration 000047 and partitions clone storage by provider repository identity. Renumber the PR-local receipt migration, remove superseded transient design artifacts, and align MCP integration fixtures with the current repository and clone identity contracts so the rebase remains runnable.\n\nValidation: go test ./internal/mcpserver -shuffle=on; go run ./tools/migrationhistorycheck; scripts/context-sync --check.\n\nGenerated with Codex\nCo-authored-by: Codex <noreply@openai.com>
The mutation-timeout test shared a deadline too short for setup on a busy server test run, so it could time out before reaching the intended mutation. Give setup a realistic budget while leaving the mocked mutation blocked long enough to preserve the ambiguity assertion.\n\nValidation: go test ./internal/mcpserver -run TestSpawnWorkspaceWithAgentMutationTimeoutPreservesAmbiguity -count=5 -shuffle=on; go test ./internal/mcpserver -shuffle=on.\n\nGenerated with Codex\nCo-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Aug 13, 2026

Copy link
Copy Markdown

roborev: Combined Review (4fea1e9)

Verdict: One high-severity security issue and four medium-severity correctness/reliability issues require attention.

High

  • Agent launch lacks enforceable human approvalinternal/mcpserver/tools_agent.go:97, internal/mcpserver/tools_items.go:213, internal/mcpserver/tools_agent_spawn.go:133, internal/mcpserver/tools_agent_spawn.go:163
    Untrusted PR bodies, issue comments, and event text are returned verbatim to the MCP model. Prompt injection could induce it to invoke kenn_forge_spawn_workspace_with_agent, creating an attacker-controlled worktree and launching a local coding agent with a model-selected prompt. Written guidance is not an enforceable security boundary. Require out-of-band, one-time human authorization bound to the source, target, and initial-message digest, or isolate and disable agent-launch tools unless explicitly enabled.

Medium

  • Full-diff export rejects valid empty patchesinternal/mcpserver/tools_diff.go:153
    Binary and metadata-only changes can legitimately have empty patches, causing the entire emit_diff_file request to fail. Preserve or synthesize records for these changes and add full-stack coverage using a real binary or mode-only change.

  • Diff snapshots overwrite one deterministic pathinternal/mcpserver/difftmp.go:25
    Later or concurrent exports for the same PR can replace a snapshot before its caller reads it, leaving summary metadata inconsistent with the file contents. Use immutable unique or content-addressed filenames and remove all snapshots when the MCP server closes.

  • Pre-write submission failures are incorrectly marked uncertaininternal/server/workspaceapi/initial_message.go:171
    Attachment and agent-kind failures can occur before any terminal write, yet permanently consume the one-shot receipt as an ambiguous delivery. Distinguish pre-write errors and release their pending reservation; reserve uncertain for failures during or after the PTY write.

  • MCP dependency addition rolls back unrelated dependenciesgo.mod:9
    Huma, GitLab, Kata, SQLite, OpenTelemetry, the OpenAPI generator, and other direct dependencies are downgraded, potentially restoring previously fixed behavior. Restore the prior versions, add only the required MCP dependencies, and regenerate API artifacts with the current generator.


Reviewers: 2 done | Synthesis: codex, 15s | Total: 19m0s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant