| author | |
| committer | |
| log | 7c901a37c4e8f35c408a27dbd1bd2efea06a74f5 |
| tree | 0066e6bdf1d8a10f7a70358796a1fb499d2963e8 |
| parent | 20885cecf77e205b8931ca1b29f0a2832836a157 |
| signature | Signed by PGP key B5690EEEBB952194 |
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: |
| 59 | 59 | |
| 60 | 60 | Then work from evidence, not assumption. In rough order of value: |
| 61 | 61 | |
| 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. | |
| 64 | 65 | - The provider event log, for problems with claude/codex/cursor sessions. |
| 65 | 66 | - The SQLite database. Read it freely, but only write when a write is necessary |
| 66 | 67 | 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) => { |
| 508 | 508 | resolved.terminalLogsDir, |
| 509 | 509 | resolved.attachmentsDir, |
| 510 | 510 | resolved.worktreesDir, |
| 511 | path.dirname(resolved.serverLogPath), | |
| 512 | 511 | path.dirname(resolved.serverTracePath), |
| 513 | 512 | ]) { |
| 514 | 513 | 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"; |
| 27 | 27 | import { Command, Flag } from "effect/unstable/cli"; |
| 28 | 28 | |
| 29 | 29 | import packageJson from "../../package.json" with { type: "json" }; |
| 30 | import * as BootService from "../cloud/bootService.ts"; | |
| 30 | 31 | import * as ServerConfig from "../config.ts"; |
| 31 | 32 | import { resolveBaseDir } from "../os-jank.ts"; |
| 32 | 33 | import { isProcessAlive, readPersistedServerRuntimeState } from "../serverRuntimeState.ts"; |
| ... | ... | @@ -201,7 +202,11 @@ export const triageCommand = Command.make("triage", { |
| 201 | 202 | dbPath: paths.dbPath, |
| 202 | 203 | settingsPath: paths.settingsPath, |
| 203 | 204 | 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*"), | |
| 205 | 210 | serverTracePath: paths.serverTracePath, |
| 206 | 211 | providerEventLogPath: paths.providerEventLogPath, |
| 207 | 212 | 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", () => { |
| 52 | 52 | dbPath: "/home/u/.t3/userdata/state.sqlite", |
| 53 | 53 | settingsPath: "/home/u/.t3/userdata/settings.json", |
| 54 | 54 | 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*", | |
| 56 | 57 | serverTracePath: "/home/u/.t3/userdata/logs/server.trace.ndjson", |
| 57 | 58 | providerEventLogPath: "/home/u/.t3/userdata/logs/provider/events.log", |
| 58 | 59 | terminalLogsDir: "/home/u/.t3/userdata/logs/terminals", |
| ... | ... | @@ -63,6 +64,8 @@ it("context file carries every path the playbook depends on", () => { |
| 63 | 64 | }); |
| 64 | 65 | assert.include(context, "/home/u/.t3/userdata/state.sqlite"); |
| 65 | 66 | 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*"); | |
| 66 | 69 | assert.include(context, "/home/u/.t3/userdata/logs/provider/events.log"); |
| 67 | 70 | assert.include(context, "/home/u/.t3/userdata/secrets"); |
| 68 | 71 | assert.include(context, "/home/u/.t3/source"); |
apps/server/src/cli/triagePrompt.ts+7-4| ... | ... | @@ -71,8 +71,9 @@ different code depending on it: |
| 71 | 71 | |
| 72 | 72 | Then work from evidence, not assumption. In rough order of value: |
| 73 | 73 | |
| 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. | |
| 76 | 77 | - The provider event log, for problems with claude/codex/cursor sessions. |
| 77 | 78 | - The SQLite database. Read it freely, but only write when a write is necessary |
| 78 | 79 | to fix the problem the user described, and get their explicit permission |
| ... | ... | @@ -176,7 +177,8 @@ export interface TriageContextInput { |
| 176 | 177 | readonly dbPath: string; |
| 177 | 178 | readonly settingsPath: string; |
| 178 | 179 | readonly logsDir: string; |
| 179 | readonly serverLogPath: string; | |
| 180 | readonly serviceLogPath: string; | |
| 181 | readonly desktopBackendLogGlob: string; | |
| 180 | 182 | readonly serverTracePath: string; |
| 181 | 183 | readonly providerEventLogPath: string; |
| 182 | 184 | readonly terminalLogsDir: string; |
| ... | ... | @@ -205,7 +207,8 @@ Generated by \`t3 triage\` at ${input.generatedAt}. |
| 205 | 207 | - Database (SQLite; write only with the user's explicit permission): ${input.paths.dbPath} |
| 206 | 208 | - Settings: ${input.paths.settingsPath} |
| 207 | 209 | - 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} | |
| 209 | 212 | - Server trace (ndjson): ${input.paths.serverTracePath} |
| 210 | 213 | - Provider event log: ${input.paths.providerEventLogPath} |
| 211 | 214 | - 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`; |
| 43 | 43 | const BOOT_SERVICE_LAUNCHD_LABEL = "com.t3tools.t3code.service"; |
| 44 | 44 | const BOOT_SERVICE_PLIST_FILE = `${BOOT_SERVICE_LAUNCHD_LABEL}.plist`; |
| 45 | 45 | const 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. */ | |
| 47 | export const BOOT_SERVICE_LOG_FILE = "boot-service.log"; | |
| 46 | 48 | |
| 47 | 49 | /** systemd expands `%` specifiers, including in unquoted append-log paths. */ |
| 48 | 50 | function escapeSystemdSpecifiers(value: string): string { |
| ... | ... | @@ -599,7 +601,7 @@ export const make = Effect.fn("cloud.boot_service.make")(function* (input: { |
| 599 | 601 | environmentPath, |
| 600 | 602 | }); |
| 601 | 603 | 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); | |
| 603 | 605 | const statePath = path.join(input.baseDir, "runtime", SERVICE_STATE_FILE); |
| 604 | 606 | const restartPendingPath = path.join(input.baseDir, "runtime", SERVICE_RESTART_PENDING_FILE); |
| 605 | 607 | const runtimePaths = pinnedRuntimePaths(path, input.baseDir, input.cliVersion, platform); |
apps/server/src/config.ts-2| ... | ... | @@ -44,7 +44,6 @@ export interface ServerDerivedPaths { |
| 44 | 44 | /** Screenshots the agent asks the collaborative browser to keep for the user. */ |
| 45 | 45 | readonly browserArtifactsDir: string; |
| 46 | 46 | readonly logsDir: string; |
| 47 | readonly serverLogPath: string; | |
| 48 | 47 | readonly serverTracePath: string; |
| 49 | 48 | readonly providerLogsDir: string; |
| 50 | 49 | readonly providerEventLogPath: string; |
| ... | ... | @@ -154,7 +153,6 @@ export const deriveServerPaths = Effect.fn(function* ( |
| 154 | 153 | attachmentsDir, |
| 155 | 154 | browserArtifactsDir: join(stateDir, "browser-artifacts"), |
| 156 | 155 | logsDir, |
| 157 | serverLogPath: join(logsDir, "server.log"), | |
| 158 | 156 | serverTracePath: join(logsDir, "server.trace.ndjson"), |
| 159 | 157 | providerLogsDir, |
| 160 | 158 | providerEventLogPath: join(providerLogsDir, "events.log"), |
docs/operations/observability.md-2| ... | ... | @@ -603,5 +603,3 @@ Current high-value span and metric boundaries include: |
| 603 | 603 | - logs outside spans are not persisted in the trace file; SSH-managed launch stdout/stderr is still |
| 604 | 604 | captured in its launcher log |
| 605 | 605 | - 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 |