@polygraph/opencode-plugin 0.4.40 → 0.4.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/agent-capture-mapping.mjs +63 -10
- package/agents/polygraph-delegate-subagent.md +10 -6
- package/agents/session-debrief.md +27 -0
- package/package.json +1 -1
- package/server.js +38 -14
- package/skills/await-polygraph-ci/SKILL.md +1 -1
- package/skills/polygraph/SKILL.md +37 -205
- package/skills/polygraph/reference/publish-changes.md +151 -0
- package/skills/session-debrief/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -46,6 +46,7 @@ It detects your AI agent — Claude Code, Codex, OpenCode, and more — and inst
|
|
|
46
46
|
|
|
47
47
|
- **polygraph-init-subagent** — Discovers candidate repositories and initializes a Polygraph session
|
|
48
48
|
- **polygraph-delegate-subagent** — Delegates work to a child agent in another repository, polls for completion
|
|
49
|
+
- **session-debrief** — Analyzes the raw logs of past Polygraph sessions and returns a structured, rank-ordered debrief for the current task
|
|
49
50
|
|
|
50
51
|
## Development
|
|
51
52
|
|
|
@@ -8,13 +8,25 @@
|
|
|
8
8
|
// every session start and compaction; the CLI reader looks for mapping-*.json
|
|
9
9
|
// files in the per-session sidecars directory.
|
|
10
10
|
//
|
|
11
|
-
// File contract:
|
|
11
|
+
// File contract (must match the Polygraph CLI reader exactly):
|
|
12
|
+
// <sessionsRoot>/<POLYGRAPH_SESSION_ID>/sidecars/mapping-opencode-<sessionId>.json
|
|
13
|
+
// where sessionsRoot = $POLYGRAPH_ROOT, else `globalRoot` from
|
|
14
|
+
// ~/.polygraph/config.json, else ~/.polygraph/sessions
|
|
15
|
+
// Legacy fallback, used ONLY when <sessionsRoot>/<POLYGRAPH_SESSION_ID>
|
|
16
|
+
// does not exist (for real sessions nothing new is written here):
|
|
12
17
|
// ~/.polygraph/sidecars/<POLYGRAPH_SESSION_ID>/mapping-opencode-<sessionId>.json
|
|
13
18
|
//
|
|
19
|
+
// The session folder is a trustworthy location for this parent-transcript
|
|
20
|
+
// binding because the Polygraph CLI's child-agent sandboxes exclude the
|
|
21
|
+
// session root — children cannot write there. The CLI reads mappings from
|
|
22
|
+
// the session folder first, with the flat dir as a read-only fallback.
|
|
23
|
+
//
|
|
14
24
|
// Behaviour:
|
|
15
25
|
// - Silent no-op when POLYGRAPH_SESSION_ID is unset or POLYGRAPH_CHILD_AGENT is set.
|
|
16
26
|
// - Atomic write via tmp-file rename.
|
|
17
|
-
// - Refresh: preserves firstSeenAt when a valid prior mapping exists
|
|
27
|
+
// - Refresh: preserves firstSeenAt when a valid prior mapping exists
|
|
28
|
+
// (checked in the new location first, then the legacy flat dir — keeps
|
|
29
|
+
// firstSeenAt continuity when migrating a mapping from the legacy dir).
|
|
18
30
|
// - All failures are silently swallowed.
|
|
19
31
|
|
|
20
32
|
import {
|
|
@@ -82,9 +94,34 @@ export function logHookFailure(
|
|
|
82
94
|
}
|
|
83
95
|
}
|
|
84
96
|
|
|
97
|
+
// Resolve the root directory that holds per-session folders:
|
|
98
|
+
// $POLYGRAPH_ROOT, else `globalRoot` from ~/.polygraph/config.json, else
|
|
99
|
+
// ~/.polygraph/sessions. Must match the Polygraph CLI's own resolution.
|
|
100
|
+
function sessionsRoot(home) {
|
|
101
|
+
const fromEnv = process.env.POLYGRAPH_ROOT?.trim();
|
|
102
|
+
if (fromEnv) return fromEnv;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const config = JSON.parse(
|
|
106
|
+
readFileSync(path.join(home, '.polygraph', 'config.json'), 'utf8')
|
|
107
|
+
);
|
|
108
|
+
if (typeof config?.globalRoot === 'string' && config.globalRoot.trim()) {
|
|
109
|
+
return config.globalRoot.trim();
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// no config — use the default
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return path.join(home, '.polygraph', 'sessions');
|
|
116
|
+
}
|
|
117
|
+
|
|
85
118
|
/**
|
|
86
119
|
* Write (or refresh) the agent-capture mapping for an OpenCode session.
|
|
87
|
-
*
|
|
120
|
+
*
|
|
121
|
+
* Written into the session folder (`<sessionsRoot>/<sessionId>/sidecars/`)
|
|
122
|
+
* when the session directory exists; only when it does not exist does the
|
|
123
|
+
* write fall back to the legacy flat `~/.polygraph/sidecars/<sessionId>/`
|
|
124
|
+
* dir. Reads POLYGRAPH_SESSION_ID and POLYGRAPH_CHILD_AGENT from process.env.
|
|
88
125
|
*
|
|
89
126
|
* @param {string} agentSessionId The OpenCode session id (input.sessionID).
|
|
90
127
|
* @param {string} [home] Override HOME for testing.
|
|
@@ -99,19 +136,34 @@ export function writeAgentCaptureMapping(
|
|
|
99
136
|
if (process.env.POLYGRAPH_CHILD_AGENT) return;
|
|
100
137
|
if (!agentSessionId) return;
|
|
101
138
|
|
|
102
|
-
const sidecarDir = path.join(home, '.polygraph', 'sidecars', polygraphSessionId);
|
|
103
|
-
mkdirSync(sidecarDir, { recursive: true });
|
|
104
|
-
|
|
105
139
|
const filenamePart = sanitizeMappingFilename(`opencode-${agentSessionId}`);
|
|
106
|
-
const
|
|
140
|
+
const fileName = `mapping-${filenamePart}.json`;
|
|
141
|
+
|
|
142
|
+
const sessionDir = path.join(sessionsRoot(home), polygraphSessionId);
|
|
143
|
+
const sessionSidecarDir = path.join(sessionDir, 'sidecars');
|
|
144
|
+
const legacyDir = path.join(home, '.polygraph', 'sidecars', polygraphSessionId);
|
|
145
|
+
|
|
146
|
+
// New location when the session directory exists; legacy flat dir only
|
|
147
|
+
// when it does not.
|
|
148
|
+
const targetDir = existsSync(sessionDir) ? sessionSidecarDir : legacyDir;
|
|
149
|
+
mkdirSync(targetDir, { recursive: true });
|
|
150
|
+
|
|
151
|
+
const finalPath = path.join(targetDir, fileName);
|
|
107
152
|
const tmpPath = `${finalPath}.tmp-${process.pid}`;
|
|
108
153
|
|
|
109
154
|
const now = Date.now();
|
|
110
|
-
let firstSeenAt = now;
|
|
111
155
|
|
|
112
|
-
|
|
156
|
+
// Refresh semantics: preserve firstSeenAt from a valid prior mapping.
|
|
157
|
+
// Check the new location first, then the legacy flat dir — this keeps
|
|
158
|
+
// firstSeenAt continuity when migrating a mapping from the legacy dir.
|
|
159
|
+
let firstSeenAt = now;
|
|
160
|
+
for (const candidate of [
|
|
161
|
+
path.join(sessionSidecarDir, fileName),
|
|
162
|
+
path.join(legacyDir, fileName),
|
|
163
|
+
]) {
|
|
164
|
+
if (!existsSync(candidate)) continue;
|
|
113
165
|
try {
|
|
114
|
-
const existing = JSON.parse(readFileSync(
|
|
166
|
+
const existing = JSON.parse(readFileSync(candidate, 'utf8'));
|
|
115
167
|
if (
|
|
116
168
|
existing.version === 1 &&
|
|
117
169
|
existing.polygraphSessionId === polygraphSessionId &&
|
|
@@ -119,6 +171,7 @@ export function writeAgentCaptureMapping(
|
|
|
119
171
|
Number.isFinite(existing.firstSeenAt)
|
|
120
172
|
) {
|
|
121
173
|
firstSeenAt = existing.firstSeenAt;
|
|
174
|
+
break;
|
|
122
175
|
}
|
|
123
176
|
} catch {
|
|
124
177
|
// ignore — treat as missing
|
|
@@ -20,13 +20,14 @@ The main agent provides these parameters in the prompt:
|
|
|
20
20
|
| `sessionId` | The Polygraph session ID |
|
|
21
21
|
| `repo` | Repository to delegate to (e.g., `org/repo-name`) |
|
|
22
22
|
| `instruction` | The task instruction for the child agent |
|
|
23
|
+
| `role` | (Optional) Agent slot within the repo; omit for the default role. Pass the SAME role on every `spawn_agent`/`show_agent`/`stop_agent` call for this delegation. |
|
|
23
24
|
| `context` | (Optional) Additional context to pass to the child agent |
|
|
24
25
|
|
|
25
26
|
## Delegating work
|
|
26
27
|
|
|
27
|
-
Call the `spawn_agent` tool to start a child agent on the repo or to send a follow-up to an active task. Follow-up routing is automatic: if
|
|
28
|
+
Call the `spawn_agent` tool to start a child agent on the repo or to send a follow-up to an active task. Follow-up routing is automatic per (repo, role): if that (repo, role) already has an active child task (working or paused on input), the orchestrator delivers your `instruction` to that task as a follow-up message rather than starting a second run; otherwise it starts a new child run. A repo therefore has at most one active child per role, though agents with different roles can run in the same repo concurrently.
|
|
28
29
|
|
|
29
|
-
`repo` must be a repository other than the one the parent agent is working in — never delegate into the parent's own repo.
|
|
30
|
+
`repo` must be a repository other than the one the parent agent is working in — never delegate into the parent's own repo.
|
|
30
31
|
|
|
31
32
|
**Resume/reconstruction is read-only.** If the parent asks you to resume, reconnect, restore, or reconstruct a preserved session without an explicit new change request from the user, do not call `spawn_agent` to continue work. Use `show_agent` only as needed to read status/log context, return a concise restoration summary, and stop. After resuming, wait for explicit user instructions before any child agent makes changes.
|
|
32
33
|
|
|
@@ -35,6 +36,7 @@ spawn_agent(
|
|
|
35
36
|
sessionId: "<sessionId>",
|
|
36
37
|
repo: "<repo>",
|
|
37
38
|
instruction: "<instruction>",
|
|
39
|
+
role: "<role, if any>",
|
|
38
40
|
context: "<context>"
|
|
39
41
|
)
|
|
40
42
|
```
|
|
@@ -49,6 +51,7 @@ Call `show_agent` in a loop, passing `waitForTransitionMs: 50000` on every call:
|
|
|
49
51
|
show_agent(
|
|
50
52
|
sessionId: "<sessionId>",
|
|
51
53
|
repo: "<repo>",
|
|
54
|
+
role: "<role, if any>",
|
|
52
55
|
waitForTransitionMs: 50000
|
|
53
56
|
)
|
|
54
57
|
```
|
|
@@ -65,7 +68,7 @@ After calling `spawn_agent`, parse the structured JSON response:
|
|
|
65
68
|
|
|
66
69
|
Then poll `show_agent` in a loop with `waitForTransitionMs: 50000`. **Do not pass a `tail` argument** — the tool's default is sized for status polling. Only set `tail` if you have a specific reason (e.g., the default truncated output you actually need to inspect, or you are hunting for an earlier failure that scrolled off). Never ratchet `tail` upward across polls; that is what causes the polling loop to flood your context window.
|
|
67
70
|
|
|
68
|
-
The response's `children[]` array has
|
|
71
|
+
The response's `children[]` array has one entry per agent matching your query. Find YOUR delegation's entry (match on `role`; absent means the default role) and inspect:
|
|
69
72
|
|
|
70
73
|
- `child.status` — an AcpRunStatus value: one of `'created'`, `'in-progress'`, `'input-required'`, `'permission-required'`, `'completed'`, `'failed'`, `'cancelled'` (British double-L on `'cancelled'`). Note `'permission-required'` and `'input-required'` are DIFFERENT states handled by different cases below — do not conflate them.
|
|
71
74
|
- `child.inputRequiredQuestion` — populated only when `child.status === 'input-required'`; contains the verbatim question the child agent has asked the parent.
|
|
@@ -79,14 +82,14 @@ State machine:
|
|
|
79
82
|
- Read `child.inputRequiredQuestion`.
|
|
80
83
|
- Surface this question verbatim to the parent/user: "The child agent in `{child.repoFullName}` needs input: {child.inputRequiredQuestion}".
|
|
81
84
|
- Wait for the parent/user to supply an answer.
|
|
82
|
-
- Call `spawn_agent` again with the same `repo` and `instruction: <the answer>` — the orchestrator routes it to
|
|
85
|
+
- Call `spawn_agent` again with the same `repo`, the same `role`, and `instruction: <the answer>` — the orchestrator routes it to that (repo, role)'s active task automatically.
|
|
83
86
|
- Resume polling.
|
|
84
87
|
|
|
85
88
|
3. `child.status === 'permission-required'` — child is paused waiting for a permission grant decision:
|
|
86
89
|
- Read `child.pendingPermission` — inspect `harness`, `action`, `target`, `repoFullName`, `scope`, `availableScopes`, and optional `reason`/`rawInput`.
|
|
87
90
|
- Surface the request to the parent/user: "Child agent in `{repoFullName}` requests `{scope}` permission to run `{action}` on `{target}`."
|
|
88
91
|
- Wait for the parent/user to decide.
|
|
89
|
-
- Call `allow_agent` (to grant) or `deny_agent` (to refuse) with `{ sessionId, repo }` — `allow_agent` also takes `scope` (`'one-time'` or `'session'`) and an optional `reason`; `deny_agent` takes only
|
|
92
|
+
- Call `allow_agent` (to grant) or `deny_agent` (to refuse) with `{ sessionId, repo, role? }` — `allow_agent` also takes `scope` (`'one-time'` or `'session'`) and an optional `reason`; `deny_agent` takes only an optional `reason` on top.
|
|
90
93
|
- **Fail-closed:** When you see `permission-required`, 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.
|
|
91
94
|
- Resume polling.
|
|
92
95
|
|
|
@@ -96,7 +99,7 @@ State machine:
|
|
|
96
99
|
|
|
97
100
|
## Cancelling a running child
|
|
98
101
|
|
|
99
|
-
To cancel a running child mid-work, call `stop_agent` with the repo. Response:
|
|
102
|
+
To cancel a running child mid-work, call `stop_agent` with the repo (and `role`, per the parameter rule above). Response:
|
|
100
103
|
|
|
101
104
|
```json
|
|
102
105
|
{
|
|
@@ -118,6 +121,7 @@ When the child agent reaches a terminal status, return a structured summary:
|
|
|
118
121
|
## Polygraph Delegation Result
|
|
119
122
|
|
|
120
123
|
**Repo:** <repo>
|
|
124
|
+
**Role:** <role, or "default" when none was given>
|
|
121
125
|
**Status:** <success | failed | cancelled>
|
|
122
126
|
**Session ID:** <sessionId>
|
|
123
127
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
description: Analyze the raw logs of one or more past Polygraph sessions and return a structured, rank-ordered debrief for the current task. Launch as a background agent with a ranked list of relevant Polygraph session IDs/lines and a one-paragraph statement of the current task; it invokes the session-debrief skill, pulls parent and child transcripts via the polygraph CLI, and returns one consolidated debrief. Read-only with respect to the inspected sessions.
|
|
4
|
+
mode: subagent
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Session Debrief Subagent
|
|
9
|
+
|
|
10
|
+
You produce debriefs of PAST Polygraph sessions so a parent agent working on a NEW task can decide what context is relevant. You run in the background and speed matters — the parent keeps working while it waits and folds your debrief in whenever it lands.
|
|
11
|
+
|
|
12
|
+
You are READ-ONLY with respect to the inspected sessions: never resume them, never spawn agents into them, never push branches, create PRs, or update their descriptions.
|
|
13
|
+
|
|
14
|
+
## Input Parameters (from Main Agent)
|
|
15
|
+
|
|
16
|
+
The main agent provides these in the prompt:
|
|
17
|
+
|
|
18
|
+
| Parameter | Description |
|
|
19
|
+
| ------------- | ------------------------------------------------------------------------------------------------ |
|
|
20
|
+
| `sessions` | A ranked list of relevant Polygraph sessions (IDs/lines, most relevant first, with optional titles/URLs) |
|
|
21
|
+
| `currentTask` | A one-paragraph statement of the task the parent is currently working on |
|
|
22
|
+
|
|
23
|
+
## What to do
|
|
24
|
+
|
|
25
|
+
Invoke the `session-debrief` skill and follow its procedure and output template exactly. Pass through the ranked session list and the current-task statement you received. The skill is the single source of truth for how to pull transcripts via the polygraph CLI, how to fan out across multiple sessions, and how to format each debrief section — do not reinvent or duplicate that procedure here.
|
|
26
|
+
|
|
27
|
+
Return the consolidated, rank-ordered debrief as your final message.
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -166,33 +166,57 @@ function polygraphCompactionNote(agentSessionId, root) {
|
|
|
166
166
|
);
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
) {
|
|
173
|
-
if (!
|
|
169
|
+
// Scan the immediate subdirectories of `baseDir`; for each, `candidatePath`
|
|
170
|
+
// maps the subdirectory name to a candidate parent-sidecar file. Returns the
|
|
171
|
+
// sessionId of the first match, or undefined.
|
|
172
|
+
function scanForParentSidecar(baseDir, candidatePath) {
|
|
173
|
+
if (!existsSync(baseDir)) {
|
|
174
174
|
return undefined;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
let entries;
|
|
178
|
+
try {
|
|
179
|
+
entries = readdirSync(baseDir, { withFileTypes: true });
|
|
180
|
+
} catch {
|
|
179
181
|
return undefined;
|
|
180
182
|
}
|
|
181
183
|
|
|
182
|
-
const
|
|
183
|
-
let polygraphSessionId;
|
|
184
|
-
for (const entry of readdirSync(sidecarsDir, { withFileTypes: true })) {
|
|
184
|
+
for (const entry of entries) {
|
|
185
185
|
if (!entry.isDirectory()) continue;
|
|
186
|
-
const candidate =
|
|
186
|
+
const candidate = candidatePath(entry.name);
|
|
187
187
|
if (existsSync(candidate)) {
|
|
188
188
|
try {
|
|
189
|
-
|
|
189
|
+
return JSON.parse(readFileSync(candidate, 'utf8')).sessionId;
|
|
190
190
|
} catch {
|
|
191
|
-
|
|
191
|
+
return undefined;
|
|
192
192
|
}
|
|
193
|
-
break;
|
|
194
193
|
}
|
|
195
194
|
}
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function readPolygraphSession(
|
|
199
|
+
agentSessionId,
|
|
200
|
+
root = path.join(homedir(), '.polygraph')
|
|
201
|
+
) {
|
|
202
|
+
if (!agentSessionId) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const fileName = `parent-${agentSessionId}.json`;
|
|
207
|
+
|
|
208
|
+
// New layout: <sessionsRoot>/<sessionId>/sidecars/parent-<agentSessionId>.json
|
|
209
|
+
// (sessionsRoot = $POLYGRAPH_ROOT or <root>/sessions), with a fallback to
|
|
210
|
+
// the legacy shared sidecars directory for sessions created by older CLIs.
|
|
211
|
+
const sessionsDir = process.env.POLYGRAPH_ROOT?.trim() || path.join(root, 'sessions');
|
|
212
|
+
const legacyDir = path.join(root, 'sidecars');
|
|
213
|
+
const polygraphSessionId =
|
|
214
|
+
scanForParentSidecar(sessionsDir, (sessionId) =>
|
|
215
|
+
path.join(sessionsDir, sessionId, 'sidecars', fileName)
|
|
216
|
+
) ??
|
|
217
|
+
scanForParentSidecar(legacyDir, (sessionId) =>
|
|
218
|
+
path.join(legacyDir, sessionId, fileName)
|
|
219
|
+
);
|
|
196
220
|
if (!polygraphSessionId) {
|
|
197
221
|
return undefined;
|
|
198
222
|
}
|
|
@@ -163,7 +163,7 @@ For each repo with `ciStatus: FAILED`, branch on the PR's `ci` object from `show
|
|
|
163
163
|
show_agent(sessionId: "<session-id>", repo: "frontend")
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
-
Poll until the child agent's status indicates completion. Use the `tail` parameter to retrieve recent output lines containing the investigation results.
|
|
166
|
+
Poll until the child agent's status indicates completion (pass the same `role` you spawned with, if any). Use the `tail` parameter to retrieve recent output lines containing the investigation results.
|
|
167
167
|
|
|
168
168
|
4. Collect each child agent's response from the status output. If a child agent fails or gets stuck, use `stop_agent` to terminate it and skip that repo.
|
|
169
169
|
|
|
@@ -20,9 +20,9 @@ Polygraph functionality is available via both MCP tools and CLI commands. Use wh
|
|
|
20
20
|
| --- | --- | --- |
|
|
21
21
|
| `list_repos` | `polygraph repo list` | Discover candidate repositories. Candidate entries do not include repository descriptions; use `semanticQuery` for natural-language discovery. |
|
|
22
22
|
| `start_session` | `polygraph session start --repo <ids>` | Initialize a Polygraph session with selected repositories |
|
|
23
|
-
| `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
|
|
24
|
-
| `show_agent` | — | Poll
|
|
25
|
-
| `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. |
|
|
23
|
+
| `spawn_agent` | — | Start a new child task or send a follow-up to an active task in another repository. Input: `{ sessionId, repo, instruction, role?, context? }`. Output: `{ taskId, message, status: 'delegated' }`. `role` selects the agent slot within the repo (see "Agent roles"). Follow-up routing is automatic per (repo, role): if that (repo, role) already has an active child task, the instruction is delivered to it as a follow-up message; otherwise a new child run starts. 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. |
|
|
24
|
+
| `show_agent` | — | Poll one repo's child status (`repo` required — one repo per call; pass `role` to narrow to that role's agent). Returns `{ children: [...] }` with one self-describing entry per matching agent, exposing `status`, `lastOutputLines`, `role` (absent for the default role), `inputRequiredQuestion`, etc. Full status enum and the poll/state-machine flow are under "Multi-turn tasks". |
|
|
25
|
+
| `stop_agent` | — | Cancel an in-progress child. Input: `{ sessionId, repo, role? }`. 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. |
|
|
26
26
|
| `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. |
|
|
27
27
|
| `create_pr` | — | Create draft PRs with session metadata linking related PRs |
|
|
28
28
|
| `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. |
|
|
@@ -71,10 +71,10 @@ After logging in (or if logged in but no org is selected), use `polygraph accoun
|
|
|
71
71
|
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.
|
|
72
72
|
|
|
73
73
|
0. **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.
|
|
74
|
-
1. **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
|
|
74
|
+
1. **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; a repo can also host multiple concurrent agents under distinct roles (see "Agent roles"). Choose the Simple (fire-and-forget) or Multi-turn (interactive) pattern described below based on whether the child may need clarification.
|
|
75
75
|
|
|
76
|
-
4. **Monitor child agents** - Use `show_agent` to poll one repo's
|
|
77
|
-
5. **Stop child agents** (if needed) - Use `stop_agent` to cancel an in-progress child agent. The
|
|
76
|
+
4. **Monitor child agents** - Use `show_agent` to poll one repo's children (`repo` is required; pass `role` to narrow to one agent) and read each entry's `status` and `lastOutputLines` from the `children[]` array.
|
|
77
|
+
5. **Stop child agents** (if needed) - Use `stop_agent` (with `role` when targeting a non-default agent) to cancel an in-progress child agent. The agent's session is preserved for later read-only context restoration; after a resume, wait for explicit user instructions before making changes.
|
|
78
78
|
6. **Push branches** - Use `push_branch` after making commits. A required `description` must follow the Session Description Policy.
|
|
79
79
|
7. **Update session description** - Use `update_session` to update the session description; must follow the Session Description Policy. Independent of PR creation or mark-ready.
|
|
80
80
|
8. **Create draft PRs** - Use `create_pr` to create linked draft PRs. Always pass `description` following the Session Description Policy.
|
|
@@ -202,15 +202,23 @@ polygraph session search --sha a1b2c3d
|
|
|
202
202
|
- Multiple sessions may match a sha. They come back newest first — pick the most relevant one and report the others if they matter.
|
|
203
203
|
- **A "no match" result does NOT prove the commit had no work behind it.** Not every commit is linked to an explicit session: commits pushed directly (rather than via an ingested PR) and gaps in ingestion metadata mean the sha may simply not be recorded, and implicit sessions are never returned. Report "no linked session found for that sha" — never assert that no work exists behind the commit.
|
|
204
204
|
|
|
205
|
+
## Agent roles
|
|
206
|
+
|
|
207
|
+
A repository in a session can host multiple child agents at once, distinguished by **role**:
|
|
208
|
+
|
|
209
|
+
- **Purpose.** Roles let independent streams of work run concurrently in one repo — e.g. a default agent implementing a feature while a `reviewer` or `ci-investigator` runs alongside. Each (repo, role) pair has at most one active child.
|
|
210
|
+
- **Default role.** An omitted `role` means the default role: `spawn_agent` without `role` starts or follows up with the repo's default-role agent.
|
|
211
|
+
- **Logs.** Only default-role agents upload logs to the cloud and appear in the multiplexed log stream (`polygraph session logs`). Inspect non-default agents locally with `polygraph agent attach --role <role>`.
|
|
212
|
+
|
|
205
213
|
## Simple tasks (fire-and-forget)
|
|
206
214
|
|
|
207
215
|
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.
|
|
208
216
|
|
|
209
217
|
**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.
|
|
210
218
|
|
|
211
|
-
1. For each repo, invoke `@polygraph-delegate-subagent` with `sessionId`, `repo`, `instruction`, and optional `context`. The subagent calls `spawn_agent`, then polls `show_agent` via chained `waitForTransitionMs` long-poll calls until terminal.
|
|
212
|
-
2. Delegate to multiple repos in parallel by launching multiple `@polygraph-delegate-subagent` invocations — one delegation per repo at a time.
|
|
213
|
-
3. The subagent watches `child.status` on
|
|
219
|
+
1. For each repo, invoke `@polygraph-delegate-subagent` with `sessionId`, `repo`, `instruction`, optional `role`, and optional `context`. The subagent calls `spawn_agent`, then polls `show_agent` via chained `waitForTransitionMs` long-poll calls until terminal.
|
|
220
|
+
2. Delegate to multiple repos in parallel by launching multiple `@polygraph-delegate-subagent` invocations — one delegation per (repo, role) at a time.
|
|
221
|
+
3. The subagent watches `child.status` on its delegation's `children[]` entry — the one matching its repo and role — and exits when it sees a terminal status — typically `'completed'` or `'failed'` (and `'cancelled'` if it was stopped).
|
|
214
222
|
4. Once all subagents report a terminal status, continue to `push_branch` + `create_pr`.
|
|
215
223
|
|
|
216
224
|
Use Simple when the task is well-defined and the child will not need clarification.
|
|
@@ -219,13 +227,13 @@ Use Simple when the task is well-defined and the child will not need clarificati
|
|
|
219
227
|
|
|
220
228
|
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.
|
|
221
229
|
|
|
222
|
-
1. Call `spawn_agent` with the initial `instruction
|
|
230
|
+
1. Call `spawn_agent` with the initial `instruction` (and optionally `role`). Parse the response:
|
|
223
231
|
|
|
224
232
|
```json
|
|
225
233
|
{ "taskId": "…", "message": "…", "status": "delegated" }
|
|
226
234
|
```
|
|
227
235
|
|
|
228
|
-
2. Poll `show_agent` via chained `waitForTransitionMs` long-poll calls (`repo` is required). The response shape is `{ children: PolygraphChildStatusItem[] }` with
|
|
236
|
+
2. Poll `show_agent` via chained `waitForTransitionMs` long-poll calls (`repo` is required; pass the same `role` you spawned with to narrow to that agent). The response shape is `{ children: PolygraphChildStatusItem[] }` with one entry per matching agent in that repo. On your delegation's entry, inspect:
|
|
229
237
|
|
|
230
238
|
- `child.status` — one of `'created'`, `'in-progress'`, `'input-required'`, `'permission-required'`, `'completed'`, `'failed'`, `'cancelled'` (British double-L on `'cancelled'`).
|
|
231
239
|
- `child.inputRequiredQuestion` — populated only when `child.status === 'input-required'`.
|
|
@@ -235,13 +243,13 @@ Use this pattern when the child may need clarification, the task is exploratory,
|
|
|
235
243
|
Drive the state machine:
|
|
236
244
|
|
|
237
245
|
- `child.status === 'in-progress'` or `'created'` — continue polling.
|
|
238
|
-
- `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
|
|
246
|
+
- `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`, the same `role`, and `instruction: <answer>` — it is routed to that (repo, role)'s active task automatically. Continue polling.
|
|
239
247
|
- `child.status === 'completed'` — read `child.lastOutputLines`, proceed to `push_branch` + `create_pr`.
|
|
240
248
|
- `child.status === 'failed'` — read `child.lastOutputLines`, surface the failure.
|
|
241
249
|
- `child.status === 'cancelled'` — the child was stopped via `stop_agent`; see below.
|
|
242
250
|
- `child.status === 'permission-required'` — the child is waiting on a permission decision; see "Handling permission requests" below.
|
|
243
251
|
|
|
244
|
-
3. To abort mid-flight, call `stop_agent` with `{ sessionId, repo }`. The response is:
|
|
252
|
+
3. To abort mid-flight, call `stop_agent` with `{ sessionId, repo, role? }`. The response is:
|
|
245
253
|
|
|
246
254
|
```json
|
|
247
255
|
{
|
|
@@ -253,7 +261,7 @@ Use this pattern when the child may need clarification, the task is exploratory,
|
|
|
253
261
|
}
|
|
254
262
|
```
|
|
255
263
|
|
|
256
|
-
Because `sessionPreserved: true`, the
|
|
264
|
+
Because `sessionPreserved: true`, the stopped agent's session can be restored later for context. After resuming, do not make changes or continue prior work until the user explicitly asks for changes.
|
|
257
265
|
|
|
258
266
|
Use Multi-turn when the child may need clarification, the task is exploratory, or interactive collaboration is desired. Otherwise use Simple.
|
|
259
267
|
|
|
@@ -267,7 +275,7 @@ Child agents running in other repositories may pause and ask the parent agent wh
|
|
|
267
275
|
|
|
268
276
|
**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.
|
|
269
277
|
|
|
270
|
-
**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.
|
|
278
|
+
**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, role?}` of the child agent.
|
|
271
279
|
|
|
272
280
|
### Answering a permission request
|
|
273
281
|
|
|
@@ -276,6 +284,7 @@ Child agents running in other repositories may pause and ask the parent agent wh
|
|
|
276
284
|
{
|
|
277
285
|
"sessionId": "...",
|
|
278
286
|
"repo": "org/repo-name",
|
|
287
|
+
"role": "reviewer", // optional
|
|
279
288
|
"scope": "session", // or "one-time"
|
|
280
289
|
"reason": "Trusted local repo" // optional
|
|
281
290
|
}
|
|
@@ -285,6 +294,7 @@ Child agents running in other repositories may pause and ask the parent agent wh
|
|
|
285
294
|
{
|
|
286
295
|
"sessionId": "...",
|
|
287
296
|
"repo": "org/repo-name",
|
|
297
|
+
"role": "reviewer", // optional
|
|
288
298
|
"reason": "Action looks risky" // optional
|
|
289
299
|
}
|
|
290
300
|
// — call `deny_agent` with this payload.
|
|
@@ -307,29 +317,14 @@ When polling `cloud_polygraph_child_status` (or `show_agent`), treat `permission
|
|
|
307
317
|
1. Read `child.pendingPermission` — inspect `harness`, `action`, `target`, `repoFullName`, and `scope`.
|
|
308
318
|
2. Surface the request to the user: "Child agent in `{repoFullName}` requests `{scope}` permission to run `{action}` on `{target}`."
|
|
309
319
|
3. Obtain the user's decision.
|
|
310
|
-
4. Call `allow_agent` (to grant) or `deny_agent` (to refuse) with `{ sessionId, repo }`.
|
|
320
|
+
4. Call `allow_agent` (to grant) or `deny_agent` (to refuse) with `{ sessionId, repo, role? }`.
|
|
311
321
|
5. Resume polling.
|
|
312
322
|
|
|
313
|
-
### 2. Push Branches
|
|
323
|
+
### 2. Publish Changes (Push Branches, Create PRs, Mark Ready)
|
|
314
324
|
|
|
315
|
-
|
|
325
|
+
Publishing covers the branch-to-PR flow: `push_branch` (push local commits; must precede PR creation), `create_pr` (linked draft PRs, including fork PRs via `targetRepository`), `mark_pr_ready` (transition drafts to OPEN), and `associate_pr` (link PRs created outside Polygraph).
|
|
316
326
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
**Parameters:**
|
|
320
|
-
|
|
321
|
-
- `sessionId` (required): The Polygraph session ID
|
|
322
|
-
- `repo` (required): Repository name or repository ID to push from
|
|
323
|
-
- `branch` (required): Branch name to push to remote
|
|
324
|
-
- `description` (required): A session description is required. Must follow the Session Description Policy.
|
|
325
|
-
|
|
326
|
-
```
|
|
327
|
-
push_branch(
|
|
328
|
-
sessionId: "<session-id>",
|
|
329
|
-
repo: "org/repo-name",
|
|
330
|
-
branch: "polygraph/ad5fa-add-user-preferences"
|
|
331
|
-
)
|
|
332
|
-
```
|
|
327
|
+
**Whenever you push a branch, create or associate a PR, or mark PRs ready, read [`reference/publish-changes.md`](reference/publish-changes.md) first.** That reference file holds the full flow: parameters and examples for each tool, `push_branch` local-checkout semantics, the PR title format rules, and the session-URL printing steps. `push_branch`, `create_pr`, and `associate_pr` all require a `description` following the Session Description Policy below.
|
|
333
328
|
|
|
334
329
|
### Session Description Policy
|
|
335
330
|
|
|
@@ -337,121 +332,18 @@ push_branch(
|
|
|
337
332
|
|
|
338
333
|
**Whenever you write or update a session description, read [`reference/session-description.md`](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`).
|
|
339
334
|
|
|
340
|
-
### 3.
|
|
341
|
-
|
|
342
|
-
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.
|
|
343
|
-
|
|
344
|
-
**Parameters:**
|
|
345
|
-
|
|
346
|
-
- `sessionId` (required): The Polygraph session ID
|
|
347
|
-
- `prs` (required): Array of PR specifications, each containing:
|
|
348
|
-
- `owner` (required): GitHub repository owner
|
|
349
|
-
- `repo` (required): GitHub repository name
|
|
350
|
-
- `title` (required): PR title
|
|
351
|
-
- `body` (required): PR description (session metadata is appended automatically)
|
|
352
|
-
- `branch` (required): Branch name that was pushed
|
|
353
|
-
- `targetRepository` (optional): Target GitHub repository for fork PR creation or registration, as `owner/repo`. Omit for same-repository PRs.
|
|
354
|
-
- `description` (required): Must follow the Session Description Policy.
|
|
355
|
-
|
|
356
|
-
**PR title format (applies to parent and child agents):**
|
|
335
|
+
### 3. Get Current Polygraph Session
|
|
357
336
|
|
|
358
|
-
|
|
359
|
-
- 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.
|
|
360
|
-
|
|
361
|
-
```
|
|
362
|
-
create_pr(
|
|
363
|
-
sessionId: "<session-id>",
|
|
364
|
-
prs: [
|
|
365
|
-
{
|
|
366
|
-
owner: "org",
|
|
367
|
-
repo: "frontend",
|
|
368
|
-
title: "feat: Add user preferences UI",
|
|
369
|
-
body: "Part of multi-repo user preferences feature",
|
|
370
|
-
branch: "polygraph/ad5fa-add-user-preferences"
|
|
371
|
-
},
|
|
372
|
-
{
|
|
373
|
-
owner: "org",
|
|
374
|
-
repo: "backend",
|
|
375
|
-
title: "feat: Add user preferences API",
|
|
376
|
-
body: "Part of multi-repo user preferences feature",
|
|
377
|
-
branch: "polygraph/ad5fa-add-user-preferences"
|
|
378
|
-
}
|
|
379
|
-
]
|
|
380
|
-
)
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
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:
|
|
384
|
-
|
|
385
|
-
```
|
|
386
|
-
create_pr(
|
|
387
|
-
sessionId: "<session-id>",
|
|
388
|
-
prs: [
|
|
389
|
-
{
|
|
390
|
-
owner: "contributor",
|
|
391
|
-
repo: "frontend-fork",
|
|
392
|
-
targetRepository: "org/frontend",
|
|
393
|
-
title: "feat: Add user preferences UI",
|
|
394
|
-
body: "Part of multi-repo user preferences feature",
|
|
395
|
-
branch: "polygraph/ad5fa-add-user-preferences"
|
|
396
|
-
}
|
|
397
|
-
]
|
|
398
|
-
)
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
**After creating PRs**, always print the Polygraph session URL:
|
|
402
|
-
|
|
403
|
-
```
|
|
404
|
-
**Polygraph session:** POLYGRAPH_SESSION_URL
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
### 4. Get Current Polygraph Session
|
|
408
|
-
|
|
409
|
-
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.
|
|
337
|
+
Check the details of a session using `show_session` or `polygraph session show --details <session-id>`. Returns the full session state — basic metadata like id, url & description timeline, plus the connected repositories, `pullRequests[]`, per-PR `ciStatus`, and `session.linkedReferences`.
|
|
410
338
|
|
|
411
339
|
**Parameters:**
|
|
412
340
|
|
|
413
341
|
- `sessionId` (required): The Polygraph session ID
|
|
414
342
|
|
|
415
|
-
**
|
|
343
|
+
**CI status rules:**
|
|
416
344
|
|
|
417
|
-
- `
|
|
418
|
-
- `
|
|
419
|
-
- `session.description`: DescriptionItem[] timeline describing the session.
|
|
420
|
-
- `session.agentSessionId`: The agent CLI session ID — captured automatically by the MCP server (null if no agent has run yet).
|
|
421
|
-
- `session.linkedReferences`: Array of references linked to this session
|
|
422
|
-
- Session repository entries: Array of connected repositories, each with:
|
|
423
|
-
- `id`: Repository ID
|
|
424
|
-
- `name`: Repository name
|
|
425
|
-
- `defaultBranch`: Default branch (e.g., `main`)
|
|
426
|
-
- `vcsConfiguration.repositoryFullName`: Full repo name (e.g., `org/repo`)
|
|
427
|
-
- `vcsConfiguration.provider`: VCS provider (e.g., `GITHUB`)
|
|
428
|
-
- description field: AI-generated description of what this repository does (may be null)
|
|
429
|
-
- `initiator`: Whether this repository initiated the session
|
|
430
|
-
- `session.dependencyGraph`: Graph of repository dependency `edges`
|
|
431
|
-
- `session.pullRequests[]`: Array of PRs, each with:
|
|
432
|
-
- `url`: PR URL
|
|
433
|
-
- `branch`: Branch name
|
|
434
|
-
- `baseBranch`: Target branch
|
|
435
|
-
- `title`: PR title
|
|
436
|
-
- `status`: One of `DRAFT`, `OPEN`, `MERGED`, `CLOSED`
|
|
437
|
-
- `repoId`: Associated repository ID
|
|
438
|
-
- `relatedPRs`: Array of related PR URLs across repos
|
|
439
|
-
- `session.ciStatus`: CI pipeline status keyed by PR ID, each containing:
|
|
440
|
-
- `status`: One of `SUCCEEDED`, `FAILED`, `IN_PROGRESS`, `NOT_STARTED` (null if no CIPE and no external CI)
|
|
441
|
-
- `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
|
|
442
|
-
- `completedAt`: Epoch millis timestamp, set only when the CIPE has completed (null otherwise)
|
|
443
|
-
- `selfHealingStatus`: The self-healing fix status string from Nx Cloud's AI fix feature (null if no AI fix exists)
|
|
444
|
-
- `externalCIRuns`: Array of external CI runs (present when no CIPE but external CI data exists, e.g., GitHub Actions). Each run contains:
|
|
445
|
-
- `runId`: GitHub Actions run ID
|
|
446
|
-
- `name`: Workflow name
|
|
447
|
-
- `status`: Run status (`completed`, `in_progress`, `queued`)
|
|
448
|
-
- `conclusion`: Run conclusion (`success`, `failure`, `cancelled`, `timed_out`, or null)
|
|
449
|
-
- `url`: GitHub Actions run URL
|
|
450
|
-
- `jobs`: Array of jobs in the run, each with:
|
|
451
|
-
- `jobId`: Job ID (use with `get_ci_logs`)
|
|
452
|
-
- `name`: Job name
|
|
453
|
-
- `status`: Job status
|
|
454
|
-
- `conclusion`: Job conclusion (or null)
|
|
345
|
+
- `ciStatus[prId].cipeUrl` (null if no CIPE) 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.
|
|
346
|
+
- When no CIPE exists, external CI data (e.g., GitHub Actions) appears in `ciStatus[prId].externalCIRuns[]` as runs with nested `jobs[]`; each job's `jobId` is the input for `get_ci_logs`.
|
|
455
347
|
|
|
456
348
|
```
|
|
457
349
|
show_session(sessionId: "<session-id>")
|
|
@@ -498,67 +390,7 @@ link_reference({
|
|
|
498
390
|
|
|
499
391
|
The canonical MCP parameters are `{ sessionId, reference }`. There is no unlink command.
|
|
500
392
|
|
|
501
|
-
###
|
|
502
|
-
|
|
503
|
-
Once all changes are verified and ready to merge, use `mark_pr_ready` to transition PRs from DRAFT to OPEN status.
|
|
504
|
-
|
|
505
|
-
**Parameters:**
|
|
506
|
-
|
|
507
|
-
- `sessionId` (required): The Polygraph session ID
|
|
508
|
-
- `prUrls` (required): Array of PR URLs to mark as ready for review
|
|
509
|
-
|
|
510
|
-
```
|
|
511
|
-
mark_pr_ready(
|
|
512
|
-
sessionId: "<session-id>",
|
|
513
|
-
prUrls: [
|
|
514
|
-
"https://github.com/org/frontend/pull/123",
|
|
515
|
-
"https://github.com/org/backend/pull/456"
|
|
516
|
-
]
|
|
517
|
-
)
|
|
518
|
-
```
|
|
519
|
-
|
|
520
|
-
**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:
|
|
521
|
-
|
|
522
|
-
```
|
|
523
|
-
**Polygraph session:** POLYGRAPH_SESSION_URL
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
Where `POLYGRAPH_SESSION_URL` is from `polygraphSessionUrl` in the response.
|
|
527
|
-
|
|
528
|
-
### 6. Associate Existing PRs
|
|
529
|
-
|
|
530
|
-
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.
|
|
531
|
-
|
|
532
|
-
Provide either a `prUrl` to associate a specific PR, or a `branch` name plus `repo` to find and associate PRs for a source repository.
|
|
533
|
-
|
|
534
|
-
**Parameters:**
|
|
535
|
-
|
|
536
|
-
- `sessionId` (required): The Polygraph session ID
|
|
537
|
-
- `prUrl` (optional): URL of an existing pull request to associate
|
|
538
|
-
- `branch` (optional): Branch name to find and associate PRs for
|
|
539
|
-
- `repo` (optional): Source repository for branch-based association. Required when using `branch` in a multi-repo session.
|
|
540
|
-
- `description` (required): Must follow the Session Description Policy.
|
|
541
|
-
|
|
542
|
-
```
|
|
543
|
-
associate_pr(
|
|
544
|
-
sessionId: "<session-id>",
|
|
545
|
-
prUrl: "https://github.com/org/repo/pull/123"
|
|
546
|
-
)
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
Or by branch:
|
|
550
|
-
|
|
551
|
-
```
|
|
552
|
-
associate_pr(
|
|
553
|
-
sessionId: "<session-id>",
|
|
554
|
-
repo: "org/repo",
|
|
555
|
-
branch: "feature/my-changes"
|
|
556
|
-
)
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
**Returns** the list of PRs now associated with the session.
|
|
560
|
-
|
|
561
|
-
### 7. Add Repositories to a Session
|
|
393
|
+
### 4. Add Repositories to a Session
|
|
562
394
|
|
|
563
395
|
Use `add_repo` to add repositories to an existing Polygraph session after it has already started.
|
|
564
396
|
|
|
@@ -578,7 +410,7 @@ add_repo(
|
|
|
578
410
|
)
|
|
579
411
|
```
|
|
580
412
|
|
|
581
|
-
###
|
|
413
|
+
### 5. Archive Session
|
|
582
414
|
|
|
583
415
|
**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.
|
|
584
416
|
|
|
@@ -692,6 +524,6 @@ If the session has a description timeline, also display:
|
|
|
692
524
|
|
|
693
525
|
1. **NEVER call `spawn_agent` or `show_agent` directly**. These MUST ALWAYS go through `@polygraph-delegate-subagent`.
|
|
694
526
|
|
|
695
|
-
1. **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.
|
|
527
|
+
1. **Use `stop_agent` to clean up** — Stop child agents that are stuck or no longer needed (pass `role` to target a non-default agent). 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.
|
|
696
528
|
1. **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.
|
|
697
529
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Publishing Changes Reference
|
|
2
|
+
|
|
3
|
+
The branch-to-PR flow: push branches, create draft PRs, mark them ready, and associate PRs created outside Polygraph. `push_branch`, `create_pr`, and `associate_pr` all require a `description` following the Session Description Policy — read [`session-description.md`](session-description.md) before writing one.
|
|
4
|
+
|
|
5
|
+
## Push Branches
|
|
6
|
+
|
|
7
|
+
Once work is complete in a repository, push the branch using `push_branch`. This must be done before creating a PR.
|
|
8
|
+
|
|
9
|
+
`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.
|
|
10
|
+
|
|
11
|
+
**Parameters:**
|
|
12
|
+
|
|
13
|
+
- `sessionId` (required): The Polygraph session ID
|
|
14
|
+
- `repo` (required): Repository name or repository ID to push from
|
|
15
|
+
- `branch` (required): Branch name to push to remote
|
|
16
|
+
- `description` (required): A session description is required. Must follow the Session Description Policy.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
push_branch(
|
|
20
|
+
sessionId: "<session-id>",
|
|
21
|
+
repo: "org/repo-name",
|
|
22
|
+
branch: "polygraph/ad5fa-add-user-preferences"
|
|
23
|
+
)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Create Draft PRs
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
**Parameters:**
|
|
31
|
+
|
|
32
|
+
- `sessionId` (required): The Polygraph session ID
|
|
33
|
+
- `prs` (required): Array of PR specifications, each containing:
|
|
34
|
+
- `owner` (required): GitHub repository owner
|
|
35
|
+
- `repo` (required): GitHub repository name
|
|
36
|
+
- `title` (required): PR title
|
|
37
|
+
- `body` (required): PR description (session metadata is appended automatically)
|
|
38
|
+
- `branch` (required): Branch name that was pushed
|
|
39
|
+
- `targetRepository` (optional): Target GitHub repository for fork PR creation or registration, as `owner/repo`. Omit for same-repository PRs.
|
|
40
|
+
- `description` (required): Must follow the Session Description Policy.
|
|
41
|
+
|
|
42
|
+
**PR title format (applies to parent and child agents):**
|
|
43
|
+
|
|
44
|
+
- 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>`).
|
|
45
|
+
- 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.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
create_pr(
|
|
49
|
+
sessionId: "<session-id>",
|
|
50
|
+
prs: [
|
|
51
|
+
{
|
|
52
|
+
owner: "org",
|
|
53
|
+
repo: "frontend",
|
|
54
|
+
title: "feat: Add user preferences UI",
|
|
55
|
+
body: "Part of multi-repo user preferences feature",
|
|
56
|
+
branch: "polygraph/ad5fa-add-user-preferences"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
owner: "org",
|
|
60
|
+
repo: "backend",
|
|
61
|
+
title: "feat: Add user preferences API",
|
|
62
|
+
body: "Part of multi-repo user preferences feature",
|
|
63
|
+
branch: "polygraph/ad5fa-add-user-preferences"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
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:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
create_pr(
|
|
73
|
+
sessionId: "<session-id>",
|
|
74
|
+
prs: [
|
|
75
|
+
{
|
|
76
|
+
owner: "contributor",
|
|
77
|
+
repo: "frontend-fork",
|
|
78
|
+
targetRepository: "org/frontend",
|
|
79
|
+
title: "feat: Add user preferences UI",
|
|
80
|
+
body: "Part of multi-repo user preferences feature",
|
|
81
|
+
branch: "polygraph/ad5fa-add-user-preferences"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**After creating PRs**, always print the Polygraph session URL:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
**Polygraph session:** POLYGRAPH_SESSION_URL
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Mark PRs Ready
|
|
94
|
+
|
|
95
|
+
Once all changes are verified and ready to merge, use `mark_pr_ready` to transition PRs from DRAFT to OPEN status.
|
|
96
|
+
|
|
97
|
+
**Parameters:**
|
|
98
|
+
|
|
99
|
+
- `sessionId` (required): The Polygraph session ID
|
|
100
|
+
- `prUrls` (required): Array of PR URLs to mark as ready for review
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
mark_pr_ready(
|
|
104
|
+
sessionId: "<session-id>",
|
|
105
|
+
prUrls: [
|
|
106
|
+
"https://github.com/org/frontend/pull/123",
|
|
107
|
+
"https://github.com/org/backend/pull/456"
|
|
108
|
+
]
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**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:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
**Polygraph session:** POLYGRAPH_SESSION_URL
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Where `POLYGRAPH_SESSION_URL` is from `polygraphSessionUrl` in the response.
|
|
119
|
+
|
|
120
|
+
## Associate Existing PRs
|
|
121
|
+
|
|
122
|
+
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.
|
|
123
|
+
|
|
124
|
+
Provide either a `prUrl` to associate a specific PR, or a `branch` name plus `repo` to find and associate PRs for a source repository.
|
|
125
|
+
|
|
126
|
+
**Parameters:**
|
|
127
|
+
|
|
128
|
+
- `sessionId` (required): The Polygraph session ID
|
|
129
|
+
- `prUrl` (optional): URL of an existing pull request to associate
|
|
130
|
+
- `branch` (optional): Branch name to find and associate PRs for
|
|
131
|
+
- `repo` (optional): Source repository for branch-based association. Required when using `branch` in a multi-repo session.
|
|
132
|
+
- `description` (required): Must follow the Session Description Policy.
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
associate_pr(
|
|
136
|
+
sessionId: "<session-id>",
|
|
137
|
+
prUrl: "https://github.com/org/repo/pull/123"
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Or by branch:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
associate_pr(
|
|
145
|
+
sessionId: "<session-id>",
|
|
146
|
+
repo: "org/repo",
|
|
147
|
+
branch: "feature/my-changes"
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Returns** the list of PRs now associated with the session.
|
|
@@ -26,7 +26,7 @@ Invoke the CLI as `${POLYGRAPH_CLI:-polygraph}` in every command: when the sessi
|
|
|
26
26
|
|
|
27
27
|
1. `${POLYGRAPH_CLI:-polygraph} session show --details <sessionId>` — metadata, description timeline, repositories, PRs. The description timeline often already summarizes goals and outcomes; mine it before reading transcripts.
|
|
28
28
|
2. Duplicate-work check: if the metadata shows this session pursuing the SAME task as the current one (not merely related work) and it is unfinished or recently active, do NOT read the transcripts. Return the debrief section immediately, with `**DUPLICATE WORK IN FLIGHT**` as the first line after the heading, followed by the session's status and last activity, one line of evidence for the match, and what resuming it would restore. The parent halts and asks the user to choose between resuming that session and continuing the current one, so speed matters more than depth here.
|
|
29
|
-
3. `${POLYGRAPH_CLI:-polygraph} session logs -s <sessionId> --all --tail none > "$TMPDIR/<sessionId>-logs.txt" 2>&1` — the parent transcript plus every child transcript, rendered as plain text, in ONE call. Then read the file directly (with offsets for large files). Do NOT fetch `--json` and do NOT query the transcript with node/python one-liners — reading the rendered text is faster and you extract while reading.
|
|
29
|
+
3. `${POLYGRAPH_CLI:-polygraph} session logs -s <sessionId> --all --tail none > "$TMPDIR/<sessionId>-logs.txt" 2>&1` — the parent transcript plus every child transcript, rendered as plain text, in ONE call. Then read the file directly (with offsets for large files). Do NOT fetch `--json` and do NOT query the transcript with node/python one-liners — reading the rendered text is faster and you extract while reading. **Coverage caveat:** `session logs --all` covers default-role children only — agents spawned with a `role` keep their transcripts local to the machine that ran them (viewable there via `polygraph agent attach --role <role>`); if the session used such agents, note in the debrief that their work is not visible in these logs.
|
|
30
30
|
4. Write the debrief section (format below).
|
|
31
31
|
|
|
32
32
|
Large transcripts: read the file in a few large chunks, prioritizing user prompts, assistant text and final messages, tool errors and failure events, and task notifications. Routine tool-use noise (file reads, searches) is safe to skim. Do not make repeated small queries against the transcript; each round trip costs more than reading a bigger chunk.
|