|
1 | 1 | import { defined } from "../../testkit/defined.js"; |
2 | | -import { withMockedModuleDuring } from "../../testkit/mock-module.js"; |
3 | 2 | import { describe, test, expect } from "bun:test"; |
4 | 3 | import fs from "node:fs"; |
5 | 4 | import os from "node:os"; |
@@ -594,7 +593,7 @@ describe("createOptimizedContextStore checkpoint", () => { |
594 | 593 | const dir = tempDir(); |
595 | 594 | const store = await createOptimizedContextStore(dir); |
596 | 595 | await store.writeTurns([turn("a")]); |
597 | | - await store.writePrompt([turn("p0")]); |
| 596 | + fs.writeFileSync(path.join(dir, "prompt.jsonl"), jsonl([turn("p0")])); |
598 | 597 | await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
599 | 598 | const first = await store.commit({ message: "base" }); |
600 | 599 |
|
@@ -1210,43 +1209,6 @@ describe("concurrent sessions write disjoint transcripts", () => { |
1210 | 1209 | expect(turnTexts((await resumed.load()).turns)).not.toContain("from-B"); |
1211 | 1210 | }); |
1212 | 1211 |
|
1213 | | - test("a prompt is not written to the shared file before ownership is decided", async () => { |
1214 | | - const dir = tempDir(); |
1215 | | - const promptFile = "prompt.jsonl"; |
1216 | | - const sessionA = await createOptimizedContextStore(dir, { |
1217 | | - ownerId: "session-A", |
1218 | | - }); |
1219 | | - await sessionA.writeTurns([turn("a"), turn("b")]); |
1220 | | - await sessionA.writePrompt([turn("a"), turn("b"), turn("ephemeral-A")]); |
1221 | | - await sessionA.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1222 | | - await sessionA.commit({ message: "session A" }); |
1223 | | - const promptPath = path.join(dir, promptFile); |
1224 | | - const ownedPrompt = fs.readFileSync(promptPath, "utf8"); |
1225 | | - |
1226 | | - const sessionB = await createOptimizedContextStore(dir, { |
1227 | | - ownerId: "session-B", |
1228 | | - }); |
1229 | | - await sessionB.writePrompt([turn("foreign-prompt")]); |
1230 | | - expect(fs.readFileSync(promptPath, "utf8")).toBe(ownedPrompt); |
1231 | | - await sessionB.writeTurns([turn("x"), turn("y")]); |
1232 | | - expect(fs.readFileSync(promptPath, "utf8")).toBe(ownedPrompt); |
1233 | | - await sessionB.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1234 | | - await sessionB.commit({ message: "session B" }); |
1235 | | - |
1236 | | - expect(fs.readFileSync(promptPath, "utf8")).toBe(ownedPrompt); |
1237 | | - expect(turnTexts(readTurnsFromFile(path.join(dir, TURNS_FILE)))).toEqual([ |
1238 | | - "a", |
1239 | | - "b", |
1240 | | - ]); |
1241 | | - const versionedPrompts = fs |
1242 | | - .readdirSync(dir) |
1243 | | - .filter((name) => /^prompt-\d+-[0-9a-f]+\.jsonl$/.test(name)); |
1244 | | - expect(versionedPrompts).toHaveLength(1); |
1245 | | - expect( |
1246 | | - fs.readFileSync(path.join(dir, defined(versionedPrompts[0])), "utf8"), |
1247 | | - ).toContain("foreign-prompt"); |
1248 | | - }); |
1249 | | - |
1250 | 1212 | test( |
1251 | 1213 | "two processes cannot both claim an empty session directory", |
1252 | 1214 | async () => { |
@@ -1619,209 +1581,6 @@ describe("createOptimizedContextStore unpublished rewrite", () => { |
1619 | 1581 | }); |
1620 | 1582 | }); |
1621 | 1583 |
|
1622 | | -describe("createOptimizedContextStore prompt dedupe (CL-9026)", () => { |
1623 | | - const PROMPT_FILE = "prompt.jsonl"; |
1624 | | - |
1625 | | - function toolResultTurn(body: string): ConversationTurn { |
1626 | | - return { |
1627 | | - role: "user", |
1628 | | - content: [ |
1629 | | - { |
1630 | | - type: "tool_result", |
1631 | | - callId: "call-1", |
1632 | | - content: [{ type: "text", text: body }], |
1633 | | - }, |
1634 | | - ], |
1635 | | - timestamp: 1, |
1636 | | - }; |
1637 | | - } |
1638 | | - |
1639 | | - function cloneTurns(turns: ConversationTurn[]): ConversationTurn[] { |
1640 | | - return turns.map((t) => JSON.parse(JSON.stringify(t)) as ConversationTurn); |
1641 | | - } |
1642 | | - |
1643 | | - test("identical writePrompt writes no prompt segment", async () => { |
1644 | | - const dir = tempDir(); |
1645 | | - const store = await createOptimizedContextStore(dir); |
1646 | | - const turns = [turn("a"), turn("b")]; |
1647 | | - await store.writeTurns([...turns]); |
1648 | | - await store.writePrompt([...turns]); |
1649 | | - await store.writePrompt(cloneTurns(turns)); |
1650 | | - |
1651 | | - expect(fs.existsSync(path.join(dir, PROMPT_FILE))).toBe(false); |
1652 | | - expect(await listSegmentFiles(dir, PROMPT_FILE)).toEqual([]); |
1653 | | - |
1654 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1655 | | - await store.commit({ message: "identical prompt is a no-op" }); |
1656 | | - expect(await gitLsTree(dir)).not.toContain(PROMPT_FILE); |
1657 | | - |
1658 | | - // A fresh instance compares against on-disk live turns and also skips. |
1659 | | - const fresh = await createOptimizedContextStore(dir); |
1660 | | - await fresh.writePrompt(cloneTurns(turns)); |
1661 | | - expect(fs.existsSync(path.join(dir, PROMPT_FILE))).toBe(false); |
1662 | | - }); |
1663 | | - |
1664 | | - test("differing writePrompt still writes the prompt snapshot", async () => { |
1665 | | - const dir = tempDir(); |
1666 | | - const store = await createOptimizedContextStore(dir); |
1667 | | - const live = [turn("a"), toolResultTurn("x".repeat(1000))]; |
1668 | | - await store.writeTurns([...live]); |
1669 | | - |
1670 | | - const shrunk = [turn("a"), toolResultTurn("summarized")]; |
1671 | | - await store.writePrompt(shrunk); |
1672 | | - expect(fs.existsSync(path.join(dir, PROMPT_FILE))).toBe(true); |
1673 | | - expect(fs.readFileSync(path.join(dir, PROMPT_FILE), "utf-8")).toBe( |
1674 | | - jsonl(shrunk), |
1675 | | - ); |
1676 | | - |
1677 | | - // An appended ephemeral turn also changes the prompt and still writes. |
1678 | | - const ephemeralDir = tempDir(); |
1679 | | - const ephemeralStore = await createOptimizedContextStore(ephemeralDir); |
1680 | | - await ephemeralStore.writeTurns([...live]); |
1681 | | - const withEphemeral = [...cloneTurns(live), turn("ephemeral")]; |
1682 | | - await ephemeralStore.writePrompt(withEphemeral); |
1683 | | - expect(fs.readFileSync(path.join(ephemeralDir, PROMPT_FILE), "utf-8")).toBe( |
1684 | | - jsonl(withEphemeral), |
1685 | | - ); |
1686 | | - }); |
1687 | | - |
1688 | | - test("turns-only store loads live turns and commits no prompt file", async () => { |
1689 | | - const dir = tempDir(); |
1690 | | - const store = await createOptimizedContextStore(dir); |
1691 | | - await store.writeTurns([turn("a"), turn("b")]); |
1692 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1693 | | - const first = await store.commit({ message: "turns only" }); |
1694 | | - |
1695 | | - expect(turnTexts((await store.load()).turns)).toEqual(["a", "b"]); |
1696 | | - |
1697 | | - const fresh = await createOptimizedContextStore(dir); |
1698 | | - expect(turnTexts((await fresh.load()).turns)).toEqual(["a", "b"]); |
1699 | | - const second = await fresh.commit({ message: "turns-only no-op" }); |
1700 | | - expect(second.hash).toBe(first.hash); |
1701 | | - expect(fs.existsSync(path.join(dir, PROMPT_FILE))).toBe(false); |
1702 | | - expect(await gitLsTree(dir)).not.toContain(PROMPT_FILE); |
1703 | | - }); |
1704 | | - |
1705 | | - test("identical writePrompt after a differing one removes stale prompt segments", async () => { |
1706 | | - const dir = tempDir(); |
1707 | | - const store = await createOptimizedContextStore(dir); |
1708 | | - const live = [turn("a"), turn("b")]; |
1709 | | - await store.writeTurns([...live]); |
1710 | | - await store.writePrompt([turn("a")]); |
1711 | | - expect(fs.existsSync(path.join(dir, PROMPT_FILE))).toBe(true); |
1712 | | - |
1713 | | - await store.writePrompt(cloneTurns(live)); |
1714 | | - expect(fs.existsSync(path.join(dir, PROMPT_FILE))).toBe(false); |
1715 | | - expect(await listSegmentFiles(dir, PROMPT_FILE)).toEqual([]); |
1716 | | - |
1717 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1718 | | - await store.commit({ message: "stale prompt removed" }); |
1719 | | - expect(await gitLsTree(dir)).not.toContain(PROMPT_FILE); |
1720 | | - expect(turnTexts((await store.load()).turns)).toEqual(["a", "b"]); |
1721 | | - }); |
1722 | | - |
1723 | | - test("committed then identical writePrompt drops prompt.jsonl from HEAD", async () => { |
1724 | | - const dir = tempDir(); |
1725 | | - const store = await createOptimizedContextStore(dir); |
1726 | | - const live = [turn("a"), turn("b")]; |
1727 | | - await store.writeTurns([...live]); |
1728 | | - await store.writePrompt([turn("a")]); |
1729 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1730 | | - await store.commit({ message: "differing prompt" }); |
1731 | | - expect(await gitLsTree(dir)).toContain(PROMPT_FILE); |
1732 | | - |
1733 | | - await store.writePrompt(cloneTurns(live)); |
1734 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1735 | | - const afterIdentical = await store.commit({ message: "identical prompt" }); |
1736 | | - expect(await gitLsTree(dir)).not.toContain(PROMPT_FILE); |
1737 | | - expect(turnTexts((await store.load()).turns)).toEqual(["a", "b"]); |
1738 | | - |
1739 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1740 | | - const empty = await store.commit({ message: "empty checkpoint" }); |
1741 | | - expect(empty.hash).toBe(afterIdentical.hash); |
1742 | | - }); |
1743 | | - |
1744 | | - test("identical writePrompt after multi-segment differing prompt drops all prompt files from HEAD", async () => { |
1745 | | - const dir = tempDir(); |
1746 | | - const store = await createOptimizedContextStore(dir); |
1747 | | - const live: ConversationTurn[] = []; |
1748 | | - const big = "x".repeat(20_000); |
1749 | | - for (let i = 0; i < 18; i++) { |
1750 | | - live.push(turn(`${i}-${big}`)); |
1751 | | - } |
1752 | | - await store.writeTurns([...live]); |
1753 | | - |
1754 | | - const differing: ConversationTurn[] = []; |
1755 | | - for (let i = 0; i < 18; i++) { |
1756 | | - differing.push(turn(`p-${i}-${big}`)); |
1757 | | - } |
1758 | | - await store.writePrompt(differing); |
1759 | | - const extraPrompt = segmentFileName(PROMPT_FILE, 1); |
1760 | | - expect(fs.existsSync(path.join(dir, PROMPT_FILE))).toBe(true); |
1761 | | - expect(fs.existsSync(path.join(dir, extraPrompt))).toBe(true); |
1762 | | - |
1763 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1764 | | - await store.commit({ message: "multi-segment prompt" }); |
1765 | | - const treeAfterDiffering = await gitLsTree(dir); |
1766 | | - expect(treeAfterDiffering).toContain(PROMPT_FILE); |
1767 | | - expect(treeAfterDiffering).toContain(extraPrompt); |
1768 | | - |
1769 | | - await store.writePrompt(cloneTurns(live)); |
1770 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1771 | | - await store.commit({ message: "identical drops prompt segments" }); |
1772 | | - const tree = await gitLsTree(dir); |
1773 | | - expect(tree).not.toContain(PROMPT_FILE); |
1774 | | - expect(tree).not.toContain(extraPrompt); |
1775 | | - expect(turnTexts((await store.load()).turns)).toEqual(turnTexts(live)); |
1776 | | - }); |
1777 | | - |
1778 | | - test("one remove failure still unstages the other paths in one index write", async () => { |
1779 | | - const dir = tempDir(); |
1780 | | - const store = await createOptimizedContextStore(dir); |
1781 | | - const huge = "x".repeat(300_000); |
1782 | | - const live = [turn(`live-${huge}`), turn("live-tail")]; |
1783 | | - await store.writeTurns([...live]); |
1784 | | - await store.writePrompt([turn(`prompt-${huge}`), turn("prompt-tail")]); |
1785 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1786 | | - await store.commit({ message: "two prompt segments" }); |
1787 | | - const extraPrompt = segmentFileName(PROMPT_FILE, 1); |
1788 | | - expect(await gitLsTree(dir)).toEqual( |
1789 | | - expect.arrayContaining([PROMPT_FILE, extraPrompt]), |
1790 | | - ); |
1791 | | - |
1792 | | - await store.writePrompt(cloneTurns(live)); |
1793 | | - await store.writeMetadata(EMPTY_CHECKPOINT_METADATA); |
1794 | | - |
1795 | | - const removeCalls: (string | string[])[] = []; |
1796 | | - await withMockedModuleDuring( |
1797 | | - import.meta.resolve("isomorphic-git"), |
1798 | | - (real: typeof import("isomorphic-git")) => { |
1799 | | - const remove: typeof real.default.remove = async (args) => { |
1800 | | - removeCalls.push(args.filepath); |
1801 | | - const paths = Array.isArray(args.filepath) |
1802 | | - ? args.filepath |
1803 | | - : [args.filepath]; |
1804 | | - if (paths.includes(extraPrompt)) { |
1805 | | - throw new Error("index write failed"); |
1806 | | - } |
1807 | | - return real.default.remove(args); |
1808 | | - }; |
1809 | | - return { ...real, default: { ...real.default, remove } }; |
1810 | | - }, |
1811 | | - () => store.commit({ message: "drop prompt segments" }), |
1812 | | - ); |
1813 | | - |
1814 | | - const tree = await gitLsTree(dir); |
1815 | | - expect(tree).not.toContain(PROMPT_FILE); |
1816 | | - expect(tree).toContain(extraPrompt); |
1817 | | - const written = removeCalls.filter((filepath) => { |
1818 | | - const paths = Array.isArray(filepath) ? filepath : [filepath]; |
1819 | | - return !paths.includes(extraPrompt); |
1820 | | - }); |
1821 | | - expect(written).toEqual([[PROMPT_FILE]]); |
1822 | | - }); |
1823 | | -}); |
1824 | | - |
1825 | 1584 | describe("createOptimizedContextStore per-turn perf (CL-10666)", () => { |
1826 | 1585 | const SEGMENT_MANIFEST_FILE = "segment-manifest.json"; |
1827 | 1586 |
|
|
0 commit comments