From 552c6256b8e27ac5040385a4abba1a829632ada6 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Mon, 25 May 2026 21:19:35 +0200 Subject: [PATCH 1/5] refactor(loader): remove dead module cache-buster from rule imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `?t=${Date.now()}` query string was introduced when archgate ran as a long-lived MCP server process where `loadRuleAdrs()` could be called multiple times. PR #50 removed the MCP server and ported everything to CLI commands, but left the cache-buster in place with an updated comment. Each `archgate check` is now a fresh Bun process — there is no module cache to bust. Closes #345 — the broader caching optimization proposed there is not warranted given current performance (~850ms total, ~188ms rule execution). Signed-off-by: Rhuan Barreto --- src/engine/loader.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/engine/loader.ts b/src/engine/loader.ts index fc7b5045..284d22df 100644 --- a/src/engine/loader.ts +++ b/src/engine/loader.ts @@ -308,11 +308,8 @@ export async function loadRuleAdrs( }; } - // Cache-bust: Bun caches import() per-process, so append a timestamp - // to force re-reading from disk on every call (critical for repeated invocations). // Use file:// URL to handle Windows backslash paths in import(). - const rulesUrl = `${pathToFileURL(rulesFile).href}?t=${Date.now()}`; - const mod = await import(rulesUrl); + const mod = await import(pathToFileURL(rulesFile).href); const parsed = RuleSetSchema.safeParse(mod.default); if (!parsed.success) { From f2b0aca8f73d99f0bd576ad54b09e8b07bf6dcd9 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Mon, 25 May 2026 21:19:42 +0200 Subject: [PATCH 2/5] chore: add baseBranch to archgate config Signed-off-by: Rhuan Barreto --- .archgate/config.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.archgate/config.json b/.archgate/config.json index e57fecf4..84becd94 100644 --- a/.archgate/config.json +++ b/.archgate/config.json @@ -1 +1,6 @@ -{ "domains": { "ci": "CI" } } +{ + "domains": { + "ci": "CI" + }, + "baseBranch": "origin/main" +} From 5d3f4c6d35b7a164c0c88e6e5f5fe5f08f370509 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Mon, 25 May 2026 21:22:25 +0200 Subject: [PATCH 3/5] style: format archgate config with oxfmt Signed-off-by: Rhuan Barreto --- .archgate/config.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.archgate/config.json b/.archgate/config.json index 84becd94..18262f90 100644 --- a/.archgate/config.json +++ b/.archgate/config.json @@ -1,6 +1 @@ -{ - "domains": { - "ci": "CI" - }, - "baseBranch": "origin/main" -} +{ "domains": { "ci": "CI" }, "baseBranch": "origin/main" } From 715408d92d5d7231d1ebf45b67dd589c3e1ac4b0 Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Mon, 25 May 2026 21:32:59 +0200 Subject: [PATCH 4/5] chore: remove stale MCP server references and regression tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update ARCH-007 context and consequences to remove outdated "MCP server" framing — the decision (Bun.spawn over Bun.$) is still valid but the rationale cited a deployment mode removed in PR #50 - Remove MCP regression tests from init-project, copilot-settings, and cursor-settings — testing what a function should NOT produce is unnecessary noise eight months after the removal Signed-off-by: Rhuan Barreto --- .../adrs/ARCH-007-cross-platform-subprocess-execution.md | 3 +-- tests/helpers/copilot-settings.test.ts | 8 -------- tests/helpers/cursor-settings.test.ts | 5 ----- tests/helpers/init-project.test.ts | 2 -- 4 files changed, 1 insertion(+), 17 deletions(-) diff --git a/.archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md b/.archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md index 5286457a..ca1419b3 100644 --- a/.archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md +++ b/.archgate/adrs/ARCH-007-cross-platform-subprocess-execution.md @@ -16,7 +16,7 @@ Bun provides two subprocess APIs: - **`Bun.$` (shell template literals)** — A shell-like API that pipes commands through a subprocess shell. Convenient syntax (`await Bun.$\`git ls-files\`.text()`), but relies on platform-specific shell behavior. - **`Bun.spawn` (array-based)** — A lower-level API that executes a command directly (no intermediate shell). Takes an array of arguments, explicit pipe configuration, and returns a process handle with `stdout`, `stderr`, and `exited` properties. -**The problem:** `Bun.$` hangs on Windows. The shell subprocess does not properly close stdin/stdout pipes, causing deadlocks that block the calling thread indefinitely. When the Archgate CLI runs as an MCP server inside Claude Code or Cursor, this deadlock freezes the entire editor's agent interface — the user must force-kill the process. This was discovered in production and fixed in commit `ca33377`, which replaced all `Bun.$` calls with `Bun.spawn`. +**The problem:** `Bun.$` hangs on Windows. The shell subprocess does not properly close stdin/stdout pipes, causing deadlocks that block the calling thread indefinitely. This was discovered in production and fixed in commit `ca33377`, which replaced all `Bun.$` calls with `Bun.spawn`. **Alternatives considered:** @@ -136,7 +136,6 @@ Bun.spawn(["git diff --cached | head -5"]); // This is a single argument, not a - **Cross-platform reliability** — `Bun.spawn` works identically on macOS, Linux, and Windows. No platform-specific pipe handling differences. - **No deadlocks** — Array-based execution avoids the stdin/stdout pipe issues that cause `Bun.$` to hang on Windows. -- **MCP server safety** — The CLI runs as a long-lived MCP server inside editors. A subprocess deadlock would freeze the entire agent interface. `Bun.spawn` eliminates this risk. - **Explicit argument handling** — Array-based arguments prevent shell injection vulnerabilities. Each argument is passed directly to the command, not interpreted by a shell. - **No shell dependency** — The command does not require a shell interpreter (bash, cmd.exe, PowerShell) to be available or configured correctly. - **Consistent error handling** — `proc.exited` returns a Promise that resolves to the exit code, making error checking uniform across all subprocess calls. diff --git a/tests/helpers/copilot-settings.test.ts b/tests/helpers/copilot-settings.test.ts index 86eaf044..1dbb9522 100644 --- a/tests/helpers/copilot-settings.test.ts +++ b/tests/helpers/copilot-settings.test.ts @@ -24,14 +24,6 @@ describe("configureCopilotSettings", () => { expect(existsSync(join(tempDir, ".github", "copilot"))).toBe(true); }); - test("does not create mcp.json", async () => { - await configureCopilotSettings(tempDir); - - expect(existsSync(join(tempDir, ".github", "copilot", "mcp.json"))).toBe( - false - ); - }); - test("returns path to .github/copilot/ directory", async () => { const result = await configureCopilotSettings(tempDir); diff --git a/tests/helpers/cursor-settings.test.ts b/tests/helpers/cursor-settings.test.ts index d6b018e6..04738412 100644 --- a/tests/helpers/cursor-settings.test.ts +++ b/tests/helpers/cursor-settings.test.ts @@ -27,9 +27,4 @@ describe("configureCursorSettings", () => { configureCursorSettings(tempDir); expect(existsSync(join(tempDir, ".cursor"))).toBe(false); }); - - test("does not create mcp.json", () => { - configureCursorSettings(tempDir); - expect(existsSync(join(tempDir, ".cursor", "mcp.json"))).toBe(false); - }); }); diff --git a/tests/helpers/init-project.test.ts b/tests/helpers/init-project.test.ts index 46ad83bf..44ac9bfb 100644 --- a/tests/helpers/init-project.test.ts +++ b/tests/helpers/init-project.test.ts @@ -120,8 +120,6 @@ describe("initProject", () => { const content = JSON.parse(await Bun.file(settingsPath).text()); expect(content.agent).toBe("archgate:developer"); - // MCP settings should not be present (MCP removed) - expect(content.enabledMcpjsonServers).toBeUndefined(); }); test("includes editorSettingsPath in result", async () => { From 7f8f7070bb72ff096e92dd62085542029acfdccb Mon Sep 17 00:00:00 2001 From: Rhuan Barreto Date: Mon, 25 May 2026 21:40:32 +0200 Subject: [PATCH 5/5] test: remove vestigial regression guards for removed features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove test assertions that only existed to guard against re-introduction of features that were removed long ago: - `--prefix` option on `adr import` (replaced by domain-aware remapping) - `defineRules` reference in rules template (removed when satisfies RuleSet replaced it) - `.cursor/rules/archgate-governance.mdc` (removed when Cursor plugin moved to VSIX) - `.cursor/mcp.json` in init-project (MCP server removed in PR #50) These add noise without value — the removed code paths don't exist, TypeScript wouldn't compile references to them, and broader assertions already cover the expected behavior. Signed-off-by: Rhuan Barreto --- tests/commands/adr/import.test.ts | 7 ------- tests/helpers/init-project.test.ts | 1 - tests/helpers/rules-shim.test.ts | 2 -- tests/integration/init.test.ts | 5 +---- 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/tests/commands/adr/import.test.ts b/tests/commands/adr/import.test.ts index 2b757c32..01e465e3 100644 --- a/tests/commands/adr/import.test.ts +++ b/tests/commands/adr/import.test.ts @@ -106,13 +106,6 @@ describe("registerAdrImportCommand", () => { expect(sub.options.find((o) => o.long === "--dry-run")).toBeDefined(); }); - test("does not have a --prefix option (domain-aware remapping)", () => { - const parent = new Command("adr"); - registerAdrImportCommand(parent); - const sub = parent.commands.find((c) => c.name() === "import")!; - expect(sub.options.find((o) => o.long === "--prefix")).toBeUndefined(); - }); - test("accepts --list option", () => { const parent = new Command("adr"); registerAdrImportCommand(parent); diff --git a/tests/helpers/init-project.test.ts b/tests/helpers/init-project.test.ts index 44ac9bfb..69506f1e 100644 --- a/tests/helpers/init-project.test.ts +++ b/tests/helpers/init-project.test.ts @@ -85,7 +85,6 @@ describe("initProject", () => { // Cursor plugin is embedded in the VSIX — no project-level files written expect(existsSync(join(tempDir, ".cursor"))).toBe(false); - expect(existsSync(join(tempDir, ".cursor", "mcp.json"))).toBe(false); // Claude settings should NOT exist expect(existsSync(join(tempDir, ".claude", "settings.local.json"))).toBe( diff --git a/tests/helpers/rules-shim.test.ts b/tests/helpers/rules-shim.test.ts index 87f4fbb6..0cc462a9 100644 --- a/tests/helpers/rules-shim.test.ts +++ b/tests/helpers/rules-shim.test.ts @@ -43,8 +43,6 @@ describe("rules-shim", () => { expect(template).toContain('/// '); expect(template).toContain("satisfies RuleSet"); expect(template).toContain("export default {"); - // Should NOT reference defineRules - expect(template).not.toContain("defineRules"); }); test("writeRulesShim creates rules.d.ts in .archgate/", async () => { diff --git a/tests/integration/init.test.ts b/tests/integration/init.test.ts index a7722b90..68ee1b42 100644 --- a/tests/integration/init.test.ts +++ b/tests/integration/init.test.ts @@ -65,10 +65,7 @@ describe("init integration", () => { expect(result.exitCode).toBe(0); // Cursor plugin is embedded in the VSIX — no .cursor/ files written - expect(existsSync(join(tempDir, ".cursor", "rules"))).toBe(false); - expect( - existsSync(join(tempDir, ".cursor", "rules", "archgate-governance.mdc")) - ).toBe(false); + expect(existsSync(join(tempDir, ".cursor"))).toBe(false); }); test("init with --editor copilot creates copilot directory", async () => {