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={
<>
+