Skip to content

Commit f7c76ef

Browse files
committed
Key crash reports the same way sessions are keyed
Reuse projectKeyFor/projectSessionsRoot instead of a second, independently derived slug, so a crash report lands next to that project's run.json and transcript rather than in a separate directory. Also log when the crash-report write itself fails, instead of exiting silently with no trace of the failure.
1 parent fdf7163 commit f7c76ef

4 files changed

Lines changed: 17 additions & 12 deletions

File tree

docs/IMPLEMENTATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ session; that tree re-write is inherent to git and left as residual cost.
329329

330330
### Crash Logging
331331

332-
`index.ts` installs `uncaughtException` and `unhandledRejection` handlers (and catches a rejected `main`). Each writes a best-effort crash report to `~/.corbits/projects/<project-slug>/errors/<timestamp>.txt`, where the slug is the cwd with non-alphanumeric runs collapsed to `-`. The file records the failure kind, an ISO timestamp, the cwd, and the stack. The logger swallows its own errors so it can never mask the original crash, then exits non-zero after printing a one-line message to stderr.
332+
`index.ts` installs `uncaughtException` and `unhandledRejection` handlers (and catches a rejected `main`). Each writes a best-effort crash report to `~/.corbits/projects/<project-key>/errors/<timestamp>.txt`, using the same `projectKeyFor`/`projectSessionsRoot` (`session/project-key.ts`) that keys that project's session directories, so a crash report lands next to the session's `run.json` and transcript rather than under a separately computed slug. The file records the failure kind, an ISO timestamp, the cwd, and the stack. The writer swallows its own errors (logging a one-line failure notice to stderr instead) so it can never mask the original crash, then exits non-zero after printing a one-line message to stderr.
333333

334334
### Event Stream
335335

src/crash/report.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { mkdtemp, readFile, readdir, rm } from "node:fs/promises";
33
import { tmpdir } from "node:os";
44
import { join } from "node:path";
55

6+
import { projectSessionsRoot } from "../session/project-key.js";
67
import { crashReportDir, writeCrashReport } from "./report.js";
78

89
let home: string | undefined;
@@ -15,7 +16,14 @@ afterEach(async () => {
1516
});
1617

1718
describe("writeCrashReport", () => {
18-
test("writes a report under ~/.corbits/projects/<slug>/errors/", async () => {
19+
test("keys the crash directory the same way as the session directory", () => {
20+
home = undefined; // no filesystem needed for this one
21+
const cwd = "/Users/dev/some project!!";
22+
const tmpHome = "/tmp/corbits-crash-key-check";
23+
expect(crashReportDir(cwd, tmpHome)).toBe(join(projectSessionsRoot(cwd, tmpHome), "errors"));
24+
});
25+
26+
test("writes a report under ~/.corbits/projects/<project-key>/errors/", async () => {
1927
home = await mkdtemp(join(tmpdir(), "corbits-crash-"));
2028
const cwd = "/Users/dev/some project!!";
2129
const file = await writeCrashReport("uncaughtException", new Error("boom"), cwd, home);

src/crash/report.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,15 @@ import { mkdir, writeFile } from "node:fs/promises";
22
import { homedir } from "node:os";
33
import { join } from "node:path";
44

5-
import { SETTINGS_DIR_NAME } from "../branding.js";
5+
import { projectSessionsRoot } from "../session/project-key.js";
66

77
export type CrashKind = "uncaughtException" | "unhandledRejection";
88

9-
// Deliberately independent of session/project-key.ts: a crash can happen
10-
// before config or git discovery ever runs, so this slug is just the raw cwd
11-
// with non-alphanumeric runs collapsed, not the hashed project key.
12-
function slugifyCwd(cwd: string): string {
13-
const slug = cwd.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
14-
return slug.length > 0 ? slug : "project";
15-
}
16-
9+
// Must share the same key as the session's own directory (projectSessionsRoot)
10+
// so a crash report lands next to that session's run.json and transcript
11+
// instead of in a second, differently-keyed tree.
1712
export function crashReportDir(cwd: string, home: string = homedir()): string {
18-
return join(home, SETTINGS_DIR_NAME, "projects", slugifyCwd(cwd), "errors");
13+
return join(projectSessionsRoot(cwd, home), "errors");
1914
}
2015

2116
function describeError(error: unknown): string {

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ async function handleFatal(kind: CrashKind, error: unknown): Promise<void> {
106106
const file = await writeCrashReport(kind, error);
107107
if (file !== null) {
108108
process.stderr.write(`crash report written to ${file}\n`);
109+
} else {
110+
process.stderr.write("failed to write crash report\n");
109111
}
110112
process.exit(1);
111113
}

0 commit comments

Comments
 (0)