import fs from "node:fs/promises"; import { describe, it } from "node:test"; import { expect } from "expect"; import { writeTruthmarkConfig } from "../helpers/truthmark-config.js"; import { runInit } from "../../src/init/init.js"; import type { LifecyclePlan } from "../../src/init/lifecycle.js"; import { TRUTHMARK_BLOCK_END, TRUTHMARK_BLOCK_START, } from "../../src/templates/agents-block.js"; import { createTempRepo } from "../helpers/temp-repo.js"; describe("runInit instruction integration", () => { it("installs a single managed AGENTS block with agent-native workflow guidance", async () => { const repo = await createTempRepo(); try { await writeTruthmarkConfig(repo.rootDir); await repo.writeFile( ".truthmark/config.yml", (await repo.readFile(".truthmark/config.yml")).replace( "version: 2\n", "version: 2\nplatforms:\n - codex\n", ), ); await runInit(repo.rootDir); const agents = await repo.readFile("AGENTS.md"); expect(agents.match(//g)).toHaveLength(1); expect(agents.match(//g)).toHaveLength(1); expect(agents.split("\n").length).toBeLessThanOrEqual(20); expect(agents.slice(0, 220)).toContain("Truthmark Workflow"); expect(agents).not.toContain("/skill truthmark-sync"); expect(agents).not.toContain("OpenCode /skill truthmark-sync"); expect(agents).toContain("Truthmark-managed block"); expect(agents).not.toContain("Generated by Truthmark"); expect(agents).toContain("After functional code changes"); expect(agents).toContain("code changed -> tests -> Sync -> report"); expect(agents).toContain("Delegation is host-owned"); expect(agents).toContain( "Explicit workflows: Truth Structure, Truth Document, Truth Realize, Truth Check", ); expect(agents).toContain("load the installed skill for details"); expect(agents).not.toContain("/skill truthmark-structure"); expect(agents).not.toContain("/skill truthmark-check"); expect(agents).not.toContain( "truthmark check --json --workflow truth-sync", ); expect(agents).not.toContain("Truth Structure: completed"); expect(agents).not.toContain("Truth Sync: completed"); expect(agents).not.toContain("Truth Sync: skipped"); expect(agents).not.toContain("Truth Realize: completed"); expect(agents).not.toContain("### Truth Check"); expect(agents).toContain("inspect checkout directly"); expect(agents).not.toContain("truthmark packet --changed"); await expect(fs.stat(`${repo.rootDir}/OPENCODE.md`)).rejects.toThrow(); } finally { await repo.cleanup(); } }); it("rerenders the managed block without duplicating it and leaves subagent choice to the host", async () => { const repo = await createTempRepo(); try { await writeTruthmarkConfig(repo.rootDir); await repo.writeFile( ".truthmark/config.yml", `version: 2 platforms: - codex truthmark: workspace: docs/truthmark generated: portal: enabled: false frontmatter: required: [] recommended: - status ignore: [] `, ); await runInit(repo.rootDir); await runInit(repo.rootDir); const agents = await repo.readFile("AGENTS.md"); expect(agents.match(//g)).toHaveLength(1); expect(agents).toContain("Delegation is host-owned"); expect(agents).not.toContain(".truthmark/local.yml"); expect(agents).not.toContain("truth_sync.sync_agent"); expect(agents).toContain( "later functional changes need a fresh Sync review", ); expect(agents).not.toContain("Explicit invocation:"); } finally { await repo.cleanup(); } }); it("ignores legacy instruction_targets and only writes platform-derived instruction surfaces", async () => { const repo = await createTempRepo(); try { await writeTruthmarkConfig(repo.rootDir); await repo.writeFile( ".truthmark/config.yml", `version: 2 platforms: - codex - claude-code truthmark: workspace: docs/truthmark generated: portal: enabled: false instruction_targets: - src/session.ts frontmatter: required: [] recommended: [] ignore: [] `, ); await repo.writeFile("src/session.ts", "export const session = true;\n"); const result = await runInit(repo.rootDir); expect( await repo.readFile("src/session.ts"), ).toBe("export const session = true;\n"); expect(await repo.readFile("AGENTS.md")).toContain("Truthmark Workflow"); expect(await repo.readFile("CLAUDE.md")).toContain("Truthmark Workflow"); expect(result.diagnostics).toEqual( expect.arrayContaining([ expect.objectContaining({ message: expect.stringContaining("instruction_targets"), }), ]), ); } finally { await repo.cleanup(); } }); it("rerenders managed AGENTS with CRLF user prefixes and suffixes preserved", async () => { const repo = await createTempRepo(); try { await writeTruthmarkConfig(repo.rootDir); await repo.writeFile( ".truthmark/config.yml", `version: 2 platforms: - codex truthmark: workspace: docs/truthmark generated: portal: enabled: false frontmatter: required: [] recommended: - status ignore: [] `, ); await runInit(repo.rootDir); const prefix = " before\r\n"; const suffix = "\r\n after \r\n\r\n"; const generated = await repo.readFile("AGENTS.md"); await repo.writeFile( "AGENTS.md", `${prefix}${generated.trimEnd().replace(/\n/g, "\r\n")}${suffix}`, ); const result = await runInit(repo.rootDir); expect(result.diagnostics).toEqual( expect.not.arrayContaining([ expect.objectContaining({ severity: "error" }), ]), ); const refreshed = await repo.readFile("AGENTS.md"); expect(refreshed.startsWith(prefix)).toBe(true); expect(refreshed.endsWith(suffix)).toBe(true); expect(refreshed).toContain("Truthmark Workflow"); expect(refreshed).toContain("Truthmark-managed block"); } finally { await repo.cleanup(); } }); for (const [caseName, malformedBlock] of [ [ "duplicate managed blocks", `${TRUTHMARK_BLOCK_START}\nManaged 1\n${TRUTHMARK_BLOCK_START}\nManaged 2\n${TRUTHMARK_BLOCK_END}`, ], [ "reversed managed markers", `${TRUTHMARK_BLOCK_END}\nManaged 1\n${TRUTHMARK_BLOCK_START}\n`, ], [ "unmatched managed start marker", `${TRUTHMARK_BLOCK_START}\nManaged 1\n`, ], [ "unmatched managed end marker", `Managed 1\n${TRUTHMARK_BLOCK_END}\n`, ], ] as const) { it(`blocks init when managed markers are ${caseName}`, async () => { const repo = await createTempRepo(); try { await writeTruthmarkConfig(repo.rootDir); await repo.writeFile( ".truthmark/config.yml", `version: 2 platforms: - codex truthmark: workspace: docs/truthmark generated: portal: enabled: false frontmatter: required: [] recommended: - status ignore: [] `, ); await runInit(repo.rootDir); await repo.writeFile("AGENTS.md", `${malformedBlock}\n`); const before = await repo.readFile("AGENTS.md"); const result = await runInit(repo.rootDir); expect(result.summary).toContain("preflight failed"); expect(await repo.readFile("AGENTS.md")).toBe(before); expect(result.diagnostics).toEqual( expect.arrayContaining([ expect.objectContaining({ category: "generated-surface", severity: "error", message: expect.stringContaining("preflight"), }), ]), ); const lifecyclePlan = result.data?.lifecyclePlan as LifecyclePlan | undefined; expect(lifecyclePlan?.entries).toEqual( expect.arrayContaining([ expect.objectContaining({ action: "manual-review", path: "AGENTS.md", }), ]), ); } finally { await repo.cleanup(); } }); } it("removes auto-removable retired generated surfaces but preserves stale Gemini surfaces", async () => { const repo = await createTempRepo(); try { await writeTruthmarkConfig(repo.rootDir); await repo.writeFile( ".truthmark/config.yml", (await repo.readFile(".truthmark/config.yml")).replace( "version: 2\n", "version: 2\nplatforms:\n - codex\n", ), ); await runInit(repo.rootDir); await repo.writeFile( ".agents/skills/truthmark-preview/SKILL.md", "# Legacy Preview Skill\n", ); await repo.writeFile( ".github/prompts/truthmark-preview.prompt.md", "# Legacy Preview Prompt\n", ); await repo.writeFile( ".gemini/commands/truthmark/preview.toml", 'description = "Legacy Preview Command"\n', ); await repo.writeFile("GEMINI.md", "# Legacy Gemini instructions\n"); await repo.writeFile( ".gemini/skills/truthmark-sync/SKILL.md", "# Legacy Gemini Sync Skill\n", ); await repo.writeFile( ".gemini/agents/truth-doc-writer.md", "# Legacy Gemini doc writer\n", ); await repo.writeFile( ".gemini/commands/truthmark/sync.toml", 'description = "Legacy Sync Command"\n', ); await repo.writeFile( ".agents/skills/truthmark-sync/helper-manifest.yml", "id: truthmark-sync\n", ); await repo.writeFile( ".opencode/skills/truthmark-sync/support/helper-policy.md", "Legacy helper policy\n", ); await runInit(repo.rootDir); await expect( fs.stat(`${repo.rootDir}/.agents/skills/truthmark-preview/SKILL.md`), ).resolves.toBeDefined(); await expect( fs.stat(`${repo.rootDir}/.github/prompts/truthmark-preview.prompt.md`), ).resolves.toBeDefined(); await expect( fs.stat(`${repo.rootDir}/.gemini/commands/truthmark/preview.toml`), ).resolves.toBeDefined(); await expect(fs.stat(`${repo.rootDir}/GEMINI.md`)).resolves.toBeDefined(); await expect( fs.stat(`${repo.rootDir}/.gemini/skills/truthmark-sync/SKILL.md`), ).resolves.toBeDefined(); await expect( fs.stat(`${repo.rootDir}/.gemini/agents/truth-doc-writer.md`), ).resolves.toBeDefined(); await expect( fs.stat(`${repo.rootDir}/.gemini/commands/truthmark/sync.toml`), ).resolves.toBeDefined(); await expect( fs.stat( `${repo.rootDir}/.agents/skills/truthmark-sync/helper-manifest.yml`, ), ).resolves.toBeDefined(); await expect( fs.stat( `${repo.rootDir}/.opencode/skills/truthmark-sync/support/helper-policy.md`, ), ).resolves.toBeDefined(); } finally { await repo.cleanup(); } }); });