name: polygraph
description: Guidance for working with Polygraph sessions, shared/resumable agent context, repository graph visibility, linked PR/CI state, and cross-repo expansion when needed. Use when starting, joining, resuming, inspecting, or sharing a Polygraph session; handing off progress; discovering related repositories; coordinating changes/branches/PRs across repos; delegating tasks to child agents in different repos; or checking CI status and logs. TRIGGER when user mentions "polygraph", resuming or sharing a session, "other repos", "other repositories", "who uses this", "what uses this", "cross-repo", "multi-repo", "consuming this API/endpoint", "dependent repositories", or asks about what other repos are doing with shared code/APIs/endpoints.
{% if platform == "claude" %}
allowed-tools:
- mcp__plugin_polygraph_polygraph-mcp
{% endif %}
{% assign has_subagents = false %}
{% if platform == "claude" or platform == "opencode" or platform == "codex" %}
{% assign has_subagents = true %}
{% endif %}
Working with Polygraph
IMPORTANT: Polygraph keeps local clones only for other repositories in the session. NEVER cd into those clones or access their files directly — work in other repositories ALWAYS happens through the Polygraph MCP spawn_agent tool, invoked {% if platform == "codex" %}via Codex Polygraph subagents{% elsif platform == "claude" %}via background polygraph-delegate-subagent Tasks{% elsif platform == "opencode" %}via @polygraph-delegate-subagent{% else %}directly{% endif %}.
{% if platform == "codex" %}
Critical Routing Rule (Codex Parent Conversation)
Read this before the tool table below — it determines which tools are yours to call directly.
- Codex
spawn_agent ≠ Polygraph MCP spawn_agent. Codex spawn_agent launches the local custom subagents (polygraph-init-subagent, polygraph-delegate-subagent). The Polygraph MCP spawn_agent runs work inside another repository and must only be invoked from inside those subagents.
- For new sessions: call Codex
spawn_agent with agent_type: "polygraph-init-subagent". Do NOT call Polygraph MCP list_repos or start_session directly from this conversation.
- For explicit repo additions to an existing session: if the user gives exact refs by ID, short name, full name, GitHub
owner/repo slug, or URL-like slug, call Polygraph MCP add_repo directly with those refs. Do NOT call list_repos or launch candidate discovery first.
- For repo work: call Codex
spawn_agent with agent_type: "polygraph-delegate-subagent". Do NOT call Polygraph MCP spawn_agent or show_agent directly from this conversation; collect results with wait_agent when needed.
- Allowed direct Polygraph MCP calls from the parent:
whoami, login, list_accounts, select_account, show_session for read-only inspection of an existing session, update_session for session metadata updates, link_reference for linking external references to sessions, and add_repo only for explicit repo additions to an existing session.
- Do NOT pass
fork_context: true to Codex spawn_agent when agent_type is a custom agent — Codex rejects it.
{% endif %}
Polygraph connects repositories and the agent work happening across them. Its central artifact is the session, which groups the repositories, branches, PRs, and CI status for one piece of work and can be shared and resumed: use it to coordinate changes across multiple repositories, and also on its own to share the session URL with collaborators, hand off progress via the session description, resume prior work, and watch CI across the session's PRs.
Polygraph operates on the current repo in place. Starting or joining a session never clones or modifies the repository you are in — you keep working in your real working directory, and push_branch pushes your local commits from that checkout. Only other repositories are worked on in separate Polygraph-managed clones via spawn_agent.
{% if platform == "claude" or platform == "codex" %}
Sandboxing in Polygraph Sessions
Polygraph launches agent sessions inside an OS-level sandbox by default. Writes are limited to the repository working tree, the session root (~/.polygraph/sessions/<session-id>/), the system temp directory, and a few allowlisted directories; network access is restricted to allowlisted hosts. In practice: binding a listening socket is denied (dev servers fail with EPERM), localhost servers are unreachable, and writes outside the allowlist are rejected. The user may not know the session is sandboxed.
Recognize sandbox denials — do not retry or work around them. When a command fails in a sandbox-shaped way (EPERM binding a port, a blocked network host, a denied write to an ordinary path), the sandbox blocked it. Do NOT retry variations, escalate through workarounds, or route the command through !-prefixed user commands — those run inside the same sandbox. One failure is enough evidence; stop and inform the user.
Warn before attempting known-blocked operations. Before starting a dev server, anything else that listens on a port, or an operation that needs writes or network access outside the allowlist, tell the user up front that it will not work while sandboxing is on and offer the options below instead of attempting it.
What to tell the user. Explain that Polygraph runs this session in a sandbox, then present both options. Each takes effect on the next agent launch, so the Polygraph session must be relaunched afterwards:
-
Keep the sandbox on and allow the specific operation (preferred). The sandbox belongs to the agent harness, so exceptions live in harness settings committed to the repository; array settings merge with Polygraph's generated allowlist rather than replacing it.{% if platform == "claude" %} Add writable paths to .claude/settings.json (or .claude/settings.local.json):
{ "sandbox": { "filesystem": { "allowWrite": ["<path>"] } } }
The .claude/ directory is read-only inside the sandbox, so give the user the exact snippet to commit — you cannot apply it yourself.{% elsif platform == "codex" %} Add writable roots or network access to .codex/config.toml:
[sandbox_workspace_write]
network_access = true
writable_roots = ["<path>"]
The .codex/ directory is read-only inside the sandbox, so give the user the exact snippet to commit — you cannot apply it yourself.{% endif %}
-
Turn sandboxing off. The user quits the agent, runs polygraph config, toggles Agent Options → {% if platform == "claude" %}Claude{% else %}Codex{% endif %} → sandbox off (or per-repo under Repo Options), and relaunches the Polygraph session. Non-interactive alternative: set agentOptions.{{ platform }}.sandbox: false (or repoOptions."org/repo".sandbox: false) in ~/.polygraph/config.json. Warn that this removes filesystem isolation — the agent can then write anywhere the OS user can.
Never blame the tooling. A sandbox denial means the environment blocked the operation — not that the repo, tool, or framework is broken. Do not record conclusions like "X is unusable" in memory, session descriptions, or messages based on sandboxed failures.
{% endif %}
Available Tools
Polygraph functionality is available via both MCP tools and CLI commands. Use whichever is available in your current environment.
| MCP Tool | CLI Equivalent | Description |
|---|
list_repos | polygraph repo list | Discover candidate repositories. Candidate entries do not include repository descriptions; use semanticQuery for natural-language discovery. |
start_session | polygraph session start --repo <ids> | Initialize a Polygraph session with selected repositories |
spawn_agent | — | Start a new child task or send a follow-up to an active task in another repository. Input: { sessionId, repo, instruction, context? }. Output: { taskId, message, status: 'delegated' }. Follow-up routing is automatic: if the repo already has an active child task, the instruction is delivered to it as a follow-up message; otherwise a new child run starts. A repo has at most one active child at a time. A session resume or reconstruction is read-only context restoration; after resuming, do not use spawn_agent to continue changes unless the user explicitly asks for changes. |
show_agent | — | Poll flat per-child status for the session. Output: { children: PolygraphChildStatusItem[] } where each item exposes repositoryId, repoFullName, status, lastOutputLines, durationMs, instruction, agentType?, inputRequiredQuestion?. status is an AcpRunStatus: 'created' | 'in-progress' | 'input-required' | 'permission-required' | 'completed' | 'failed' | 'cancelled' (British double-L on 'cancelled'). inputRequiredQuestion is populated only when status === 'input-required'. |
stop_agent | — | Cancel an in-progress child. Output: { taskId, state: 'cancelled', sessionPreserved: true, output, message }. Because sessionPreserved: true, the preserved agent session can be restored later for context, but resume must wait for explicit user instructions before making changes. |
push_branch | — | Push a local git branch to the remote repository. For the repo you are in, this pushes from your current checkout. Requires a session description. |
create_pr | — | Create draft PRs with session metadata linking related PRs |
show_session | polygraph session show <id> [--details] | Query status of the current session. Use details when session summary, repo IDs, PR URLs, and PR descriptions are needed. |
update_session | polygraph session update --session <id> [--title] [--description] | Update the session title and/or description (at least one required). Set the description from a synthesized progress summary or user-provided text. This updates session metadata only; it does not require PR creation or mark-ready. |
link_reference | — | Link an external reference to a session. |
mark_pr_ready | — | Mark draft PRs as ready for review |
associate_pr | — | Associate an existing PR with a session |
add_repo | — | Add repositories to a running Polygraph session. For explicit refs, pass the refs directly and skip list_repos. |
archive_session | polygraph session archive <id> | Archive a session, hiding it from active lists (it can still be resumed) |
get_ci_logs | — | Retrieve full plain-text log for a specific CI job |
login | polygraph auth login [--token] | Authenticate with Polygraph (use --token for headless/CI) |
logout | polygraph auth logout | Log out of Polygraph |
list_sessions | polygraph session list | List sessions. By default only active sessions created by the current git user; pass recommendedFilters: false for all sessions. |
list_accounts | polygraph account list | List available organizations |
select_account | polygraph account select | Select the organization that future commands run against |
whoami | polygraph whoami | Show current auth status and org |
{% if platform == "claude" or platform == "opencode" %}
Delegation rules: list_repos and start_session MUST be called via the polygraph-init-subagent as described in step 0. Direct add_repo is allowed only when the user provides exact repo refs for an existing session. spawn_agent and show_agent MUST ALWAYS be called via {% if platform == "claude" %}background Task subagents (run_in_background: true){% else %}@polygraph-delegate-subagent{% endif %} as described in the delegation sections below — NEVER call them directly in the main conversation.{% if platform == "claude" %} The subagents are plugin-namespaced: pass subagent_type: "polygraph:polygraph-init-subagent" / "polygraph:polygraph-delegate-subagent"; fall back to the bare name only if the namespaced form is not found.{% endif %}
{% elsif platform == "codex" %}
Routing reminder: Per the Critical Routing Rule above, the parent conversation must use Codex spawn_agent with agent_type: "polygraph-init-subagent" for new sessions and agent_type: "polygraph-delegate-subagent" for repo work — not the Polygraph MCP tools shown in the table. wait_agent collects results when needed.
{% endif %}
CLI Statefulness
The Polygraph CLI (polygraph) is stateful. When you select an organization — via polygraph account select or the equivalent MCP tool — that selection is saved globally and all subsequent CLI commands and MCP tool calls operate against it. You do not need to pass the org on every command.
Setup
Before using Polygraph tools, ensure the CLI is authenticated and an organization is selected.
Check Authentication
Use polygraph whoami (or the whoami MCP tool) before session work to check if the user is currently logged in and which organization is active.
- If the user is logged in and an org is selected → proceed to the workflow.
- If auth is missing, expired, or no org is selected → stop session work. Do not keep trying session creation, repository discovery, delegation, or CI checks.
- Facilitate user reauth through the browser-based login flow, such as
polygraph auth login (or the login MCP tool). In interactive desktop clients, browser reauth is usually user-driven; surface the need clearly and wait for the user to complete it.
- After login, an organization must be selected. Use
polygraph account select (or the MCP equivalent) when needed.
- Re-run
polygraph whoami (or whoami) after reauth and org selection. Continue only after it confirms a valid login and selected organization.
Select Organization
After logging in (or if logged in but no org is selected), use polygraph account select (or the equivalent MCP tool) to choose the organization that future commands will run against.
Workflow Overview
The delegate/monitor/stop steps apply only when working across repos. A single-repo session skips them and still benefits from shared progress, resume, and CI visibility.
{% if has_subagents %}
- Initialize or join Polygraph session - If you were spawned inside an existing session (the startup banner names a session ID), reuse it. Call
show_session first; if it already has repos and the user did not ask to add more, you're done. If the user asks to add exact repo refs, call add_repo directly with those refs and skip candidate discovery. If the session has no repos and no exact refs were provided, launch the polygraph-init-subagent with that sessionId so it discovers candidates and uses add_repo (NOT start_session). Only when there is no session ID at all should the init subagent create a new session.
- Delegate work to each repo - Use the
polygraph-delegate-subagent to start child agents in other repositories. Delegate only to other repos — never to the repo you are in; work on it directly (your regular subagents are fine for local work — only Polygraph delegation is reserved for other repos). Parallel delegation across repos is encouraged, but only one active child per repo. Choose the Simple (fire-and-forget) or Multi-turn (interactive) pattern described below based on whether the child may need clarification.
{% else %}
- Initialize or join Polygraph session - If you already have a session ID, call
show_session to fetch details. If the user asks to add exact repo refs, call add_repo directly with those refs and skip candidate discovery. Otherwise, discover candidate repos, select relevant repositories, and create a new session via list_repos and start_session.
- Delegate work to each repo - Use
spawn_agent to start child agents in other repositories (returns immediately). Delegate only to other repos — never to the repo you are in; work on it directly. Parallel delegation across repos is encouraged, but only one active child per repo. Choose the Simple (fire-and-forget) or Multi-turn (interactive) pattern described below.
{% endif %}
- Monitor child agents - Use
show_agent to poll progress and read the flat children[] array for each child's status and lastOutputLines.
- Stop child agents (if needed) - Use
stop_agent to cancel an in-progress child agent. The underlying agent session is preserved for later read-only context restoration; after a resume, wait for explicit user instructions before making changes.
- Push branches - Use
push_branch after making commits. A required description must follow the Session Description Policy.
- Update session description - Use
update_session to update the session description; must follow the Session Description Policy. Independent of PR creation or mark-ready.
- Create draft PRs - Use
create_pr to create linked draft PRs. Always pass description following the Session Description Policy.
- Associate existing PRs (optional) - Use
associate_pr to link PRs created outside Polygraph.
- Query PR status - Use
show_session to check progress.
- Mark PRs ready - Use
mark_pr_ready when work is complete.
- Archive session - Use
archive_session to archive the session when the user requests it.
Step-by-Step Guide
0. Initialize or Join Polygraph Session
There are three cases. Pick exactly one before calling any tool. The case labels are internal routing shorthand — never mention them in anything you show the user.
Hard rule: if a session ID is already in scope (e.g., the startup banner says "You're in Polygraph session …", or the user passed one), that session ID is authoritative for this entire conversation. NEVER call start_session — doing so creates a brand-new session and orphans the one the parent harness is pointed at. Reuse the existing session via show_session and, if needed, add_repo.
Case A — Existing session, already has repos. Call show_session directly with the known session ID. Skip the init subagent entirely, show the session details (format below), and proceed.
Case B — Existing session, no repos yet (or user wants to add more). If the user gives exact repo refs by ID, short name, full name, GitHub owner/repo slug, or URL-like slug, call add_repo(sessionId, repoIds: [...]) directly with those refs. Do NOT call list_repos, do NOT ask for candidates, and do NOT launch the init subagent just to resolve those refs. If the user wants discovery/filtering instead, launch the polygraph-init-subagent, passing both the existing sessionId and userContext. The subagent will discover candidates, select relevant repositories, and call add_repo against the existing session — it will NOT call start_session.
Case C — No session at all. Launch the polygraph-init-subagent with only userContext (no sessionId). The subagent will discover candidates and call start_session to create a new session.
{% if has_subagents %}
In case B, call add_repo yourself when exact repo refs were provided; otherwise the subagent handles discovery and attachment. In case C the subagent handles session creation. In case A you call show_session yourself.
{% else %}
In case B, direct exact repo refs go straight to add_repo; use list_repos only when discovery/filtering is needed. In case C, discover candidate repos using list_repos, select relevant repositories, and call start_session. In case A, just call show_session.
{% endif %}
Session ID handling:
- For a new session (case C),
start_session auto-generates a unique session ID. You do NOT need to pass one.
- For cases A and B, the session ID already exists; reuse it everywhere — never let
start_session run in this conversation.
- The parent conversation is responsible for detecting an existing session ID from current context, the startup banner, or a user-provided session URL/ID, then passing it explicitly to
polygraph-init-subagent. The init subagent cannot infer parent session context by itself.
- For a fresh Codex Desktop conversation started with
/polygraph:session-start, no sessionId is expected; launch polygraph-init-subagent without sessionId so it creates a new session.
{% if platform == "claude" %}
Launch the init subagent (cases B and C — skip in case A):
{% raw %}
Task(
subagent_type: "polygraph:polygraph-init-subagent",
description: "Init Polygraph session",
prompt: """
Parameters:
- sessionId: "<existing-session-id-or-omit-for-new-session>"
- userContext: "<description of what the user wants to do>"
If sessionId is provided, reuse that session and use add_repo to attach repositories — do NOT call start_session. If exact repo refs were provided, pass them directly to add_repo and do NOT call list_repos. If discovery is needed, discover candidates and select relevant repos. If sessionId is omitted, create a new session via start_session. Return a structured summary.
"""
)
{% endraw %}
Omit the sessionId line for case C. Include it (with the existing session ID) for case B.
{% elsif platform == "opencode" %}
Launch the init subagent using @polygraph-init-subagent (cases B and C — skip in case A):
Invoke the polygraph-init-subagent agent when discovery is needed. Always pass userContext. If you are in case B (existing session with no repos) and no exact repo refs were provided, also pass the existing sessionId and instruct the subagent to use add_repo rather than start_session. The subagent returns a structured summary.
{% elsif platform == "codex" %}
Launch polygraph-init-subagent (cases B and C — skip in case A):
Use Codex's spawn_agent tool to start the custom Polygraph init subagent:
{% raw %}
spawn_agent(
agent_type: "polygraph-init-subagent",
message: """
Parameters:
- sessionId: "<existing-session-id-or-omit-for-new-session>"
- userContext: "<description of what the user wants to do>"
If sessionId is provided, reuse that session and use add_repo to attach repositories — do NOT call start_session. If exact repo refs were provided, pass them directly to add_repo and do NOT call list_repos. If discovery is needed, discover candidates and select relevant repos. If sessionId is omitted, create a new session via start_session. Return a structured summary.
"""
)
{% endraw %}
Omit the sessionId line for case C. Include it (with the existing session ID) for case B. When the main flow needs the session before proceeding, collect the result with wait_agent.
{% else %}
If an existing sessionId is in scope and the user provided exact repo refs, call add_repo directly with those refs. Otherwise call list_repos to discover available repositories, select relevant repos based on user context, then call start_session with the selected repository IDs.
{% endif %}
The subagent will:
- Use exact repo refs directly when provided for an existing session; otherwise call
list_repos to discover available repositories
- Select relevant repos based on the user context (or include all if uncertain)
- Either call
start_session (case C, no sessionId) or call add_repo against the existing session (case B). It will never call start_session when a sessionId was provided.
- Call
show_session to retrieve session details
- Return a summary with session URL and repo info
When the init subagent has just created a brand-new session, render the session welcome card instead of the session-details block below. Prefer the session_intro MCP tool — call it with the session ID; it returns the card as markdown. If that tool is unavailable, run polygraph session intro -s <sessionId> via the CLI instead. The CLI command is intentionally hidden/internal and may not appear in public command listings, but it remains the correct skill fallback for rendering the welcome card. Either way, print the result to the user verbatim as markdown — do NOT wrap it in a code block or reformat it (the logo is pre-fenced; the rest is live markdown). It needs no other input, and you do not need to call show_session first. Then continue (ask the user what they want, or start the requested task).
For an existing session — after show_session returns or the init subagent's summary arrives — show the session details:
Session: POLYGRAPH_SESSION_URL
Repositories in this session:
Explore an Existing Session
Use this workflow when the user gives a Polygraph session ID and asks to understand, resume, inspect, or investigate prior work.
Resume is not a work command. If the user's intent is to resume, reconnect, or reconstruct a prior Polygraph session, fetch and summarize the restored context, then stop. Do not edit files, push branches, add repos, delegate new work, or continue previous changes until the user explicitly asks for changes. Treat "resume" as context restoration followed by waiting for user instructions.
- Fetch detailed session context:
- Prefer
show_session with details: true when the MCP tool exposes that option.
- Otherwise run
polygraph session show --details <session-id>.
- Treat the detailed output as authoritative context. It should include:
<summary> — the session summary.
<repositories> — relevant repos, including each repo's <id> and <name>.
<pullRequests> — relevant PRs, including <url>, <repoId>, <repoName>, branch metadata, and <description>.
- Parse the XML-style blocks and XML-unescape text inside
<summary> and <description>.
- Build a repo/PR map:
- repo id
- repo full name
- PR URL
- branch
- base branch
- title
- status
- PR description
- If the request was resume/reconnect/reconstruct only, report the restored session context and wait for the user's next instruction.
- If the user explicitly asked to inspect or investigate prior work, use the PR descriptions and session summary to decide whether more repo investigation is needed.
- If the repo to investigate is already part of the session, delegate directly to that repo (unless it is the repo you are in — investigate that one directly).
- If the repo to investigate is not currently initialized in the session, and either the user provided an exact repo ref or the repo appears in
<repositories>, call add_repo with that ref or repo <id> directly. Do not call list_repos just to resolve the repo.
- After
add_repo, call show_session again to verify the repo was added, then delegate to that repo.
- Fall back to
list_repos only when the desired repo is not an exact ref and is missing from <repositories>, or when the details output came from an older Polygraph version that did not include repo IDs.
When delegating investigation from a PR, include the PR context in the child instruction:
Session: <session-id>
Repo: <repoName>
Repo ID: <repoId>
PR: <url>
Branch: <branch>
Base branch: <baseBranch>
Description:
<description>
Inspect the PR commits/diff and investigate the requested behavior. Report findings with file paths and concrete evidence.
Simple tasks (fire-and-forget)
Use this pattern when the task is well-defined and the child is not expected to need clarification. It is a single-round delegation: kick it off, poll until terminal, then push branch + create PR.
{% if platform == "claude" %}
CRITICAL: spawn_agent and show_agent MUST ALWAYS be called via background Task subagents (run_in_background: true), NEVER directly from the main conversation. Direct calls flood the context window with polling noise and degrade the user experience. This is a hard requirement, not a suggestion.
- Launch a background
Task subagent per repo using polygraph-delegate-subagent. The subagent calls spawn_agent, then polls show_agent on backoff until terminal.
{% raw %}
Task(
subagent_type: "polygraph:polygraph-delegate-subagent",
run_in_background: true,
description: "Delegate to <repo-name>",
prompt: """
Parameters:
- sessionId: "<session-id>"
- repo: "<org/repo-name>"
- instruction: "<the task instruction>"
- context: "<optional context>"
Delegate the work, poll for completion, and return a structured summary.
"""
)
{% endraw %}
- Delegate to multiple repos in parallel by launching multiple background Task subagents at the same time — one delegation per repo at a time. Read the output files later to check progress.
- For each child, the subagent watches
child.status in the flat children[] response and exits when it sees a terminal status — typically 'completed' or 'failed' (and 'cancelled' if it was stopped).
- Once all background subagents report a terminal status, continue to
push_branch + create_pr.
In rare cases where you need to check the raw child agent status directly (e.g., debugging a stuck subagent), you may call show_agent as a one-off tool call. Do NOT use this for regular polling — that MUST happen in background subagents.
{% elsif platform == "opencode" %}
CRITICAL: spawn_agent and show_agent MUST ALWAYS be called via @polygraph-delegate-subagent, NEVER directly from the main conversation. Direct calls flood the context window with polling noise and degrade the user experience. This is a hard requirement, not a suggestion.
- For each repo, invoke
@polygraph-delegate-subagent with sessionId, repo, instruction, and optional context. The subagent calls spawn_agent, then polls show_agent on backoff until terminal.
- Delegate to multiple repos in parallel by launching multiple
@polygraph-delegate-subagent invocations — one delegation per repo at a time.
- For each child, the subagent watches
child.status in the flat children[] response and exits when it sees a terminal status — typically 'completed' or 'failed' (and 'cancelled' if it was stopped).
- Once all subagents report a terminal status, continue to
push_branch + create_pr.
{% elsif platform == "codex" %}
CRITICAL: Routine Polygraph MCP spawn_agent and show_agent calls MUST run inside the custom Codex polygraph-delegate-subagent, not directly in the main conversation. Codex spawn_agent launches a local subagent; the Polygraph MCP spawn_agent starts work in another repository. Keeping the MCP delegate-and-poll loop inside polygraph-delegate-subagent prevents polling noise from filling the user's context.
- For each repo, launch
polygraph-delegate-subagent via Codex's spawn_agent:
{% raw %}
spawn_agent(
agent_type: "polygraph-delegate-subagent",
message: """
Parameters:
- sessionId: "<session-id>"
- repo: "<org/repo-name>"
- instruction: "<the task instruction>"
- context: "<optional context>"
Call the Polygraph MCP spawn_agent for the repo, then poll show_agent on backoff until terminal. Return a structured summary with repo, status, session ID, and result text.
"""
)
{% endraw %}
- Delegate to multiple repos in parallel by launching multiple
polygraph-delegate-subagent instances before waiting for results — one delegation per repo at a time.
- For each child, the subagent watches
child.status in the flat children[] response and exits when it sees a terminal status — typically 'completed' or 'failed' (and 'cancelled' if it was stopped).
- Collect completed results with
wait_agent when the main flow needs them, then continue to push_branch + create_pr.
In rare cases where you need to check the raw child agent status directly (e.g., debugging a stuck subagent), you may call the Polygraph MCP show_agent as a one-off tool call. Do NOT use this for regular polling — that belongs inside polygraph-delegate-subagent.
{% else %}
- Call
spawn_agent with sessionId, repo, and instruction for each repo. The call returns immediately.
- Poll
show_agent on backoff; for each child, watch child.status in the flat children[] response until it reaches a terminal status — typically 'completed' or 'failed' (and 'cancelled' if it was stopped).
- Review
child.lastOutputLines for the final log tail.
- Continue to
push_branch + create_pr.
{% endif %}
Use Simple when the task is well-defined and the child will not need clarification.
Multi-turn tasks (interactive)
Use this pattern when the child may need clarification, the task is exploratory, or interactive collaboration is desired. The orchestrator exposes paused children via the 'input-required' status.
-
Call spawn_agent with the initial instruction. Parse the response:
{ "taskId": "…", "message": "…", "status": "delegated" }
-
Poll show_agent. The response shape is { children: PolygraphChildStatusItem[] }. For each child, inspect:
child.status — one of 'created', 'in-progress', 'input-required', 'permission-required', 'completed', 'failed', 'cancelled' (British double-L on 'cancelled').
child.inputRequiredQuestion — populated only when child.status === 'input-required'.
child.lastOutputLines — recent log tail.
child.repoFullName — which repo is talking.
Drive the state machine:
child.status === 'in-progress' or 'created' — continue polling.
child.status === 'input-required' — read child.inputRequiredQuestion, surface it to the user verbatim (e.g. "The child agent in {child.repoFullName} needs input: {child.inputRequiredQuestion}"), get the answer, then call spawn_agent again with the same repo and instruction: <answer> — it is routed to the active task automatically. Continue polling.
child.status === 'completed' — read child.lastOutputLines, proceed to push_branch + create_pr.
child.status === 'failed' — read child.lastOutputLines, surface the failure.
child.status === 'cancelled' — the child was stopped via stop_agent; see below.
child.status === 'permission-required' — the child is waiting on a permission decision; see "Handling permission requests" below.
-
To abort mid-flight, call stop_agent with { sessionId, repo }. The response is:
{
"taskId": "…",
"state": "cancelled",
"sessionPreserved": true,
"output": "…",
"message": "…"
}
Because sessionPreserved: true, the preserved agent session can be restored later for context. After resuming, do not make changes or continue prior work until the user explicitly asks for changes.
Use Multi-turn when the child may need clarification, the task is exploratory, or interactive collaboration is desired. Otherwise use Simple.
{% if platform == "opencode" %}
Handling permission requests
Child agents running in other repositories may pause and ask the parent agent whether a specific action is permitted. Polygraph exposes this via two wire paths.
Native path (MCP permission dialog): If your MCP client supports the permission dialog UI, the user picks directly in that dialog — you (the agent) won't see permission-required tasks in that flow.
Structured fallback path: When cloud_polygraph_child_status reports a task in permission-required state, read the pendingPermission object on that task (carries harness, action, target, repositoryId, repoFullName, scope, availableScopes, optional reason, optional rawInput), then call allow_agent (to grant the requested action) or deny_agent (to refuse it) with the {sessionId, repo} of the child agent.
Answering a permission request
{
"sessionId": "...",
"repo": "org/repo-name",
"scope": "session",
"reason": "Trusted local repo"
}
{
"sessionId": "...",
"repo": "org/repo-name",
"reason": "Action looks risky"
}
The three decisions:
allow_agent with scope: 'one-time' — permits the single action only; the child must ask again for the next action of the same type.
allow_agent with scope: 'session' — permits the action and remembers that grant for the rest of the child's session; the child will not ask again.
deny_agent — rejects the request; child continues without performing the action.
Fail-closed default: When you see a task in permission-required state, you MUST call either allow_agent or deny_agent. Failing to call one leaves the gate held open until the child's idle timer fires; the child cannot make progress until you decide.
OpenCode caveat: OpenCode children sometimes request permissions without specific command/path (target is empty). Dialog says 'session' grant covers ALL ${action} calls this session — read carefully before granting session scope.
Polling for permission-required in the fallback path
When polling cloud_polygraph_child_status (or show_agent), treat permission-required like input-required:
- Read
child.pendingPermission — inspect harness, action, target, repoFullName, and scope.
- Surface the request to the user: "Child agent in
{repoFullName} requests {scope} permission to run {action} on {target}."
- Obtain the user's decision.
- Call
allow_agent (to grant) or deny_agent (to refuse) with { sessionId, repo }.
- Resume polling.
{% else %}
Handling permission requests
Your MCP client supports the native permission dialog. When a child agent requests permission, the dialog renders directly in your UI and the user picks — the decision routes back through polygraph-mcp automatically.
Critical: do NOT call allow_agent or deny_agent yourself. If show_agent (or cloud_polygraph_child_status) briefly reports a child in permission-required state with pendingPermission populated, that is a transient state the dialog is in the middle of resolving. Your job is to keep polling — the next poll will see the child back in in-progress (or failed / cancelled if the user denied or dismissed).
If you call allow_agent while the dialog is already open, you create a race: the user's pick lands first and the explicit allow fails with Task <id> is in state 'completed', not 'permission-required'. The child receives the user's choice; your call is wasted work.
The allow_agent and deny_agent tools exist for parents whose MCP clients do NOT advertise elicitation capability (opencode TUI today). They are not part of your flow.
{% endif %}
2. Push Branches
Once work is complete in a repository, push the branch using push_branch. This must be done before creating a PR.
push_branch pushes from the local checkout: for the repo you are in, that is your current working directory with your commits; for delegated repos, it is the Polygraph-managed clone the child agent worked in. There is no separate session copy of the current repo.
Parameters:
sessionId (required): The Polygraph session ID
repo (required): Repository name or repository ID to push from
branch (required): Branch name to push to remote
description (required): A session description is required. Must follow the Session Description Policy.
push_branch(
sessionId: "<session-id>",
repo: "org/repo-name",
branch: "polygraph/ad5fa-add-user-preferences"
)
Session Description Policy
description is user-facing Polygraph session context. It is required for push_branch, create_pr, and associate_pr, and is the primary input to update_session (mark_pr_ready does not take a description).
Whenever you write or update a session description, read reference/session-description.md first. That reference file holds the full policy: the canonical Markdown-heading template (## Goal / ## Current progress / ## What worked / ## Next steps), the dual-audience guidance (humans in the web UI now, agents reconstructing history later), and the formatting building blocks the app renders (callouts, tables, mermaid, links, link_reference).
3. Create Draft PRs
Create PRs for all repositories at once using create_pr. PRs are created as drafts with session metadata that links related PRs across repos. Branches must be pushed first. For fork PR creation or registration, include targetRepository on the PR spec to identify the repository that should receive the PR.
Parameters:
sessionId (required): The Polygraph session ID
prs (required): Array of PR specifications, each containing:
owner (required): GitHub repository owner
repo (required): GitHub repository name
title (required): PR title
body (required): PR description (session metadata is appended automatically)
branch (required): Branch name that was pushed
targetRepository (optional): Target GitHub repository for fork PR creation or registration, as owner/repo. Omit for same-repository PRs.
description (required): Must follow the Session Description Policy.
PR title format (applies to parent and child agents):
- PR titles become squash-merge commit messages in most repos. They MUST follow the target repo's commit convention (e.g., Conventional Commits:
<type>(<scope>): <subject>).
- Do NOT add agent-identifier prefixes such as
[codex], [claude], or [opencode] to PR titles. These prefixes violate commit-lint rules and pollute the git history.
create_pr(
sessionId: "<session-id>",
prs: [
{
owner: "org",
repo: "frontend",
title: "feat: Add user preferences UI",
body: "Part of multi-repo user preferences feature",
branch: "polygraph/ad5fa-add-user-preferences"
},
{
owner: "org",
repo: "backend",
title: "feat: Add user preferences API",
body: "Part of multi-repo user preferences feature",
branch: "polygraph/ad5fa-add-user-preferences"
}
]
)
For fork PR creation or registration, keep owner and repo set to the source repository that owns the pushed branch and set targetRepository to the target repository:
create_pr(
sessionId: "<session-id>",
prs: [
{
owner: "contributor",
repo: "frontend-fork",
targetRepository: "org/frontend",
title: "feat: Add user preferences UI",
body: "Part of multi-repo user preferences feature",
branch: "polygraph/ad5fa-add-user-preferences"
}
]
)
After creating PRs, always print the Polygraph session URL:
**Polygraph session:** POLYGRAPH_SESSION_URL
4. Get Current Polygraph Session
Check the details of a session using show_session or polygraph session show --details <session-id>. Returns the full session state including repositories, PRs, CI status, and the Polygraph session URL.
Parameters:
sessionId (required): The Polygraph session ID
Returns:
session.sessionId: The session ID
session.polygraphSessionUrl: URL to the Polygraph session UI
session.description: DescriptionItem[] timeline describing the session.
session.agentSessionId: The agent CLI session ID — captured automatically by the MCP server (null if no agent has run yet).
session.linkedReferences: Array of references linked to this session
- Session repository entries: Array of connected repositories, each with:
id: Repository ID
name: Repository name
defaultBranch: Default branch (e.g., main)
vcsConfiguration.repositoryFullName: Full repo name (e.g., org/repo)
vcsConfiguration.provider: VCS provider (e.g., GITHUB)
- description field: AI-generated description of what this repository does (may be null)
initiator: Whether this repository initiated the session
session.dependencyGraph: Graph of repository dependency edges
session.pullRequests[]: Array of PRs, each with:
url: PR URL
branch: Branch name
baseBranch: Target branch
title: PR title
status: One of DRAFT, OPEN, MERGED, CLOSED
repoId: Associated repository ID
relatedPRs: Array of related PR URLs across repos
session.ciStatus: CI pipeline status keyed by PR ID, each containing:
status: One of SUCCEEDED, FAILED, IN_PROGRESS, NOT_STARTED (null if no CIPE and no external CI)
cipeUrl: URL to the CI pipeline execution details (null if no CIPE). This is a human-facing Nx Cloud web link — display it to the user, but never fetch, curl, or poll it directly; CIPE data is only accessible programmatically via the Nx MCP ci_information tool
completedAt: Epoch millis timestamp, set only when the CIPE has completed (null otherwise)
selfHealingStatus: The self-healing fix status string from Nx Cloud's AI fix feature (null if no AI fix exists)
externalCIRuns: Array of external CI runs (present when no CIPE but external CI data exists, e.g., GitHub Actions). Each run contains:
runId: GitHub Actions run ID
name: Workflow name
status: Run status (completed, in_progress, queued)
conclusion: Run conclusion (success, failure, cancelled, timed_out, or null)
url: GitHub Actions run URL
jobs: Array of jobs in the run, each with:
jobId: Job ID (use with get_ci_logs)
name: Job name
status: Job status
conclusion: Job conclusion (or null)
show_session(sessionId: "<session-id>")
Linked References
Use link_reference to link an external reference to the current Polygraph session.
Parameters:
sessionId (required): The Polygraph session receiving the linked reference
reference (required): Reference metadata with type, url, and label
reference.sessionId (session references only): The referenced Polygraph session ID when reference.type is session
When an external resource is mentioned during a Polygraph session and appears relevant to the current work, the parent agent should record it with link_reference({ sessionId, reference }). This applies to relevant external resources such as pull requests, GitHub issues, other Polygraph sessions, and Linear issues.
Invoke the MCP tool with a single object containing { sessionId, reference }. For example, to record a relevant pull request:
link_reference({
sessionId: "<current-session-id>",
reference: {
type: "github_pr",
url: "https://github.com/nrwl/polygraph-skills/pull/123",
label: "Implementation PR"
}
})
To record a relevant Polygraph session, use the same invocation shape and include reference.sessionId:
link_reference({
sessionId: "<current-session-id>",
reference: {
type: "session",
url: "https://polygraph.example/s/<inspected-session-id>",
label: "Inspected Polygraph session",
sessionId: "<inspected-session-id>"
}
})
The canonical MCP parameters are { sessionId, reference }. There is no unlink command.
5. Mark PRs Ready
Once all changes are verified and ready to merge, use mark_pr_ready to transition PRs from DRAFT to OPEN status.
Parameters:
sessionId (required): The Polygraph session ID
prUrls (required): Array of PR URLs to mark as ready for review
mark_pr_ready(
sessionId: "<session-id>",
prUrls: [
"https://github.com/org/frontend/pull/123",
"https://github.com/org/backend/pull/456"
]
)
After marking PRs as ready, always print the Polygraph session URL so the user can easily access the session overview. Call show_session and display:
**Polygraph session:** POLYGRAPH_SESSION_URL
Where POLYGRAPH_SESSION_URL is from polygraphSessionUrl in the response.
6. Associate Existing PRs
Use associate_pr to link pull requests that were created outside of Polygraph (e.g., manually or by CI) to the current session. This is useful when PRs already exist for the branches in the session and you want Polygraph to track them.
Provide either a prUrl to associate a specific PR, or a branch name plus repo to find and associate PRs for a source repository.
Parameters:
sessionId (required): The Polygraph session ID
prUrl (optional): URL of an existing pull request to associate
branch (optional): Branch name to find and associate PRs for
repo (optional): Source repository for branch-based association. Required when using branch in a multi-repo session.
description (required): Must follow the Session Description Policy.
associate_pr(
sessionId: "<session-id>",
prUrl: "https://github.com/org/repo/pull/123"
)
Or by branch:
associate_pr(
sessionId: "<session-id>",
repo: "org/repo",
branch: "feature/my-changes"
)
Returns the list of PRs now associated with the session.
7. Add Repositories to a Session
Use add_repo to add repositories to an existing Polygraph session after it has already started.
Direct-add rule: When the user provides exact repo refs by ID, short name, full name, GitHub owner/repo slug, or URL-like slug, pass those refs directly to add_repo and do not call list_repos first. Candidate discovery remains account-repo-only and is only for cases where the user does not know the exact repo or asks to choose/filter candidates.
Not limited to your organization: repos outside the org — including public open-source repos — can be added by GitHub owner/repo slug or URL. Only list_repos discovery is org-scoped, so a repo missing from list_repos can still be added directly.
Parameters:
sessionId (required): The Polygraph session ID
repoIds (required): Repository IDs or exact repository refs to add. Accepts IDs, short names, full names, GitHub owner/repo slugs, and URL-like slugs.
add_repo(
sessionId: "<session-id>",
repoIds: ["org/repo-name", "facebook/react"]
)
8. Archive Session
IMPORTANT: Only call this tool when the user explicitly asks to archive or close the session. Do not archive sessions automatically as part of the workflow.
Use archive_session (CLI: polygraph session archive <id>) to archive the session. Archiving only hides the session from active lists — it can still be resumed and interacted with afterwards. It is idempotent — archiving an already-archived session returns success.
Parameters:
sessionId (required): The Polygraph session ID
clean (optional): Remove the local clones Polygraph created for delegated repos after archiving
Returns:
sessionId: The session ID
completed: Boolean indicating the session is archived
archive_session(
sessionId: "<session-id>"
)
When to call: all work is finished, PRs are created and marked ready, and the user explicitly confirms they are done with the session.
Other Capabilities
Retrieving CI Job Logs
Use get_ci_logs to retrieve the full plain-text log for a specific CI job. This is the drill-in tool for investigating CI failures after identifying a failed job from the session's CI status.
ONLY use this tool when NO CIPE (CI Pipeline Execution) exists for the PR. When a CIPE exists (ciStatus[prId].cipeUrl is non-null), logs and failure data are available through the CIPE system (Nx Cloud) via the Nx MCP ci_information tool — do NOT call get_ci_logs, and do NOT fetch or poll the cipeUrl over HTTP (it is a browser link for the user, not an API). This tool is specifically for PRs where only external CI runs exist (e.g., GitHub Actions runs without an Nx Cloud CIPE).
Parameters:
sessionId (required): The Polygraph session ID
repoId (required): Repository ID (MongoDB ObjectId hex string, from the session repository entry)
jobId (required): GitHub Actions job ID (from ciStatus[prId].externalCIRuns[].jobs[].jobId in the show_session response)
Returns:
- On success:
{ success: true, jobId: number, logFile: string, sizeBytes: number }
- On failure:
{ success: false, error: string }
The tool saves the log to a local temp file and returns the path in logFile. Use the Read tool to examine the file contents. For large logs, use offset and limit parameters to read specific sections.
get_ci_logs(
sessionId: "<session-id>",
repoId: "<repo-id>",
jobId: 12345678
)
// Returns: { success: true, jobId: 12345678, logFile: "/tmp/ci-logs/job-12345678.log", sizeBytes: 152340 }
// Then: Read(logFile) to examine the log
Typical flow:
- Use
show_session to see PR CI status
- Check
ciStatus[prId].cipeUrl — if a CIPE exists, use ci_information for logs and skip this tool
- If NO CIPE exists, check
ciStatus[prId].externalCIRuns — examine runs and jobs directly from the session data
- For a failed job, call
get_ci_logs(sessionId, repoId, jobId) to save the log to a file
- Use
Read(logFile) to examine the log content — use offset/limit for large files
Important: Logs can be large (100KB+). Only fetch logs for failed or relevant jobs, and read only the sections you need.
Update Session Description
Use this when the user asks to summarize progress, update the session description, or capture the current state.
Read reference/session-description.md for the full update procedure (what to read before writing, how to append vs. replace) and the canonical Markdown-heading format. Then call update_session with the resulting summary as description.
Be liberal about updating the session description when you make changes that affect the scope of the session, how logic flows between repos, or anything else important for posterity. Avoid updating it for small implementation details that are not relevant outside of this session. An up-to-date session description matters for maintainability.
Print Polygraph Session Details
When asked to print polygraph session details, use show_session or polygraph session show --details <session-id> and display in the following format.
Session: POLYGRAPH_SESSION_URL
| Repo | PR | PR Status | CI Status | Self-Healing | CI Link |
|---|
| REPO_FULL_NAME | PR_TITLE | PR_STATUS | CI_STATUS | SELF_HEALING_STATUS | View |
If the session has a description timeline, also display:
Description: SESSION_DESCRIPTION
(Omit the Description line if description is empty.)
- REPO_FULL_NAME: from the session repository entries (match repository to PR via
repoId)
- PR_URL, PR_TITLE, PR_STATUS: from
pullRequests[]
- CI_STATUS: from
ciStatus[prId].status
- SELF_HEALING_STATUS: from
ciStatus[prId].selfHealingStatus (omit or show - if null)
- CIPE_URL: from
ciStatus[prId].cipeUrl
- POLYGRAPH_SESSION_URL: from
polygraphSessionUrl
- SESSION_DESCRIPTION: from the latest/current item in
description
Best Practices
{% if platform == "claude" %}
- MUST delegate via background subagents — You MUST use
Task(run_in_background: true) for every spawn_agent and show_agent call. NEVER call these directly in the main conversation — it floods the context window with polling noise.
{% elsif platform == "opencode" %}
- MUST delegate via subagents — You MUST use
@polygraph-delegate-subagent for every spawn_agent and show_agent call. NEVER call these directly in the main conversation — it floods the context window with polling noise.
{% elsif platform == "codex" %}
- MUST route through Codex Polygraph subagents — Use Codex
spawn_agent with agent_type: "polygraph-init-subagent" to create new sessions and agent_type: "polygraph-delegate-subagent" for every routine Polygraph MCP spawn_agent / show_agent delegate-and-poll loop. Collect results with wait_agent when needed.
{% else %}
- Delegate asynchronously — Use
spawn_agent which returns immediately, then poll with show_agent.
{% endif %}
- Poll child status before proceeding — Always verify child agents have reached a terminal
child.status ('completed', 'failed', or 'cancelled') via show_agent before pushing branches or creating PRs
- Link PRs in descriptions - Reference related PRs in each PR body
- Keep PRs as drafts until all repos are ready
- Always pass
description when calling create_pr, associate_pr, or update_session — it is required and must follow the Session Description Policy
- Test integration before marking PRs ready
- Coordinate merge order if there are deployment dependencies
{% if platform == "claude" %}
- NEVER call
spawn_agent or show_agent directly. These MUST ALWAYS go through background Task subagents (run_in_background: true).
{% elsif platform == "opencode" %}
- NEVER call
spawn_agent or show_agent directly. These MUST ALWAYS go through @polygraph-delegate-subagent.
{% elsif platform == "codex" %}
- NEVER call the Polygraph MCP
spawn_agent or show_agent directly for routine delegation. These MUST run inside polygraph-delegate-subagent.
{% endif %}
- Use
stop_agent to clean up — Stop child agents that are stuck or no longer needed. The child's session is preserved (sessionPreserved: true) so the context can be restored later, but after resuming you must wait for explicit user instructions before making changes.
- Only archive sessions when asked — Only call
archive_session when the user explicitly requests it. Archiving hides the session from active lists; it can still be resumed later.
{% if platform == "claude" or platform == "codex" %}
- Respect the sandbox — When a command fails with a sandbox denial (
EPERM binding a port, blocked host, denied write), stop instead of retrying and point the user to the options in "Sandboxing in Polygraph Sessions": commit harness sandbox settings to the repo, or toggle sandboxing via polygraph config.
{% endif %}