authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-09-25 19:15:06-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-25 19:15:06-07:00
log7c901a37c4e8f35c408a27dbd1bd2efea06a74f5
tree0066e6bdf1d8a10f7a70358796a1fb499d2963e8
parent20885cecf77e205b8931ca1b29f0a2832836a157
signature Signed by PGP key B5690EEEBB952194

fix(cli): t3 triage points agents at log files that exist (#13685)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

8 files changed, 23 insertions(+), 14 deletions(-)

.github/triage/PLAYBOOK.md+3-2
......@@ -59,8 +59,9 @@ different code depending on it:
5959
6060Then work from evidence, not assumption. In rough order of value:
6161
62- The server log and the trace file (`server.trace.ndjson`) around the time of the
63 problem. Recent failures usually leave a trail here.
62- The trace file (`server.trace.ndjson`) around the time of the problem, plus the
63 service log or desktop backend logs from the context file if they exist. Recent
64 failures usually leave a trail here.
6465- The provider event log, for problems with claude/codex/cursor sessions.
6566- The SQLite database. Read it freely, but only write when a write is necessary
6667 to fix the problem the user described, and get their explicit permission
apps/server/src/cli/config.test.ts-1
......@@ -508,7 +508,6 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => {
508508 resolved.terminalLogsDir,
509509 resolved.attachmentsDir,
510510 resolved.worktreesDir,
511 path.dirname(resolved.serverLogPath),
512511 path.dirname(resolved.serverTracePath),
513512 ]) {
514513 expect(yield* fs.exists(directory)).toBe(true);
apps/server/src/cli/triage.ts+6-1
......@@ -27,6 +27,7 @@ import * as Schema from "effect/Schema";
2727import { Command, Flag } from "effect/unstable/cli";
2828
2929import packageJson from "../../package.json" with { type: "json" };
30import * as BootService from "../cloud/bootService.ts";
3031import * as ServerConfig from "../config.ts";
3132import { resolveBaseDir } from "../os-jank.ts";
3233import { isProcessAlive, readPersistedServerRuntimeState } from "../serverRuntimeState.ts";
......@@ -201,7 +202,11 @@ export const triageCommand = Command.make("triage", {
201202 dbPath: paths.dbPath,
202203 settingsPath: paths.settingsPath,
203204 logsDir: paths.logsDir,
204 serverLogPath: paths.serverLogPath,
205 // The server writes no log file of its own. Service installs and the
206 // desktop app capture its output. The glob covers every desktop backend
207 // (such as WSL) and rotated copies; names come from DesktopObservability.ts.
208 serviceLogPath: path.join(paths.logsDir, BootService.BOOT_SERVICE_LOG_FILE),
209 desktopBackendLogGlob: path.join(paths.logsDir, "server-child*.log*"),
205210 serverTracePath: paths.serverTracePath,
206211 providerEventLogPath: paths.providerEventLogPath,
207212 terminalLogsDir: paths.terminalLogsDir,
apps/server/src/cli/triagePrompt.test.ts+4-1
......@@ -52,7 +52,8 @@ it("context file carries every path the playbook depends on", () => {
5252 dbPath: "/home/u/.t3/userdata/state.sqlite",
5353 settingsPath: "/home/u/.t3/userdata/settings.json",
5454 logsDir: "/home/u/.t3/userdata/logs",
55 serverLogPath: "/home/u/.t3/userdata/logs/server.log",
55 serviceLogPath: "/home/u/.t3/userdata/logs/boot-service.log",
56 desktopBackendLogGlob: "/home/u/.t3/userdata/logs/server-child*.log*",
5657 serverTracePath: "/home/u/.t3/userdata/logs/server.trace.ndjson",
5758 providerEventLogPath: "/home/u/.t3/userdata/logs/provider/events.log",
5859 terminalLogsDir: "/home/u/.t3/userdata/logs/terminals",
......@@ -63,6 +64,8 @@ it("context file carries every path the playbook depends on", () => {
6364 });
6465 assert.include(context, "/home/u/.t3/userdata/state.sqlite");
6566 assert.include(context, "/home/u/.t3/userdata/logs/server.trace.ndjson");
67 assert.include(context, "/home/u/.t3/userdata/logs/boot-service.log");
68 assert.include(context, "/home/u/.t3/userdata/logs/server-child*.log*");
6669 assert.include(context, "/home/u/.t3/userdata/logs/provider/events.log");
6770 assert.include(context, "/home/u/.t3/userdata/secrets");
6871 assert.include(context, "/home/u/.t3/source");
apps/server/src/cli/triagePrompt.ts+7-4
......@@ -71,8 +71,9 @@ different code depending on it:
7171
7272Then work from evidence, not assumption. In rough order of value:
7373
74- The server log and the trace file (\`server.trace.ndjson\`) around the time of the
75 problem. Recent failures usually leave a trail here.
74- The trace file (\`server.trace.ndjson\`) around the time of the problem, plus the
75 service log or desktop backend logs from the context file if they exist. Recent
76 failures usually leave a trail here.
7677- The provider event log, for problems with claude/codex/cursor sessions.
7778- The SQLite database. Read it freely, but only write when a write is necessary
7879 to fix the problem the user described, and get their explicit permission
......@@ -176,7 +177,8 @@ export interface TriageContextInput {
176177 readonly dbPath: string;
177178 readonly settingsPath: string;
178179 readonly logsDir: string;
179 readonly serverLogPath: string;
180 readonly serviceLogPath: string;
181 readonly desktopBackendLogGlob: string;
180182 readonly serverTracePath: string;
181183 readonly providerEventLogPath: string;
182184 readonly terminalLogsDir: string;
......@@ -205,7 +207,8 @@ Generated by \`t3 triage\` at ${input.generatedAt}.
205207- Database (SQLite; write only with the user's explicit permission): ${input.paths.dbPath}
206208- Settings: ${input.paths.settingsPath}
207209- Logs dir: ${input.paths.logsDir}
208- Server log: ${input.paths.serverLogPath}
210- Service log (systemd/launchd service installs only): ${input.paths.serviceLogPath}
211- Desktop backend logs (glob; one file per backend plus rotated copies; written only when a backend crashes or fails to start): ${input.paths.desktopBackendLogGlob}
209212- Server trace (ndjson): ${input.paths.serverTracePath}
210213- Provider event log: ${input.paths.providerEventLogPath}
211214- Terminal logs: ${input.paths.terminalLogsDir}
apps/server/src/cloud/bootService.ts+3-1
......@@ -43,6 +43,8 @@ const BOOT_SERVICE_UNIT_FILE = `${BOOT_SERVICE_NAME}.service`;
4343const BOOT_SERVICE_LAUNCHD_LABEL = "com.t3tools.t3code.service";
4444const BOOT_SERVICE_PLIST_FILE = `${BOOT_SERVICE_LAUNCHD_LABEL}.plist`;
4545const BOOT_SERVICE_UNIT_ENV = "T3_BOOT_SERVICE_UNIT";
46/** File in the logs dir that receives the service's stdout and stderr. `t3 triage` points agents at it. */
47export const BOOT_SERVICE_LOG_FILE = "boot-service.log";
4648
4749/** systemd expands `%` specifiers, including in unquoted append-log paths. */
4850function escapeSystemdSpecifiers(value: string): string {
......@@ -599,7 +601,7 @@ export const make = Effect.fn("cloud.boot_service.make")(function* (input: {
599601 environmentPath,
600602 });
601603 const unitPath = detectedManager?.unitPath ?? "";
602 const logPath = path.join(input.logsDir, "boot-service.log");
604 const logPath = path.join(input.logsDir, BOOT_SERVICE_LOG_FILE);
603605 const statePath = path.join(input.baseDir, "runtime", SERVICE_STATE_FILE);
604606 const restartPendingPath = path.join(input.baseDir, "runtime", SERVICE_RESTART_PENDING_FILE);
605607 const runtimePaths = pinnedRuntimePaths(path, input.baseDir, input.cliVersion, platform);
apps/server/src/config.ts-2
......@@ -44,7 +44,6 @@ export interface ServerDerivedPaths {
4444 /** Screenshots the agent asks the collaborative browser to keep for the user. */
4545 readonly browserArtifactsDir: string;
4646 readonly logsDir: string;
47 readonly serverLogPath: string;
4847 readonly serverTracePath: string;
4948 readonly providerLogsDir: string;
5049 readonly providerEventLogPath: string;
......@@ -154,7 +153,6 @@ export const deriveServerPaths = Effect.fn(function* (
154153 attachmentsDir,
155154 browserArtifactsDir: join(stateDir, "browser-artifacts"),
156155 logsDir,
157 serverLogPath: join(logsDir, "server.log"),
158156 serverTracePath: join(logsDir, "server.trace.ndjson"),
159157 providerLogsDir,
160158 providerEventLogPath: join(providerLogsDir, "events.log"),
docs/operations/observability.md-2
......@@ -603,5 +603,3 @@ Current high-value span and metric boundaries include:
603603- logs outside spans are not persisted in the trace file; SSH-managed launch stdout/stderr is still
604604 captured in its launcher log
605605- metrics are not snapshotted locally
606- the old `serverLogPath` still exists in config for compatibility, but the trace file is the primary
607 structured persisted artifact