
























@@ -108,6 +108,31 @@ async function createHeartbeatAgentsWorkspace() {
108108return workspaceDir;
109109}
110110111+async function writeCompletedWorkspaceState(workspaceDir: string): Promise<void> {
112+await fs.writeFile(
113+path.join(workspaceDir, "openclaw-workspace-state.json"),
114+`${JSON.stringify({
115+ version: 1,
116+ bootstrapSeededAt: "2026-05-16T00:00:00.000Z",
117+ setupCompletedAt: "2026-05-16T00:00:01.000Z",
118+ })}\n`,
119+"utf8",
120+);
121+}
122+123+async function writeLegacyCompletedWorkspaceState(workspaceDir: string): Promise<void> {
124+await fs.mkdir(path.join(workspaceDir, ".openclaw"), { recursive: true });
125+await fs.writeFile(
126+path.join(workspaceDir, ".openclaw", "workspace-state.json"),
127+`${JSON.stringify({
128+ version: 1,
129+ bootstrapSeededAt: "2026-05-16T00:00:00.000Z",
130+ setupCompletedAt: "2026-05-16T00:00:01.000Z",
131+ })}\n`,
132+"utf8",
133+);
134+}
135+111136function expectHeartbeatExcludedAndAgentsKept(files: WorkspaceBootstrapFile[]) {
112137// Heartbeat policy can remove HEARTBEAT.md for normal turns, but project rules
113138// must remain in the bootstrap set.
@@ -174,16 +199,19 @@ describe("resolveBootstrapFilesForRun", () => {
174199175200it("ignores stale workspace BOOTSTRAP.md once setup is completed", async () => {
176201const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
177-await fs.mkdir(path.join(workspaceDir, ".openclaw"), { recursive: true });
178-await fs.writeFile(
179-path.join(workspaceDir, ".openclaw", "workspace-state.json"),
180-`${JSON.stringify({
181- version: 1,
182- bootstrapSeededAt: "2026-05-16T00:00:00.000Z",
183- setupCompletedAt: "2026-05-16T00:00:01.000Z",
184- })}\n`,
185-"utf8",
186-);
202+await writeCompletedWorkspaceState(workspaceDir);
203+await fs.writeFile(path.join(workspaceDir, "AGENTS.md"), "rules", "utf8");
204+await fs.writeFile(path.join(workspaceDir, "BOOTSTRAP.md"), "stale ritual", "utf8");
205+206+const files = await resolveBootstrapFilesForRun({ workspaceDir });
207+208+expect(files.map((file) => file.name)).toContain("AGENTS.md");
209+expect(files.map((file) => file.name)).not.toContain("BOOTSTRAP.md");
210+});
211+212+it("ignores stale workspace BOOTSTRAP.md when legacy setup state is completed", async () => {
213+const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
214+await writeLegacyCompletedWorkspaceState(workspaceDir);
187215await fs.writeFile(path.join(workspaceDir, "AGENTS.md"), "rules", "utf8");
188216await fs.writeFile(path.join(workspaceDir, "BOOTSTRAP.md"), "stale ritual", "utf8");
189217@@ -193,9 +221,9 @@ describe("resolveBootstrapFilesForRun", () => {
193221expect(files.map((file) => file.name)).not.toContain("BOOTSTRAP.md");
194222});
195223196-it("keeps BOOTSTRAP.md when setup state cannot be read", async () => {
224+it("keeps BOOTSTRAP.md when current setup state cannot be read", async () => {
197225const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
198-await fs.mkdir(path.join(workspaceDir, ".openclaw", "workspace-state.json"), {
226+await fs.mkdir(path.join(workspaceDir, "openclaw-workspace-state.json"), {
199227recursive: true,
200228});
201229await fs.writeFile(path.join(workspaceDir, "AGENTS.md"), "rules", "utf8");
@@ -209,16 +237,7 @@ describe("resolveBootstrapFilesForRun", () => {
209237it("does not let hooks re-add stale root BOOTSTRAP.md after setup is completed", async () => {
210238registerBootstrapFileHook();
211239const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
212-await fs.mkdir(path.join(workspaceDir, ".openclaw"), { recursive: true });
213-await fs.writeFile(
214-path.join(workspaceDir, ".openclaw", "workspace-state.json"),
215-`${JSON.stringify({
216- version: 1,
217- bootstrapSeededAt: "2026-05-16T00:00:00.000Z",
218- setupCompletedAt: "2026-05-16T00:00:01.000Z",
219- })}\n`,
220-"utf8",
221-);
240+await writeCompletedWorkspaceState(workspaceDir);
222241await fs.writeFile(path.join(workspaceDir, "AGENTS.md"), "rules", "utf8");
223242await fs.writeFile(path.join(workspaceDir, "BOOTSTRAP.md"), "stale ritual", "utf8");
224243@@ -231,16 +250,8 @@ describe("resolveBootstrapFilesForRun", () => {
231250registerBootstrapFileHook();
232251const parentDir = await makeTempWorkspace("openclaw-bootstrap-home-");
233252const workspaceDir = path.join(parentDir, "workspace");
234-await fs.mkdir(path.join(workspaceDir, ".openclaw"), { recursive: true });
235-await fs.writeFile(
236-path.join(workspaceDir, ".openclaw", "workspace-state.json"),
237-`${JSON.stringify({
238- version: 1,
239- bootstrapSeededAt: "2026-05-16T00:00:00.000Z",
240- setupCompletedAt: "2026-05-16T00:00:01.000Z",
241- })}\n`,
242-"utf8",
243-);
253+await fs.mkdir(workspaceDir, { recursive: true });
254+await writeCompletedWorkspaceState(workspaceDir);
244255await fs.writeFile(path.join(workspaceDir, "AGENTS.md"), "rules", "utf8");
245256await fs.writeFile(path.join(workspaceDir, "BOOTSTRAP.md"), "stale ritual", "utf8");
246257@@ -263,17 +274,8 @@ describe("resolveBootstrapFilesForRun", () => {
263274it("keeps hook-added nested BOOTSTRAP.md after setup is completed", async () => {
264275registerBootstrapFileHook(path.join("packages", "core", "BOOTSTRAP.md"));
265276const workspaceDir = await makeTempWorkspace("openclaw-bootstrap-");
266-await fs.mkdir(path.join(workspaceDir, ".openclaw"), { recursive: true });
267277await fs.mkdir(path.join(workspaceDir, "packages", "core"), { recursive: true });
268-await fs.writeFile(
269-path.join(workspaceDir, ".openclaw", "workspace-state.json"),
270-`${JSON.stringify({
271- version: 1,
272- bootstrapSeededAt: "2026-05-16T00:00:00.000Z",
273- setupCompletedAt: "2026-05-16T00:00:01.000Z",
274- })}\n`,
275-"utf8",
276-);
278+await writeCompletedWorkspaceState(workspaceDir);
277279await fs.writeFile(path.join(workspaceDir, "AGENTS.md"), "rules", "utf8");
278280await fs.writeFile(path.join(workspaceDir, "BOOTSTRAP.md"), "stale ritual", "utf8");
279281await fs.writeFile(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。