diff --git a/DEVLOG.md b/DEVLOG.md index 0a9c386a..69d8940a 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -12,6 +12,16 @@ see the packaged application version too. - Added unit and instance-admin integration coverage for the standalone-runtime case where npm lifecycle metadata is absent. +## 2026-07-25 — Copyable local-agent issue handoffs + +- Added a compact issue-header action that copies the issue key, title, and + canonical human-readable URL for use in a local agent task. +- Included a concise Forge MCP workflow contract in the copied reference: + reconcile Delivery ownership before acting, use one isolated code-delivery + path, attach one native implementation PR, report meaningful lifecycle + updates, and preserve explicit merge/release/deploy authority. +- Added unit coverage for the generated contract and Playwright coverage for + the browser clipboard interaction. ## 2026-07-25 — v0.29.7 release preparation diff --git a/docs/guide/issues.md b/docs/guide/issues.md index 5ba8af3f..f0a2eab2 100644 --- a/docs/guide/issues.md +++ b/docs/guide/issues.md @@ -70,6 +70,24 @@ external-resource bodies. Forge currently uses ordinary indexed relational filters and deterministic cursor sorting; the current data scale does not justify a full-text or trigram extension. +## Local-agent handoff + +Use **Copy handoff** in the issue header when you want to continue an issue in a +local agent task. Forge copies the issue key and title, its canonical +human-readable link, and a short workflow contract for the receiving agent. + +The contract tells the agent to use Forge MCP as delivery truth, inspect the +current Delivery session before acting, continue or explicitly +claim/join/handoff ownership, work from one isolated issue branch and worktree, +link one primary PR through the native `IMPLEMENTS` relation, report meaningful +lifecycle updates, and finish the run visibly. It also preserves the operator +boundary around merge, release, and deployment. The title is explicitly marked +as reference data so issue content does not become operational instruction. + +This clipboard reference does not change assignment, start a run, or transfer +Delivery ownership. The receiving agent must reconcile those facts through +Forge rather than treating the pasted prompt as authority. + ## The two assignment slots Forge keeps human and agent assignment **independent**. An issue can have: diff --git a/src/app/(app)/w/[slug]/issues/[id]/page.tsx b/src/app/(app)/w/[slug]/issues/[id]/page.tsx index 64c30bf5..1b84c7e0 100644 --- a/src/app/(app)/w/[slug]/issues/[id]/page.tsx +++ b/src/app/(app)/w/[slug]/issues/[id]/page.tsx @@ -8,6 +8,7 @@ import { ArchiveRestore, Bot, Clock3, + Copy, LogOut, Paperclip, Plus, @@ -64,6 +65,7 @@ import { DispatchReasonChip, type DispatchReason } from "@/components/dispatch-r import { IssueSiblingNav } from "@/components/issue-sibling-nav"; import { useHotkey } from "@/lib/keyboard"; import { useRealtimeConnection } from "@/hooks/use-realtime"; +import { buildIssueAgentHandoff } from "@/lib/issue-agent-handoff"; const PRIORITIES = ["NONE", "LOW", "MEDIUM", "HIGH", "URGENT"] as const; @@ -267,6 +269,25 @@ export default function IssueDetailPage({ params }: { params: Promise<{ id: stri ); const issueKey = formatIssueId(workspace.key, issue.number); + const issueTitle = issue.title; + + async function copyAgentHandoff() { + const issueUrl = + `${window.location.origin}/w/${encodeURIComponent(slug)}` + + `/i/${encodeURIComponent(issueKey)}`; + const handoff = buildIssueAgentHandoff({ + issueKey, + title: issueTitle, + url: issueUrl, + }); + + try { + await navigator.clipboard.writeText(handoff); + toast.success("Local-agent handoff copied."); + } catch { + toast.error("Couldn't copy the handoff. Check clipboard permissions."); + } + } // Phase 1B: replace the title-only header with a real path. Project → // issue when the issue is grouped; otherwise fall back to the issues @@ -361,6 +382,16 @@ export default function IssueDetailPage({ params }: { params: Promise<{ id: stri subtitle={{issue.status.name}} actions={ <> +