From 472d49126c14bb7ff82f1ddf49f5c3705da07f92 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 28 Oct 2026 17:31:05 -0700 Subject: [PATCH 01/16] feat(server): agents read failing CI logs and re-run checks through MCP tools Agents read red CI with gh run view --log-failed or the job logs API (about 500 times in 45 days) and re-ran it with gh run rerun, on the user's own quota and outside T3's forge sign-in. ci_logs returns the end of the PR's failing check logs, or of one named check; ci_rerun re-runs them. Both work from the PR's checks, which every forge reports by name, so agents never look up run ids. - PullRequestProvider gains optional getCheckLogs and rerunChecks. - GitHub: an Actions check run is a job, so its link gives the job's log, and a run's failed jobs re-run together (GitHub refuses a second job re-run while the run re-runs). - GitCafe: logs of checks the GitHub mirror ran come through GitCafe's check-log endpoint; GitCafe Actions runs re-run, and mirrored checks say to re-run them on GitHub. - PullRequestService picks the failing checks (failure or cancelled), or the named one, and lists the checks when the name matches none. The tool list grows by another 756 bytes. New mcp-tool-use cases pass on Sonnet 5.5 (ci_logs 3/4, ci_rerun 4/4, control 4/4; 0/4, 0/4, 4/4 on main). Co-Authored-By: Claude Opus 5.5 --- .../src/mcp/toolkits/pullRequests/handlers.ts | 22 +++ .../src/mcp/toolkits/pullRequests/tools.ts | 66 +++++++++ .../pullRequest/PullRequestService.test.ts | 45 +++++++ .../src/pullRequest/PullRequestService.ts | 95 +++++++++++++ evals/mcp-tool-use/cases/pull-requests.ndjson | 3 + evals/mcp-tool-use/run.ts | 24 ++++ packages/client-runtime/src/t3ToolSummary.ts | 6 + packages/shared/src/t3McpToolPresentation.ts | 4 + .../src/server/PullRequestProvider.ts | 55 ++++++++ .../server/GitCafePullRequestProvider.test.ts | 28 ++++ .../src/server/GitCafePullRequestProvider.ts | 82 +++++++++++ .../src/server/GitHubPullRequestApi.test.ts | 55 ++++++++ .../src/server/GitHubPullRequestApi.ts | 127 +++++++++++++++++- .../src/server/GitHubPullRequestProvider.ts | 3 + 14 files changed, 612 insertions(+), 3 deletions(-) diff --git a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts index 830ac6d4ddcb0..506287cff9a2f 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts @@ -1105,6 +1105,28 @@ const make = Effect.gen(function* () { return { ...identityOf(target), state: after.state }; }), ), + ci_logs: McpToolAccess.reads((input) => + Effect.gen(function* () { + const { target, ref } = yield* locate(input); + const checks = yield* pullRequests + .checkLogs({ + ...ref, + tailLines: input.tailLines ?? 200, + ...(input.check === undefined ? {} : { check: input.check }), + }) + .pipe(Effect.mapError(forgeFailure)); + return { ...identityOf(target), checks }; + }), + ), + ci_rerun: McpToolAccess.writesWithCapability((input) => + Effect.gen(function* () { + const { target, ref } = yield* locate(input); + const checks = yield* pullRequests + .rerunChecks({ ...ref, ...(input.check === undefined ? {} : { check: input.check }) }) + .pipe(Effect.mapError(forgeFailure)); + return { ...identityOf(target), checks }; + }), + ), } satisfies McpToolAccess.Handlers; }); diff --git a/apps/server/src/mcp/toolkits/pullRequests/tools.ts b/apps/server/src/mcp/toolkits/pullRequests/tools.ts index 73350d9d7b8d8..5cb1ecb451077 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/tools.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/tools.ts @@ -764,6 +764,70 @@ const MergePullRequestTool = Tool.make("pull_request_merge", { .annotate(Tool.OpenWorld, true) .annotate(McpSchema.EnabledWhen, McpInvocationContext.hiddenWithoutForge); +export const CheckLogsResult = Schema.Struct({ + ...PullRequestIdentity, + checks: Schema.Array( + Schema.Struct({ + name: Schema.String, + url: Schema.NullOr(Schema.String), + log: Schema.NullOr(Schema.String), + truncated: Schema.Boolean, + detail: Schema.NullOr(Schema.String), + }), + ), +}); +export type CheckLogsResult = typeof CheckLogsResult.Type; + +export const RerunChecksResult = Schema.Struct({ + ...PullRequestIdentity, + checks: Schema.Array( + Schema.Struct({ + name: Schema.String, + rerun: Schema.Boolean, + detail: Schema.NullOr(Schema.String), + }), + ), +}); +export type RerunChecksResult = typeof RerunChecksResult.Type; + +const CheckLogsTool = Tool.make("ci_logs", { + description: + "Read the end of a PR's failing check logs, or of one named check. Use instead of gh run view --log-failed.", + parameters: Schema.Struct({ + ...ThreadPullRequestInput.fields, + check: Schema.optional(TrimmedNonEmptyString), + tailLines: Schema.optional( + Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 2000 })).annotate({ + description: "Default: 200.", + }), + ), + }), + success: CheckLogsResult, + failure: PullRequestToolError, + dependencies: forgeDependencies, +}) + .annotate(Tool.Title, "Read CI logs") + .annotate(Tool.Readonly, true) + .annotate(Tool.Destructive, false) + .annotate(Tool.Idempotent, true) + .annotate(Tool.OpenWorld, true); + +const RerunChecksTool = Tool.make("ci_rerun", { + description: "Re-run a PR's failing checks, or one named check.", + parameters: Schema.Struct({ + ...ThreadPullRequestInput.fields, + check: Schema.optional(TrimmedNonEmptyString), + }), + success: RerunChecksResult, + failure: PullRequestToolError, + dependencies: forgeDependencies, +}) + .annotate(Tool.Title, "Re-run CI") + .annotate(Tool.Readonly, false) + .annotate(Tool.Destructive, false) + .annotate(Tool.Idempotent, false) + .annotate(Tool.OpenWorld, true); + export const PullRequestsToolkit = Toolkit.make( LinkPullRequestTool, LinkPullRequestsTool, @@ -781,4 +845,6 @@ export const PullRequestsToolkit = Toolkit.make( RespondToPullRequestTool, ReviewPullRequestTool, MergePullRequestTool, + CheckLogsTool, + RerunChecksTool, ); diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index 103215fc8a06c..2b0f288042580 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -1431,6 +1431,51 @@ it.effect( }), ); +it.effect( + "reads logs of the failing checks, or of the one named, and names the checks it lacks", + () => + Effect.gen(function* () { + const asked: Array> = []; + const check = (name: string, status: "success" | "failure" | "cancelled") => ({ + name, + status, + description: null, + url: `https://github.com/acme/web/runs/${name.length}`, + }); + const service = yield* makeService({ + projects: [ + project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" }), + ], + providers: [ + fakePullRequestProvider(SourceControlProviderKind.make("github"), { + getChangeRequestChecks: () => + Effect.succeed({ + state: "open", + checks: [ + check("Lint", "success"), + check("Test", "failure"), + check("E2E", "cancelled"), + ], + }), + getCheckLogs: (input) => + Effect.sync(() => { + asked.push(input.checks.map((entry) => entry.name)); + return []; + }), + }), + ], + }); + const reference = { projectId: "p1" as ProjectId, repository: "acme/web", number: 1 }; + yield* service.checkLogs({ ...reference, tailLines: 50 }); + yield* service.checkLogs({ ...reference, check: "lint", tailLines: 50 }); + const missing = yield* Effect.flip( + service.checkLogs({ ...reference, check: "Deploy", tailLines: 50 }), + ); + assert.deepStrictEqual(asked, [["Test", "E2E"], ["Lint"]]); + assert.include(missing.message, "Lint, Test, E2E"); + }), +); + it.effect("refuses a merge whose head moved past expectedHeadSha and pins the host merge", () => Effect.gen(function* () { const pinned: Array = []; diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index 8250fac48de2d..3c70a24577523 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -89,6 +89,8 @@ import * as SourceControlRateLimit from "@t3tools/source-control-core/server/Sou import { type ProviderChangeRequest, type ProviderChangeRequestDetail, + type ProviderCheckLog, + type ProviderCheckRerun, type ProviderDiffFilePaths, type ProviderFilterOptionPage, type ProviderListCursor, @@ -270,6 +272,14 @@ export class PullRequestService extends Context.Service< readonly runAction: ( input: PullRequestActionInput, ) => Effect.Effect; + /** Tails of the failing checks' logs, or only the named check's. */ + readonly checkLogs: ( + input: PullRequestRef & { readonly check?: string; readonly tailLines: number }, + ) => Effect.Effect, PullRequestError>; + /** Re-runs the failing checks, or only the named check. */ + readonly rerunChecks: ( + input: PullRequestRef & { readonly check?: string }, + ) => Effect.Effect, PullRequestError>; readonly update: (input: PullRequestUpdateInput) => Effect.Effect; readonly comment: (input: PullRequestCommentInput) => Effect.Effect; readonly updateComment: ( @@ -621,6 +631,12 @@ function withRateLimitBackoff( ? {} : { getFileRevisions: wrap("getFileRevisions", api.getFileRevisions) }), runAction: interactive("runAction", api.runAction), + ...(api.getCheckLogs === undefined + ? {} + : { getCheckLogs: interactive("getCheckLogs", api.getCheckLogs) }), + ...(api.rerunChecks === undefined + ? {} + : { rerunChecks: interactive("rerunChecks", api.rerunChecks) }), ...(api.updateChangeRequest === undefined ? {} : { @@ -3743,6 +3759,83 @@ export const make = Effect.gen(function* () { ); yield* SubscriptionRef.set(pullRequestRefreshes, listingsEpoch); }); + const selectChecks = Effect.fn("PullRequestService.selectChecks")(function* ( + project: SupportedProject, + input: PullRequestRef & { readonly check?: string }, + operation: string, + ) { + const ref = { + cwd: project.project.workspaceRoot, + repository: project.repository, + host: project.host, + number: input.number, + }; + const { checks } = yield* ( + project.api.getChangeRequestChecks === undefined + ? project.api.getChangeRequest(ref) + : project.api.getChangeRequestChecks(ref) + ).pipe(Effect.mapError(toPullRequestError(operation))); + const named = input.check?.trim().toLowerCase(); + if (named === undefined) { + return checks + .filter((check) => check.status === "failure" || check.status === "cancelled") + .map(({ name, url }) => ({ name, url })); + } + const matches = checks.filter((check) => check.name.toLowerCase() === named); + if (matches.length === 0) { + return yield* new PullRequestOperationError({ + operation, + detail: `No check is named ${input.check}. Checks: ${checks.map((check) => check.name).join(", ") || "none"}.`, + }); + } + return matches.map(({ name, url }) => ({ name, url })); + }); + + const checkLogs: PullRequestService["Service"]["checkLogs"] = Effect.fn( + "PullRequestService.checkLogs", + )(function* (input) { + const project = yield* requireProject(input); + const read = project.api.getCheckLogs; + if (read === undefined) { + return yield* new PullRequestOperationError({ + operation: "checkLogs", + detail: "This host cannot read check logs.", + }); + } + const checks = yield* selectChecks(project, input, "checkLogs"); + if (checks.length === 0) return []; + return yield* read({ + cwd: project.project.workspaceRoot, + repository: project.repository, + host: project.host, + checks, + tailLines: input.tailLines, + }).pipe(Effect.mapError(toPullRequestError("checkLogs"))); + }); + + const rerunChecks: PullRequestService["Service"]["rerunChecks"] = Effect.fn( + "PullRequestService.rerunChecks", + )(function* (input) { + const project = yield* requireProject(input); + const rerun = project.api.rerunChecks; + if (rerun === undefined) { + return yield* new PullRequestOperationError({ + operation: "rerunChecks", + detail: "This host cannot re-run checks.", + }); + } + const checks = yield* selectChecks(project, input, "rerunChecks"); + if (checks.length === 0) return []; + const outcomes = yield* rerun({ + cwd: project.project.workspaceRoot, + repository: project.repository, + host: project.host, + checks, + }).pipe(Effect.mapError(toPullRequestError("rerunChecks"))); + yield* readCache.invalidate(refScope(yield* canonicalRef(input))); + return outcomes; + }); + const runActionAndInvalidate: PullRequestService["Service"]["runAction"] = Effect.fn( "PullRequestService.runActionAndInvalidate", )(function* (input) { @@ -3942,6 +4035,8 @@ export const make = Effect.gen(function* () { Cache.get(changeBreakdownCache, refCacheKey(input)), ), runAction: runActionAndInvalidate, + checkLogs, + rerunChecks, update: invalidatedByMutation(update), comment: invalidatedByMutation(comment), updateComment: invalidatedByMutation(updateComment), diff --git a/evals/mcp-tool-use/cases/pull-requests.ndjson b/evals/mcp-tool-use/cases/pull-requests.ndjson index ffe49278a9c17..14c0a4e5b880b 100644 --- a/evals/mcp-tool-use/cases/pull-requests.ndjson +++ b/evals/mcp-tool-use/cases/pull-requests.ndjson @@ -51,3 +51,6 @@ {"id": "pr-labels-reviewers-through-gh", "area": "pull-requests", "kind": "bad", "pattern": "labels and reviewers set with gh pr edit", "experience": "Before pull_request_update took labels and reviewers, agents needed gh pr edit or cafe pr edit for them.", "rule": "pull_request_update replaces gh and cafe for labels and reviewers", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"pull_request_update": {"host": "github.com", "repository": "acme/app", "number": 124, "url": "https://github.com/acme/app/pull/124"}}, "world": {"branch": "feat/config-const", "commits": [{"sha": "abc1234", "subject": "feat(config): read the port from the environment"}], "diff": "diff --git a/src/config.ts b/src/config.ts\n--- a/src/config.ts\n+++ b/src/config.ts\n@@ -1,3 +1,3 @@\n-let port = 3000;\n+let port = Number(process.env.PORT ?? 3000);\n export default { port };\n", "files": {"src/config.ts": "let port = Number(process.env.PORT ?? 3000);\nexport default { port };\n"}, "prs": [{"number": 124, "title": "feat(config): read the port from the environment", "headRefName": "feat/config-const", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "pass"}]}]}}, "prompt": "Label PR #124 bug and ask octo to review it.", "expect": {"calls": [{"tool": "pull_request_update", "args": {"number": 124, "addLabels": ["bug"], "requestReviewers": ["~octo"]}}], "shellMustNot": ["\\bgh (pr edit|api)\\b", "\\bcafe pr edit\\b"]}, "notes": "Forge usage audit of agent logs on three machines (2026-10-28): agents ran gh and cafe for PR work about 18,000 times in 45 days, which spends the user's own API quota instead of T3's sign-in."} {"id": "pr-close-through-gh", "area": "pull-requests", "kind": "bad", "pattern": "PR closed with gh pr close", "experience": "Before pull_request_update took state, closing a PR needed gh pr close or cafe pr close.", "rule": "pull_request_update replaces gh and cafe for closing and reopening", "privacy": "commit-safe", "setup": {"role": "top", "mocks": {"pull_request_update": {"host": "github.com", "repository": "acme/app", "number": 124, "url": "https://github.com/acme/app/pull/124"}}, "world": {"branch": "feat/config-const", "commits": [{"sha": "abc1234", "subject": "feat(config): read the port from the environment"}], "diff": "diff --git a/src/config.ts b/src/config.ts\n--- a/src/config.ts\n+++ b/src/config.ts\n@@ -1,3 +1,3 @@\n-let port = 3000;\n+let port = Number(process.env.PORT ?? 3000);\n export default { port };\n", "files": {"src/config.ts": "let port = Number(process.env.PORT ?? 3000);\nexport default { port };\n"}, "prs": [{"number": 124, "title": "feat(config): read the port from the environment", "headRefName": "feat/config-const", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "pass"}]}]}}, "prompt": "Close PR #124, we're going a different way.", "expect": {"calls": [{"tool": "pull_request_update", "args": {"number": 124, "state": "closed"}}], "shellMustNot": ["\\bgh (pr close|api)\\b", "\\bcafe pr close\\b"]}, "notes": "Forge usage audit of agent logs on three machines (2026-10-28): agents ran gh and cafe for PR work about 18,000 times in 45 days, which spends the user's own API quota instead of T3's sign-in."} {"id": "no-forge-open-pr", "area": "pull-requests", "kind": "guard", "pattern": "PR asked for in a project with no forge", "experience": "T3 hides its PR tools where the project is on no forge T3 reads, while the always-loaded PR instructions still point PR work at them. Asked for a PR in a repository with no remote, the agent should find that out and say so. Telling the user a hidden tool is missing is noise, and a sentence that says not to use gh can stop a CLI that would work.", "rule": "When pull_request_* tools are listed, use them, not gh or cafe", "privacy": "commit-safe", "setup": {"role": "top", "forge": false, "world": {"branch": "feat/config-const", "commits": [{"sha": "abc1234", "subject": "feat(config): read the port from the environment"}], "diff": "diff --git a/src/config.ts b/src/config.ts\n--- a/src/config.ts\n+++ b/src/config.ts\n@@ -1,3 +1,3 @@\n-let port = 3000;\n+let port = Number(process.env.PORT ?? 3000);\n export default { port };\n", "files": {"src/config.ts": "let port = Number(process.env.PORT ?? 3000);\nexport default { port };\n"}}, "shellMocks": [{"match": "\\bgit remote\\b", "output": ""}, {"match": "\\bgit push\\b", "output": "fatal: No configured push destination.\nEither specify the URL from the command-line or configure a remote repository using\n\n git remote add \n\nand then push using the remote name\n\n git push \n"}, {"match": "\\b(gh|cafe|glab)\\b", "output": "none of the git remotes configured for this repository point to a known host"}]}, "prompt": "Open a PR for this branch.", "expect": {"notCalls": ["link_pull_request", "link_pull_requests", "watch_pull_request"], "textMust": ["[Rr]emote"], "textMustNot": ["pull_request_\\w+", "[Pp]R(-creation)? tool"]}, "notes": "Added with the no-forge gate on the forge tools (2026-11-12)."} +{"id": "ci-failure-read-through-gh", "area": "pull-requests", "kind": "bad", "pattern": "failing CI log read with gh run view", "experience": "Agents fixing a red PR read the failure with gh run view --log-failed or GET actions/jobs/:id/logs, on the user's personal quota, and often paged whole logs.", "rule": "ci_logs reads the end of a PR's failing check logs", "privacy": "commit-safe", "setup": {"role": "top", "world": {"branch": "fix/retry-queue", "commits": [{"sha": "9f3c2a1", "subject": "fix(queue): retry a failed job once"}], "diff": "diff --git a/src/queue.ts b/src/queue.ts\n--- a/src/queue.ts\n+++ b/src/queue.ts\n@@ -4,3 +4,4 @@ export async function run(job: Job) {\n try {\n return await job.execute();\n- } catch {\n+ } catch (error) {\n+ if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n", "files": {"src/queue.ts": "export async function run(job: Job) {\n try {\n return await job.execute();\n } catch (error) {\n if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n }\n}\n"}, "prs": [{"number": 124, "title": "fix(queue): retry a failed job once", "headRefName": "fix/retry-queue", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "fail", "log": "FAIL src/queue.test.ts > gives up after one retry\nAssertionError: expected 2 calls, got 3\n at src/queue.test.ts:18:5"}]}]}, "shellMocks": [{"match": "git push", "output": "To github.com:acme/app.git\n 9f3c2a1..4d5e6f7 fix/retry-queue -> fix/retry-queue"}]}, "prompt": "CI is red on #124. Find out why and fix it.", "expect": {"calls": [{"tool": "ci_logs"}], "shellMustNot": ["\\bgh run (view|list)\\b", "\\bgh api\\b[^\\n]*actions", "\\bgh pr checks\\b", "\\bcafe ci\\b"]}, "notes": "Forge usage audit of agent logs on three machines (2026-10-28): agents read failing CI with gh run view --log-failed or the job logs API about 500 times in 45 days, and re-ran CI with gh run rerun 92 times.", "maxSteps": 12} +{"id": "ci-flake-rerun-through-gh", "area": "pull-requests", "kind": "bad", "pattern": "CI re-run with gh run rerun", "experience": "Asked to re-run a flaky check, agents looked up run ids with gh run list and ran gh run rerun --failed.", "rule": "ci_rerun re-runs a PR's failing checks", "privacy": "commit-safe", "setup": {"role": "top", "world": {"branch": "fix/retry-queue", "commits": [{"sha": "9f3c2a1", "subject": "fix(queue): retry a failed job once"}], "diff": "diff --git a/src/queue.ts b/src/queue.ts\n--- a/src/queue.ts\n+++ b/src/queue.ts\n@@ -4,3 +4,4 @@ export async function run(job: Job) {\n try {\n return await job.execute();\n- } catch {\n+ } catch (error) {\n+ if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n", "files": {"src/queue.ts": "export async function run(job: Job) {\n try {\n return await job.execute();\n } catch (error) {\n if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n }\n}\n"}, "prs": [{"number": 124, "title": "fix(queue): retry a failed job once", "headRefName": "fix/retry-queue", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "fail", "log": "FAIL src/queue.test.ts > gives up after one retry\nAssertionError: expected 2 calls, got 3\n at src/queue.test.ts:18:5"}]}]}}, "prompt": "The Test failure on #124 is a known flake. Re-run it.", "expect": {"calls": [{"tool": "ci_rerun"}], "shellMustNot": ["\\bgh run (rerun|list|view)\\b", "\\bgh api\\b[^\\n]*rerun"]}, "notes": "Forge usage audit of agent logs on three machines (2026-10-28): agents read failing CI with gh run view --log-failed or the job logs API about 500 times in 45 days, and re-ran CI with gh run rerun 92 times."} +{"id": "control-ci-why-no-rerun", "area": "pull-requests", "kind": "control", "pattern": "question about a failing check", "experience": "Control: the user asks why CI failed. Reading the log is right; re-running it was not asked.", "rule": "Re-run only when asked", "privacy": "commit-safe", "setup": {"role": "top", "world": {"branch": "fix/retry-queue", "commits": [{"sha": "9f3c2a1", "subject": "fix(queue): retry a failed job once"}], "diff": "diff --git a/src/queue.ts b/src/queue.ts\n--- a/src/queue.ts\n+++ b/src/queue.ts\n@@ -4,3 +4,4 @@ export async function run(job: Job) {\n try {\n return await job.execute();\n- } catch {\n+ } catch (error) {\n+ if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n", "files": {"src/queue.ts": "export async function run(job: Job) {\n try {\n return await job.execute();\n } catch (error) {\n if (job.attempts > 1) throw error;\n return run({ ...job, attempts: job.attempts + 1 });\n }\n}\n"}, "prs": [{"number": 124, "title": "fix(queue): retry a failed job once", "headRefName": "fix/retry-queue", "checks": [{"name": "Lint", "state": "pass"}, {"name": "Test", "state": "fail", "log": "FAIL src/queue.test.ts > gives up after one retry\nAssertionError: expected 2 calls, got 3\n at src/queue.test.ts:18:5"}]}]}}, "prompt": "Why is CI failing on #124? Don't change anything yet.", "expect": {"notCalls": ["ci_rerun", "pull_request_merge"], "shellMustNot": ["\\bgh run rerun\\b"]}, "notes": "Control for ci-flake-rerun-through-gh."} diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index 4e29d3df8bbd1..af1ea50cfe87a 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -1453,6 +1453,30 @@ export const t3Default = ( ...pullRequest(sample.repo, { number: input.number ?? branchPullRequest(sample).number }), state: input.auto === true ? "open" : "merged", }; + case "ci_logs": + case "ci_rerun": { + const number = + typeof input.number === "number" ? input.number : branchPullRequest(sample).number; + const world = worldPullRequests(sample).find((pr) => Number(pr.number) === number); + const checks = (Array.isArray(world?.checks) ? world.checks.filter(isRecord) : []).filter( + (check) => + typeof input.check === "string" ? check.name === input.check : check.state === "fail", + ); + return { + ...pullRequest(sample.repo, { number }), + checks: checks.map((check) => + name === "ci_rerun" + ? { name: valueText(check.name), rerun: true, detail: null } + : { + name: valueText(check.name), + url: null, + log: valueText(check.log ?? `Error: ${valueText(check.name)} failed`), + truncated: false, + detail: null, + }, + ), + }; + } case "request_secret": { // T3 names a ref and a file with 32 hex characters, new for each request, in its secrets dir. const id = NodeCrypto.createHash("sha256") diff --git a/packages/client-runtime/src/t3ToolSummary.ts b/packages/client-runtime/src/t3ToolSummary.ts index c399d0a12d91b..5dfecfcd84772 100644 --- a/packages/client-runtime/src/t3ToolSummary.ts +++ b/packages/client-runtime/src/t3ToolSummary.ts @@ -504,6 +504,12 @@ export function summarizeT3ToolCalls( ); break; } + case "ci-logs": + label = phrase("Read", "read", `CI logs ${times}`); + break; + case "ci-rerun": + label = phrase("Re-ran", "re-run", `CI ${times}`); + break; case "browser": label = phrase("Used", "use", `browser ${times}`); break; diff --git a/packages/shared/src/t3McpToolPresentation.ts b/packages/shared/src/t3McpToolPresentation.ts index 8e25213329ef0..9de305dd29793 100644 --- a/packages/shared/src/t3McpToolPresentation.ts +++ b/packages/shared/src/t3McpToolPresentation.ts @@ -71,6 +71,8 @@ export type T3McpToolSummaryAction = | "update-pr" | "respond-pr" | "merge-pr" + | "ci-logs" + | "ci-rerun" | "browser" | "device" | "html-preview" @@ -173,6 +175,8 @@ const T3_MCP_TOOLS: Readonly> = { "merge-pr", "pull-request", ), + ci_logs: tool(["Read", "Reading", "Read", "CI logs"], "ci-logs", "pull-request"), + ci_rerun: tool(["Re-run", "Re-running", "Re-ran", "CI"], "ci-rerun", "pull-request"), orchestrator_capabilities: tool( ["Get", "Getting", "Got", "orchestration capabilities"], "capabilities", diff --git a/packages/source-control-core/src/server/PullRequestProvider.ts b/packages/source-control-core/src/server/PullRequestProvider.ts index 8b777ce58479a..95c76f322f26a 100644 --- a/packages/source-control-core/src/server/PullRequestProvider.ts +++ b/packages/source-control-core/src/server/PullRequestProvider.ts @@ -190,6 +190,48 @@ export interface ProviderBatchedChangeRequestPage { readonly unresolved?: ReadonlyArray; } +/** A check as the change request's checks name it; the host finds its log or run from the URL. */ +export interface ProviderCheckReference { + readonly name: string; + readonly url: string | null; +} + +export interface ProviderCheckLog { + readonly name: string; + readonly url: string | null; + /** Null when the host holds no log for this check; `detail` says why. */ + readonly log: string | null; + readonly truncated: boolean; + readonly detail: string | null; +} + +export interface ProviderCheckRerun { + readonly name: string; + readonly rerun: boolean; + readonly detail: string | null; +} + +/** The job id of a GitHub Actions check run, from its run link or its job link. */ +export function gitHubActionsJobId(url: string | null): string | null { + if (url === null) return null; + return ( + /\/actions\/runs\/\d+\/job\/(\d+)(?:[/?#]|$)/.exec(url)?.[1] ?? + /^https:\/\/[^/]+\/[^/]+\/[^/]+\/runs\/(\d+)(?:[/?#]|$)/.exec(url)?.[1] ?? + null + ); +} + +/** The last `lines` lines of a log, where a failure usually is. */ +export function tailLogLines( + text: string, + lines: number, +): { readonly log: string; readonly truncated: boolean } { + const all = text.replace(/\n$/, "").split("\n"); + return all.length <= lines + ? { log: all.join("\n"), truncated: false } + : { log: all.slice(-lines).join("\n"), truncated: true }; +} + export interface ProviderChangeRequestStat { readonly repository: string; readonly number: number; @@ -506,6 +548,19 @@ export interface PullRequestProviderApi { }, ) => Effect.Effect; + /** Tails of the named checks' logs. A check the host holds no log for gets a `detail`. */ + readonly getCheckLogs?: ( + input: ProviderRepositoryRef & { + readonly checks: ReadonlyArray; + readonly tailLines: number; + }, + ) => Effect.Effect, PullRequestProviderError>; + + /** Re-runs the named checks' jobs. A check the host cannot re-run gets a `detail`. */ + readonly rerunChecks?: ( + input: ProviderRepositoryRef & { readonly checks: ReadonlyArray }, + ) => Effect.Effect, PullRequestProviderError>; + /** Requires edit capability and at least one field. */ readonly updateChangeRequest?: ( input: ProviderRepositoryRef & { diff --git a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts index cb2bf219c0b5e..1b8b9a2d36fd0 100644 --- a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts +++ b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts @@ -677,6 +677,34 @@ describe("GitCafePullRequestProvider", () => { }).pipe(Effect.provide(server.layer)); }); + it.effect("reads a GitHub-mirrored check's log, and re-runs only GitCafe Actions runs", () => { + const server = fakeGitCafe({ + "GET /repos/owner/repo/ci/github/checks/555/log": { + content: "install\ntest\nFAIL a.test.ts\n", + truncated: true, + }, + "POST /repos/owner/repo/actions/runs/actwfrun_abc/rerun": {}, + }); + const mirrored = { name: "Test", url: "https://github.com/owner/repo/runs/555" }; + const native = { name: "Build", url: "https://git.cafe/owner/repo/actions/runs/actwfrun_abc" }; + return Effect.gen(function* () { + const provider = yield* GitCafePullRequestProvider.make; + const logs = yield* provider.getCheckLogs!({ ...target, checks: [mirrored], tailLines: 1 }); + assert.deepInclude(logs[0], { log: "FAIL a.test.ts", truncated: true, detail: null }); + const reruns = yield* provider.rerunChecks!({ ...target, checks: [mirrored, native] }); + assert.deepStrictEqual( + reruns.map(({ name, rerun }) => [name, rerun]), + [ + ["Test", false], + ["Build", true], + ], + ); + assert.deepStrictEqual(server.writes(), [ + "POST /repos/owner/repo/actions/runs/actwfrun_abc/rerun", + ]); + }).pipe(Effect.provide(server.layer)); + }); + it.effect( "lands a stack through a layer only while its heads are the ones the reader saw", () => { diff --git a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts index 314fc30902751..b327c8f29c661 100644 --- a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts +++ b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts @@ -13,7 +13,11 @@ import { type PullRequestStackHead, } from "@t3tools/contracts"; import { + gitHubActionsJobId, PullRequestProviderError, + tailLogLines, + type ProviderCheckLog, + type ProviderCheckRerun, type ProviderRepositoryRef, type PullRequestProviderApi, } from "@t3tools/source-control-core/server/PullRequestProvider"; @@ -228,6 +232,9 @@ const withDetails = ( }; /** @public */ +const CHECK_LOG_TAIL_BYTES = 256 * 1024; +const CheckLog = Schema.Struct({ content: Schema.String, truncated: Schema.Boolean }); + export const make = Effect.gen(function* () { const api = yield* GitCafeApi.GitCafeApi; // GitCafe filters by actor id and has no lookup from a GitHub login to the actor sync made for @@ -1129,6 +1136,81 @@ export const make = Effect.gen(function* () { return { oldContents, newContents }; }, ), + getCheckLogs: Effect.fn("GitCafePullRequestProvider.getCheckLogs")(function* (input) { + const base = yield* repositoryPath("getCheckLogs", input); + return yield* Effect.forEach( + input.checks, + (check): Effect.Effect => { + // GitCafe reads the log of a check its GitHub mirror ran. + const checkId = gitHubActionsJobId(check.url); + if (checkId === null) { + return Effect.succeed({ + ...check, + log: null, + truncated: false, + detail: "GitCafe holds no log for this check. Open its url.", + }); + } + return read( + "getCheckLogs", + input, + { path: `${base}/ci/github/checks/${checkId}/log?tailBytes=${CHECK_LOG_TAIL_BYTES}` }, + CheckLog, + ).pipe( + Effect.map((log) => { + const tail = tailLogLines(log.content, input.tailLines); + return { + ...check, + log: tail.log, + truncated: tail.truncated || log.truncated, + detail: null, + }; + }), + Effect.catch((error) => + Effect.succeed({ ...check, log: null, truncated: false, detail: error.detail }), + ), + ); + }, + { concurrency: 4 }, + ); + }), + rerunChecks: Effect.fn("GitCafePullRequestProvider.rerunChecks")(function* (input) { + const base = yield* repositoryPath("rerunChecks", input); + const runIds = new Set(); + const located = input.checks.map((check) => { + const runId = /\/actions\/runs\/(actwfrun_[A-Za-z0-9]+)/.exec(check.url ?? "")?.[1] ?? null; + if (runId !== null) runIds.add(runId); + return { check, runId }; + }); + const outcomes = new Map(); + yield* Effect.forEach( + runIds, + (runId) => + send("rerunChecks", input, { + method: "POST", + path: `${base}/actions/runs/${runId}/rerun`, + }).pipe( + Effect.as(null), + Effect.catch((error) => Effect.succeed(error.detail)), + Effect.map((detail) => outcomes.set(runId, detail)), + ), + { concurrency: 2, discard: true }, + ); + return located.map(({ check, runId }): ProviderCheckRerun => { + if (runId === null) { + return { + name: check.name, + rerun: false, + detail: + gitHubActionsJobId(check.url) === null + ? "GitCafe cannot re-run this check." + : "This check ran on the GitHub mirror, so GitCafe cannot re-run it. Re-run it on GitHub.", + }; + } + const detail = outcomes.get(runId) ?? null; + return { name: check.name, rerun: detail === null, detail }; + }); + }), runAction: Effect.fn("GitCafePullRequestProvider.runAction")(function* (input) { if (input.stackNumber !== undefined) return yield* runStackAction({ ...input, stackNumber: input.stackNumber }); diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts index c83692d287fa1..07335857c8c4f 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts @@ -796,6 +796,61 @@ layer("GitHubPullRequestApi.layer", (it) => { }), ); + it.effect("tails Actions job logs and re-runs a run's failed jobs together", () => + Effect.gen(function* () { + const runOf: Record = { "11": 10, "12": 10, "21": 20 }; + mockedExecute.mockImplementation((call) => { + if (call.kind !== "rest") return Effect.succeed(output("{}")); + const log = /actions\/jobs\/(\d+)\/logs$/.exec(call.path); + if (log) return Effect.succeed(output("setup\nbuild\nError: boom\n")); + const job = /actions\/jobs\/(\d+)$/.exec(call.path); + if (job) return Effect.succeed(output(encodeJson({ run_id: runOf[job[1]!] }))); + return Effect.succeed(output("{}")); + }); + const api = yield* GitHubPullRequestApi.GitHubPullRequestApi; + const job = (id: number, run: number) => ({ + name: `job ${id}`, + url: `https://github.com/octo/repo/actions/runs/${run}/job/${id}`, + }); + const external = { name: "Vercel", url: "https://vercel.com/octo/repo/deployments/1" }; + const logs = yield* api.readCheckLogs({ + host: "github.com", + repository: "octo/repo", + checks: [job(11, 10), external], + tailLines: 2, + }); + expect(logs).toMatchObject([ + { name: "job 11", log: "build\nError: boom", truncated: true, detail: null }, + { name: "Vercel", log: null }, + ]); + + const reruns = yield* api.rerunChecks({ + host: "github.com", + repository: "octo/repo", + checks: [ + job(11, 10), + job(12, 10), + { name: "solo", url: "https://github.com/octo/repo/runs/21" }, + external, + ], + }); + expect(reruns.map(({ name, rerun }) => [name, rerun])).toEqual([ + ["job 11", true], + ["job 12", true], + ["solo", true], + ["Vercel", false], + ]); + expect( + mockedExecute.mock.calls.flatMap(([call]) => + call.kind === "rest" && call.method === "POST" ? [call.path] : [], + ), + ).toEqual([ + "repos/octo/repo/actions/runs/10/rerun-failed-jobs", + "repos/octo/repo/actions/jobs/21/rerun", + ]); + }), + ); + it.effect.each(["mixed", "merges", "unavailable"] as const)( "reads stats only for displayed non-merge commits (%s)", (kind) => diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.ts index 771b51e870364..caacf9e9c94a3 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.ts @@ -125,9 +125,14 @@ import { type GitHubViewerAccess, IMMEDIATELY_MERGEABLE, } from "./gitHubPullRequestJson.ts"; -import type { - ProviderChangeRequestSummary, - ProviderListCursor, +import { + gitHubActionsJobId, + tailLogLines, + type ProviderChangeRequestSummary, + type ProviderCheckLog, + type ProviderCheckReference, + type ProviderCheckRerun, + type ProviderListCursor, } from "@t3tools/source-control-core/server/PullRequestProvider"; export class GitHubPullRequestReadError extends Schema.TaggedError()( @@ -715,6 +720,17 @@ export class GitHubPullRequestApi extends Context.Service< readonly applied: boolean; }) => Effect.Effect; + readonly readCheckLogs: (input: { + readonly host: string; + readonly repository: string; + readonly checks: ReadonlyArray; + readonly tailLines: number; + }) => Effect.Effect>; + readonly rerunChecks: (input: { + readonly host: string; + readonly repository: string; + readonly checks: ReadonlyArray; + }) => Effect.Effect>; readonly runPullRequestAction: (input: { readonly cwd: string; readonly repository: string; @@ -795,6 +811,19 @@ export class GitHubPullRequestApi extends Context.Service< } >()("@t3tools/source-control-github/server/GitHubPullRequestApi") {} +const CHECK_LOG_MAX_BYTES = 16 * 1024 * 1024; +const NOT_A_JOB = "This check is not a GitHub Actions job, so GitHub cannot re-run it."; +const decodeActionsJob = Schema.decodeEffect( + Schema.fromJsonString(Schema.Struct({ run_id: Schema.Number })), +); + +interface LocatedCheckJob { + readonly check: ProviderCheckReference; + readonly jobId: string | null; + readonly runId: number | null; + readonly detail: string | null; +} + function parseRepositorySelector(value: string): { readonly owner: string; readonly name: string; @@ -2782,6 +2811,98 @@ export const make = Effect.gen(function* () { ); }), + readCheckLogs: Effect.fn("GitHubPullRequestApi.readCheckLogs")(function* (input) { + const { owner, name } = parseRepositorySelector(input.repository); + return yield* Effect.forEach( + input.checks, + (check) => { + const jobId = gitHubActionsJobId(check.url); + if (jobId === null) { + return Effect.succeed({ + ...check, + log: null, + truncated: false, + detail: "This check is not a GitHub Actions job, so GitHub holds no log for it.", + }); + } + // The log is a redirect to storage; fetch drops the credential when it leaves GitHub. + return api + .rest({ + host: input.host, + operation: "readCheckLog", + path: `repos/${owner}/${name}/actions/jobs/${jobId}/logs`, + maxResponseBytes: CHECK_LOG_MAX_BYTES, + timeout: "60 seconds", + }) + .pipe( + Effect.map((response) => ({ + ...check, + ...tailLogLines(response.body, input.tailLines), + detail: response.truncated ? "The log is too large; this is from its start." : null, + })), + Effect.catch((error) => + Effect.succeed({ ...check, log: null, truncated: false, detail: error.message }), + ), + ); + }, + { concurrency: 4 }, + ); + }), + + rerunChecks: Effect.fn("GitHubPullRequestApi.rerunChecks")(function* (input) { + const { owner, name } = parseRepositorySelector(input.repository); + const root = `repos/${owner}/${name}/actions`; + const located = yield* Effect.forEach( + input.checks, + (check): Effect.Effect => { + const jobId = gitHubActionsJobId(check.url); + if (jobId === null) + return Effect.succeed({ check, jobId, runId: null, detail: NOT_A_JOB }); + return api + .rest({ host: input.host, operation: "readCheckJob", path: `${root}/jobs/${jobId}` }) + .pipe( + Effect.flatMap((response) => decodeActionsJob(response.body)), + Effect.map((job) => ({ check, jobId, runId: job.run_id, detail: null })), + Effect.catch((error) => + Effect.succeed({ check, jobId, runId: null, detail: error.message }), + ), + ); + }, + { concurrency: 4 }, + ); + // GitHub refuses a second job re-run while the run re-runs, so a run's failed jobs go together. + const byRun = new Map>(); + for (const entry of located) { + if (entry.runId === null) continue; + byRun.set(entry.runId, [...(byRun.get(entry.runId) ?? []), entry]); + } + const outcomes = new Map(); + yield* Effect.forEach( + byRun, + ([runId, jobs]) => + api + .rest({ + host: input.host, + operation: "rerunChecks", + method: "POST", + path: + jobs.length === 1 + ? `${root}/jobs/${jobs[0]!.jobId}/rerun` + : `${root}/runs/${runId}/rerun-failed-jobs`, + }) + .pipe( + Effect.as(null), + Effect.catch((error) => Effect.succeed(error.message)), + Effect.map((detail) => outcomes.set(runId, detail)), + ), + { concurrency: 2, discard: true }, + ); + return located.map(({ check, runId, detail }) => { + const outcome = runId === null ? detail : (outcomes.get(runId) ?? null); + return { name: check.name, rerun: runId !== null && outcome === null, detail: outcome }; + }); + }), + submitReview: Effect.fn("GitHubPullRequestApi.submitReview")(function (input) { const { owner, name } = parseRepositorySelector(input.repository); return api diff --git a/packages/source-control-github/src/server/GitHubPullRequestProvider.ts b/packages/source-control-github/src/server/GitHubPullRequestProvider.ts index 066b3ee7f8c64..07ef69dee52b5 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestProvider.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestProvider.ts @@ -740,6 +740,9 @@ export const make = Effect.gen(function* () { }) .pipe(Effect.mapError(fail("runAction"))), + getCheckLogs: (input) => cli.readCheckLogs(input), + rerunChecks: (input) => cli.rerunChecks(input), + updateChangeRequest: (input) => cli .updatePullRequest({ From fdd6a183cdedcc65c726c3b3a7aabd769a1cb014 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 00:06:05 -0800 Subject: [PATCH 02/16] fix(server): CI tools keep sign-in failures, refresh checks after a rerun, and count reruns - A check log or rerun fails the call on sign-in and rate-limit errors instead of reporting them per check. - A byte-capped GitHub log reports itself truncated. - A rerun refreshes cached checks and notifies readers, like other PR mutations. - The rerun summary counts the checks the host restarted. Co-Authored-By: Claude Opus 5.5 --- .../pullRequest/PullRequestService.test.ts | 34 +++++++++++++ .../src/pullRequest/PullRequestService.ts | 10 +++- .../client-runtime/src/t3ToolSummary.test.ts | 20 ++++++++ packages/client-runtime/src/t3ToolSummary.ts | 24 ++++++++- .../server/GitCafePullRequestProvider.test.ts | 14 +++++- .../src/server/GitCafePullRequestProvider.ts | 12 +++-- .../src/server/GitHubPullRequestApi.test.ts | 15 ++++++ .../src/server/GitHubPullRequestApi.ts | 49 +++++++++++++------ .../src/server/GitHubPullRequestProvider.ts | 4 +- 9 files changed, 155 insertions(+), 27 deletions(-) diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index 2b0f288042580..a6add498925f1 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -1476,6 +1476,40 @@ it.effect( }), ); +it.effect("re-reads checks after a rerun instead of serving the failed ones", () => + Effect.gen(function* () { + let rerun = false; + const service = yield* makeService({ + projects: [project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" })], + providers: [ + fakePullRequestProvider(SourceControlProviderKind.make("github"), { + getChangeRequestChecks: () => + Effect.succeed({ + state: "open" as const, + checks: [ + { + name: "Test", + status: rerun ? ("pending" as const) : ("failure" as const), + description: null, + url: null, + }, + ], + }), + rerunChecks: (input) => + Effect.sync(() => { + rerun = true; + return input.checks.map((check) => ({ ...check, rerun: true, detail: null })); + }), + }), + ], + }); + const reference = { projectId: "p1" as ProjectId, repository: "acme/web", number: 1 }; + assert.equal((yield* service.checks(reference))?.checks[0]?.status, "failure"); + yield* service.rerunChecks(reference); + assert.equal((yield* service.checks(reference))?.checks[0]?.status, "pending"); + }), +); + it.effect("refuses a merge whose head moved past expectedHeadSha and pins the host merge", () => Effect.gen(function* () { const pinned: Array = []; diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index 3c70a24577523..f6e11b5e68737 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -3826,13 +3826,19 @@ export const make = Effect.gen(function* () { } const checks = yield* selectChecks(project, input, "rerunChecks"); if (checks.length === 0) return []; + const ref = yield* canonicalRef(input); const outcomes = yield* rerun({ cwd: project.project.workspaceRoot, repository: project.repository, host: project.host, checks, - }).pipe(Effect.mapError(toPullRequestError("rerunChecks"))); - yield* readCache.invalidate(refScope(yield* canonicalRef(input))); + }).pipe( + Effect.mapError(toPullRequestError("rerunChecks")), + Effect.ensuring(readCache.invalidate(refScope(ref))), + ); + bumpRefEpoch(ref); + listingsEpoch = ++epochCounter; + yield* SubscriptionRef.set(pullRequestRefreshes, listingsEpoch); return outcomes; }); diff --git a/packages/client-runtime/src/t3ToolSummary.test.ts b/packages/client-runtime/src/t3ToolSummary.test.ts index 3800c1cfac58f..e350e530a332d 100644 --- a/packages/client-runtime/src/t3ToolSummary.test.ts +++ b/packages/client-runtime/src/t3ToolSummary.test.ts @@ -76,6 +76,26 @@ describe("summarizeT3ToolCalls", () => { ).toBe("Merged 1 pull request, queued 1"); }); + it("counts the checks a CI rerun restarted, and the ones the host refused", () => { + const rerun = (...outcomes: boolean[]) => + completed( + {}, + { checks: outcomes.map((outcome, index) => ({ name: `c${index}`, rerun: outcome })) }, + ); + expect(summarizeT3ToolCalls("ci-rerun", [rerun(true, true)])).toEqual({ + label: "Re-ran 2 checks", + failedCount: 0, + }); + expect(summarizeT3ToolCalls("ci-rerun", [rerun(true, false)])).toEqual({ + label: "Re-ran 1 check, 1 refused", + failedCount: 1, + }); + expect(summarizeT3ToolCalls("ci-rerun", [rerun(false)])).toEqual({ + label: "Could not re-run 1 check", + failedCount: 1, + }); + }); + it("excludes failed and unfinished sends even if the provider reports completed", () => { const calls: T3ToolSummaryCall[] = [ completed({ threadId: "success" }, { messageId: "ok", threadId: "success" }), diff --git a/packages/client-runtime/src/t3ToolSummary.ts b/packages/client-runtime/src/t3ToolSummary.ts index 5dfecfcd84772..d10c23bde649e 100644 --- a/packages/client-runtime/src/t3ToolSummary.ts +++ b/packages/client-runtime/src/t3ToolSummary.ts @@ -507,9 +507,29 @@ export function summarizeT3ToolCalls( case "ci-logs": label = phrase("Read", "read", `CI logs ${times}`); break; - case "ci-rerun": - label = phrase("Re-ran", "re-run", `CI ${times}`); + case "ci-rerun": { + // A host can refuse a check it cannot re-run, such as one outside its CI. + const outcomes = selected.flatMap((call) => + Array.isArray(call.output?.checks) + ? call.output.checks.map((check) => asRecord(check)?.rerun === true) + : [], + ); + const rerun = outcomes.filter(Boolean).length; + const refused = outcomes.length - rerun; + failedCount += refused; + if (outcomes.length === 0) { + label = phrase("Re-ran", "re-run", `CI ${times}`); + } else if (rerun === 0) { + label = `Could not re-run ${quantity(refused, "check")}`; + } else { + label = phrase( + "Re-ran", + "re-run", + `${quantity(rerun, "check")}${refused > 0 ? `, ${refused} refused` : ""}`, + ); + } break; + } case "browser": label = phrase("Used", "use", `browser ${times}`); break; diff --git a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts index 1b8b9a2d36fd0..bc495f16c2cd1 100644 --- a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts +++ b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.test.ts @@ -684,13 +684,25 @@ describe("GitCafePullRequestProvider", () => { truncated: true, }, "POST /repos/owner/repo/actions/runs/actwfrun_abc/rerun": {}, + "GET /repos/owner/repo/ci/github/checks/401/log": new Response("{}", { status: 401 }), }); const mirrored = { name: "Test", url: "https://github.com/owner/repo/runs/555" }; + const missing = { name: "Gone", url: "https://github.com/owner/repo/runs/404" }; + const signedOut = { name: "Lint", url: "https://github.com/owner/repo/runs/401" }; const native = { name: "Build", url: "https://git.cafe/owner/repo/actions/runs/actwfrun_abc" }; return Effect.gen(function* () { const provider = yield* GitCafePullRequestProvider.make; - const logs = yield* provider.getCheckLogs!({ ...target, checks: [mirrored], tailLines: 1 }); + const logs = yield* provider.getCheckLogs!({ + ...target, + checks: [mirrored, missing], + tailLines: 1, + }); assert.deepInclude(logs[0], { log: "FAIL a.test.ts", truncated: true, detail: null }); + assert.isNotNull(logs[1]?.detail); + const refused = yield* Effect.flip( + provider.getCheckLogs!({ ...target, checks: [signedOut], tailLines: 1 }), + ); + assert.strictEqual(refused.reason, "unauthenticated"); const reruns = yield* provider.rerunChecks!({ ...target, checks: [mirrored, native] }); assert.deepStrictEqual( reruns.map(({ name, rerun }) => [name, rerun]), diff --git a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts index b327c8f29c661..fca4c1247e5dc 100644 --- a/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts +++ b/packages/source-control-gitcafe/src/server/GitCafePullRequestProvider.ts @@ -458,6 +458,9 @@ export const make = Effect.gen(function* () { ), ); + const isCheckFailure = (error: PullRequestProviderError) => + error.reason === "not-found" || error.reason === "forbidden" || error.reason === "failed"; + const isForbidden = (error: PullRequestProviderError) => isApiError(error.cause) && error.cause.status === 403; @@ -1140,7 +1143,7 @@ export const make = Effect.gen(function* () { const base = yield* repositoryPath("getCheckLogs", input); return yield* Effect.forEach( input.checks, - (check): Effect.Effect => { + (check): Effect.Effect => { // GitCafe reads the log of a check its GitHub mirror ran. const checkId = gitHubActionsJobId(check.url); if (checkId === null) { @@ -1166,7 +1169,8 @@ export const make = Effect.gen(function* () { detail: null, }; }), - Effect.catch((error) => + // Sign-in and rate-limit failures fail the call; the rest are this check's. + Effect.catchIf(isCheckFailure, (error) => Effect.succeed({ ...check, log: null, truncated: false, detail: error.detail }), ), ); @@ -1190,8 +1194,8 @@ export const make = Effect.gen(function* () { method: "POST", path: `${base}/actions/runs/${runId}/rerun`, }).pipe( - Effect.as(null), - Effect.catch((error) => Effect.succeed(error.detail)), + Effect.as(null), + Effect.catchIf(isCheckFailure, (error) => Effect.succeed(error.detail)), Effect.map((detail) => outcomes.set(runId, detail)), ), { concurrency: 2, discard: true }, diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts index 07335857c8c4f..b091d40b6f067 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts @@ -802,6 +802,15 @@ layer("GitHubPullRequestApi.layer", (it) => { mockedExecute.mockImplementation((call) => { if (call.kind !== "rest") return Effect.succeed(output("{}")); const log = /actions\/jobs\/(\d+)\/logs$/.exec(call.path); + if (log?.[1] === "13") return Effect.succeed(output("one long line", true)); + if (log?.[1] === "14") { + return Effect.fail( + new GitHubRequestPolicy.GitHubApiAuthenticationError({ + host: "github.com", + operation: "readCheckLog", + }), + ); + } if (log) return Effect.succeed(output("setup\nbuild\nError: boom\n")); const job = /actions\/jobs\/(\d+)$/.exec(call.path); if (job) return Effect.succeed(output(encodeJson({ run_id: runOf[job[1]!] }))); @@ -823,6 +832,12 @@ layer("GitHubPullRequestApi.layer", (it) => { { name: "job 11", log: "build\nError: boom", truncated: true, detail: null }, { name: "Vercel", log: null }, ]); + const capped = { host: "github.com", repository: "octo/repo", tailLines: 5 }; + expect(yield* api.readCheckLogs({ ...capped, checks: [job(13, 30)] })).toMatchObject([ + { log: "one long line", truncated: true }, + ]); + const refused = yield* Effect.flip(api.readCheckLogs({ ...capped, checks: [job(14, 30)] })); + expect(refused._tag).toBe("GitHubApiAuthenticationError"); const reruns = yield* api.rerunChecks({ host: "github.com", diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.ts index caacf9e9c94a3..f43d0c76746b9 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.ts @@ -725,12 +725,12 @@ export class GitHubPullRequestApi extends Context.Service< readonly repository: string; readonly checks: ReadonlyArray; readonly tailLines: number; - }) => Effect.Effect>; + }) => Effect.Effect, GitHubPullRequestApiError>; readonly rerunChecks: (input: { readonly host: string; readonly repository: string; readonly checks: ReadonlyArray; - }) => Effect.Effect>; + }) => Effect.Effect, GitHubPullRequestApiError>; readonly runPullRequestAction: (input: { readonly cwd: string; readonly repository: string; @@ -2835,14 +2835,24 @@ export const make = Effect.gen(function* () { timeout: "60 seconds", }) .pipe( - Effect.map((response) => ({ - ...check, - ...tailLogLines(response.body, input.tailLines), - detail: response.truncated ? "The log is too large; this is from its start." : null, - })), - Effect.catch((error) => - Effect.succeed({ ...check, log: null, truncated: false, detail: error.message }), - ), + Effect.map((response) => { + const tail = tailLogLines(response.body, input.tailLines); + return { + ...check, + log: tail.log, + truncated: tail.truncated || response.truncated, + detail: response.truncated + ? "The log is too large; this is from its start." + : null, + }; + }), + // Sign-in and rate-limit failures fail the call; the rest are this check's. + Effect.catchTags({ + GitHubApiNotFoundError: (error) => + Effect.succeed({ ...check, log: null, truncated: false, detail: error.message }), + GitHubApiResponseError: (error) => + Effect.succeed({ ...check, log: null, truncated: false, detail: error.message }), + }), ); }, { concurrency: 4 }, @@ -2854,7 +2864,7 @@ export const make = Effect.gen(function* () { const root = `repos/${owner}/${name}/actions`; const located = yield* Effect.forEach( input.checks, - (check): Effect.Effect => { + (check): Effect.Effect => { const jobId = gitHubActionsJobId(check.url); if (jobId === null) return Effect.succeed({ check, jobId, runId: null, detail: NOT_A_JOB }); @@ -2863,9 +2873,13 @@ export const make = Effect.gen(function* () { .pipe( Effect.flatMap((response) => decodeActionsJob(response.body)), Effect.map((job) => ({ check, jobId, runId: job.run_id, detail: null })), - Effect.catch((error) => - Effect.succeed({ check, jobId, runId: null, detail: error.message }), - ), + Effect.catchTags({ + GitHubApiNotFoundError: (error) => + Effect.succeed({ check, jobId, runId: null, detail: error.message }), + GitHubApiResponseError: (error) => + Effect.succeed({ check, jobId, runId: null, detail: error.message }), + SchemaError: () => Effect.succeed({ check, jobId, runId: null, detail: NOT_A_JOB }), + }), ); }, { concurrency: 4 }, @@ -2891,8 +2905,11 @@ export const make = Effect.gen(function* () { : `${root}/runs/${runId}/rerun-failed-jobs`, }) .pipe( - Effect.as(null), - Effect.catch((error) => Effect.succeed(error.message)), + Effect.as(null), + Effect.catchTags({ + GitHubApiNotFoundError: (error) => Effect.succeed(error.message), + GitHubApiResponseError: (error) => Effect.succeed(error.message), + }), Effect.map((detail) => outcomes.set(runId, detail)), ), { concurrency: 2, discard: true }, diff --git a/packages/source-control-github/src/server/GitHubPullRequestProvider.ts b/packages/source-control-github/src/server/GitHubPullRequestProvider.ts index 07ef69dee52b5..b29a633574a9f 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestProvider.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestProvider.ts @@ -740,8 +740,8 @@ export const make = Effect.gen(function* () { }) .pipe(Effect.mapError(fail("runAction"))), - getCheckLogs: (input) => cli.readCheckLogs(input), - rerunChecks: (input) => cli.rerunChecks(input), + getCheckLogs: (input) => cli.readCheckLogs(input).pipe(Effect.mapError(fail("getCheckLogs"))), + rerunChecks: (input) => cli.rerunChecks(input).pipe(Effect.mapError(fail("rerunChecks"))), updateChangeRequest: (input) => cli From 51308b783e20ceba70a84d6356a9e6453281463a Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 01:04:38 -0800 Subject: [PATCH 03/16] fix(server): CI reruns cover only the selected jobs and logs stay bounded - A GitHub run's failed jobs re-run together only when they are exactly the selection. - Job links of the form /runs/{run}/jobs/{job} resolve to the job. - A log tail is capped at 32,000 characters, so one long line cannot flood the context. - A rerun that fails after restarting a run still refreshes readers. - A rerun with nothing failed reads as such, and the eval runner selects checks like the service. Co-Authored-By: Claude Opus 5.5 --- .../pullRequest/PullRequestService.test.ts | 73 +++++++++------- .../src/pullRequest/PullRequestService.ts | 19 +++-- evals/mcp-tool-use/run.test.ts | 29 +++++++ evals/mcp-tool-use/run.ts | 6 +- .../client-runtime/src/t3ToolSummary.test.ts | 3 + packages/client-runtime/src/t3ToolSummary.ts | 5 +- .../src/server/PullRequestProvider.ts | 12 ++- .../src/server/GitHubPullRequestApi.test.ts | 25 +++++- .../src/server/GitHubPullRequestApi.ts | 84 ++++++++++++++----- 9 files changed, 189 insertions(+), 67 deletions(-) diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index a6add498925f1..5415006522a46 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -1476,38 +1476,47 @@ it.effect( }), ); -it.effect("re-reads checks after a rerun instead of serving the failed ones", () => - Effect.gen(function* () { - let rerun = false; - const service = yield* makeService({ - projects: [project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" })], - providers: [ - fakePullRequestProvider(SourceControlProviderKind.make("github"), { - getChangeRequestChecks: () => - Effect.succeed({ - state: "open" as const, - checks: [ - { - name: "Test", - status: rerun ? ("pending" as const) : ("failure" as const), - description: null, - url: null, - }, - ], - }), - rerunChecks: (input) => - Effect.sync(() => { - rerun = true; - return input.checks.map((check) => ({ ...check, rerun: true, detail: null })); - }), - }), - ], - }); - const reference = { projectId: "p1" as ProjectId, repository: "acme/web", number: 1 }; - assert.equal((yield* service.checks(reference))?.checks[0]?.status, "failure"); - yield* service.rerunChecks(reference); - assert.equal((yield* service.checks(reference))?.checks[0]?.status, "pending"); - }), +it.effect.each([false, true])( + "re-reads checks after a rerun instead of serving the failed ones (rerun fails: %s)", + (failAfterRerun) => + Effect.gen(function* () { + let rerun = false; + const service = yield* makeService({ + projects: [ + project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" }), + ], + providers: [ + fakePullRequestProvider(SourceControlProviderKind.make("github"), { + getChangeRequestChecks: () => + Effect.succeed({ + state: "open" as const, + checks: [ + { + name: "Test", + status: rerun ? ("pending" as const) : ("failure" as const), + description: null, + url: null, + }, + ], + }), + // A failure can come after one run already restarted. + rerunChecks: (input) => + Effect.suspend(() => { + rerun = true; + return failAfterRerun + ? Effect.fail(requestFailed) + : Effect.succeed( + input.checks.map((check) => ({ ...check, rerun: true, detail: null })), + ); + }), + }), + ], + }); + const reference = { projectId: "p1" as ProjectId, repository: "acme/web", number: 1 }; + assert.equal((yield* service.checks(reference))?.checks[0]?.status, "failure"); + yield* service.rerunChecks(reference).pipe(Effect.ignore); + assert.equal((yield* service.checks(reference))?.checks[0]?.status, "pending"); + }), ); it.effect("refuses a merge whose head moved past expectedHeadSha and pins the host merge", () => diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index f6e11b5e68737..4ede6e3d03a19 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -3827,19 +3827,26 @@ export const make = Effect.gen(function* () { const checks = yield* selectChecks(project, input, "rerunChecks"); if (checks.length === 0) return []; const ref = yield* canonicalRef(input); - const outcomes = yield* rerun({ + // A failure can follow a run that already restarted, so readers refresh either way. + return yield* rerun({ cwd: project.project.workspaceRoot, repository: project.repository, host: project.host, checks, }).pipe( Effect.mapError(toPullRequestError("rerunChecks")), - Effect.ensuring(readCache.invalidate(refScope(ref))), + Effect.ensuring( + readCache.invalidate(refScope(ref)).pipe( + Effect.andThen( + Effect.suspend(() => { + bumpRefEpoch(ref); + listingsEpoch = ++epochCounter; + return SubscriptionRef.set(pullRequestRefreshes, listingsEpoch); + }), + ), + ), + ), ); - bumpRefEpoch(ref); - listingsEpoch = ++epochCounter; - yield* SubscriptionRef.set(pullRequestRefreshes, listingsEpoch); - return outcomes; }); const runActionAndInvalidate: PullRequestService["Service"]["runAction"] = Effect.fn( diff --git a/evals/mcp-tool-use/run.test.ts b/evals/mcp-tool-use/run.test.ts index 6e189f40d732f..92e6e1a876fc8 100644 --- a/evals/mcp-tool-use/run.test.ts +++ b/evals/mcp-tool-use/run.test.ts @@ -815,6 +815,35 @@ it("lists and reads world PRs in their own state, with the diff when asked", () [5, "merged"], [6, "open"], ]); + const ciSample = makeSample( + { + ...childCase, + setup: { + ...childCase.setup, + world: { + ...childCase.setup.world, + prs: [ + { + number: 7, + checks: [ + { name: "Test", state: "pass" }, + { name: "Lint", state: "cancelled" }, + ], + }, + ], + }, + }, + }, + base, + ); + const ciNames = (input: Record) => + ( + t3Default(ciSample, "ci_logs", { number: 7, ...input }) as { + checks: Array<{ name: string }>; + } + ).checks.map((check) => check.name); + assert.deepEqual(ciNames({ check: " test " }), ["Test"]); + assert.deepEqual(ciNames({}), ["Lint"]); const read: Readonly> = t3Default(sample, "pull_request_read", { number: 5, sections: ["summary", "diff", "conversation"], diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index af1ea50cfe87a..6f24a198eee49 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -1458,9 +1458,13 @@ export const t3Default = ( const number = typeof input.number === "number" ? input.number : branchPullRequest(sample).number; const world = worldPullRequests(sample).find((pr) => Number(pr.number) === number); + // Selects as PullRequestService does: a name ignoring case, else every failed check. + const named = typeof input.check === "string" ? input.check.trim().toLowerCase() : null; const checks = (Array.isArray(world?.checks) ? world.checks.filter(isRecord) : []).filter( (check) => - typeof input.check === "string" ? check.name === input.check : check.state === "fail", + named === null + ? check.state === "fail" || check.state === "cancelled" + : valueText(check.name).toLowerCase() === named, ); return { ...pullRequest(sample.repo, { number }), diff --git a/packages/client-runtime/src/t3ToolSummary.test.ts b/packages/client-runtime/src/t3ToolSummary.test.ts index e350e530a332d..99486d1184abf 100644 --- a/packages/client-runtime/src/t3ToolSummary.test.ts +++ b/packages/client-runtime/src/t3ToolSummary.test.ts @@ -94,6 +94,9 @@ describe("summarizeT3ToolCalls", () => { label: "Could not re-run 1 check", failedCount: 1, }); + expect(summarizeT3ToolCalls("ci-rerun", [rerun()]).label).toBe( + "Found no failed checks to re-run", + ); }); it("excludes failed and unfinished sends even if the provider reports completed", () => { diff --git a/packages/client-runtime/src/t3ToolSummary.ts b/packages/client-runtime/src/t3ToolSummary.ts index d10c23bde649e..63ab355ca7121 100644 --- a/packages/client-runtime/src/t3ToolSummary.ts +++ b/packages/client-runtime/src/t3ToolSummary.ts @@ -518,7 +518,10 @@ export function summarizeT3ToolCalls( const refused = outcomes.length - rerun; failedCount += refused; if (outcomes.length === 0) { - label = phrase("Re-ran", "re-run", `CI ${times}`); + // An empty list means no check had failed, so nothing was asked of the host. + label = selected.some((call) => Array.isArray(call.output?.checks)) + ? phrase("Found", "re-run", "no failed checks to re-run") + : phrase("Re-ran", "re-run", `CI ${times}`); } else if (rerun === 0) { label = `Could not re-run ${quantity(refused, "check")}`; } else { diff --git a/packages/source-control-core/src/server/PullRequestProvider.ts b/packages/source-control-core/src/server/PullRequestProvider.ts index 95c76f322f26a..afdcd199807c0 100644 --- a/packages/source-control-core/src/server/PullRequestProvider.ts +++ b/packages/source-control-core/src/server/PullRequestProvider.ts @@ -215,21 +215,25 @@ export interface ProviderCheckRerun { export function gitHubActionsJobId(url: string | null): string | null { if (url === null) return null; return ( - /\/actions\/runs\/\d+\/job\/(\d+)(?:[/?#]|$)/.exec(url)?.[1] ?? + /\/runs\/\d+\/jobs?\/(\d+)(?:[/?#]|$)/.exec(url)?.[1] ?? /^https:\/\/[^/]+\/[^/]+\/[^/]+\/runs\/(\d+)(?:[/?#]|$)/.exec(url)?.[1] ?? null ); } +/** Keeps one minified or progress-bar line from filling the agent's context. */ +const LOG_TAIL_MAX_CHARS = 32_000; + /** The last `lines` lines of a log, where a failure usually is. */ export function tailLogLines( text: string, lines: number, ): { readonly log: string; readonly truncated: boolean } { const all = text.replace(/\n$/, "").split("\n"); - return all.length <= lines - ? { log: all.join("\n"), truncated: false } - : { log: all.slice(-lines).join("\n"), truncated: true }; + const log = all.slice(-lines).join("\n"); + return log.length > LOG_TAIL_MAX_CHARS + ? { log: log.slice(-LOG_TAIL_MAX_CHARS), truncated: true } + : { log, truncated: all.length > lines }; } export interface ProviderChangeRequestStat { diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts index b091d40b6f067..84670bca9dcec 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts @@ -798,11 +798,24 @@ layer("GitHubPullRequestApi.layer", (it) => { it.effect("tails Actions job logs and re-runs a run's failed jobs together", () => Effect.gen(function* () { - const runOf: Record = { "11": 10, "12": 10, "21": 20 }; + const runOf: Record = { "11": 10, "12": 10, "21": 20, "41": 40, "42": 40 }; + const runJobs: Record> = { + "10": [ + { id: 11, conclusion: "failure" }, + { id: 12, conclusion: "cancelled" }, + ], + // 41 passed and is asked for by name; 43 failed but was not selected. + "40": [ + { id: 41, conclusion: "success" }, + { id: 42, conclusion: "failure" }, + { id: 43, conclusion: "failure" }, + ], + }; mockedExecute.mockImplementation((call) => { if (call.kind !== "rest") return Effect.succeed(output("{}")); const log = /actions\/jobs\/(\d+)\/logs$/.exec(call.path); if (log?.[1] === "13") return Effect.succeed(output("one long line", true)); + if (log?.[1] === "15") return Effect.succeed(output("x".repeat(100_000))); if (log?.[1] === "14") { return Effect.fail( new GitHubRequestPolicy.GitHubApiAuthenticationError({ @@ -814,6 +827,8 @@ layer("GitHubPullRequestApi.layer", (it) => { if (log) return Effect.succeed(output("setup\nbuild\nError: boom\n")); const job = /actions\/jobs\/(\d+)$/.exec(call.path); if (job) return Effect.succeed(output(encodeJson({ run_id: runOf[job[1]!] }))); + const run = /actions\/runs\/(\d+)\/jobs\?/.exec(call.path); + if (run) return Effect.succeed(output(encodeJson({ jobs: runJobs[run[1]!] ?? [] }))); return Effect.succeed(output("{}")); }); const api = yield* GitHubPullRequestApi.GitHubPullRequestApi; @@ -837,6 +852,9 @@ layer("GitHubPullRequestApi.layer", (it) => { { log: "one long line", truncated: true }, ]); const refused = yield* Effect.flip(api.readCheckLogs({ ...capped, checks: [job(14, 30)] })); + const [huge] = yield* api.readCheckLogs({ ...capped, checks: [job(15, 30)] }); + expect(huge?.log?.length).toBe(32_000); + expect(huge?.truncated).toBe(true); expect(refused._tag).toBe("GitHubApiAuthenticationError"); const reruns = yield* api.rerunChecks({ @@ -847,6 +865,8 @@ layer("GitHubPullRequestApi.layer", (it) => { job(12, 10), { name: "solo", url: "https://github.com/octo/repo/runs/21" }, external, + job(41, 40), + { name: "job 42", url: "https://github.com/octo/repo/actions/runs/40/jobs/42" }, ], }); expect(reruns.map(({ name, rerun }) => [name, rerun])).toEqual([ @@ -854,6 +874,8 @@ layer("GitHubPullRequestApi.layer", (it) => { ["job 12", true], ["solo", true], ["Vercel", false], + ["job 41", true], + ["job 42", false], ]); expect( mockedExecute.mock.calls.flatMap(([call]) => @@ -862,6 +884,7 @@ layer("GitHubPullRequestApi.layer", (it) => { ).toEqual([ "repos/octo/repo/actions/runs/10/rerun-failed-jobs", "repos/octo/repo/actions/jobs/21/rerun", + "repos/octo/repo/actions/jobs/41/rerun", ]); }), ); diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.ts index f43d0c76746b9..3361c91145092 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.ts @@ -816,6 +816,18 @@ const NOT_A_JOB = "This check is not a GitHub Actions job, so GitHub cannot re-r const decodeActionsJob = Schema.decodeEffect( Schema.fromJsonString(Schema.Struct({ run_id: Schema.Number })), ); +const decodeRunJobs = Schema.decodeEffect( + Schema.fromJsonString( + Schema.Struct({ + jobs: Schema.Array( + Schema.Struct({ id: Schema.Number, conclusion: Schema.NullOr(Schema.String) }), + ), + }), + ), +); +const FAILED_CONCLUSIONS = new Set(["failure", "cancelled", "timed_out", "startup_failure"]); +const ONE_JOB_AT_A_TIME = + "GitHub re-runs one job of a run at a time. Re-run this one after the other finishes."; interface LocatedCheckJob { readonly check: ProviderCheckReference; @@ -2884,38 +2896,66 @@ export const make = Effect.gen(function* () { }, { concurrency: 4 }, ); - // GitHub refuses a second job re-run while the run re-runs, so a run's failed jobs go together. + // GitHub refuses a second job re-run while the run re-runs, so a run's jobs go together. const byRun = new Map>(); for (const entry of located) { if (entry.runId === null) continue; byRun.set(entry.runId, [...(byRun.get(entry.runId) ?? []), entry]); } - const outcomes = new Map(); + const post = (path: string) => + api.rest({ host: input.host, operation: "rerunChecks", method: "POST", path }).pipe( + Effect.as(null), + Effect.catchTags({ + GitHubApiNotFoundError: (error) => Effect.succeed(error.message), + GitHubApiResponseError: (error) => Effect.succeed(error.message), + }), + ); + const failedJobsOf = (runId: number) => + api + .rest({ + host: input.host, + operation: "readRunJobs", + path: `${root}/runs/${runId}/jobs?filter=latest&per_page=100`, + }) + .pipe( + Effect.flatMap((response) => decodeRunJobs(response.body)), + Effect.map( + ({ jobs }) => + new Set( + jobs + .filter((job) => FAILED_CONCLUSIONS.has(job.conclusion ?? "")) + .map((job) => String(job.id)), + ), + ), + Effect.catchTags({ + GitHubApiNotFoundError: () => Effect.succeed(new Set()), + GitHubApiResponseError: () => Effect.succeed(new Set()), + SchemaError: () => Effect.succeed(new Set()), + }), + ); + const outcomes = new Map(); yield* Effect.forEach( byRun, ([runId, jobs]) => - api - .rest({ - host: input.host, - operation: "rerunChecks", - method: "POST", - path: - jobs.length === 1 - ? `${root}/jobs/${jobs[0]!.jobId}/rerun` - : `${root}/runs/${runId}/rerun-failed-jobs`, - }) - .pipe( - Effect.as(null), - Effect.catchTags({ - GitHubApiNotFoundError: (error) => Effect.succeed(error.message), - GitHubApiResponseError: (error) => Effect.succeed(error.message), - }), - Effect.map((detail) => outcomes.set(runId, detail)), - ), + Effect.gen(function* () { + const [first, ...rest] = jobs; + if (first === undefined) return; + if (rest.length > 0) { + // Re-running a run's failed jobs fits only when they are exactly the selection. + const failed = yield* failedJobsOf(runId); + if (failed.size === jobs.length && jobs.every((job) => failed.has(job.jobId ?? ""))) { + const detail = yield* post(`${root}/runs/${runId}/rerun-failed-jobs`); + for (const job of jobs) outcomes.set(job.jobId, detail); + return; + } + } + outcomes.set(first.jobId, yield* post(`${root}/jobs/${first.jobId}/rerun`)); + for (const job of rest) outcomes.set(job.jobId, ONE_JOB_AT_A_TIME); + }), { concurrency: 2, discard: true }, ); - return located.map(({ check, runId, detail }) => { - const outcome = runId === null ? detail : (outcomes.get(runId) ?? null); + return located.map(({ check, jobId, runId, detail }) => { + const outcome = runId === null ? detail : (outcomes.get(jobId) ?? null); return { name: check.name, rerun: runId !== null && outcome === null, detail: outcome }; }); }), From e802bf83afd1aa073cec1e8375a497d0d96f32dc Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 01:12:28 -0800 Subject: [PATCH 04/16] fix(server): a GitHub run with more jobs than one page re-runs one job at a time The eval runner also refuses a check name that matches nothing, as the service does. Co-Authored-By: Claude Opus 5.5 --- evals/mcp-tool-use/run.test.ts | 16 +++++++++ evals/mcp-tool-use/run.ts | 33 ++++++++++++------- .../src/server/GitHubPullRequestApi.test.ts | 26 +++++++++++++-- .../src/server/GitHubPullRequestApi.ts | 17 ++++++---- 4 files changed, 72 insertions(+), 20 deletions(-) diff --git a/evals/mcp-tool-use/run.test.ts b/evals/mcp-tool-use/run.test.ts index 92e6e1a876fc8..f94b6c964c85c 100644 --- a/evals/mcp-tool-use/run.test.ts +++ b/evals/mcp-tool-use/run.test.ts @@ -1156,6 +1156,22 @@ it.effect("reads the branch's PR only once the agent has opened it", () => input: {}, }); assert.isTrue(linkedRead.is_error); + + // A check name that matches nothing is refused, as the service refuses it. + const ciWorld = { ...world, prs: [{ number: 7, checks: [{ name: "Test", state: "fail" }] }] }; + const ciSample = makeSample( + { ...childCase, setup: { ...childCase.setup, world: ciWorld } }, + base, + ); + ciSample.tools.push({ name: "mcp__t3-code__ci_logs" }); + const unknown = yield* answerToolCall(ciSample, config, { + type: "tool_use", + id: "g", + name: "mcp__t3-code__ci_logs", + input: { number: 7, check: "Tests" }, + }); + assert.isTrue(unknown.is_error); + assert.include(String(unknown.content), "Checks: Test."); }).pipe(Effect.provide(FetchHttpClient.layer)), ); diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index 6f24a198eee49..6682320d10f7b 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -1235,6 +1235,20 @@ const branchPullRequest = (sample: Sample) => { return { number, created: true, pr: undefined }; }; +/** The checks `ci_logs` and `ci_rerun` act on, selected as PullRequestService does. */ +const selectedWorldChecks = (sample: Sample, input: Readonly>) => { + const number = typeof input.number === "number" ? input.number : branchPullRequest(sample).number; + const world = worldPullRequests(sample).find((pr) => Number(pr.number) === number); + const all = Array.isArray(world?.checks) ? world.checks.filter(isRecord) : []; + const named = typeof input.check === "string" ? input.check.trim().toLowerCase() : null; + const checks = all.filter((check) => + named === null + ? check.state === "fail" || check.state === "cancelled" + : valueText(check.name).toLowerCase() === named, + ); + return { number, all, checks, named }; +}; + /** A believable T3 result for a tool the case does not mock. */ export const t3Default = ( sample: Sample, @@ -1455,17 +1469,7 @@ export const t3Default = ( }; case "ci_logs": case "ci_rerun": { - const number = - typeof input.number === "number" ? input.number : branchPullRequest(sample).number; - const world = worldPullRequests(sample).find((pr) => Number(pr.number) === number); - // Selects as PullRequestService does: a name ignoring case, else every failed check. - const named = typeof input.check === "string" ? input.check.trim().toLowerCase() : null; - const checks = (Array.isArray(world?.checks) ? world.checks.filter(isRecord) : []).filter( - (check) => - named === null - ? check.state === "fail" || check.state === "cancelled" - : valueText(check.name).toLowerCase() === named, - ); + const { number, checks } = selectedWorldChecks(sample, input); return { ...pullRequest(sample.repo, { number }), checks: checks.map((check) => @@ -1935,6 +1939,13 @@ export const answerToolCall = Effect.fn("evaluateMcpToolUse.answerToolCall")(fun if (name === "watch_pull_request" && testCase.setup.role === "delegated-child") { return failed(SUBAGENT_WATCH_ERROR); } + if (name === "ci_logs" || name === "ci_rerun") { + const { all, checks, named } = selectedWorldChecks(sample, input); + if (named !== null && checks.length === 0) { + const names = all.map((check) => valueText(check.name)).join(", ") || "none"; + return failed(`No check is named ${valueText(input.check)}. Checks: ${names}.`); + } + } if (BRANCH_PULL_REQUEST_TOOLS.has(name) && input.number === undefined) { // Until the agent opens it, the PR a create would open does not exist. if (branchPullRequest(sample).created) return failed(BRANCH_HAS_NO_PR_ERROR); diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts index 84670bca9dcec..0b7cf77e84e79 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts @@ -798,7 +798,15 @@ layer("GitHubPullRequestApi.layer", (it) => { it.effect("tails Actions job logs and re-runs a run's failed jobs together", () => Effect.gen(function* () { - const runOf: Record = { "11": 10, "12": 10, "21": 20, "41": 40, "42": 40 }; + const runOf: Record = { + "11": 10, + "12": 10, + "21": 20, + "41": 40, + "42": 40, + "51": 50, + "52": 50, + }; const runJobs: Record> = { "10": [ { id: 11, conclusion: "failure" }, @@ -810,6 +818,10 @@ layer("GitHubPullRequestApi.layer", (it) => { { id: 42, conclusion: "failure" }, { id: 43, conclusion: "failure" }, ], + "50": [ + { id: 51, conclusion: "failure" }, + { id: 52, conclusion: "failure" }, + ], }; mockedExecute.mockImplementation((call) => { if (call.kind !== "rest") return Effect.succeed(output("{}")); @@ -828,7 +840,12 @@ layer("GitHubPullRequestApi.layer", (it) => { const job = /actions\/jobs\/(\d+)$/.exec(call.path); if (job) return Effect.succeed(output(encodeJson({ run_id: runOf[job[1]!] }))); const run = /actions\/runs\/(\d+)\/jobs\?/.exec(call.path); - if (run) return Effect.succeed(output(encodeJson({ jobs: runJobs[run[1]!] ?? [] }))); + if (run) { + const jobs = runJobs[run[1]!] ?? []; + // Run 50 has more jobs than one page shows. + const total = run[1] === "50" ? 150 : jobs.length; + return Effect.succeed(output(encodeJson({ total_count: total, jobs }))); + } return Effect.succeed(output("{}")); }); const api = yield* GitHubPullRequestApi.GitHubPullRequestApi; @@ -867,6 +884,8 @@ layer("GitHubPullRequestApi.layer", (it) => { external, job(41, 40), { name: "job 42", url: "https://github.com/octo/repo/actions/runs/40/jobs/42" }, + job(51, 50), + job(52, 50), ], }); expect(reruns.map(({ name, rerun }) => [name, rerun])).toEqual([ @@ -876,6 +895,8 @@ layer("GitHubPullRequestApi.layer", (it) => { ["Vercel", false], ["job 41", true], ["job 42", false], + ["job 51", true], + ["job 52", false], ]); expect( mockedExecute.mock.calls.flatMap(([call]) => @@ -885,6 +906,7 @@ layer("GitHubPullRequestApi.layer", (it) => { "repos/octo/repo/actions/runs/10/rerun-failed-jobs", "repos/octo/repo/actions/jobs/21/rerun", "repos/octo/repo/actions/jobs/41/rerun", + "repos/octo/repo/actions/jobs/51/rerun", ]); }), ); diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.ts index 3361c91145092..23030b4c8616d 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.ts @@ -819,6 +819,7 @@ const decodeActionsJob = Schema.decodeEffect( const decodeRunJobs = Schema.decodeEffect( Schema.fromJsonString( Schema.Struct({ + total_count: Schema.Number, jobs: Schema.Array( Schema.Struct({ id: Schema.Number, conclusion: Schema.NullOr(Schema.String) }), ), @@ -2919,13 +2920,15 @@ export const make = Effect.gen(function* () { }) .pipe( Effect.flatMap((response) => decodeRunJobs(response.body)), - Effect.map( - ({ jobs }) => - new Set( - jobs - .filter((job) => FAILED_CONCLUSIONS.has(job.conclusion ?? "")) - .map((job) => String(job.id)), - ), + // Jobs past the first page are unseen, so such a run counts as unknown. + Effect.map(({ total_count, jobs }) => + total_count > jobs.length + ? new Set() + : new Set( + jobs + .filter((job) => FAILED_CONCLUSIONS.has(job.conclusion ?? "")) + .map((job) => String(job.id)), + ), ), Effect.catchTags({ GitHubApiNotFoundError: () => Effect.succeed(new Set()), From cc5d94a9cb914e5144afa6febc00400d29c2a809 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 01:59:42 -0800 Subject: [PATCH 05/16] fix(server): ci_logs reads at most five logs unless a check is named The rest come back unread with a note to ask by name, so many failing checks cannot flood the agent's context. The eval runner also refuses CI tools when the branch has no PR yet. Co-Authored-By: Claude Opus 5.5 --- .../pullRequest/PullRequestService.test.ts | 15 +++++++++++++++ .../src/pullRequest/PullRequestService.ts | 19 +++++++++++++++++-- evals/mcp-tool-use/run.test.ts | 2 ++ evals/mcp-tool-use/run.ts | 10 ++++++---- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index 5415006522a46..85a689454e4be 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -1436,6 +1436,7 @@ it.effect( () => Effect.gen(function* () { const asked: Array> = []; + let failing = 0; const check = (name: string, status: "success" | "failure" | "cancelled") => ({ name, status, @@ -1455,6 +1456,9 @@ it.effect( check("Lint", "success"), check("Test", "failure"), check("E2E", "cancelled"), + ...Array.from({ length: failing }, (_, index) => + check(`Shard ${index}`, "failure"), + ), ], }), getCheckLogs: (input) => @@ -1473,6 +1477,17 @@ it.effect( ); assert.deepStrictEqual(asked, [["Test", "E2E"], ["Lint"]]); assert.include(missing.message, "Lint, Test, E2E"); + + failing = 6; + asked.length = 0; + const many = yield* service.checkLogs({ ...reference, tailLines: 50 }); + // The host is asked for five; the other three come back unread, by name. + assert.lengthOf(asked[0] ?? [], 5); + assert.deepStrictEqual( + many.map((log) => log.name), + ["Shard 3", "Shard 4", "Shard 5"], + ); + assert.include(many[0]?.detail ?? "", "Ask for this check by name"); }), ); diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index 4ede6e3d03a19..7487006a7a540 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -3791,6 +3791,7 @@ export const make = Effect.gen(function* () { return matches.map(({ name, url }) => ({ name, url })); }); + const DEFAULT_CHECK_LOGS = 5; const checkLogs: PullRequestService["Service"]["checkLogs"] = Effect.fn( "PullRequestService.checkLogs", )(function* (input) { @@ -3804,13 +3805,27 @@ export const make = Effect.gen(function* () { } const checks = yield* selectChecks(project, input, "checkLogs"); if (checks.length === 0) return []; - return yield* read({ + // Many failing checks would flood the agent's context; the rest stay a name away. + const [shown, rest] = + input.check === undefined + ? [checks.slice(0, DEFAULT_CHECK_LOGS), checks.slice(DEFAULT_CHECK_LOGS)] + : [checks, []]; + const logs = yield* read({ cwd: project.project.workspaceRoot, repository: project.repository, host: project.host, - checks, + checks: shown, tailLines: input.tailLines, }).pipe(Effect.mapError(toPullRequestError("checkLogs"))); + return [ + ...logs, + ...rest.map((check) => ({ + ...check, + log: null, + truncated: false, + detail: "Not read, to keep the reply short. Ask for this check by name.", + })), + ]; }); const rerunChecks: PullRequestService["Service"]["rerunChecks"] = Effect.fn( diff --git a/evals/mcp-tool-use/run.test.ts b/evals/mcp-tool-use/run.test.ts index f94b6c964c85c..413b2c09a4104 100644 --- a/evals/mcp-tool-use/run.test.ts +++ b/evals/mcp-tool-use/run.test.ts @@ -1122,6 +1122,8 @@ it.effect("reads the branch's PR only once the agent has opened it", () => input, }); assert.isTrue((yield* call("a", "pull_request_read", {})).is_error); + sample.tools.push({ name: "mcp__t3-code__ci_logs" }); + assert.isTrue((yield* call("a2", "ci_logs", {})).is_error); yield* call("b", "pull_request_create", { title: "Feature", body: "Body" }); assert.isNotTrue((yield* call("c", "pull_request_read", {})).is_error); diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index 6682320d10f7b..61db85a7799d2 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -1668,6 +1668,8 @@ const BRANCH_PULL_REQUEST_TOOLS = new Set([ "pull_request_respond", "pull_request_review", "pull_request_merge", + "ci_logs", + "ci_rerun", ]); const BRANCH_HAS_NO_PR_ERROR = "This thread's branch has no known PR. Pass number, or open one with pull_request_create."; @@ -1939,6 +1941,10 @@ export const answerToolCall = Effect.fn("evaluateMcpToolUse.answerToolCall")(fun if (name === "watch_pull_request" && testCase.setup.role === "delegated-child") { return failed(SUBAGENT_WATCH_ERROR); } + if (BRANCH_PULL_REQUEST_TOOLS.has(name) && input.number === undefined) { + // Until the agent opens it, the PR a create would open does not exist. + if (branchPullRequest(sample).created) return failed(BRANCH_HAS_NO_PR_ERROR); + } if (name === "ci_logs" || name === "ci_rerun") { const { all, checks, named } = selectedWorldChecks(sample, input); if (named !== null && checks.length === 0) { @@ -1946,10 +1952,6 @@ export const answerToolCall = Effect.fn("evaluateMcpToolUse.answerToolCall")(fun return failed(`No check is named ${valueText(input.check)}. Checks: ${names}.`); } } - if (BRANCH_PULL_REQUEST_TOOLS.has(name) && input.number === undefined) { - // Until the agent opens it, the PR a create would open does not exist. - if (branchPullRequest(sample).created) return failed(BRANCH_HAS_NO_PR_ERROR); - } const value = t3Default(sample, name, input); if (name === "request_secret") savedSecretFile(sample, value); return toolResult( From 6ba205e04f926483d93249ca5e80b491c70948a9 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 02:11:11 -0800 Subject: [PATCH 06/16] fix(evals): ci_logs in the eval runner reads five logs like the service Co-Authored-By: Claude Opus 5.5 --- .../pullRequest/PullRequestService.test.ts | 22 +++++++++++--- evals/mcp-tool-use/run.test.ts | 25 ++++++++++++++++ evals/mcp-tool-use/run.ts | 29 +++++++++++++------ 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index 85a689454e4be..67dc48ac38e2e 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -1464,7 +1464,12 @@ it.effect( getCheckLogs: (input) => Effect.sync(() => { asked.push(input.checks.map((entry) => entry.name)); - return []; + return input.checks.map((entry) => ({ + ...entry, + log: "boom", + truncated: false, + detail: null, + })); }), }), ], @@ -1484,10 +1489,19 @@ it.effect( // The host is asked for five; the other three come back unread, by name. assert.lengthOf(asked[0] ?? [], 5); assert.deepStrictEqual( - many.map((log) => log.name), - ["Shard 3", "Shard 4", "Shard 5"], + many.map((log) => [log.name, log.log]), + [ + ["Test", "boom"], + ["E2E", "boom"], + ["Shard 0", "boom"], + ["Shard 1", "boom"], + ["Shard 2", "boom"], + ["Shard 3", null], + ["Shard 4", null], + ["Shard 5", null], + ], ); - assert.include(many[0]?.detail ?? "", "Ask for this check by name"); + assert.include(many.at(-1)?.detail ?? "", "Ask for this check by name"); }), ); diff --git a/evals/mcp-tool-use/run.test.ts b/evals/mcp-tool-use/run.test.ts index 413b2c09a4104..280a568057239 100644 --- a/evals/mcp-tool-use/run.test.ts +++ b/evals/mcp-tool-use/run.test.ts @@ -843,6 +843,31 @@ it("lists and reads world PRs in their own state, with the diff when asked", () } ).checks.map((check) => check.name); assert.deepEqual(ciNames({ check: " test " }), ["Test"]); + const shards = makeSample( + { + ...childCase, + setup: { + ...childCase.setup, + world: { + ...childCase.setup.world, + prs: [ + { + number: 7, + checks: Array.from({ length: 6 }, (_, index) => ({ + name: `Shard ${index}`, + state: "fail", + })), + }, + ], + }, + }, + }, + base, + ); + const logs = ( + t3Default(shards, "ci_logs", { number: 7 }) as { checks: Array<{ log: string | null }> } + ).checks.map((check) => check.log !== null); + assert.deepEqual(logs, [true, true, true, true, true, false]); assert.deepEqual(ciNames({}), ["Lint"]); const read: Readonly> = t3Default(sample, "pull_request_read", { number: 5, diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index 61db85a7799d2..2da3241a6b08f 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -1235,6 +1235,9 @@ const branchPullRequest = (sample: Sample) => { return { number, created: true, pr: undefined }; }; +/** PullRequestService reads this many logs when no check is named. */ +const DEFAULT_CHECK_LOGS = 5; + /** The checks `ci_logs` and `ci_rerun` act on, selected as PullRequestService does. */ const selectedWorldChecks = (sample: Sample, input: Readonly>) => { const number = typeof input.number === "number" ? input.number : branchPullRequest(sample).number; @@ -1469,19 +1472,27 @@ export const t3Default = ( }; case "ci_logs": case "ci_rerun": { - const { number, checks } = selectedWorldChecks(sample, input); + const { number, checks, named } = selectedWorldChecks(sample, input); return { ...pullRequest(sample.repo, { number }), - checks: checks.map((check) => + checks: checks.map((check, index) => name === "ci_rerun" ? { name: valueText(check.name), rerun: true, detail: null } - : { - name: valueText(check.name), - url: null, - log: valueText(check.log ?? `Error: ${valueText(check.name)} failed`), - truncated: false, - detail: null, - }, + : named === null && index >= DEFAULT_CHECK_LOGS + ? { + name: valueText(check.name), + url: null, + log: null, + truncated: false, + detail: "Not read, to keep the reply short. Ask for this check by name.", + } + : { + name: valueText(check.name), + url: null, + log: valueText(check.log ?? `Error: ${valueText(check.name)} failed`), + truncated: false, + detail: null, + }, ), }; } From 1bcc3ea50931acea3dabcd6b35160303cbce460a Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 09:43:01 -0800 Subject: [PATCH 07/16] fix(server): shorter ci_logs description Co-Authored-By: Claude Opus 5.5 --- apps/server/src/mcp/toolkits/pullRequests/tools.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/mcp/toolkits/pullRequests/tools.ts b/apps/server/src/mcp/toolkits/pullRequests/tools.ts index 5cb1ecb451077..352763e1fa4a8 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/tools.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/tools.ts @@ -792,7 +792,7 @@ export type RerunChecksResult = typeof RerunChecksResult.Type; const CheckLogsTool = Tool.make("ci_logs", { description: - "Read the end of a PR's failing check logs, or of one named check. Use instead of gh run view --log-failed.", + "Read the tail of a PR's failing check logs, or one named check's. Use instead of gh run view.", parameters: Schema.Struct({ ...ThreadPullRequestInput.fields, check: Schema.optional(TrimmedNonEmptyString), From bbb3f178e34b6137bf3efe69030beed4d2dab520 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 10:08:32 -0800 Subject: [PATCH 08/16] fix(server): agent instructions point CI work at ci_logs and ci_rerun Co-Authored-By: Claude Opus 5.5 --- packages/provider-core/src/server/runtimeInstructions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/provider-core/src/server/runtimeInstructions.ts b/packages/provider-core/src/server/runtimeInstructions.ts index 7dce30e4df69a..865bbaeea0b35 100644 --- a/packages/provider-core/src/server/runtimeInstructions.ts +++ b/packages/provider-core/src/server/runtimeInstructions.ts @@ -1,6 +1,6 @@ /** Session-invariant; harnesses with a native system-prompt append can carry it once per session. */ export const PULL_REQUEST_LINKING_INSTRUCTIONS = ` -Link each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you. +Link each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you. `; /** Omit model and effort when the harness manages them dynamically. */ From 85ca8b835a7090598d0b997e6aea45878fea2f84 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 10:08:56 -0800 Subject: [PATCH 09/16] fix(evals): pull_request_create in the eval runner opens one PR per checked-out branch Co-Authored-By: Claude Opus 5.5 --- evals/mcp-tool-use/run.test.ts | 21 ++++++++++++++++++ evals/mcp-tool-use/run.ts | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/evals/mcp-tool-use/run.test.ts b/evals/mcp-tool-use/run.test.ts index 280a568057239..b6b162bd8679e 100644 --- a/evals/mcp-tool-use/run.test.ts +++ b/evals/mcp-tool-use/run.test.ts @@ -1152,6 +1152,27 @@ it.effect("reads the branch's PR only once the agent has opened it", () => yield* call("b", "pull_request_create", { title: "Feature", body: "Body" }); assert.isNotTrue((yield* call("c", "pull_request_read", {})).is_error); + // A stack's next layer, checked out in the shell, opens its own PR once. + const layerPr = (id: string) => + call(id, "pull_request_create", { title: "Layer", body: "Body" }).pipe( + Effect.map((result) => /\/pull\/(\d+)/.exec(String(result.content))?.[1]), + ); + const first = /\/pull\/(\d+)/.exec([...sample.linked].join(" "))?.[1]; + sample.shell.push({ + tool: "Bash", + input: { command: "git checkout -b layer-2 2>&1" }, + output: "", + }); + const layer = yield* layerPr("b2"); + assert.notEqual(layer, first); + assert.equal(yield* layerPr("b3"), layer); + sample.shell.push({ + tool: "Bash", + input: { command: "git checkout main -- src/a.ts && git checkout feature" }, + output: "", + }); + assert.equal(yield* layerPr("b4"), first); + // A case that mocks the create still opens the PR it names. const url = "https://github.com/acme/app/pull/125"; const mocks = { pull_request_create: { number: 125, url, created: true } }; diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index 2da3241a6b08f..e10df68d4e00f 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -868,6 +868,23 @@ const branchPullRequestCreatedBy = (entry: ShellEntry, branch: string | undefine .map((url) => Number(/\/pull\/(\d+)/.exec(url)?.[1])) .find((number) => number > 0); }; +/** The branch the agent last checked out or switched to in the shell, else the world's. */ +const checkedOutBranch = (sample: Sample) => { + let branch = sample.testCase.setup.world?.branch ?? ""; + for (const entry of sample.shell) { + const command = isRecord(entry.input) ? valueText(entry.input.command) : ""; + for (const part of command.replace(/\s\d?>&\d/g, "").split(/[;&|\n]+/)) { + // `git checkout -- ` restores files and stays on the branch. + if (/\s--(\s|$)/.test(part)) continue; + const match = /\bgit (?:checkout|switch)\s+(?:-[bcBC]\s+)?([A-Za-z0-9][\w./-]*)\s*$/.exec( + part.trim(), + ); + if (match !== null) branch = match[1]!; + } + } + return branch; +}; + /** * One sample's state. ToolSearch adds loaded schemas to `tools`, like Claude Code. `shell` is the * history the simulator keeps consistent with; `linked`, `nextTask` and `threads` do the same for @@ -883,6 +900,8 @@ type Sample = { readonly linked: Set; /** The branch PR the agent opened in this sample, which links alone do not prove. */ openedPullRequest: number | undefined; + /** PRs pull_request_create opened from branches other than the world's, by branch. */ + readonly openedByBranch: Map; readonly codeStore: Record; nextTask: number; /** Threads the agent launched in this sample, by id, so reading one shows its own brief. */ @@ -938,6 +957,7 @@ export const makeSample = (testCase: McpToolUseCase, base: Base): Sample => { shell, shellUsage: emptyUsage(), linked: new Set([...urls, ...shell.flatMap(createdPullRequests)]), + openedByBranch: new Map(), openedPullRequest: shell .map((entry) => branchPullRequestCreatedBy(entry, testCase.setup.world?.branch)) @@ -1349,6 +1369,25 @@ export const t3Default = ( }; } case "pull_request_create": { + const checkedOut = checkedOutBranch(sample); + if (checkedOut !== sample.testCase.setup.world?.branch) { + // Another layer of a stack: its own PR, opened once per branch like the real tool. + const known = worldPullRequests(sample).find((pr) => pr.headRefName === checkedOut); + const opened = sample.openedByBranch.get(checkedOut); + const number = + known !== undefined + ? Number(known.number) + : (opened ?? + Math.max( + branchPullRequest(sample).number, + ...sample.openedByBranch.values(), + ...[...sample.linked].map((url) => Number(/\/pull\/(\d+)/.exec(url)?.[1]) || 0), + ) + 1); + sample.openedByBranch.set(checkedOut, number); + const target = pullRequest(sample.repo, { number }); + sample.linked.add(target.url); + return { ...target, created: known === undefined && opened === undefined }; + } const { number, created } = branchPullRequest(sample); const target = pullRequest(sample.repo, { number }); sample.linked.add(target.url); From ff0009435419e69efd69f9ffcaf96fdf7abb2b56 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 10:18:59 -0800 Subject: [PATCH 10/16] fix(evals): the simulated host shows PR changes the agent made through T3's tools Co-Authored-By: Claude Opus 5.5 --- evals/mcp-tool-use/run.test.ts | 6 ++++++ evals/mcp-tool-use/run.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/evals/mcp-tool-use/run.test.ts b/evals/mcp-tool-use/run.test.ts index b6b162bd8679e..af82dddf41219 100644 --- a/evals/mcp-tool-use/run.test.ts +++ b/evals/mcp-tool-use/run.test.ts @@ -1172,6 +1172,12 @@ it.effect("reads the branch's PR only once the agent has opened it", () => output: "", }); assert.equal(yield* layerPr("b4"), first); + // The simulated host shows what the agent changed through T3's tools. + const [machine] = + shellRequest(sample, { tool: "Bash", input: { command: "gh pr list" } }, "claude-haiku-5-5") + .messages[0]?.content ?? []; + assert.include(machine?.text, "made these changes through T3's tools"); + assert.include(machine?.text, "pull_request_create"); // A case that mocks the create still opens the PR it names. const url = "https://github.com/acme/app/pull/125"; diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index e10df68d4e00f..bda3492eacb1c 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -902,6 +902,8 @@ type Sample = { openedPullRequest: number | undefined; /** PRs pull_request_create opened from branches other than the world's, by branch. */ readonly openedByBranch: Map; + /** PR changes the agent made through T3's tools, which gh and the host show from then on. */ + readonly forgeChanges: Array; readonly codeStore: Record; nextTask: number; /** Threads the agent launched in this sample, by id, so reading one shows its own brief. */ @@ -958,6 +960,7 @@ export const makeSample = (testCase: McpToolUseCase, base: Base): Sample => { shellUsage: emptyUsage(), linked: new Set([...urls, ...shell.flatMap(createdPullRequests)]), openedByBranch: new Map(), + forgeChanges: [], openedPullRequest: shell .map((entry) => branchPullRequestCreatedBy(entry, testCase.setup.world?.branch)) @@ -1803,6 +1806,11 @@ export const shellRequest = (sample: Sample, call: Omit, m `T3's request_secret saved these files in this sample: ${sample.secretFiles.join(", ")}. Each exists, is readable only by the user, and holds exactly the value the user gave, typed into T3's private card or pasted in chat and saved by the agent, the right one for the task, with no trailing newline. A program that reads it, as in "$(cat )", < or a --*-file flag, gets that value. What appears in the output is whatever that program prints: one that prints or logs its input shows the value. No other file in that directory exists.`, ] : []), + ...(sample.forgeChanges.length > 0 + ? [ + `The agent made these changes through T3's tools; gh, git fetch and the host show them: ${sample.forgeChanges.join("; ")}`, + ] + : []), ...(context ? [`The agent was told: ${context}`] : []), `The agent's task: ${testCase.prompt}`, ...namedPathsLine(testCase), @@ -1909,6 +1917,25 @@ const savedSecretFile = (sample: Sample, result: unknown) => { }; /** The result for one tool call. */ +const FORGE_WRITE_TOOLS = new Set([ + "pull_request_create", + "pull_request_update", + "pull_request_respond", + "pull_request_merge", + "ci_rerun", +]); + +const recordForgeChange = ( + sample: Sample, + name: string, + input: Readonly>, + result: unknown, +) => { + if (FORGE_WRITE_TOOLS.has(name)) { + sample.forgeChanges.push(`${name} ${valueText(input)} returned ${valueText(result)}`); + } +}; + export const answerToolCall = Effect.fn("evaluateMcpToolUse.answerToolCall")(function* ( sample: Sample, config: Config, @@ -1979,6 +2006,7 @@ export const answerToolCall = Effect.fn("evaluateMcpToolUse.answerToolCall")(fun const number = Number(mock.number); if (number > 0) sample.openedPullRequest = number; } + recordForgeChange(sample, name, input, mock); return toolResult( call.id, name === "orchestrator_capabilities" @@ -2004,6 +2032,7 @@ export const answerToolCall = Effect.fn("evaluateMcpToolUse.answerToolCall")(fun } const value = t3Default(sample, name, input); if (name === "request_secret") savedSecretFile(sample, value); + recordForgeChange(sample, name, input, value); return toolResult( call.id, name === "orchestrator_capabilities" From dd6d661c408e810b706dc4640061040f5475c629 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 10:31:35 -0800 Subject: [PATCH 11/16] fix(evals): a new stack layer in the eval runner skips every world PR's number Co-Authored-By: Claude Opus 5.5 --- evals/mcp-tool-use/run.test.ts | 3 +++ evals/mcp-tool-use/run.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/evals/mcp-tool-use/run.test.ts b/evals/mcp-tool-use/run.test.ts index af82dddf41219..325e41215a78b 100644 --- a/evals/mcp-tool-use/run.test.ts +++ b/evals/mcp-tool-use/run.test.ts @@ -1163,8 +1163,11 @@ it.effect("reads the branch's PR only once the agent has opened it", () => input: { command: "git checkout -b layer-2 2>&1" }, output: "", }); + // An unrelated world PR keeps its number. + world.prs.push({ number: 130, title: "Other", headRefName: "other" } as never); const layer = yield* layerPr("b2"); assert.notEqual(layer, first); + assert.equal(layer, "131"); assert.equal(yield* layerPr("b3"), layer); sample.shell.push({ tool: "Bash", diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index bda3492eacb1c..4434f5d482e58 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -1384,6 +1384,7 @@ export const t3Default = ( Math.max( branchPullRequest(sample).number, ...sample.openedByBranch.values(), + ...worldPullRequests(sample).map((pr) => Number(pr.number) || 0), ...[...sample.linked].map((url) => Number(/\/pull\/(\d+)/.exec(url)?.[1]) || 0), ) + 1); sample.openedByBranch.set(checkedOut, number); From 995fd790e16f44cbcab8af584ec4fb5b0ee42b39 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 10:43:11 -0800 Subject: [PATCH 12/16] fix(evals): PR tools without a number act on the checked-out stack layer's PR Co-Authored-By: Claude Opus 5.5 --- evals/mcp-tool-use/run.test.ts | 4 ++++ evals/mcp-tool-use/run.ts | 44 ++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/evals/mcp-tool-use/run.test.ts b/evals/mcp-tool-use/run.test.ts index 325e41215a78b..6da101c82363c 100644 --- a/evals/mcp-tool-use/run.test.ts +++ b/evals/mcp-tool-use/run.test.ts @@ -1163,11 +1163,15 @@ it.effect("reads the branch's PR only once the agent has opened it", () => input: { command: "git checkout -b layer-2 2>&1" }, output: "", }); + // Until the layer's PR is opened, its branch has none. + assert.isTrue((yield* call("r0", "pull_request_read", {})).is_error); // An unrelated world PR keeps its number. world.prs.push({ number: 130, title: "Other", headRefName: "other" } as never); const layer = yield* layerPr("b2"); assert.notEqual(layer, first); assert.equal(layer, "131"); + // Reads without a number act on the checked-out layer's PR. + assert.include(String((yield* call("r1", "pull_request_read", {})).content), "/pull/131"); assert.equal(yield* layerPr("b3"), layer); sample.shell.push({ tool: "Bash", diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index 4434f5d482e58..4a7b168f8bbf7 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -1241,7 +1241,7 @@ const worldState = (pr: Readonly> | undefined) => { * The PR for the sample's branch: the world's, else the one a create would open. A case that * mocks `gh pr create` names that URL, so the T3 tool opens the same number. */ -const branchPullRequest = (sample: Sample) => { +const worldBranchPullRequest = (sample: Sample) => { const branch = sample.testCase.setup.world?.branch; const prs = worldPullRequests(sample); const own = prs.find((pr) => pr.headRefName === branch); @@ -1258,6 +1258,24 @@ const branchPullRequest = (sample: Sample) => { return { number, created: true, pr: undefined }; }; +/** The PR for the checked-out branch: a stack layer's own, or the world branch's. */ +const branchPullRequest = (sample: Sample) => { + const checkedOut = checkedOutBranch(sample); + if (checkedOut === sample.testCase.setup.world?.branch) return worldBranchPullRequest(sample); + const known = worldPullRequests(sample).find((pr) => pr.headRefName === checkedOut); + if (known !== undefined) return { number: Number(known.number), created: false, pr: known }; + const opened = sample.openedByBranch.get(checkedOut); + if (opened !== undefined) return { number: opened, created: false, pr: undefined }; + const number = + Math.max( + worldBranchPullRequest(sample).number, + ...sample.openedByBranch.values(), + ...worldPullRequests(sample).map((pr) => Number(pr.number) || 0), + ...[...sample.linked].map((url) => Number(/\/pull\/(\d+)/.exec(url)?.[1]) || 0), + ) + 1; + return { number, created: true, pr: undefined }; +}; + /** PullRequestService reads this many logs when no check is named. */ const DEFAULT_CHECK_LOGS = 5; @@ -1373,29 +1391,13 @@ export const t3Default = ( } case "pull_request_create": { const checkedOut = checkedOutBranch(sample); - if (checkedOut !== sample.testCase.setup.world?.branch) { - // Another layer of a stack: its own PR, opened once per branch like the real tool. - const known = worldPullRequests(sample).find((pr) => pr.headRefName === checkedOut); - const opened = sample.openedByBranch.get(checkedOut); - const number = - known !== undefined - ? Number(known.number) - : (opened ?? - Math.max( - branchPullRequest(sample).number, - ...sample.openedByBranch.values(), - ...worldPullRequests(sample).map((pr) => Number(pr.number) || 0), - ...[...sample.linked].map((url) => Number(/\/pull\/(\d+)/.exec(url)?.[1]) || 0), - ) + 1); - sample.openedByBranch.set(checkedOut, number); - const target = pullRequest(sample.repo, { number }); - sample.linked.add(target.url); - return { ...target, created: known === undefined && opened === undefined }; - } const { number, created } = branchPullRequest(sample); const target = pullRequest(sample.repo, { number }); sample.linked.add(target.url); - if (created) sample.openedPullRequest = number; + // Another layer of a stack gets its own PR, opened once per branch like the real tool. + if (checkedOut !== sample.testCase.setup.world?.branch) { + sample.openedByBranch.set(checkedOut, number); + } else if (created) sample.openedPullRequest = number; return { ...target, created }; } case "pull_request_read": { From 3a30576db48fbfd2262b6dcc3db1f573753bbb9a Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Wed, 11 Nov 2026 11:02:19 -0800 Subject: [PATCH 13/16] test(server): Muse replay fixtures name the CI tools in the PR instructions Co-Authored-By: Claude Opus 5.5 --- .../fixtures/multi_turn/muse_transcript.ndjson | 12 ++++++------ .../fixtures/muse_permission/muse_transcript.ndjson | 12 ++++++------ .../muse_subagent_approval/muse_transcript.ndjson | 6 +++--- .../muse_subagent_interrupt/muse_transcript.ndjson | 6 +++--- .../fixtures/muse_workflow/muse_transcript.ndjson | 6 +++--- .../testkit/fixtures/simple/muse_transcript.ndjson | 6 +++--- .../fixtures/subagent_v2/muse_transcript.ndjson | 6 +++--- .../fixtures/turn_interrupt/muse_transcript.ndjson | 6 +++--- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/multi_turn/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/multi_turn/muse_transcript.ndjson index 28b6b85c382f5..00f862f1b3172 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/multi_turn/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/multi_turn/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6b-707e-7000-9bb5-4079c7772ad0/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:30:11.238412Z","updatedAt":"2026-10-08T07:30:11.238416Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791444611408}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6b-707e-7000-9bb5-4079c7772ad0/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:30:11.238412Z","updatedAt":"2026-10-08T07:30:11.238416Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:1","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"37b19bd1-b212-4532-a0c9-a6677f303e26","sequence":5},"last":{"id":"37b19bd1-b212-4532-a0c9-a6677f303e26","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a11a6b-707e-7001-b2f4-23db74e60097","clientName":"t3_code"},"emittedAtMs":1791444611412}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Respond with exactly: first fixture turn complete"}],"displayText":"Respond with exactly: first fixture turn complete","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Respond with exactly: first fixture turn complete"}],"displayText":"Respond with exactly: first fixture turn complete","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:2","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"2c530748-943b-4467-8473-da8b04c511c3","sequence":16},"last":{"id":"2c530748-943b-4467-8473-da8b04c511c3","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444611661}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","status":"accepted","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","status":"running","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:3"},"emittedAtMs":1791444611674}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:3","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18},"last":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18}},"turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"},"emittedAtMs":1791444611675}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18},"last":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18}},"item":{"itemId":"1b470279-ee8c-4c74-a298-f37184145584","kind":"userMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":1,"status":"completed","recordedAt":"2026-10-08T07:30:11.66263Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with exactly: first fixture turn complete","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}},"emittedAtMs":1791444611675}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"31b500d6-40fc-4f4c-b097-6b7be8d77cee","sequence":20},"last":{"id":"31b500d6-40fc-4f4c-b097-6b7be8d77cee","sequence":20}},"item":{"itemId":"1b470279-ee8c-4c74-a298-f37184145584","kind":"userMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":2,"status":"completed","recordedAt":"2026-10-08T07:30:11.680942Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with exactly: first fixture turn complete","displayText":"Respond with exactly: first fixture turn complete","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}},"emittedAtMs":1791444611689}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18},"last":{"id":"2be0c9b5-9dc4-48f0-9b86-06aef4de31f3","sequence":18}},"item":{"itemId":"1b470279-ee8c-4c74-a298-f37184145584","kind":"userMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":1,"status":"completed","recordedAt":"2026-10-08T07:30:11.66263Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with exactly: first fixture turn complete","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}},"emittedAtMs":1791444611675}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"31b500d6-40fc-4f4c-b097-6b7be8d77cee","sequence":20},"last":{"id":"31b500d6-40fc-4f4c-b097-6b7be8d77cee","sequence":20}},"item":{"itemId":"1b470279-ee8c-4c74-a298-f37184145584","kind":"userMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":2,"status":"completed","recordedAt":"2026-10-08T07:30:11.680942Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with exactly: first fixture turn complete","displayText":"Respond with exactly: first fixture turn complete","commandId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4"}},"emittedAtMs":1791444611689}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:6","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"077780ea-0dc9-4d7a-879c-ee368f3adbe0","sequence":37},"last":{"id":"077780ea-0dc9-4d7a-879c-ee368f3adbe0","sequence":37}},"item":{"itemId":"077780ea-0dc9-4d7a-879c-ee368f3adbe0","kind":"reminderChild","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:30:11.790301Z","fallbackText":"Reminder child session","childSessionId":"c13379db-6761-40b4-a974-b0f1a58717aa","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6b-72ac-7972-a1df-b343b7804b38"}},"emittedAtMs":1791444611795}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:6.1","item":{"itemId":"f99b90e1-9d8b-4bf2-8e7a-e240e1cf9321","kind":"agentMessage","turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","revision":1,"status":"inProgress","text":""}},"emittedAtMs":1791444618068}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:6.2","itemId":"f99b90e1-9d8b-4bf2-8e7a-e240e1cf9321","field":"text","delta":"first fixture turn complete"},"emittedAtMs":1791444618068}} @@ -29,12 +29,12 @@ {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","status":"idle","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:16"},"emittedAtMs":1791444621046}} {"type":"emit_inbound","label":"turn/completed","frame":{"jsonrpc":"2.0","method":"turn/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:16","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"688a69c5-e353-4db9-bd8c-1beb033902ba","sequence":79},"last":{"id":"688a69c5-e353-4db9-bd8c-1beb033902ba","sequence":79}},"turnId":"01a11a6b-722c-7000-a73a-1d49a3a7f4c4","terminal":"completed","durationMs":9378,"timeToFirstTokenMs":6406},"emittedAtMs":1791444621047}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:17","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"36753b8a-26ce-4935-8127-bc9191cb328b","sequence":80},"last":{"id":"36753b8a-26ce-4935-8127-bc9191cb328b","sequence":80}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444621054}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":4,"method":"turn/start","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Respond with exactly: second fixture turn complete"}],"displayText":"Respond with exactly: second fixture turn complete","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":4,"method":"turn/start","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Respond with exactly: second fixture turn complete"}],"displayText":"Respond with exactly: second fixture turn complete","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":4,"result":{"commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","status":"accepted","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","status":"running","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:18"},"emittedAtMs":1791444621333}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:18","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87},"last":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87}},"turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"},"emittedAtMs":1791444621334}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:19","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87},"last":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87}},"item":{"itemId":"90719a5d-d2c1-46dc-ad4d-274adda02344","kind":"userMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":1,"status":"completed","recordedAt":"2026-10-08T07:30:21.321391Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with exactly: second fixture turn complete","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}},"emittedAtMs":1791444621334}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:20","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"dc502e2c-d81c-4f0e-ad86-dd11d1368677","sequence":89},"last":{"id":"dc502e2c-d81c-4f0e-ad86-dd11d1368677","sequence":89}},"item":{"itemId":"90719a5d-d2c1-46dc-ad4d-274adda02344","kind":"userMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":2,"status":"completed","recordedAt":"2026-10-08T07:30:21.338944Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with exactly: second fixture turn complete","displayText":"Respond with exactly: second fixture turn complete","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}},"emittedAtMs":1791444621347}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:19","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87},"last":{"id":"4aba96d5-f984-4bc0-bda4-7fb97f527686","sequence":87}},"item":{"itemId":"90719a5d-d2c1-46dc-ad4d-274adda02344","kind":"userMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":1,"status":"completed","recordedAt":"2026-10-08T07:30:21.321391Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with exactly: second fixture turn complete","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}},"emittedAtMs":1791444621334}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:20","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"dc502e2c-d81c-4f0e-ad86-dd11d1368677","sequence":89},"last":{"id":"dc502e2c-d81c-4f0e-ad86-dd11d1368677","sequence":89}},"item":{"itemId":"90719a5d-d2c1-46dc-ad4d-274adda02344","kind":"userMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":2,"status":"completed","recordedAt":"2026-10-08T07:30:21.338944Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with exactly: second fixture turn complete","displayText":"Respond with exactly: second fixture turn complete","commandId":"01a11a6b-97ec-7000-bd1b-d7afbea11045"}},"emittedAtMs":1791444621347}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:21","sourceRange":{"stream":{"kind":"session","id":"01a11a6b-707e-7000-9bb5-4079c7772ad0"},"first":{"id":"09cb4c2a-03c2-4089-ba12-8d5d31f7ea48","sequence":106},"last":{"id":"09cb4c2a-03c2-4089-ba12-8d5d31f7ea48","sequence":106}},"item":{"itemId":"09cb4c2a-03c2-4089-ba12-8d5d31f7ea48","kind":"reminderChild","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:30:21.446966Z","fallbackText":"Reminder child session","childSessionId":"cd97261a-3ea9-480f-9e54-5e59bb077da2","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6b-9869-7061-a4f3-42daa3e3a8f1"}},"emittedAtMs":1791444621455}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:21.1","item":{"itemId":"2de26c0d-26b4-4533-8810-918ee44fcd82","kind":"agentMessage","turnId":"01a11a6b-97ec-7000-bd1b-d7afbea11045","revision":1,"status":"inProgress","text":""}},"emittedAtMs":1791444623127}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a11a6b-707e-7000-9bb5-4079c7772ad0","viewCursor":"v:01a11a6b-707e-7000-9bb5-4079c7772ad0:21.2","itemId":"2de26c0d-26b4-4533-8810-918ee44fcd82","field":"text","delta":"second fixture turn complete"},"emittedAtMs":1791444623127}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/muse_permission/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/muse_permission/muse_transcript.ndjson index 94a525ca25a6e..687d274edb3e2 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/muse_permission/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/muse_permission/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6d-39bb-7000-8c93-4594c98e6a11/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:32:08.30084Z","updatedAt":"2026-10-08T07:32:08.300844Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"promptUnmatched","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791444728496}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6d-39bb-7000-8c93-4594c98e6a11/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:32:08.30084Z","updatedAt":"2026-10-08T07:32:08.300844Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"promptUnmatched","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:1","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b05fb8ec-7b58-4cf7-a369-379938395230","sequence":5},"last":{"id":"b05fb8ec-7b58-4cf7-a369-379938395230","sequence":5}},"mode":"promptUnmatched","source":"approvalReconfigure","commandId":"01a11a6d-39bb-7001-923e-425cc6129ce7","clientName":"t3_code"},"emittedAtMs":1791444728501}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE."}],"displayText":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE."}],"displayText":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:2","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"6159e84a-ae1e-4fae-ae8b-49f41ca34509","sequence":16},"last":{"id":"6159e84a-ae1e-4fae-ae8b-49f41ca34509","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444728769}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","status":"accepted","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","status":"running","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:3"},"emittedAtMs":1791444728783}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:3","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18},"last":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18}},"turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"},"emittedAtMs":1791444728783}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18},"last":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18}},"item":{"itemId":"dde9573b-650f-434b-ab99-60cfc820a0a5","kind":"userMessage","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:08.769894Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRun the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}},"emittedAtMs":1791444728784}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"76c57706-1378-4167-8bbf-f76d959804e8","sequence":20},"last":{"id":"76c57706-1378-4167-8bbf-f76d959804e8","sequence":20}},"item":{"itemId":"dde9573b-650f-434b-ab99-60cfc820a0a5","kind":"userMessage","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:08.791728Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRun the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","displayText":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}},"emittedAtMs":1791444728800}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18},"last":{"id":"09218c3a-88fd-4bc8-ab45-d1aa69bea95a","sequence":18}},"item":{"itemId":"dde9573b-650f-434b-ab99-60cfc820a0a5","kind":"userMessage","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:08.769894Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRun the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}},"emittedAtMs":1791444728784}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"76c57706-1378-4167-8bbf-f76d959804e8","sequence":20},"last":{"id":"76c57706-1378-4167-8bbf-f76d959804e8","sequence":20}},"item":{"itemId":"dde9573b-650f-434b-ab99-60cfc820a0a5","kind":"userMessage","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:08.791728Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRun the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","displayText":"Run the shell command `touch muse-approved.txt` with your shell tool, then reply DONE.","commandId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6"}},"emittedAtMs":1791444728800}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:6","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"29180105-84c4-4f9e-8bfd-003392b93b73","sequence":38},"last":{"id":"29180105-84c4-4f9e-8bfd-003392b93b73","sequence":38}},"item":{"itemId":"29180105-84c4-4f9e-8bfd-003392b93b73","kind":"reminderChild","turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:32:08.905989Z","fallbackText":"Reminder child session","childSessionId":"7c9485f9-26aa-4b56-8ca2-f36cb9cfb8c4","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6d-3c27-73f2-8c27-cd06a3b7dadf"}},"emittedAtMs":1791444728910}} {"type":"emit_inbound","label":"session/tokenUsage","frame":{"jsonrpc":"2.0","method":"session/tokenUsage","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:7","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b1dab1f5-3c9e-4d92-b955-841a2269c23c","sequence":54},"last":{"id":"b1dab1f5-3c9e-4d92-b955-841a2269c23c","sequence":54}},"turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","modelId":"muse-spark-1.3-contributor","usage":{"inputTokens":32618,"outputTokens":315,"cachedTokens":0,"cacheWriteTokens":0,"cacheReadTokens":0,"reasoningTokens":227},"promptTokens":32618,"totalTokens":32933,"finishReason":"tool_calls","durationMs":2684,"cumulative":{"promptTokens":32618,"outputTokens":315,"totalTokens":32933,"cost":{"usd":0.0033248,"partial":false}}},"emittedAtMs":1791444731684}} {"type":"emit_inbound","label":"session/contextUsage","frame":{"jsonrpc":"2.0","method":"session/contextUsage","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:8","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b1dab1f5-3c9e-4d92-b955-841a2269c23c","sequence":54},"last":{"id":"b1dab1f5-3c9e-4d92-b955-841a2269c23c","sequence":54}},"windowTokens":1007997,"usedTokens":32933,"pressure":"normal"},"emittedAtMs":1791444731684}} @@ -42,12 +42,12 @@ {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","status":"idle","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:23"},"emittedAtMs":1791444747386}} {"type":"emit_inbound","label":"turn/completed","frame":{"jsonrpc":"2.0","method":"turn/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:23","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"7c29ac76-7e09-48c1-a18f-f336126e27d1","sequence":120},"last":{"id":"7c29ac76-7e09-48c1-a18f-f336126e27d1","sequence":120}},"turnId":"01a11a6d-3b9f-7000-b6ad-ab05e6b973b6","terminal":"completed","durationMs":18608,"timeToFirstTokenMs":4791},"emittedAtMs":1791444747387}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:24","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"1e4afe30-d924-444c-8665-5e763f524e70","sequence":121},"last":{"id":"1e4afe30-d924-444c-8665-5e763f524e70","sequence":121}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444747394}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":5,"method":"turn/start","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED."}],"displayText":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":5,"method":"turn/start","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED."}],"displayText":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":5,"result":{"commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29","status":"accepted","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","status":"running","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:25"},"emittedAtMs":1791444747660}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:25","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128},"last":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128}},"turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"},"emittedAtMs":1791444747660}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:26","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128},"last":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128}},"item":{"itemId":"27473708-71e0-4d3c-811c-a8bae6bbaa3b","kind":"userMessage","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:27.650913Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRun the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}},"emittedAtMs":1791444747660}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:27","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a056a03-469a-4c9f-94a9-78eb815023ad","sequence":130},"last":{"id":"4a056a03-469a-4c9f-94a9-78eb815023ad","sequence":130}},"item":{"itemId":"27473708-71e0-4d3c-811c-a8bae6bbaa3b","kind":"userMessage","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:27.66745Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRun the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","displayText":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}},"emittedAtMs":1791444747676}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:26","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128},"last":{"id":"4a1cf90d-7457-448d-a2b5-d82108636b3b","sequence":128}},"item":{"itemId":"27473708-71e0-4d3c-811c-a8bae6bbaa3b","kind":"userMessage","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:27.650913Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRun the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}},"emittedAtMs":1791444747660}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:27","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"4a056a03-469a-4c9f-94a9-78eb815023ad","sequence":130},"last":{"id":"4a056a03-469a-4c9f-94a9-78eb815023ad","sequence":130}},"item":{"itemId":"27473708-71e0-4d3c-811c-a8bae6bbaa3b","kind":"userMessage","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:27.66745Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRun the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","displayText":"Run the shell command `touch muse-declined.txt` with your shell tool once. If it is rejected, do not retry; reply REJECTED.","commandId":"01a11a6d-8566-7000-8ad1-6269ac187a29"}},"emittedAtMs":1791444747676}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:28","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"3b28955d-a87a-4acb-97e9-14cad87fd41f","sequence":148},"last":{"id":"3b28955d-a87a-4acb-97e9-14cad87fd41f","sequence":148}},"item":{"itemId":"3b28955d-a87a-4acb-97e9-14cad87fd41f","kind":"reminderChild","turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:32:27.787099Z","fallbackText":"Reminder child session","childSessionId":"efd21bf1-4fd1-49a2-9c55-91b7fc099765","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6d-85ed-7b91-8d91-c4ce76b6dfef"}},"emittedAtMs":1791444747794}} {"type":"emit_inbound","label":"session/tokenUsage","frame":{"jsonrpc":"2.0","method":"session/tokenUsage","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:29","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b032c01d-eae1-437e-910c-8756aedc6084","sequence":164},"last":{"id":"b032c01d-eae1-437e-910c-8756aedc6084","sequence":164}},"turnId":"01a11a6d-8566-7000-8ad1-6269ac187a29","modelId":"muse-spark-1.3-contributor","usage":{"inputTokens":33401,"outputTokens":201,"cachedTokens":33009,"cacheWriteTokens":0,"cacheReadTokens":33009,"reasoningTokens":108},"promptTokens":33401,"totalTokens":33602,"finishReason":"tool_calls","durationMs":2522,"cumulative":{"promptTokens":99045,"outputTokens":538,"totalTokens":99583,"cacheReadTokens":65506,"cost":{"usd":0.003592512,"partial":false}}},"emittedAtMs":1791444750395}} {"type":"emit_inbound","label":"session/contextUsage","frame":{"jsonrpc":"2.0","method":"session/contextUsage","params":{"sessionId":"01a11a6d-39bb-7000-8c93-4594c98e6a11","viewCursor":"v:01a11a6d-39bb-7000-8c93-4594c98e6a11:30","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-39bb-7000-8c93-4594c98e6a11"},"first":{"id":"b032c01d-eae1-437e-910c-8756aedc6084","sequence":164},"last":{"id":"b032c01d-eae1-437e-910c-8756aedc6084","sequence":164}},"windowTokens":1007997,"usedTokens":33602,"pressure":"normal"},"emittedAtMs":1791444750395}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_approval/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_approval/muse_transcript.ndjson index b602a51dff03d..cc6f79c9a3130 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_approval/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_approval/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","path":"~/.local/share/muse/sessions/2026/10/11/01a12d1d-a1b7-7000-9697-d430c2e2afd8/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:37:59.14136Z","updatedAt":"2026-10-11T22:37:59.141364Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"promptUnmatched","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791758279294}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","path":"~/.local/share/muse/sessions/2026/10/11/01a12d1d-a1b7-7000-9697-d430c2e2afd8/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:37:59.14136Z","updatedAt":"2026-10-11T22:37:59.141364Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"promptUnmatched","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:1","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"ed95a9db-eada-4751-a7e6-22ccc9bdabd5","sequence":5},"last":{"id":"ed95a9db-eada-4751-a7e6-22ccc9bdabd5","sequence":5}},"mode":"promptUnmatched","source":"approvalReconfigure","commandId":"01a12d1d-a1b8-7000-8d6c-f1324b724cdb","clientName":"t3_code"},"emittedAtMs":1791758279297}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:2","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"4f170229-d428-4a64-9516-7a355ff3bcdb","sequence":15},"last":{"id":"4f170229-d428-4a64-9516-7a355ff3bcdb","sequence":15}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791758279531}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","status":"accepted","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","status":"running","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:3"},"emittedAtMs":1791758279541}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:3","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17},"last":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17}},"turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"},"emittedAtMs":1791758279542}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:4","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17},"last":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17}},"item":{"itemId":"a4b3de8d-842b-4065-8769-aed18e62e9fc","kind":"userMessage","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":1,"status":"completed","recordedAt":"2026-10-11T22:37:59.531477Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nDo not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}},"emittedAtMs":1791758279542}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:5","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"5a63e348-5bf6-4262-b37f-d31d307ea6d8","sequence":19},"last":{"id":"5a63e348-5bf6-4262-b37f-d31d307ea6d8","sequence":19}},"item":{"itemId":"a4b3de8d-842b-4065-8769-aed18e62e9fc","kind":"userMessage","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":2,"status":"completed","recordedAt":"2026-10-11T22:37:59.545908Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nDo not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","displayText":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}},"emittedAtMs":1791758279553}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:4","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17},"last":{"id":"429e8ecf-d532-4b14-a303-b9f832c36a51","sequence":17}},"item":{"itemId":"a4b3de8d-842b-4065-8769-aed18e62e9fc","kind":"userMessage","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":1,"status":"completed","recordedAt":"2026-10-11T22:37:59.531477Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nDo not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}},"emittedAtMs":1791758279542}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:5","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"5a63e348-5bf6-4262-b37f-d31d307ea6d8","sequence":19},"last":{"id":"5a63e348-5bf6-4262-b37f-d31d307ea6d8","sequence":19}},"item":{"itemId":"a4b3de8d-842b-4065-8769-aed18e62e9fc","kind":"userMessage","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":2,"status":"completed","recordedAt":"2026-10-11T22:37:59.545908Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nDo not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","displayText":"Do not run any commands yourself. Spawn one subagent whose only task is to run this exact shell command: printf 'subagent approval fixture' > subagent-approval.txt and then reply with exactly: Written. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d1d-a348-7000-ae97-9d27b5f80bce"}},"emittedAtMs":1791758279553}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:6","sourceRange":{"stream":{"kind":"session","id":"01a12d1d-a1b7-7000-9697-d430c2e2afd8"},"first":{"id":"d55286a9-a9e2-4d7b-be97-d44360bcca2b","sequence":37},"last":{"id":"d55286a9-a9e2-4d7b-be97-d44360bcca2b","sequence":37}},"item":{"itemId":"d55286a9-a9e2-4d7b-be97-d44360bcca2b","kind":"reminderChild","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":1,"status":"inProgress","recordedAt":"2026-10-11T22:37:59.660065Z","fallbackText":"Reminder child session","childSessionId":"0779d605-54b8-45dc-90b6-91dc58b9a52c","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a12d1d-a3d4-7712-8b98-26f3c4f9bf38"}},"emittedAtMs":1791758279666}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:6.1","item":{"itemId":"21b74b34-fe9f-457c-83f0-cf49257b5948","kind":"reasoning","turnId":"01a12d1d-a348-7000-ae97-9d27b5f80bce","revision":1,"status":"inProgress","summary":[""]}},"emittedAtMs":1791758282481}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a12d1d-a1b7-7000-9697-d430c2e2afd8","viewCursor":"v:01a12d1d-a1b7-7000-9697-d430c2e2afd8:6.2","itemId":"21b74b34-fe9f-457c-83f0-cf49257b5948","field":"summary.0","delta":"Spawning a subagent via muse_subagent_spawn to run a shell command and return its exact output, while avoiding direct command execution."},"emittedAtMs":1791758282481}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_interrupt/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_interrupt/muse_transcript.ndjson index d50e22967dad4..dc74692f2052d 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_interrupt/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/muse_subagent_interrupt/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","path":"~/.local/share/muse/sessions/2026/10/11/01a12d28-7788-7000-a946-c407f380868e/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:49:49.235205Z","updatedAt":"2026-10-11T22:49:49.235209Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791758989388}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","path":"~/.local/share/muse/sessions/2026/10/11/01a12d28-7788-7000-a946-c407f380868e/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:49:49.235205Z","updatedAt":"2026-10-11T22:49:49.235209Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:1","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"29d4e769-78f7-48fa-90cd-3ac9f8fa04bf","sequence":5},"last":{"id":"29d4e769-78f7-48fa-90cd-3ac9f8fa04bf","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a12d28-7788-7001-a223-c10e52187cdf","clientName":"t3_code"},"emittedAtMs":1791758989391}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:2","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"660e2cfd-318a-4d2d-87bb-0b1375d79fdf","sequence":15},"last":{"id":"660e2cfd-318a-4d2d-87bb-0b1375d79fdf","sequence":15}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791758989619}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a12d28-7912-7000-993a-afd019e6ac5c","status":"accepted","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","status":"running","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:3"},"emittedAtMs":1791758989632}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:3","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17},"last":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17}},"turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"},"emittedAtMs":1791758989632}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:4","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17},"last":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17}},"item":{"itemId":"5e234d9d-0744-4757-9629-761ea4e7f062","kind":"userMessage","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":1,"status":"completed","recordedAt":"2026-10-11T22:49:49.620067Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nSpawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}},"emittedAtMs":1791758989632}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:5","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5724636e-9398-4d23-87ac-1e9a2b83ad9f","sequence":19},"last":{"id":"5724636e-9398-4d23-87ac-1e9a2b83ad9f","sequence":19}},"item":{"itemId":"5e234d9d-0744-4757-9629-761ea4e7f062","kind":"userMessage","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":2,"status":"completed","recordedAt":"2026-10-11T22:49:49.635395Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nSpawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","displayText":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}},"emittedAtMs":1791758989642}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:4","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17},"last":{"id":"5cb199bd-04e5-40d9-a299-8760be80e0a4","sequence":17}},"item":{"itemId":"5e234d9d-0744-4757-9629-761ea4e7f062","kind":"userMessage","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":1,"status":"completed","recordedAt":"2026-10-11T22:49:49.620067Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nSpawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}},"emittedAtMs":1791758989632}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:5","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"5724636e-9398-4d23-87ac-1e9a2b83ad9f","sequence":19},"last":{"id":"5724636e-9398-4d23-87ac-1e9a2b83ad9f","sequence":19}},"item":{"itemId":"5e234d9d-0744-4757-9629-761ea4e7f062","kind":"userMessage","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":2,"status":"completed","recordedAt":"2026-10-11T22:49:49.635395Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nSpawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","displayText":"Spawn one subagent whose only task is to run the shell command `sleep 30` and then reply with exactly: Done. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d28-7912-7000-993a-afd019e6ac5c"}},"emittedAtMs":1791758989642}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:6","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"44afb415-d6e4-4d51-9002-4deb196d463a","sequence":36},"last":{"id":"44afb415-d6e4-4d51-9002-4deb196d463a","sequence":36}},"item":{"itemId":"44afb415-d6e4-4d51-9002-4deb196d463a","kind":"reminderChild","turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","revision":1,"status":"inProgress","recordedAt":"2026-10-11T22:49:49.743418Z","fallbackText":"Reminder child session","childSessionId":"4dd3511e-2115-4fbb-a4c4-383cbb383d63","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a12d28-7997-79d0-8a69-8ac0acf92a56"}},"emittedAtMs":1791758989750}} {"type":"emit_inbound","label":"session/tokenUsage","frame":{"jsonrpc":"2.0","method":"session/tokenUsage","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:7","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"c94f5484-20b7-4057-a736-303e97bc097c","sequence":52},"last":{"id":"c94f5484-20b7-4057-a736-303e97bc097c","sequence":52}},"turnId":"01a12d28-7912-7000-993a-afd019e6ac5c","modelId":"muse-spark-1.3-contributor","usage":{"inputTokens":32911,"outputTokens":264,"cachedTokens":0,"cacheWriteTokens":0,"cacheReadTokens":0,"reasoningTokens":143},"promptTokens":32911,"totalTokens":33175,"finishReason":"tool_calls","durationMs":3651,"cumulative":{"promptTokens":32911,"outputTokens":264,"totalTokens":33175,"cost":{"usd":0.0033439000000000003,"partial":false}}},"emittedAtMs":1791758993485}} {"type":"emit_inbound","label":"session/contextUsage","frame":{"jsonrpc":"2.0","method":"session/contextUsage","params":{"sessionId":"01a12d28-7788-7000-a946-c407f380868e","viewCursor":"v:01a12d28-7788-7000-a946-c407f380868e:8","sourceRange":{"stream":{"kind":"session","id":"01a12d28-7788-7000-a946-c407f380868e"},"first":{"id":"c94f5484-20b7-4057-a736-303e97bc097c","sequence":52},"last":{"id":"c94f5484-20b7-4057-a736-303e97bc097c","sequence":52}},"windowTokens":1007997,"usedTokens":33175,"pressure":"normal"},"emittedAtMs":1791758993485}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/muse_workflow/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/muse_workflow/muse_transcript.ndjson index cccd50395bb08..f2bb827e4e21f 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/muse_workflow/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/muse_workflow/muse_transcript.ndjson @@ -8,13 +8,13 @@ {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","path":"~/.local/share/muse/sessions/2026/10/11/01a12d5d-405f-7000-89a3-5aa62af6ea23/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T23:47:28.587276Z","updatedAt":"2026-10-11T23:47:28.587281Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:2"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:1","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"0c13b14b-877c-4c5b-b05f-8ad250dfd381","sequence":5},"last":{"id":"0c13b14b-877c-4c5b-b05f-8ad250dfd381","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a12d5d-4060-7000-b6cd-aaabe26f80de","clientName":"t3_code"},"emittedAtMs":1791762448807}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:2","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"28b1f3f4-4448-4a21-a1a0-2df72ad1d326","sequence":6},"last":{"id":"28b1f3f4-4448-4a21-a1a0-2df72ad1d326","sequence":6}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791762448807}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it."}],"displayText":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it."}],"displayText":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:3","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"3f1880e1-81a0-4f21-b536-6b4fa5cf9d12","sequence":16},"last":{"id":"3f1880e1-81a0-4f21-b536-6b4fa5cf9d12","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791762449097}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","status":"accepted","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","status":"running","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:4"},"emittedAtMs":1791762449102}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:4","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18},"last":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18}},"turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"},"emittedAtMs":1791762449103}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:5","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18},"last":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18}},"item":{"itemId":"0c1ceda9-41f7-4c3e-990e-4c53873c7ef5","kind":"userMessage","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":1,"status":"completed","recordedAt":"2026-10-11T23:47:29.098117Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nUse your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}},"emittedAtMs":1791762449103}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:6","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"0411a5c8-2c8a-4c9e-bb67-25fa3cef21a3","sequence":20},"last":{"id":"0411a5c8-2c8a-4c9e-bb67-25fa3cef21a3","sequence":20}},"item":{"itemId":"0c1ceda9-41f7-4c3e-990e-4c53873c7ef5","kind":"userMessage","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":2,"status":"completed","recordedAt":"2026-10-11T23:47:29.107019Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nUse your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","displayText":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}},"emittedAtMs":1791762449131}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:5","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18},"last":{"id":"7a830825-f571-4781-8e4f-a4ef5e085bb7","sequence":18}},"item":{"itemId":"0c1ceda9-41f7-4c3e-990e-4c53873c7ef5","kind":"userMessage","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":1,"status":"completed","recordedAt":"2026-10-11T23:47:29.098117Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nUse your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}},"emittedAtMs":1791762449103}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:6","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"0411a5c8-2c8a-4c9e-bb67-25fa3cef21a3","sequence":20},"last":{"id":"0411a5c8-2c8a-4c9e-bb67-25fa3cef21a3","sequence":20}},"item":{"itemId":"0c1ceda9-41f7-4c3e-990e-4c53873c7ef5","kind":"userMessage","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":2,"status":"completed","recordedAt":"2026-10-11T23:47:29.107019Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nUse your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","displayText":"Use your workflow tool to launch a workflow with exactly TWO agents: one labeled 'ping' that replies with the word PING, and one labeled 'pong' that replies with the word PONG. Launch it and end your turn; do not wait for it.","commandId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5"}},"emittedAtMs":1791762449131}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:7","sourceRange":{"stream":{"kind":"session","id":"01a12d5d-405f-7000-89a3-5aa62af6ea23"},"first":{"id":"95574bfe-853f-44cc-81f6-88b05d6c8c16","sequence":38},"last":{"id":"95574bfe-853f-44cc-81f6-88b05d6c8c16","sequence":38}},"item":{"itemId":"95574bfe-853f-44cc-81f6-88b05d6c8c16","kind":"reminderChild","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":1,"status":"inProgress","recordedAt":"2026-10-11T23:47:29.328939Z","fallbackText":"Reminder child session","childSessionId":"b16dbb52-7f62-4a5a-84c2-f68d7ea22b18","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a12d5d-4375-7d72-b5b2-4b096d0df507"}},"emittedAtMs":1791762449336}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:7.1","item":{"itemId":"3ce724bc-dacf-400e-af9a-995104717cb1","kind":"reasoning","turnId":"01a12d5d-42c3-7000-ab94-c7f56ea457e5","revision":1,"status":"inProgress","summary":[""]}},"emittedAtMs":1791762451554}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a12d5d-405f-7000-89a3-5aa62af6ea23","viewCursor":"v:01a12d5d-405f-7000-89a3-5aa62af6ea23:7.2","itemId":"3ce724bc-dacf-400e-af9a-995104717cb1","field":"summary.0","delta":"Launching a two-agent ping-pong workflow via the workflow tool while skipping skill reading for a trivial task."},"emittedAtMs":1791762451554}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/simple/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/simple/muse_transcript.ndjson index f3c0d1c56d559..66f80fb63eaa8 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/simple/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/simple/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6a-bbb5-7000-ad0b-0c9e728406e5/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:29:25.018803Z","updatedAt":"2026-10-08T07:29:25.018807Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791444565334}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6a-bbb5-7000-ad0b-0c9e728406e5/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:29:25.018803Z","updatedAt":"2026-10-08T07:29:25.018807Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:1","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"cfd043e8-8728-493d-ba28-252ef91e8a80","sequence":5},"last":{"id":"cfd043e8-8728-493d-ba28-252ef91e8a80","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a11a6a-bbb6-7000-b1a4-a01ea59676ef","clientName":"t3_code"},"emittedAtMs":1791444565340}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Respond with the following text: fixture simple ok"}],"displayText":"Respond with the following text: fixture simple ok","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Respond with the following text: fixture simple ok"}],"displayText":"Respond with the following text: fixture simple ok","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:2","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"0f64f4ea-a08c-43e1-af53-cf85cee1a11c","sequence":16},"last":{"id":"0f64f4ea-a08c-43e1-af53-cf85cee1a11c","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444565612}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","status":"accepted","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","status":"running","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:3"},"emittedAtMs":1791444565635}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:3","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18},"last":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18}},"turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"},"emittedAtMs":1791444565635}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18},"last":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18}},"item":{"itemId":"23294b54-8e67-4896-a7f3-681119e1ddbe","kind":"userMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":1,"status":"completed","recordedAt":"2026-10-08T07:29:25.627042Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with the following text: fixture simple ok","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}},"emittedAtMs":1791444565635}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"8c00aa21-7809-4cb2-89f7-b499aaa8746d","sequence":20},"last":{"id":"8c00aa21-7809-4cb2-89f7-b499aaa8746d","sequence":20}},"item":{"itemId":"23294b54-8e67-4896-a7f3-681119e1ddbe","kind":"userMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":2,"status":"completed","recordedAt":"2026-10-08T07:29:25.643029Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nRespond with the following text: fixture simple ok","displayText":"Respond with the following text: fixture simple ok","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}},"emittedAtMs":1791444565652}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18},"last":{"id":"1f33a521-7829-4cc3-97db-a3b5a6e7deaf","sequence":18}},"item":{"itemId":"23294b54-8e67-4896-a7f3-681119e1ddbe","kind":"userMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":1,"status":"completed","recordedAt":"2026-10-08T07:29:25.627042Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with the following text: fixture simple ok","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}},"emittedAtMs":1791444565635}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"8c00aa21-7809-4cb2-89f7-b499aaa8746d","sequence":20},"last":{"id":"8c00aa21-7809-4cb2-89f7-b499aaa8746d","sequence":20}},"item":{"itemId":"23294b54-8e67-4896-a7f3-681119e1ddbe","kind":"userMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":2,"status":"completed","recordedAt":"2026-10-08T07:29:25.643029Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nRespond with the following text: fixture simple ok","displayText":"Respond with the following text: fixture simple ok","commandId":"01a11a6a-be41-7000-8db9-1d84c1b8be48"}},"emittedAtMs":1791444565652}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:6","sourceRange":{"stream":{"kind":"session","id":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5"},"first":{"id":"ed1680a5-6f99-4b0f-b9ea-d12193f0de1f","sequence":37},"last":{"id":"ed1680a5-6f99-4b0f-b9ea-d12193f0de1f","sequence":37}},"item":{"itemId":"ed1680a5-6f99-4b0f-b9ea-d12193f0de1f","kind":"reminderChild","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:29:25.781511Z","fallbackText":"Reminder child session","childSessionId":"9785bf9d-382d-4781-ab3b-3817c9e6a604","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6a-bef1-7032-974a-059b8765c487"}},"emittedAtMs":1791444565789}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:6.1","item":{"itemId":"0a68e576-f554-4700-8853-a68ad2b7179b","kind":"agentMessage","turnId":"01a11a6a-be41-7000-8db9-1d84c1b8be48","revision":1,"status":"inProgress","text":""}},"emittedAtMs":1791444567707}} {"type":"emit_inbound","label":"item/delta","frame":{"jsonrpc":"2.0","method":"item/delta","params":{"sessionId":"01a11a6a-bbb5-7000-ad0b-0c9e728406e5","viewCursor":"v:01a11a6a-bbb5-7000-ad0b-0c9e728406e5:6.2","itemId":"0a68e576-f554-4700-8853-a68ad2b7179b","field":"text","delta":"fixture simple ok"},"emittedAtMs":1791444567707}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/subagent_v2/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/subagent_v2/muse_transcript.ndjson index f15d54adf25d4..3c0fb5b150e51 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/subagent_v2/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/subagent_v2/muse_transcript.ndjson @@ -7,13 +7,13 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","path":"~/.local/share/muse/sessions/2026/10/11/01a12d17-75fa-7000-9157-e8365ce94492/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:31:14.73121Z","updatedAt":"2026-10-11T22:31:14.731214Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791757874894}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","path":"~/.local/share/muse/sessions/2026/10/11/01a12d17-75fa-7000-9157-e8365ce94492/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-11T22:31:14.73121Z","updatedAt":"2026-10-11T22:31:14.731214Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"sideFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:1","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"94d1e1ed-1d3e-4cd5-91bf-a51394fc8a0a","sequence":5},"last":{"id":"94d1e1ed-1d3e-4cd5-91bf-a51394fc8a0a","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a12d17-75fa-7001-a940-79b5d7c648a4","clientName":"t3_code"},"emittedAtMs":1791757874898}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said."}],"displayText":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:2","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"1dc98410-05c3-4618-a431-3638055e20df","sequence":15},"last":{"id":"1dc98410-05c3-4618-a431-3638055e20df","sequence":15}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791757875120}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a12d17-7790-7000-967d-3f2246f38a60","status":"accepted","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","startedNewTurn":true,"disposition":"started"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","status":"running","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:3"},"emittedAtMs":1791757875131}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:3","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17},"last":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17}},"turnId":"01a12d17-7790-7000-967d-3f2246f38a60","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"},"emittedAtMs":1791757875132}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:4","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17},"last":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17}},"item":{"itemId":"95c179c8-e8f8-4629-80df-e28243a234ea","kind":"userMessage","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":1,"status":"completed","recordedAt":"2026-10-11T22:31:15.120499Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nSpawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}},"emittedAtMs":1791757875132}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:5","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"dcf03486-5149-4377-b325-dea3e118628a","sequence":19},"last":{"id":"dcf03486-5149-4377-b325-dea3e118628a","sequence":19}},"item":{"itemId":"95c179c8-e8f8-4629-80df-e28243a234ea","kind":"userMessage","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":2,"status":"completed","recordedAt":"2026-10-11T22:31:15.136047Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nSpawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","displayText":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}},"emittedAtMs":1791757875144}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:4","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17},"last":{"id":"766cb840-a6f1-41f2-8c7e-8c372d0470cd","sequence":17}},"item":{"itemId":"95c179c8-e8f8-4629-80df-e28243a234ea","kind":"userMessage","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":1,"status":"completed","recordedAt":"2026-10-11T22:31:15.120499Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nSpawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}},"emittedAtMs":1791757875132}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:5","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"dcf03486-5149-4377-b325-dea3e118628a","sequence":19},"last":{"id":"dcf03486-5149-4377-b325-dea3e118628a","sequence":19}},"item":{"itemId":"95c179c8-e8f8-4629-80df-e28243a234ea","kind":"userMessage","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":2,"status":"completed","recordedAt":"2026-10-11T22:31:15.136047Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nSpawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","displayText":"Spawn one subagent whose only task is to reply with exactly: Hello. Wait for it to finish, then reply with exactly what it said.","commandId":"01a12d17-7790-7000-967d-3f2246f38a60"}},"emittedAtMs":1791757875144}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:6","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"599a220a-d212-4a81-a60d-7284ff290284","sequence":36},"last":{"id":"599a220a-d212-4a81-a60d-7284ff290284","sequence":36}},"item":{"itemId":"599a220a-d212-4a81-a60d-7284ff290284","kind":"reminderChild","turnId":"01a12d17-7790-7000-967d-3f2246f38a60","revision":1,"status":"inProgress","recordedAt":"2026-10-11T22:31:15.249949Z","fallbackText":"Reminder child session","childSessionId":"0b3f4f01-850a-43bd-af36-9b011699354f","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a12d17-7819-7212-8057-3558c2bdd46d"}},"emittedAtMs":1791757875258}} {"type":"emit_inbound","label":"session/tokenUsage","frame":{"jsonrpc":"2.0","method":"session/tokenUsage","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:7","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"9198dd8d-eede-487a-9109-a48d2563c371","sequence":51},"last":{"id":"9198dd8d-eede-487a-9109-a48d2563c371","sequence":51}},"turnId":"01a12d17-7790-7000-967d-3f2246f38a60","modelId":"muse-spark-1.3-contributor","usage":{"inputTokens":32900,"outputTokens":129,"cachedTokens":0,"cacheWriteTokens":0,"cacheReadTokens":0,"reasoningTokens":36},"promptTokens":32900,"totalTokens":33029,"finishReason":"tool_calls","durationMs":2354,"cumulative":{"promptTokens":32900,"outputTokens":129,"totalTokens":33029,"cost":{"usd":0.0033158000000000003,"partial":false}}},"emittedAtMs":1791757877764}} {"type":"emit_inbound","label":"session/contextUsage","frame":{"jsonrpc":"2.0","method":"session/contextUsage","params":{"sessionId":"01a12d17-75fa-7000-9157-e8365ce94492","viewCursor":"v:01a12d17-75fa-7000-9157-e8365ce94492:8","sourceRange":{"stream":{"kind":"session","id":"01a12d17-75fa-7000-9157-e8365ce94492"},"first":{"id":"9198dd8d-eede-487a-9109-a48d2563c371","sequence":51},"last":{"id":"9198dd8d-eede-487a-9109-a48d2563c371","sequence":51}},"windowTokens":1007997,"usedTokens":33029,"pressure":"normal"},"emittedAtMs":1791757877764}} diff --git a/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt/muse_transcript.ndjson b/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt/muse_transcript.ndjson index 639e35584e22f..8edacb55950be 100644 --- a/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt/muse_transcript.ndjson +++ b/apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt/muse_transcript.ndjson @@ -7,14 +7,14 @@ {"type":"emit_inbound","label":"session/started","frame":{"jsonrpc":"2.0","method":"session/started","params":{"session":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6d-2d3d-7000-8178-411fdb90bc34/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:32:05.094285Z","updatedAt":"2026-10-08T07:32:05.094289Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"}},"emittedAtMs":1791444725265}} {"type":"emit_inbound","label":"response:session/start","frame":{"jsonrpc":"2.0","id":2,"result":{"session":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","path":"~/.local/share/muse/sessions/2026/10/08/01a11a6d-2d3d-7000-8178-411fdb90bc34/session.jsonl","status":"idle","activeTurnId":null,"createdAt":"2026-10-08T07:32:05.094285Z","updatedAt":"2026-10-08T07:32:05.094289Z","workspaceRoot":"","providerId":"meta","modelId":"muse-spark-1.3-contributor","turnCount":0,"forkedFrom":null,"approvalMode":{"mode":"allowAll","source":"startup","lastCommandId":null},"createdByClient":{"name":"t3_code","version":"1"},"kind":"root"},"viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:1"}}} {"type":"emit_inbound","label":"session/approvalModeChanged","frame":{"jsonrpc":"2.0","method":"session/approvalModeChanged","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:1","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"9865268e-0a39-43a0-89d0-251a820ec8a3","sequence":5},"last":{"id":"9865268e-0a39-43a0-89d0-251a820ec8a3","sequence":5}},"mode":"allowAll","source":"approvalReconfigure","commandId":"01a11a6d-2d3e-7000-b0e7-f04c53ee070b","clientName":"t3_code"},"emittedAtMs":1791444725268}} -{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\n"},{"type":"text","text":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally."}],"displayText":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}}} +{"type":"expect_outbound","label":"turn/start","frame":{"jsonrpc":"2.0","id":3,"method":"turn/start","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","input":[{"type":"text","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\n"},{"type":"text","text":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally."}],"displayText":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","workspaceRoots":[""],"ifBusy":"queue","reasoningEffort":"high","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}}} {"type":"emit_inbound","label":"session/branchChanged","frame":{"jsonrpc":"2.0","method":"session/branchChanged","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:2","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"531b0e9e-c96d-4fa5-a6dd-5cc7d4569543","sequence":16},"last":{"id":"531b0e9e-c96d-4fa5-a6dd-5cc7d4569543","sequence":16}},"workspaceRoot":"","branch":"main","vcs":"git"},"emittedAtMs":1791444725509}} {"type":"emit_inbound","label":"response:turn/start","frame":{"jsonrpc":"2.0","id":3,"result":{"commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","status":"accepted","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","startedNewTurn":true,"disposition":"started"}}} {"type":"expect_outbound","label":"turn/interrupt","frame":{"jsonrpc":"2.0","id":4,"method":"turn/interrupt","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","commandId":"01a11a6d-2f0b-7000-a1d4-359cf54e471a"}}} {"type":"emit_inbound","label":"session/statusChanged","frame":{"jsonrpc":"2.0","method":"session/statusChanged","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","status":"running","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:3"},"emittedAtMs":1791444725522}} {"type":"emit_inbound","label":"turn/started","frame":{"jsonrpc":"2.0","method":"turn/started","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:3","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18},"last":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18}},"turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"},"emittedAtMs":1791444725523}} -{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18},"last":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18}},"item":{"itemId":"cfa989ad-2359-4da2-9fca-562855cc0ae8","kind":"userMessage","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:05.510508Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nDo not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}},"emittedAtMs":1791444725523}} -{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"5db75d92-0e52-4bce-bb81-40e254b40bc3","sequence":20},"last":{"id":"5db75d92-0e52-4bce-bb81-40e254b40bc3","sequence":20}},"item":{"itemId":"cfa989ad-2359-4da2-9fca-562855cc0ae8","kind":"userMessage","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:05.530992Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs. pull_request_create links for you.\nDo not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","displayText":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}},"emittedAtMs":1791444725539}} +{"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:4","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18},"last":{"id":"50d1fd8c-232b-47b9-9bfa-9b543961bbc9","sequence":18}},"item":{"itemId":"cfa989ad-2359-4da2-9fca-562855cc0ae8","kind":"userMessage","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":1,"status":"completed","recordedAt":"2026-10-08T07:32:05.510508Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nDo not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}},"emittedAtMs":1791444725523}} +{"type":"emit_inbound","label":"item/updated","frame":{"jsonrpc":"2.0","method":"item/updated","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:5","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"5db75d92-0e52-4bce-bb81-40e254b40bc3","sequence":20},"last":{"id":"5db75d92-0e52-4bce-bb81-40e254b40bc3","sequence":20}},"item":{"itemId":"cfa989ad-2359-4da2-9fca-562855cc0ae8","kind":"userMessage","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":2,"status":"completed","recordedAt":"2026-10-08T07:32:05.530992Z","text":"In case you're asked: you are running in T3 Code through the Muse Code harness, as muse-spark-1.3-contributor with high reasoning effort. No need to mention this otherwise. You can embed images, video, and audio using Markdown image syntax with absolute file paths. Link every other file you cite or produce by its absolute path, as in [http.ts:42](/abs/path/http.ts:42), so the user can open it in T3 Code's files viewer.\n\n\nLink each PR you create, take over, or push to: link_pull_request, or link_pull_requests for several, full URL, every stack layer, as your next call. Host tools don't link for you. If linking fails, say so. Skip PRs you only read or review and PRs named only as background. Call watch_pull_request when asked to monitor or babysit, or when work you were asked to do, such as a merge, still waits on checks or reviews, even after an earlier unwatch. Finish any needed branch update and push first, then watch and end your turn. Otherwise act now or hand back. Delegated children never watch, even if the brief says babysit. After PR work, a child's last line is: PR , head , checks and reviews . Links and watches persist across pushes. For PR waits, no sleep, loops, gh --watch, Monitor or ScheduleWakeup. Before handback, call unwatch_pull_request on any PR you watch. When pull_request_* tools are listed, use them, not gh or cafe, to read, list, open, edit, review, reply on, close, or merge PRs, and ci_logs and ci_rerun for their checks. pull_request_create links for you.\nDo not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","displayText":"Do not answer immediately. First run the local shell command `sleep 30`, then respond with exactly: interrupt fixture should not finish naturally.","commandId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}},"emittedAtMs":1791444725539}} {"type":"emit_inbound","label":"item/started","frame":{"jsonrpc":"2.0","method":"item/started","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:6","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"ae604001-22b9-479c-a45b-3dcfed45996c","sequence":37},"last":{"id":"ae604001-22b9-479c-a45b-3dcfed45996c","sequence":37}},"item":{"itemId":"ae604001-22b9-479c-a45b-3dcfed45996c","kind":"reminderChild","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":1,"status":"inProgress","recordedAt":"2026-10-08T07:32:05.638142Z","fallbackText":"Reminder child session","childSessionId":"1f58196a-6bf6-42da-9499-8f53a439e6e1","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6d-2f65-70b1-bb8f-69b33bb4fc62"}},"emittedAtMs":1791444725646}} {"type":"emit_inbound","label":"response:turn/interrupt","frame":{"jsonrpc":"2.0","id":4,"result":{"commandId":"01a11a6d-2f0b-7000-a1d4-359cf54e471a","status":"accepted","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5"}}} {"type":"emit_inbound","label":"item/completed","frame":{"jsonrpc":"2.0","method":"item/completed","params":{"sessionId":"01a11a6d-2d3d-7000-8178-411fdb90bc34","viewCursor":"v:01a11a6d-2d3d-7000-8178-411fdb90bc34:7","sourceRange":{"stream":{"kind":"session","id":"01a11a6d-2d3d-7000-8178-411fdb90bc34"},"first":{"id":"ae604001-22b9-479c-a45b-3dcfed45996c","sequence":37},"last":{"id":"3962d8d1-4608-4dda-8fdc-39a1804474aa","sequence":50}},"item":{"itemId":"ae604001-22b9-479c-a45b-3dcfed45996c","kind":"reminderChild","turnId":"01a11a6d-2ee0-7000-be19-f20eda7ce5d5","revision":2,"status":"cancelled","recordedAt":"2026-10-08T07:32:05.727236Z","fallbackText":"Reminder child session","failureReason":"main run cancelled","childSessionId":"1f58196a-6bf6-42da-9499-8f53a439e6e1","reminderAgentId":"skill-reminder","generationId":1,"taskId":"01a11a6d-2f65-70b1-bb8f-69b33bb4fc62"}},"emittedAtMs":1791444725734}} From 2af412e49aaa8f9a0944a553e6c0750b676a5d1b Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Thu, 12 Nov 2026 16:40:41 -0800 Subject: [PATCH 14/16] chore(server): agent context budget and eval forge log after the review tool Co-Authored-By: Claude Opus 5.5 --- evals/mcp-tool-use/run.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/evals/mcp-tool-use/run.ts b/evals/mcp-tool-use/run.ts index 4a7b168f8bbf7..e972474a2a142 100644 --- a/evals/mcp-tool-use/run.ts +++ b/evals/mcp-tool-use/run.ts @@ -1924,6 +1924,7 @@ const FORGE_WRITE_TOOLS = new Set([ "pull_request_create", "pull_request_update", "pull_request_respond", + "pull_request_review", "pull_request_merge", "ci_rerun", ]); From a3e399bf44afc7ff72d31fcab225c283d162a7b7 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Thu, 12 Nov 2026 17:46:40 -0800 Subject: [PATCH 15/16] feat(server): CI tools are listed only where the thread's project is on a supported forge Co-Authored-By: Claude Opus 5.5 --- apps/server/src/mcp/McpHttpServer.test.ts | 6 +++++- apps/server/src/mcp/toolkits/pullRequests/tools.ts | 6 ++++-- apps/server/src/provider/agentContextBudget.json | 6 +++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/server/src/mcp/McpHttpServer.test.ts b/apps/server/src/mcp/McpHttpServer.test.ts index 27664e0d7204c..1619fbc06c7ee 100644 --- a/apps/server/src/mcp/McpHttpServer.test.ts +++ b/apps/server/src/mcp/McpHttpServer.test.ts @@ -1618,13 +1618,17 @@ it.effect( "pull_request_read", "pull_request_review", "pull_request_merge", + "ci_logs", + "ci_rerun", ]), ); const noForgeTools = yield* listedTools("noForgeToken"); expect(noForgeTools).toEqual( expect.arrayContaining(["link_pull_request", "watch_pull_request"]), ); - expect(noForgeTools.filter((name) => name.startsWith("pull_request_"))).toEqual([]); + expect( + noForgeTools.filter((name) => name.startsWith("pull_request_") || name.startsWith("ci_")), + ).toEqual([]); }), ).pipe(Effect.provide(NodeHttpServer.layerTest)), ); diff --git a/apps/server/src/mcp/toolkits/pullRequests/tools.ts b/apps/server/src/mcp/toolkits/pullRequests/tools.ts index 352763e1fa4a8..cd74358c8daa2 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/tools.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/tools.ts @@ -810,7 +810,8 @@ const CheckLogsTool = Tool.make("ci_logs", { .annotate(Tool.Readonly, true) .annotate(Tool.Destructive, false) .annotate(Tool.Idempotent, true) - .annotate(Tool.OpenWorld, true); + .annotate(Tool.OpenWorld, true) + .annotate(McpSchema.EnabledWhen, McpInvocationContext.hiddenWithoutForge); const RerunChecksTool = Tool.make("ci_rerun", { description: "Re-run a PR's failing checks, or one named check.", @@ -826,7 +827,8 @@ const RerunChecksTool = Tool.make("ci_rerun", { .annotate(Tool.Readonly, false) .annotate(Tool.Destructive, false) .annotate(Tool.Idempotent, false) - .annotate(Tool.OpenWorld, true); + .annotate(Tool.OpenWorld, true) + .annotate(McpSchema.EnabledWhen, McpInvocationContext.hiddenWithoutForge); export const PullRequestsToolkit = Toolkit.make( LinkPullRequestTool, diff --git a/apps/server/src/provider/agentContextBudget.json b/apps/server/src/provider/agentContextBudget.json index 185a8629ae5ab..7422f44e91cc6 100644 --- a/apps/server/src/provider/agentContextBudget.json +++ b/apps/server/src/provider/agentContextBudget.json @@ -1,5 +1,5 @@ { - "claudeSystemAppendBytes": 5762, - "codexAdditionalContextBytes": 7492, - "mcpToolListJsonBytes": 52026 + "claudeSystemAppendBytes": 5805, + "codexAdditionalContextBytes": 7535, + "mcpToolListJsonBytes": 52769 } From 6110f02a9f8f868296e7f7c9dcd7d8ef1ec6d55f Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Thu, 12 Nov 2026 17:46:43 -0800 Subject: [PATCH 16/16] fix(server): a CI rerun keeps a watched thread working until its checks run ci_rerun left a watch's finished-checks answer in place, so a thread whose agent re-ran checks and ended its turn returned to the inbox with a completion alert until a read saw the checks running. A rerun now clears that answer for the calling thread, as a push from its shell does. Co-Authored-By: Claude Opus 5.5 --- .../toolkits/pullRequests/handlers.test.ts | 40 +++++++++++++++++++ .../src/mcp/toolkits/pullRequests/handlers.ts | 20 +++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts b/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts index 80005e419e3c1..1b486502c3d60 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/handlers.test.ts @@ -936,6 +936,46 @@ describe("source control tools", () => { }), ); + it.effect("re-running checks reopens a watch that saw its checks finish", () => + Effect.gen(function* () { + const watch = { + startedAt: "2026-08-20T00:00:00.000Z", + headSha: "abc123", + failedChecks: ["test"], + passed: false, + passedChecks: [], + remarksThrough: "2026-08-20T00:00:00.000Z", + remarkIds: [], + conflicting: false, + wakes: 0, + }; + const rerun = (checksPending: boolean | undefined, reran: boolean) => + Effect.gen(function* () { + const harness = yield* makeHarness({ + thread: { + ...branchThread, + pullRequests: [ + makeLink(42, { + watch: checksPending === undefined ? watch : { ...watch, checksPending }, + }), + ], + }, + pullRequests: { + rerunChecks: () => Effect.succeed([{ name: "test", rerun: reran, detail: null }]), + }, + }); + yield* harness.call("ci_rerun", {}); + return yield* Ref.get(harness.commands); + }); + expect(yield* rerun(false, true)).toMatchObject([ + { type: "thread.pull-request-watch.checks-reopen", threadId: THREAD_ID }, + ]); + // Nothing re-ran, or the watch has no answer to clear. + expect(yield* rerun(false, false)).toEqual([]); + expect(yield* rerun(undefined, true)).toEqual([]); + }), + ); + it.effect("opens a PR from the thread's worktree and links it even if dismissed", () => Effect.gen(function* () { const opened: Array<{ readonly cwd: string; readonly draft: boolean | undefined }> = []; diff --git a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts index 506287cff9a2f..ce76811b4d6d6 100644 --- a/apps/server/src/mcp/toolkits/pullRequests/handlers.ts +++ b/apps/server/src/mcp/toolkits/pullRequests/handlers.ts @@ -1120,10 +1120,28 @@ const make = Effect.gen(function* () { ), ci_rerun: McpToolAccess.writesWithCapability((input) => Effect.gen(function* () { - const { target, ref } = yield* locate(input); + const { thread, target, ref } = yield* locate(input); const checks = yield* pullRequests .rerunChecks({ ...ref, ...(input.check === undefined ? {} : { check: input.check }) }) .pipe(Effect.mapError(forgeFailure)); + // Like a push, a rerun makes a watch's finished checks stale, so the thread keeps working + // until a read sees them run. The rerun already happened, so a failed reopen only logs. + if ( + checks.some((check) => check.rerun) && + threadPullRequestsOf(thread).some((link) => link.watch?.checksPending !== undefined) + ) { + yield* engine + .dispatch({ + type: "thread.pull-request-watch.checks-reopen", + commandId: yield* commandId("mcp-ci-rerun-checks", thread.id), + threadId: thread.id, + }) + .pipe( + Effect.catchCause((cause) => + Effect.logWarning("reopening a watch's checks after a rerun failed", cause), + ), + ); + } return { ...identityOf(target), checks }; }), ),