1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
import { describe, expect, test } from "bun:test";
import {
PURPOSE_MAX,
isProjectPurpose,
projectPurpose,
projectRelPath,
sanitizePurpose,
} from "../src/core/purpose.ts";
describe("sanitizePurpose (must match the fork)", () => {
test("keeps the fork's allowed set", () => {
expect(sanitizePurpose("a-b_c.d:e/f")).toBe("a-b_c.d:e/f");
});
// The fork's own doc uses this exact example.
test("strips shell metacharacters", () => {
expect(sanitizePurpose('rm -rf /; echo "x"')).toBe("rm-rf/echox");
});
test("strips non-ascii", () => {
expect(sanitizePurpose("café–π")).toBe("caf");
});
test("truncates to the fork's limit", () => {
expect(sanitizePurpose("x".repeat(200))).toHaveLength(PURPOSE_MAX);
});
});
describe("projectPurpose", () => {
test("is the plain relative path for ordinary names", () => {
expect(projectPurpose("sl0ppi")).toBe("project:sl0ppi");
expect(projectPurpose("group/nested")).toBe("project:group/nested");
});
test("never exceeds what the fork will store", () => {
for (const name of ["ok", "a".repeat(200), "wéîrd name!", "x/".repeat(50)]) {
expect(projectPurpose(name).length).toBeLessThanOrEqual(PURPOSE_MAX);
}
});
test("survives the fork's sanitiser unchanged (round-trip through the terminal)", () => {
for (const name of ["ok", "a".repeat(200), "wéîrd name!", "dots...", "x/".repeat(50)]) {
const purpose = projectPurpose(name);
expect(sanitizePurpose(purpose)).toBe(purpose);
}
});
test("distinct projects get distinct purposes, even when truncated", () => {
const a = projectPurpose(`${"a".repeat(80)}/one`);
const b = projectPurpose(`${"a".repeat(80)}/two`);
expect(a).not.toBe(b);
});
test("is stable across calls", () => {
expect(projectPurpose("foo")).toBe(projectPurpose("foo"));
expect(projectPurpose("wéîrd name!")).toBe(projectPurpose("wéîrd name!"));
});
});
describe("projectRelPath", () => {
test("reconstructs an ordinary path", () => {
expect(projectRelPath("project:sl0ppi")).toBe("sl0ppi");
expect(projectRelPath("project:group/nested")).toBe("group/nested");
});
test("refuses to reconstruct a truncated/hashed purpose", () => {
expect(projectRelPath(projectPurpose("a".repeat(200)))).toBeNull();
});
test("refuses non-project purposes", () => {
expect(projectRelPath("agent:main")).toBeNull();
expect(projectRelPath("project:")).toBeNull();
});
test("round-trips every reconstructable name", () => {
for (const name of ["foo", "group/nested", "dots.in.name", "a-b_c"]) {
expect(projectRelPath(projectPurpose(name))).toBe(name);
}
});
});
// The safety invariant, fuzzed. Closing a tab is destructive, so the one thing
// projectRelPath must never do is return a path that is not the original.
// Wrong answers close the wrong tab; refusing (null) only leaves a stale one.
describe("projectRelPath never lies (fuzz)", () => {
const alphabet = "abzAZ09_-./: !@#\u00e9\u4e2d\"';";
function randomName(rng: () => number): string {
const len = 1 + Math.floor(rng() * 90);
let out = "";
for (let i = 0; i < len; i++) {
out += alphabet[Math.floor(rng() * alphabet.length)];
}
return out;
}
// Deterministic PRNG so a failure is reproducible.
function mulberry32(seed: number): () => number {
return () => {
seed = (seed + 0x6d2b79f5) | 0;
let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
test("reconstructs exactly, or refuses — never something else", () => {
const rng = mulberry32(0x510991);
for (let i = 0; i < 5000; i++) {
const name = randomName(rng);
const purpose = projectPurpose(name);
expect(purpose.length).toBeLessThanOrEqual(PURPOSE_MAX);
expect(sanitizePurpose(purpose)).toBe(purpose);
const back = projectRelPath(purpose);
if (back !== null) expect(back).toBe(name);
}
});
test("distinct names never collide on a purpose", () => {
const rng = mulberry32(12345);
const seen = new Map<string, string>();
for (let i = 0; i < 5000; i++) {
const name = randomName(rng);
const purpose = projectPurpose(name);
const prior = seen.get(purpose);
if (prior !== undefined) expect(prior).toBe(name);
seen.set(purpose, name);
}
});
});
describe("isProjectPurpose", () => {
test("classifies", () => {
expect(isProjectPurpose("project:foo")).toBe(true);
expect(isProjectPurpose("agent:main")).toBe(false);
expect(isProjectPurpose(undefined)).toBe(false);
});
});
|