Skip to content

Commit fb04775

Browse files
Merge commit '667dbe0b6b3e'
Co-authored-by: Alfonso <alfonso-magic-context@users.noreply.github.com>
2 parents 0ebe5b8 + 667dbe0 commit fb04775

2 files changed

Lines changed: 149 additions & 9 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { describe, expect, it } from "bun:test";
2+
import { Database } from "../../shared/sqlite";
3+
import {
4+
computeProtectionWindow,
5+
getProtectionWindowForSession,
6+
type ProtectionWindowRow,
7+
} from "./protection-window";
8+
import { TAG_SELECT_COLUMNS } from "./storage-tags";
9+
10+
type SeedRow = { tag: number; tokens: number | null; type?: string };
11+
const generated = (count: number): SeedRow[] =>
12+
Array.from({ length: count }, (_, i) => ({
13+
tag: i + 1,
14+
tokens: i % 7 === 0 ? null : i % 5 === 0 ? 0 : 100,
15+
}));
16+
17+
function compareWithReference(seeds: SeedRow[], floor: number) {
18+
const db = new Database(":memory:");
19+
try {
20+
// Legacy chronology can contain duplicate tag numbers; the current migration
21+
// adds uniqueness, so use the reader's older supported row shape for tie coverage.
22+
db.exec(`CREATE TABLE tags (
23+
id INTEGER PRIMARY KEY, session_id TEXT, message_id TEXT, type TEXT,
24+
status TEXT, drop_mode TEXT, tool_name TEXT, input_byte_size INTEGER DEFAULT 0,
25+
byte_size INTEGER, reasoning_byte_size INTEGER DEFAULT 0,
26+
tag_number INTEGER NOT NULL, caveman_depth INTEGER DEFAULT 0,
27+
tool_owner_message_id TEXT, token_count INTEGER
28+
); CREATE INDEX tags_chronology ON tags(session_id, tag_number, id)`);
29+
const insert = db.prepare(`INSERT INTO tags
30+
(session_id, message_id, type, status, tag_number, token_count, byte_size,
31+
tool_name, tool_owner_message_id, drop_mode)
32+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
33+
db.transaction(() => {
34+
seeds.forEach((row, i) => {
35+
insert.run(
36+
"paged",
37+
`message-${i}`,
38+
row.type ?? "tool",
39+
i % 2 === 0 ? "dropped" : "active",
40+
row.tag,
41+
row.tokens,
42+
i * 13,
43+
"read",
44+
`owner-${i}`,
45+
i % 2 === 0 ? "skeleton" : null,
46+
);
47+
});
48+
insert.run(
49+
"other-session",
50+
"other",
51+
"tool",
52+
"active",
53+
999999,
54+
99999,
55+
1,
56+
"read",
57+
"other",
58+
null,
59+
);
60+
})();
61+
const allRows = db
62+
.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags
63+
WHERE session_id = ? AND type = 'tool' ORDER BY tag_number ASC, id ASC`)
64+
.all("paged") as ProtectionWindowRow[];
65+
const expected = computeProtectionWindow(allRows, floor);
66+
const actual = getProtectionWindowForSession(db, "paged", floor);
67+
const { isProtected: expectedPredicate, ...expectedFields } = expected;
68+
const { isProtected: actualPredicate, ...actualFields } = actual;
69+
expect(actualFields).toEqual(expectedFields);
70+
for (const row of allRows) expect(actualPredicate(row)).toBe(expectedPredicate(row));
71+
return actual;
72+
} finally {
73+
db.close();
74+
}
75+
}
76+
77+
describe("paged protection-window differential", () => {
78+
it("completes a duplicate tag group across the 256-row page boundary", () => {
79+
const seeds = generated(27);
80+
for (let i = 0; i < 300; i++) seeds.push({ tag: 28, tokens: 100 });
81+
seeds.push({ tag: 29, tokens: 100 }, { tag: 30, tokens: 100 });
82+
const result = compareWithReference(seeds, 100);
83+
expect(result.status.protectedCount).toBe(302);
84+
expect(result.cutoff).toBe(28);
85+
});
86+
87+
for (const count of [0, 1, 2, 256, 257, 20000]) {
88+
for (const floor of [0, 4000, count * 100 + 1]) {
89+
it(`matches all fields for ${count} rows at floor ${floor}`, () => {
90+
compareWithReference(generated(count), floor);
91+
});
92+
}
93+
}
94+
95+
it("exhausts zero and NULL mass history with interleaved dropped rows", () => {
96+
compareWithReference(
97+
generated(600).map((row, i) => ({ ...row, tokens: i % 2 ? 0 : null })),
98+
4000,
99+
);
100+
});
101+
102+
it("retains complete ties with fewer than three distinct tool tags", () => {
103+
compareWithReference(
104+
generated(600).map((row, i) => ({ ...row, tag: i % 2 })),
105+
0,
106+
);
107+
});
108+
109+
it("ignores non-tool chronology and other sessions", () => {
110+
compareWithReference(
111+
generated(600).map((row, i) => ({ ...row, type: i % 3 ? "tool" : "message" })),
112+
4000,
113+
);
114+
});
115+
});

‎packages/plugin/src/features/magic-context/protection-window.ts‎

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,9 @@ export function readEpochFloorSnapshot(db: Database, sessionId: string): number
261261
}
262262

263263
/**
264-
* Compute the protection window for a session by loading all persisted tool rows
265-
* from the database.
264+
* Read only the persisted tool suffix needed for protection. A tag-number group
265+
* can span pages, so test the stopping condition only when the next group starts.
266+
* Dropped rows still contribute to chronology and mass, just as in the pure walk.
266267
*/
267268
export function getProtectionWindowForSession(
268269
db: Database,
@@ -274,13 +275,37 @@ export function getProtectionWindowForSession(
274275
? floor
275276
: (readEpochFloorSnapshot(db, sessionId) ?? 0);
276277

277-
const rows = db
278-
.prepare(
279-
`SELECT ${TAG_SELECT_COLUMNS} FROM tags
280-
WHERE session_id = ? AND type = 'tool'
281-
ORDER BY tag_number ASC, id ASC`,
282-
)
283-
.all(sessionId) as ProtectionWindowRow[];
278+
const pageSize = 256;
279+
// Exclude the tool-owner partial index from planning: without ANALYZE it can
280+
// win over chronology and force a whole-session sort before each small page.
281+
const projection = `SELECT ${TAG_SELECT_COLUMNS} FROM tags
282+
WHERE session_id = ? AND +type = 'tool'`;
283+
const order = `ORDER BY tag_number DESC, id DESC LIMIT ${pageSize}`;
284+
let page = db.prepare(`${projection} ${order}`).all(sessionId) as ProtectionWindowRow[];
285+
const rows: ProtectionWindowRow[] = [];
286+
let mass = 0;
287+
let distinctTags = 0;
288+
let currentTag: number | null = null;
289+
290+
while (page.length > 0) {
291+
for (const row of page) {
292+
const tagNumber = getRowTagNumber(row);
293+
if (tagNumber !== currentTag) {
294+
if (distinctTags >= 3 && mass >= effectiveFloor) {
295+
return computeProtectionWindow(rows, effectiveFloor);
296+
}
297+
distinctTags++;
298+
currentTag = tagNumber;
299+
}
300+
rows.push(row);
301+
mass += rowWindowMass(row);
302+
}
303+
if (page.length < pageSize) break;
304+
const last = page[page.length - 1];
305+
page = db
306+
.prepare(`${projection} AND (tag_number, id) < (?, ?) ${order}`)
307+
.all(sessionId, getRowTagNumber(last), getRowIdentity(last)) as ProtectionWindowRow[];
308+
}
284309

285310
return computeProtectionWindow(rows, effectiveFloor);
286311
}

0 commit comments

Comments
 (0)