From 7e9005f84f5d47362a05dfbb53fb45fb5f2f9b85 Mon Sep 17 00:00:00 2001 From: ngallodev Date: Thu, 1 Oct 2026 14:35:23 -0700 Subject: [PATCH] fix(tests): repair PR #12 Windows hook-lifecycle and macOS connection-cap failures Windows: cli_windows_lifecycle (tests/test_cli.c:9090) installed hooks bound to /.local/bin/codebase-memory-mcp.exe, but `uninstall` derives its expected owned-script bytes from cbm_agent_installed_binary_path(), which in this fork returns codebase-memory-cli.exe. The seeded .cmd scripts and their extensionless twins therefore never matched the owned shapes and were left in place. The test now uses the fork's installed binary name, matching the path uninstall actually resolves. Upstream's test used mcp.exe because its installed binary still has that name. macOS: daemon_runtime_connection_cap_covers_slow_hello_and_stopping_is_terminal keeps a raw connection open without sending hello so it occupies one of two slots. The fixture's 2 s server hello deadline could expire during the two full client handshakes on a loaded sanitized runner (the suite took 427 s on macos-14), freeing the slot before the overflow connect was sampled. This one fixture now gets a 30 s request timeout through a static override, following the existing runtime_test_fixture_permanent pattern. The test closes the slow connection itself, so nothing depends on the shorter deadline. The capacity assertion is unchanged. --- tests/test_cli.c | 1071 ++++++++++++++++++++++------------- tests/test_daemon_runtime.c | 47 +- 2 files changed, 693 insertions(+), 425 deletions(-) diff --git a/tests/test_cli.c b/tests/test_cli.c index fd0635fe99..02d7d7aeb3 100644 --- a/tests/test_cli.c +++ b/tests/test_cli.c @@ -1449,7 +1449,8 @@ TEST(cli_install_config_failure_keeps_published_binary) { struct stat binary_status; bool binary_published = stat(bin_target, &binary_status) == 0; struct stat guarded_status; - bool refused_asset_preserved = stat(factory_hooks, &guarded_status) == 0 && S_ISDIR(guarded_status.st_mode); + bool refused_asset_preserved = + stat(factory_hooks, &guarded_status) == 0 && S_ISDIR(guarded_status.st_mode); restore_test_env("PATH", old_path); restore_test_env("SHELL", old_shell); cli_activation_restore_env(old_home, old_cache); @@ -2447,8 +2448,6 @@ TEST(cli_codex_instructions) { * Editor MCP config tests (Cursor/Windsurf/Gemini) * ═══════════════════════════════════════════════════════════════════ */ - - /* The OS-reported running image is positive prior-install identity. An update * may replace that exact command without probing it or granting the same * authority to uninstall/remove paths. */ @@ -2471,14 +2470,16 @@ TEST(cli_editor_mcp_rejects_unsafe_windows_probe_namespaces) { ASSERT_FALSE( cbm_legacy_command_path_probe_safe_for_testing("/mnt/remote/codebase-memory-mcp", false)); ASSERT_FALSE(cbm_legacy_command_path_probe_safe_for_testing("/tmp/codebase-memory-mcp", false)); - ASSERT_TRUE(cbm_legacy_command_path_probe_safe_for_testing("C:/local/codebase-memory-mcp", true)); + ASSERT_TRUE( + cbm_legacy_command_path_probe_safe_for_testing("C:/local/codebase-memory-mcp", true)); ASSERT_TRUE( cbm_legacy_command_path_probe_safe_for_testing("D:\\local\\codebase-memory-mcp", true)); ASSERT_FALSE( cbm_legacy_command_path_probe_safe_for_testing("//server/share/codebase-memory-mcp", true)); ASSERT_FALSE(cbm_legacy_command_path_probe_safe_for_testing( "\\\\server\\share\\codebase-memory-mcp", true)); - ASSERT_FALSE(cbm_legacy_command_path_probe_safe_for_testing("//?/C:/codebase-memory-mcp", true)); + ASSERT_FALSE( + cbm_legacy_command_path_probe_safe_for_testing("//?/C:/codebase-memory-mcp", true)); ASSERT_FALSE( cbm_legacy_command_path_probe_safe_for_testing("\\\\.\\pipe\\codebase-memory-mcp", true)); PASS(); @@ -2499,10 +2500,6 @@ TEST(cli_editor_mcp_rejects_unsafe_windows_probe_namespaces) { * is somebody's deliberate configuration: upsert must keep refusing to touch * it. Pins the protective half of the ownership check. */ - - - - TEST(cli_goose_block_carries_required_name_issue1675) { /* goose's ExtensionConfig::Stdio declares `name` as a required serde field * with no default, and its loader silently drops entries that fail to @@ -2524,14 +2521,6 @@ TEST(cli_goose_block_carries_required_name_issue1675) { PASS(); } - - - - - - - - TEST(cli_openclaw_compaction_preserves_user_owned_section) { char tmpdir[256]; snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-openclaw-compaction-XXXXXX"); @@ -2627,7 +2616,8 @@ TEST(cli_openclaw_profile_uses_profile_state_and_default_workspace) { cbm_detected_agents_t agents = cbm_detect_agents(tmpdir); char *asset_plan = cbm_build_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); - char *hook_plan = cbm_build_hook_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); + char *hook_plan = + cbm_build_hook_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); bool correct = agents.openclaw && asset_plan && hook_plan && strstr(hook_plan, "/.openclaw-work/openclaw.json") && strstr(asset_plan, "/.openclaw/workspace-work/AGENTS.md") && @@ -2681,17 +2671,10 @@ TEST(cli_openclaw_uninstall_removes_compaction_when_workspace_is_ambiguous) { * VS Code MCP config tests * ═══════════════════════════════════════════════════════════════════ */ - - - /* ═══════════════════════════════════════════════════════════════════ * Zed MCP config tests * ═══════════════════════════════════════════════════════════════════ */ - - - - /* ═══════════════════════════════════════════════════════════════════ * PATH management tests (port of TestCLI_InstallPATHAppend) * ═══════════════════════════════════════════════════════════════════ */ @@ -3299,7 +3282,8 @@ TEST(cli_special_hook_failures_propagate_from_install_and_uninstall) { char *saved_path = save_test_env("PATH"); cbm_setenv("HOME", tmpdir, 1); cbm_setenv("PATH", tmpdir, 1); - int install_rc = cbm_install_agent_hooks_for_testing(tmpdir, "/opt/codebase-memory-mcp", false, false); + int install_rc = + cbm_install_agent_hooks_for_testing(tmpdir, "/opt/codebase-memory-mcp", false, false); char *args[] = {"-n"}; int uninstall_rc = cli_test_cmd_uninstall(1, args); @@ -3542,13 +3526,50 @@ TEST(cli_detect_agents_finds_cursor_issue222) { /* Supported-agent metadata must track the real installer surface. */ TEST(cli_supported_agent_surfaces_match_installers) { const char *const required_agents[] = { - "Claude Code", "Codex CLI", "Gemini CLI", "Zed", "OpenCode", "Antigravity", "Aider", - "KiloCode", "VS Code", "Cursor", "Windsurf", "Augment / Auggie", "OpenClaw", "Kiro", - "Junie", "Hermes", "OpenHands", "Cline", "Warp", "Qwen Code", "GitHub Copilot CLI", - "Factory Droid", "Crush", "Goose", "Mistral Vibe", "Grok Build", "Qoder CLI", - "Kimi Code CLI", "GitLab Duo CLI", "Rovo Dev CLI", "Amp", "Devin CLI / Local", - "Tabnine", "Continue / cn", "Visual Studio", "TRAE", "Roo Code", "Amazon Q Developer IDE", - "CodeBuddy Code CLI", "IBM Bob IDE", "IBM Bob Shell", "Pochi", "Pi", "Sourcegraph Cody", + "Claude Code", + "Codex CLI", + "Gemini CLI", + "Zed", + "OpenCode", + "Antigravity", + "Aider", + "KiloCode", + "VS Code", + "Cursor", + "Windsurf", + "Augment / Auggie", + "OpenClaw", + "Kiro", + "Junie", + "Hermes", + "OpenHands", + "Cline", + "Warp", + "Qwen Code", + "GitHub Copilot CLI", + "Factory Droid", + "Crush", + "Goose", + "Mistral Vibe", + "Grok Build", + "Qoder CLI", + "Kimi Code CLI", + "GitLab Duo CLI", + "Rovo Dev CLI", + "Amp", + "Devin CLI / Local", + "Tabnine", + "Continue / cn", + "Visual Studio", + "TRAE", + "Roo Code", + "Amazon Q Developer IDE", + "CodeBuddy Code CLI", + "IBM Bob IDE", + "IBM Bob Shell", + "Pochi", + "Pi", + "Sourcegraph Cody", "Oh My Pi (omp)", }; ASSERT_EQ(sizeof(required_agents) / sizeof(required_agents[0]), 45U); @@ -3556,7 +3577,8 @@ TEST(cli_supported_agent_surfaces_match_installers) { char *data = read_test_file_alloc("README.md"); if (!data) FAIL("could not read README.md for supported-agent contract"); - if (!strstr(data, "CLI-first skills") || !strstr(data, "**never installs hooks**") || !strstr(data, "does not create, migrate, or remove `codebase-memory-mcp`")) { + if (!strstr(data, "CLI-first skills") || !strstr(data, "**never installs hooks**") || + !strstr(data, "does not create, migrate, or remove `codebase-memory-mcp`")) { free(data); FAIL("README must describe the current CLI-first integration contract"); } @@ -3603,7 +3625,9 @@ TEST(cli_supported_agent_surfaces_match_installers) { if (!data) FAIL("could not read docs/llms.txt for supported-agent contract"); if (!strstr(data, "CLI-first skills") || - !strstr(data, "Neither lifecycle surface creates, migrates, adopts, or removes MCP registrations")) { + !strstr( + data, + "Neither lifecycle surface creates, migrates, adopts, or removes MCP registrations")) { free(data); FAIL("llms.txt must describe the current CLI-first integration contract"); } @@ -3633,14 +3657,20 @@ TEST(cli_new_agent_install_plans_use_documented_paths) { #endif const char *const dirs[] = { - ".hermes", ".openhands", ".cline", ".qwen", ".copilot", ".factory", + ".hermes", + ".openhands", + ".cline", + ".qwen", + ".copilot", + ".factory", ".config/crush", #ifdef _WIN32 "AppData/Roaming/Block/goose/config", #else ".config/goose", #endif - ".vibe", ".grok", + ".vibe", + ".grok", }; char path[768]; for (size_t i = 0; i < sizeof(dirs) / sizeof(dirs[0]); i++) { @@ -3653,35 +3683,58 @@ TEST(cli_new_agent_install_plans_use_documented_paths) { write_test_file(path, "{}\n"); char *asset_json = cbm_build_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); - char *hook_json = cbm_build_hook_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); - size_t combined_len = (asset_json ? strlen(asset_json) : 0U) + (hook_json ? strlen(hook_json) : 0U) + 2U; + char *hook_json = + cbm_build_hook_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); + size_t combined_len = + (asset_json ? strlen(asset_json) : 0U) + (hook_json ? strlen(hook_json) : 0U) + 2U; char *json = calloc(combined_len, 1U); - if (json) snprintf(json, combined_len, "%s\n%s", asset_json ? asset_json : "", hook_json ? hook_json : ""); + if (json) + snprintf(json, combined_len, "%s\n%s", asset_json ? asset_json : "", + hook_json ? hook_json : ""); free(asset_json); free(hook_json); const char *const required[] = { - "\"hermes\"", "/.hermes/config.yaml", "/.hermes/skills/codebase-memory-cli/SKILL.md", - "\"openhands\"", "/.agents/skills/codebase-memory-cli/SKILL.md", - "\"cline\"", "/.cline/rules/codebase-memory-cli.md", + "\"hermes\"", + "/.hermes/config.yaml", + "/.hermes/skills/codebase-memory-cli/SKILL.md", + "\"openhands\"", + "/.agents/skills/codebase-memory-cli/SKILL.md", + "\"cline\"", + "/.cline/rules/codebase-memory-cli.md", "/.cline/skills/codebase-memory-cli/SKILL.md", - "\"qwen\"", "/.qwen/QWEN.md", "/.qwen/settings.json", + "\"qwen\"", + "/.qwen/QWEN.md", + "/.qwen/settings.json", "/.qwen/skills/codebase-memory-cli/SKILL.md", - "\"copilot-cli\"", "/.copilot/copilot-instructions.md", - "/.copilot/hooks/codebase-memory-cli.json", "/.copilot/skills/codebase-memory-cli/SKILL.md", - "\"factory-droid\"", "/.factory/AGENTS.md", + "\"copilot-cli\"", + "/.copilot/copilot-instructions.md", + "/.copilot/hooks/codebase-memory-cli.json", + "/.copilot/skills/codebase-memory-cli/SKILL.md", + "\"factory-droid\"", + "/.factory/AGENTS.md", "/.factory/skills/codebase-memory-cli/SKILL.md", #ifndef _WIN32 "/.factory/hooks.json", #endif - "\"crush\"", "/.config/crush/crush.json", "/.config/crush/codebase-memory.md", - "\"goose\"", "/.config/goose/.goosehints", - "\"mistral-vibe\"", "/.vibe/AGENTS.md", "/.vibe/skills/codebase-memory-cli/SKILL.md", - "\"grok\"", "/.grok/rules/codebase-memory.md", + "\"crush\"", + "/.config/crush/crush.json", + "/.config/crush/codebase-memory.md", + "\"goose\"", + "/.config/goose/.goosehints", + "\"mistral-vibe\"", + "/.vibe/AGENTS.md", + "/.vibe/skills/codebase-memory-cli/SKILL.md", + "\"grok\"", + "/.grok/rules/codebase-memory.md", "/.grok/skills/codebase-memory-cli/SKILL.md", }; const char *const forbidden[] = { - "/.openhands/mcp.json", "/.cline/mcp.json", "/settings/cline_mcp_settings.json", - "/.copilot/mcp-config.json", "/.factory/mcp.json", "/.grok/config.toml", + "/.openhands/mcp.json", + "/.cline/mcp.json", + "/settings/cline_mcp_settings.json", + "/.copilot/mcp-config.json", + "/.factory/mcp.json", + "/.grok/config.toml", "/.grok/agents/codebase-memory.md", }; const char *missing = NULL; @@ -3698,8 +3751,8 @@ TEST(cli_new_agent_install_plans_use_documented_paths) { } bool has_json = json != NULL; if (missing || unexpected) - fprintf(stderr, "CLI-first plan diag missing=%s unexpected=%s\n", - missing ? missing : "-", unexpected ? unexpected : "-"); + fprintf(stderr, "CLI-first plan diag missing=%s unexpected=%s\n", missing ? missing : "-", + unexpected ? unexpected : "-"); free(json); restore_test_env("COPILOT_HOME", saved_copilot); restore_test_env("CRUSH_GLOBAL_CONFIG", saved_crush); @@ -3709,7 +3762,8 @@ TEST(cli_new_agent_install_plans_use_documented_paths) { test_rmdir_r(tmpdir); if (!has_json || missing || unexpected) - FAIL("new agent plan must contain current CLI-first integrations and no fresh MCP registrations"); + FAIL("new agent plan must contain current CLI-first integrations and no fresh MCP " + "registrations"); PASS(); } @@ -3775,7 +3829,8 @@ TEST(cli_install_preserves_hook_entries_when_scripts_unowned_issue1387) { "# SessionStart hook: remind agent to use codebase-memory-mcp tools.\n" "echo my-extra-team-reminder\n"; char session_path[768]; - snprintf(session_path, sizeof(session_path), "%s/codebase-memory-cli-session-reminder", hooks_dir); + snprintf(session_path, sizeof(session_path), "%s/codebase-memory-cli-session-reminder", + hooks_dir); write_test_file(session_path, unowned_session); static const char settings_before[] = @@ -3980,7 +4035,8 @@ TEST(cli_cline_data_dir_only_redirects_data_state) { char *plan = cbm_build_install_plan_json(tmpdir, "/opt/codebase-memory-cli"); bool plan_ok = plan && strstr(plan, rules) && strstr(plan, skill) && !strstr(plan, cli_mcp) && - !strstr(plan, ide_mcp) && !strstr(plan, wrong_rules) && !strstr(plan, wrong_skill); + !strstr(plan, ide_mcp) && !strstr(plan, wrong_rules) && + !strstr(plan, wrong_skill); for (size_t i = 0U; plan_ok && i < sizeof(hook_events) / sizeof(hook_events[0]); i++) { plan_ok = strstr(plan, hook_paths[i]) == NULL; } @@ -3989,14 +4045,13 @@ TEST(cli_cline_data_dir_only_redirects_data_state) { int install_rc = cbm_install_agent_configs(tmpdir, "/opt/codebase-memory-cli", false, false); struct stat state; const char *const instruction_terms[] = {"Codebase Memory", "codebase-memory-cli search", - "codebase-memory-cli trace"}; + "codebase-memory-cli trace"}; const char *const skill_terms[] = {"name: codebase-memory", "codebase-memory-cli search", - "codebase-memory-cli trace", "coverage"}; - bool installed = install_rc == 0 && - test_file_contains_all(rules, instruction_terms, 3U) && - test_file_contains_all(skill, skill_terms, 4U) && - stat(cli_mcp, &state) != 0 && stat(ide_mcp, &state) != 0 && - stat(wrong_rules, &state) != 0 && stat(wrong_skill, &state) != 0; + "codebase-memory-cli trace", "coverage"}; + bool installed = install_rc == 0 && test_file_contains_all(rules, instruction_terms, 3U) && + test_file_contains_all(skill, skill_terms, 4U) && stat(cli_mcp, &state) != 0 && + stat(ide_mcp, &state) != 0 && stat(wrong_rules, &state) != 0 && + stat(wrong_skill, &state) != 0; for (size_t i = 0U; installed && i + 1U < sizeof(hook_events) / sizeof(hook_events[0]); i++) { installed = stat(hook_paths[i], &state) != 0; } @@ -4007,8 +4062,8 @@ TEST(cli_cline_data_dir_only_redirects_data_state) { char *argv[] = {"uninstall", "--yes"}; int uninstall_rc = cli_test_cmd_uninstall(2, argv); preserved_hook = read_test_file_alloc(hook_paths[3]); - bool removed = stat(skill, &state) != 0 && preserved_hook && - strcmp(preserved_hook, modified_hook) == 0; + bool removed = + stat(skill, &state) != 0 && preserved_hook && strcmp(preserved_hook, modified_hook) == 0; free(preserved_hook); restore_test_env("HOME", saved_home); @@ -4050,7 +4105,8 @@ TEST(cli_warp_installs_shared_skill_without_mcp_or_permissions) { char skill_path[640]; char warp_mcp[640]; - snprintf(skill_path, sizeof(skill_path), "%s/.agents/skills/codebase-memory-cli/SKILL.md", tmpdir); + snprintf(skill_path, sizeof(skill_path), "%s/.agents/skills/codebase-memory-cli/SKILL.md", + tmpdir); snprintf(warp_mcp, sizeof(warp_mcp), "%s/.warp/mcp.json", tmpdir); char *plan = cbm_build_install_plan_json(tmpdir, "/opt/codebase-memory-mcp"); yyjson_doc *plan_doc = plan ? yyjson_read(plan, strlen(plan), 0) : NULL; @@ -4168,19 +4224,19 @@ TEST(cli_junie_current_durable_context_contract) { char *agent_after_install = read_test_file_alloc(agent_path); char *settings_after_install = read_test_file_alloc(settings_path); struct stat state; - bool installed = first_rc == 0 && skill_once && - strstr(skill_once, "codebase-memory-cli search") && - strstr(skill_once, "codebase-memory-cli trace") && - strstr(skill_once, "coverage") && stat(mcp_path, &state) != 0 && - agent_after_install && strcmp(agent_after_install, foreign_agent) == 0 && - settings_after_install && strcmp(settings_after_install, foreign_settings) == 0; + bool installed = + first_rc == 0 && skill_once && strstr(skill_once, "codebase-memory-cli search") && + strstr(skill_once, "codebase-memory-cli trace") && strstr(skill_once, "coverage") && + stat(mcp_path, &state) != 0 && agent_after_install && + strcmp(agent_after_install, foreign_agent) == 0 && settings_after_install && + strcmp(settings_after_install, foreign_settings) == 0; free(agent_after_install); free(settings_after_install); int second_rc = cbm_install_agent_configs(tmpdir, "/opt/codebase-memory-cli", false, false); char *skill_twice = read_test_file_alloc(skill_path); - bool idempotent = second_rc == 0 && skill_once && skill_twice && - strcmp(skill_once, skill_twice) == 0; + bool idempotent = + second_rc == 0 && skill_once && skill_twice && strcmp(skill_once, skill_twice) == 0; free(skill_once); free(skill_twice); @@ -4213,7 +4269,8 @@ TEST(cli_junie_current_durable_context_contract) { "reinstall_rc=%d modified_rc=%d modified_preserved=%d\n", plan_ok, installed, idempotent, exact_uninstall_rc, exact_removed, reinstall_rc, modified_uninstall_rc, modified_preserved); - FAIL("Junie must install only CLI-first skill context and preserve foreign agent/settings files"); + FAIL("Junie must install only CLI-first skill context and preserve foreign agent/settings " + "files"); } PASS(); } @@ -4356,8 +4413,8 @@ TEST(cli_hermes_stable_shell_context_contract) { fprintf(stderr, "Hermes diag plan=%d rc=%d/%d merged=%d exact_rc=%d exact_removed=%d " "reinstall_rc=%d modified_rc=%d modified_preserved=%d hook_modified=%d\n", - plan_ok, first_rc, second_rc, merged, exact_uninstall_rc, exact_removed, reinstall_rc, - modified_uninstall_rc, modified_preserved, hook_was_modified); + plan_ok, first_rc, second_rc, merged, exact_uninstall_rc, exact_removed, + reinstall_rc, modified_uninstall_rc, modified_preserved, hook_was_modified); FAIL("Hermes must merge one consent-preserving pre_llm_call shell hook and remove only " "its canonical owned entry"); } @@ -4455,7 +4512,8 @@ TEST(cli_dry_run_predicts_refused_hook_script_issue1387) { redirected = dup2(fileno(capture), STDOUT_FILENO) >= 0; } if (redirected) { - (void)cbm_install_agent_hooks_for_testing(tmpdir, "/opt/other/codebase-memory-mcp", false, true); + (void)cbm_install_agent_hooks_for_testing(tmpdir, "/opt/other/codebase-memory-mcp", false, + true); fflush(stdout); (void)dup2(saved_stdout, STDOUT_FILENO); } @@ -4494,9 +4552,13 @@ TEST(cli_agent_client_registry_routes_plan_install_and_uninstall) { if (!cbm_mkdtemp(tmpdir)) FAIL("cbm_mkdtemp failed"); - const char *const env_names[] = {"HOME", "PATH", "CBM_ROO_CONFIG_PATH", - "CBM_CODY_CONFIG_PATH", "PI_CODING_AGENT_DIR", - "XDG_CONFIG_HOME", "APPDATA"}; + const char *const env_names[] = {"HOME", + "PATH", + "CBM_ROO_CONFIG_PATH", + "CBM_CODY_CONFIG_PATH", + "PI_CODING_AGENT_DIR", + "XDG_CONFIG_HOME", + "APPDATA"}; char *saved_env[sizeof(env_names) / sizeof(env_names[0])]; for (size_t i = 0U; i < sizeof(env_names) / sizeof(env_names[0]); i++) { saved_env[i] = save_test_env(env_names[i]); @@ -4509,8 +4571,11 @@ TEST(cli_agent_client_registry_routes_plan_install_and_uninstall) { snprintf(qoder_dir, sizeof(qoder_dir), "%s/.qoder", tmpdir); snprintf(amazon_dir, sizeof(amazon_dir), "%s/.aws/amazonq/agents", tmpdir); snprintf(pi_dir, sizeof(pi_dir), "%s/.pi/agent", tmpdir); - test_mkdirp(bin_dir); test_mkdirp(explicit_dir); test_mkdirp(qoder_dir); - test_mkdirp(amazon_dir); test_mkdirp(pi_dir); + test_mkdirp(bin_dir); + test_mkdirp(explicit_dir); + test_mkdirp(qoder_dir); + test_mkdirp(amazon_dir); + test_mkdirp(pi_dir); char qoder_command[640], pi_command[640], qoder_settings[640], qoder_skill[640]; char qoder_agent[640], amazon_config[640], pi_instructions[640], pi_skill[640], pi_mcp[640]; @@ -4537,7 +4602,8 @@ TEST(cli_agent_client_registry_routes_plan_install_and_uninstall) { write_test_file(qoder_command, "#!/bin/sh\nexit 0\n"); write_test_file(pi_command, "#!/bin/sh\nexit 0\n"); #ifndef _WIN32 - chmod(qoder_command, 0700); chmod(pi_command, 0700); + chmod(qoder_command, 0700); + chmod(pi_command, 0700); #endif const char *qoder_foreign = "{\"theme\":\"dark\"}\n"; const char *amazon_foreign = "{\"keep\":\"amazon\"}\n"; @@ -4548,7 +4614,8 @@ TEST(cli_agent_client_registry_routes_plan_install_and_uninstall) { write_test_file(roo_config, roo_foreign); write_test_file(cody_config, cody_foreign); - cbm_setenv("HOME", tmpdir, 1); cbm_setenv("PATH", bin_dir, 1); + cbm_setenv("HOME", tmpdir, 1); + cbm_setenv("PATH", bin_dir, 1); cbm_setenv("CBM_ROO_CONFIG_PATH", roo_config, 1); cbm_setenv("CBM_CODY_CONFIG_PATH", cody_config, 1); @@ -4558,42 +4625,55 @@ TEST(cli_agent_client_registry_routes_plan_install_and_uninstall) { yyjson_doc *hook_doc = hook_plan ? yyjson_read(hook_plan, strlen(hook_plan), 0) : NULL; yyjson_val *root = plan_doc ? yyjson_doc_get_root(plan_doc) : NULL; yyjson_val *hook_root = hook_doc ? yyjson_doc_get_root(hook_doc) : NULL; - bool plan_ok = root && hook_root && test_json_string_array_contains(root, "skill_files_planned", qoder_skill) && - test_plan_hook_contains(hook_root, "Qoder CLI", qoder_settings) && - test_json_string_array_contains(root, "instruction_files_planned", pi_instructions) && - test_json_string_array_contains(root, "skill_files_planned", pi_skill) && - !test_json_string_array_contains(root, "agent_files_planned", qoder_agent) && - plan && !strstr(plan, amazon_config) && !strstr(plan, roo_config) && - !strstr(plan, cody_config) && !strstr(plan, pi_mcp); - yyjson_doc_free(plan_doc); yyjson_doc_free(hook_doc); free(plan); free(hook_plan); + bool plan_ok = + root && hook_root && + test_json_string_array_contains(root, "skill_files_planned", qoder_skill) && + test_plan_hook_contains(hook_root, "Qoder CLI", qoder_settings) && + test_json_string_array_contains(root, "instruction_files_planned", pi_instructions) && + test_json_string_array_contains(root, "skill_files_planned", pi_skill) && + !test_json_string_array_contains(root, "agent_files_planned", qoder_agent) && plan && + !strstr(plan, amazon_config) && !strstr(plan, roo_config) && !strstr(plan, cody_config) && + !strstr(plan, pi_mcp); + yyjson_doc_free(plan_doc); + yyjson_doc_free(hook_doc); + free(plan); + free(hook_plan); int install_rc = cbm_install_agent_configs(tmpdir, binary_path, false, false); - if (install_rc == 0) install_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); + if (install_rc == 0) + install_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); char *qoder_data = read_test_file_alloc(qoder_settings); yyjson_doc *qdoc = qoder_data ? yyjson_read(qoder_data, strlen(qoder_data), 0) : NULL; yyjson_val *qroot = qdoc ? yyjson_doc_get_root(qdoc) : NULL; yyjson_val *hooks = qroot ? yyjson_obj_get(qroot, "hooks") : NULL; struct stat state; - bool qoder_ok = qoder_data && strstr(qoder_data, "\"theme\"") && hooks && - yyjson_obj_get(hooks, "SessionStart") && yyjson_obj_get(hooks, "SubagentStart") && - yyjson_obj_get(hooks, "PostToolUse") && strstr(qoder_data, "hook-augment") && - strstr(qoder_data, "--dialect qoder") && !strstr(qoder_data, "mcpServers") && - stat(qoder_agent, &state) != 0 && - test_file_contains_all(qoder_skill, - (const char *const[]){"codebase-memory-cli search", "codebase-memory-cli trace", "coverage"}, 3U); - yyjson_doc_free(qdoc); free(qoder_data); - bool pi_ok = test_file_contains_all(pi_instructions, - (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && - test_file_contains_all(pi_skill, - (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && - stat(pi_mcp, &state) != 0; + bool qoder_ok = + qoder_data && strstr(qoder_data, "\"theme\"") && hooks && + yyjson_obj_get(hooks, "SessionStart") && yyjson_obj_get(hooks, "SubagentStart") && + yyjson_obj_get(hooks, "PostToolUse") && strstr(qoder_data, "hook-augment") && + strstr(qoder_data, "--dialect qoder") && !strstr(qoder_data, "mcpServers") && + stat(qoder_agent, &state) != 0 && + test_file_contains_all(qoder_skill, + (const char *const[]){"codebase-memory-cli search", + "codebase-memory-cli trace", "coverage"}, + 3U); + yyjson_doc_free(qdoc); + free(qoder_data); + bool pi_ok = + test_file_contains_all( + pi_instructions, (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && + test_file_contains_all( + pi_skill, (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && + stat(pi_mcp, &state) != 0; char *amazon_after = read_test_file_alloc(amazon_config); char *roo_after = read_test_file_alloc(roo_config); char *cody_after = read_test_file_alloc(cody_config); - bool foreign_untouched = amazon_after && roo_after && cody_after && - strcmp(amazon_after, amazon_foreign) == 0 && - strcmp(roo_after, roo_foreign) == 0 && strcmp(cody_after, cody_foreign) == 0; - free(amazon_after); free(roo_after); free(cody_after); + bool foreign_untouched = + amazon_after && roo_after && cody_after && strcmp(amazon_after, amazon_foreign) == 0 && + strcmp(roo_after, roo_foreign) == 0 && strcmp(cody_after, cody_foreign) == 0; + free(amazon_after); + free(roo_after); + free(cody_after); char *argv[] = {"uninstall", "--yes"}; int uninstall_rc = cli_test_cmd_uninstall(2, argv); @@ -4608,14 +4688,18 @@ TEST(cli_agent_client_registry_routes_plan_install_and_uninstall) { cleanup_ok = cleanup_ok && amazon_after && roo_after && cody_after && strcmp(amazon_after, amazon_foreign) == 0 && strcmp(roo_after, roo_foreign) == 0 && strcmp(cody_after, cody_foreign) == 0; - free(amazon_after); free(roo_after); free(cody_after); + free(amazon_after); + free(roo_after); + free(cody_after); for (size_t i = 0U; i < sizeof(env_names) / sizeof(env_names[0]); i++) restore_test_env(env_names[i], saved_env[i]); test_rmdir_r(tmpdir); if (!plan_ok || install_rc != 0 || !qoder_ok || !pi_ok || !foreign_untouched || uninstall_rc != 0 || !cleanup_ok) { - fprintf(stderr, "registry CLI-first diag plan=%d install=%d qoder=%d pi=%d foreign=%d uninstall=%d cleanup=%d\n", + fprintf(stderr, + "registry CLI-first diag plan=%d install=%d qoder=%d pi=%d foreign=%d uninstall=%d " + "cleanup=%d\n", plan_ok, install_rc, qoder_ok, pi_ok, foreign_untouched, uninstall_rc, cleanup_ok); FAIL("registry must route current Qoder/Pi integrations without creating legacy MCP state"); } @@ -4631,7 +4715,8 @@ TEST(cli_registry_installs_kimi_rovo_amp_durable_context) { const char *const env_names[] = {"HOME", "PATH", "KIMI_CODE_HOME", "XDG_CONFIG_HOME"}; char *saved_env[sizeof(env_names) / sizeof(env_names[0])]; for (size_t i = 0U; i < sizeof(env_names) / sizeof(env_names[0]); i++) { - saved_env[i] = save_test_env(env_names[i]); cbm_unsetenv(env_names[i]); + saved_env[i] = save_test_env(env_names[i]); + cbm_unsetenv(env_names[i]); } char kimi_home[512], rovo_home[512], amp_home[512], xdg_home[512]; @@ -4639,9 +4724,14 @@ TEST(cli_registry_installs_kimi_rovo_amp_durable_context) { snprintf(rovo_home, sizeof(rovo_home), "%s/.rovodev", tmpdir); snprintf(amp_home, sizeof(amp_home), "%s/.config/amp", tmpdir); snprintf(xdg_home, sizeof(xdg_home), "%s/xdg-decoy", tmpdir); - test_mkdirp(kimi_home); test_mkdirp(rovo_home); test_mkdirp(amp_home); test_mkdirp(xdg_home); - cbm_setenv("HOME", tmpdir, 1); cbm_setenv("PATH", tmpdir, 1); - cbm_setenv("KIMI_CODE_HOME", kimi_home, 1); cbm_setenv("XDG_CONFIG_HOME", xdg_home, 1); + test_mkdirp(kimi_home); + test_mkdirp(rovo_home); + test_mkdirp(amp_home); + test_mkdirp(xdg_home); + cbm_setenv("HOME", tmpdir, 1); + cbm_setenv("PATH", tmpdir, 1); + cbm_setenv("KIMI_CODE_HOME", kimi_home, 1); + cbm_setenv("XDG_CONFIG_HOME", xdg_home, 1); char binary_path[640], kimi_mcp[640], kimi_config[640], kimi_instructions[640], kimi_skill[640]; char rovo_mcp[640], rovo_instructions[640], rovo_skill[640], rovo_agent[640]; @@ -4661,14 +4751,17 @@ TEST(cli_registry_installs_kimi_rovo_amp_durable_context) { snprintf(rovo_agent, sizeof(rovo_agent), "%s/subagents/codebase-memory.md", rovo_home); snprintf(amp_mcp, sizeof(amp_mcp), "%s/.config/agents/skills/codebase-memory/mcp.json", tmpdir); snprintf(amp_instructions, sizeof(amp_instructions), "%s/AGENTS.md", amp_home); - snprintf(amp_skill, sizeof(amp_skill), "%s/.config/agents/skills/codebase-memory-cli/SKILL.md", tmpdir); + snprintf(amp_skill, sizeof(amp_skill), "%s/.config/agents/skills/codebase-memory-cli/SKILL.md", + tmpdir); const char *kimi_personal = "# Personal Kimi guidance\n"; const char *kimi_config_personal = "theme = \"dark\"\n"; const char *rovo_personal = "# Personal Rovo guidance\n"; const char *amp_personal = "# Personal Amp guidance\n"; - write_test_file(kimi_instructions, kimi_personal); write_test_file(kimi_config, kimi_config_personal); - write_test_file(rovo_instructions, rovo_personal); write_test_file(amp_instructions, amp_personal); + write_test_file(kimi_instructions, kimi_personal); + write_test_file(kimi_config, kimi_config_personal); + write_test_file(rovo_instructions, rovo_personal); + write_test_file(amp_instructions, amp_personal); char *plan = cbm_build_install_plan_json(tmpdir, binary_path); char *hook_plan = cbm_build_hook_install_plan_json(tmpdir, binary_path); @@ -4676,7 +4769,8 @@ TEST(cli_registry_installs_kimi_rovo_amp_durable_context) { yyjson_doc *hook_doc = hook_plan ? yyjson_read(hook_plan, strlen(hook_plan), 0) : NULL; yyjson_val *root = doc ? yyjson_doc_get_root(doc) : NULL; yyjson_val *hook_root = hook_doc ? yyjson_doc_get_root(hook_doc) : NULL; - bool plan_ok = root && hook_root && plan && hook_plan && !strstr(plan, xdg_home) && + bool plan_ok = + root && hook_root && plan && hook_plan && !strstr(plan, xdg_home) && !strstr(hook_plan, xdg_home) && test_plan_hook_contains(hook_root, "Kimi Code CLI", kimi_config) && test_json_string_array_contains(root, "instruction_files_planned", kimi_instructions) && @@ -4687,61 +4781,93 @@ TEST(cli_registry_installs_kimi_rovo_amp_durable_context) { test_json_string_array_contains(root, "skill_files_planned", amp_skill) && !test_json_string_array_contains(root, "agent_files_planned", rovo_agent) && !strstr(plan, kimi_mcp) && !strstr(plan, rovo_mcp) && !strstr(plan, amp_mcp); - yyjson_doc_free(doc); yyjson_doc_free(hook_doc); free(plan); free(hook_plan); + yyjson_doc_free(doc); + yyjson_doc_free(hook_doc); + free(plan); + free(hook_plan); int first_rc = cbm_install_agent_configs(tmpdir, binary_path, false, false); - if (first_rc == 0) first_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); - char *k1 = read_test_file_alloc(kimi_instructions); char *kc1 = read_test_file_alloc(kimi_config); - char *r1 = read_test_file_alloc(rovo_instructions); char *a1 = read_test_file_alloc(amp_instructions); + if (first_rc == 0) + first_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); + char *k1 = read_test_file_alloc(kimi_instructions); + char *kc1 = read_test_file_alloc(kimi_config); + char *r1 = read_test_file_alloc(rovo_instructions); + char *a1 = read_test_file_alloc(amp_instructions); struct stat state; - bool installed = first_rc == 0 && k1 && kc1 && r1 && a1 && - strstr(k1, kimi_personal) && strstr(k1, "codebase-memory-cli search") && - strstr(kc1, kimi_config_personal) && strstr(kc1, "UserPromptSubmit") && - strstr(kc1, "--dialect kimi") && strstr(r1, rovo_personal) && - strstr(r1, "codebase-memory-cli search") && strstr(a1, amp_personal) && - strstr(a1, "codebase-memory-cli search") && - test_file_contains_all(kimi_skill, (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && - test_file_contains_all(rovo_skill, (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && - test_file_contains_all(amp_skill, (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && + bool installed = + first_rc == 0 && k1 && kc1 && r1 && a1 && strstr(k1, kimi_personal) && + strstr(k1, "codebase-memory-cli search") && strstr(kc1, kimi_config_personal) && + strstr(kc1, "UserPromptSubmit") && strstr(kc1, "--dialect kimi") && + strstr(r1, rovo_personal) && strstr(r1, "codebase-memory-cli search") && + strstr(a1, amp_personal) && strstr(a1, "codebase-memory-cli search") && + test_file_contains_all( + kimi_skill, (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && + test_file_contains_all( + rovo_skill, (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && + test_file_contains_all( + amp_skill, (const char *const[]){"codebase-memory-cli search", "coverage"}, 2U) && stat(kimi_mcp, &state) != 0 && stat(rovo_mcp, &state) != 0 && stat(amp_mcp, &state) != 0 && stat(rovo_agent, &state) != 0; int second_rc = cbm_install_agent_configs(tmpdir, binary_path, false, false); - if (second_rc == 0) second_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); - char *k2 = read_test_file_alloc(kimi_instructions); char *kc2 = read_test_file_alloc(kimi_config); - char *r2 = read_test_file_alloc(rovo_instructions); char *a2 = read_test_file_alloc(amp_instructions); + if (second_rc == 0) + second_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); + char *k2 = read_test_file_alloc(kimi_instructions); + char *kc2 = read_test_file_alloc(kimi_config); + char *r2 = read_test_file_alloc(rovo_instructions); + char *a2 = read_test_file_alloc(amp_instructions); bool idempotent = second_rc == 0 && k1 && k2 && kc1 && kc2 && r1 && r2 && a1 && a2 && - strcmp(k1,k2)==0 && strcmp(kc1,kc2)==0 && strcmp(r1,r2)==0 && strcmp(a1,a2)==0; - free(k1); free(k2); free(kc1); free(kc2); free(r1); free(r2); free(a1); free(a2); + strcmp(k1, k2) == 0 && strcmp(kc1, kc2) == 0 && strcmp(r1, r2) == 0 && + strcmp(a1, a2) == 0; + free(k1); + free(k2); + free(kc1); + free(kc2); + free(r1); + free(r2); + free(a1); + free(a2); char *argv[] = {"uninstall", "--yes"}; int uninstall_rc = cli_test_cmd_uninstall(2, argv); - char *ku = read_test_file_alloc(kimi_instructions); char *kcu = read_test_file_alloc(kimi_config); - char *ru = read_test_file_alloc(rovo_instructions); char *au = read_test_file_alloc(amp_instructions); - bool cleaned = uninstall_rc == 0 && ku && kcu && ru && au && - strcmp(ku, kimi_personal) == 0 && strcmp(ru, rovo_personal) == 0 && - strcmp(au, amp_personal) == 0 && strstr(kcu, kimi_config_personal) && - !strstr(kcu, "--dialect kimi") && stat(kimi_skill, &state) != 0 && - stat(rovo_skill, &state) != 0 && stat(amp_skill, &state) != 0; - free(ku); free(kcu); free(ru); free(au); + char *ku = read_test_file_alloc(kimi_instructions); + char *kcu = read_test_file_alloc(kimi_config); + char *ru = read_test_file_alloc(rovo_instructions); + char *au = read_test_file_alloc(amp_instructions); + bool cleaned = uninstall_rc == 0 && ku && kcu && ru && au && strcmp(ku, kimi_personal) == 0 && + strcmp(ru, rovo_personal) == 0 && strcmp(au, amp_personal) == 0 && + strstr(kcu, kimi_config_personal) && !strstr(kcu, "--dialect kimi") && + stat(kimi_skill, &state) != 0 && stat(rovo_skill, &state) != 0 && + stat(amp_skill, &state) != 0; + free(ku); + free(kcu); + free(ru); + free(au); int reinstall_rc = cbm_install_agent_configs(tmpdir, binary_path, false, false); - if (reinstall_rc == 0) reinstall_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); + if (reinstall_rc == 0) + reinstall_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); const char *modified_kimi_skill = "---\nname: codebase-memory\n---\nUser-owned Kimi skill.\n"; const char *modified_amp_skill = "---\nname: codebase-memory\n---\nUser-owned Amp skill.\n"; - write_test_file(kimi_skill, modified_kimi_skill); write_test_file(amp_skill, modified_amp_skill); + write_test_file(kimi_skill, modified_kimi_skill); + write_test_file(amp_skill, modified_amp_skill); int modified_uninstall_rc = cli_test_cmd_uninstall(2, argv); - char *ks = read_test_file_alloc(kimi_skill); char *as = read_test_file_alloc(amp_skill); + char *ks = read_test_file_alloc(kimi_skill); + char *as = read_test_file_alloc(amp_skill); bool modified_preserved = reinstall_rc == 0 && modified_uninstall_rc == 0 && ks && as && - strcmp(ks, modified_kimi_skill) == 0 && strcmp(as, modified_amp_skill) == 0; - free(ks); free(as); + strcmp(ks, modified_kimi_skill) == 0 && + strcmp(as, modified_amp_skill) == 0; + free(ks); + free(as); for (size_t i = 0U; i < sizeof(env_names) / sizeof(env_names[0]); i++) restore_test_env(env_names[i], saved_env[i]); test_rmdir_r(tmpdir); if (!plan_ok || !installed || !idempotent || !cleaned || !modified_preserved) { - fprintf(stderr, "Kimi/Rovo/Amp CLI-first diag plan=%d installed=%d idem=%d cleaned=%d modified=%d\n", - plan_ok, installed, idempotent, cleaned, modified_preserved); + fprintf( + stderr, + "Kimi/Rovo/Amp CLI-first diag plan=%d installed=%d idem=%d cleaned=%d modified=%d\n", + plan_ok, installed, idempotent, cleaned, modified_preserved); FAIL("Kimi, Rovo, and Amp must install CLI-first durable context with exact-owned cleanup"); } PASS(); @@ -4753,13 +4879,15 @@ TEST(cli_registry_routes_omp_via_profile_and_pi_coding_agent_dir) { if (!cbm_mkdtemp(tmpdir)) FAIL("cbm_mkdtemp failed"); - const char *const env_names[] = {"HOME", "PATH", "OMP_PROFILE", "PI_CODING_AGENT_DIR", - "XDG_CONFIG_HOME", "APPDATA"}; + const char *const env_names[] = { + "HOME", "PATH", "OMP_PROFILE", "PI_CODING_AGENT_DIR", "XDG_CONFIG_HOME", "APPDATA"}; char *saved_env[sizeof(env_names) / sizeof(env_names[0])]; for (size_t i = 0U; i < sizeof(env_names) / sizeof(env_names[0]); i++) { - saved_env[i] = save_test_env(env_names[i]); cbm_unsetenv(env_names[i]); + saved_env[i] = save_test_env(env_names[i]); + cbm_unsetenv(env_names[i]); } - cbm_setenv("HOME", tmpdir, 1); cbm_setenv("PATH", tmpdir, 1); + cbm_setenv("HOME", tmpdir, 1); + cbm_setenv("PATH", tmpdir, 1); char default_dir[640], profile_dir[640], relocated_dir[640]; char default_skill[768], profile_skill[768], relocated_skill[768]; @@ -4768,16 +4896,22 @@ TEST(cli_registry_routes_omp_via_profile_and_pi_coding_agent_dir) { snprintf(default_dir, sizeof(default_dir), "%s/.omp/agent", tmpdir); snprintf(profile_dir, sizeof(profile_dir), "%s/.omp/profiles/work/agent", tmpdir); snprintf(relocated_dir, sizeof(relocated_dir), "%s/custom-agent", tmpdir); - snprintf(default_skill, sizeof(default_skill), "%s/skills/codebase-memory-cli/SKILL.md", default_dir); - snprintf(profile_skill, sizeof(profile_skill), "%s/skills/codebase-memory-cli/SKILL.md", profile_dir); - snprintf(relocated_skill, sizeof(relocated_skill), "%s/skills/codebase-memory-cli/SKILL.md", relocated_dir); + snprintf(default_skill, sizeof(default_skill), "%s/skills/codebase-memory-cli/SKILL.md", + default_dir); + snprintf(profile_skill, sizeof(profile_skill), "%s/skills/codebase-memory-cli/SKILL.md", + profile_dir); + snprintf(relocated_skill, sizeof(relocated_skill), "%s/skills/codebase-memory-cli/SKILL.md", + relocated_dir); snprintf(default_mcp, sizeof(default_mcp), "%s/mcp.json", default_dir); snprintf(profile_mcp, sizeof(profile_mcp), "%s/mcp.json", profile_dir); snprintf(relocated_mcp, sizeof(relocated_mcp), "%s/mcp.json", relocated_dir); snprintf(default_agent, sizeof(default_agent), "%s/agents/codebase-memory.md", default_dir); snprintf(profile_agent, sizeof(profile_agent), "%s/agents/codebase-memory.md", profile_dir); - snprintf(relocated_agent, sizeof(relocated_agent), "%s/agents/codebase-memory.md", relocated_dir); - test_mkdirp(default_dir); test_mkdirp(profile_dir); test_mkdirp(relocated_dir); + snprintf(relocated_agent, sizeof(relocated_agent), "%s/agents/codebase-memory.md", + relocated_dir); + test_mkdirp(default_dir); + test_mkdirp(profile_dir); + test_mkdirp(relocated_dir); char *plan = cbm_build_install_plan_json(tmpdir, "/opt/codebase-memory-cli"); yyjson_doc *doc = plan ? yyjson_read(plan, strlen(plan), 0) : NULL; @@ -4785,31 +4919,44 @@ TEST(cli_registry_routes_omp_via_profile_and_pi_coding_agent_dir) { bool default_ok = plan && strstr(plan, "\"omp\"") && test_json_string_array_contains(root, "skill_files_planned", default_skill) && !strstr(plan, default_mcp) && !strstr(plan, default_agent); - yyjson_doc_free(doc); free(plan); + yyjson_doc_free(doc); + free(plan); cbm_setenv("OMP_PROFILE", "work", 1); plan = cbm_build_install_plan_json(tmpdir, "/opt/codebase-memory-cli"); - doc = plan ? yyjson_read(plan, strlen(plan), 0) : NULL; root = doc ? yyjson_doc_get_root(doc) : NULL; + doc = plan ? yyjson_read(plan, strlen(plan), 0) : NULL; + root = doc ? yyjson_doc_get_root(doc) : NULL; bool profile_ok = plan && strstr(plan, "\"omp\"") && test_json_string_array_contains(root, "skill_files_planned", profile_skill) && - !strstr(plan, default_skill) && !strstr(plan, profile_mcp) && !strstr(plan, profile_agent); - yyjson_doc_free(doc); free(plan); cbm_unsetenv("OMP_PROFILE"); + !strstr(plan, default_skill) && !strstr(plan, profile_mcp) && + !strstr(plan, profile_agent); + yyjson_doc_free(doc); + free(plan); + cbm_unsetenv("OMP_PROFILE"); cbm_setenv("PI_CODING_AGENT_DIR", relocated_dir, 1); plan = cbm_build_install_plan_json(tmpdir, "/opt/codebase-memory-cli"); - doc = plan ? yyjson_read(plan, strlen(plan), 0) : NULL; root = doc ? yyjson_doc_get_root(doc) : NULL; - bool relocated_ok = plan && strstr(plan, "\"omp\"") && - test_json_string_array_contains(root, "skill_files_planned", relocated_skill) && - !strstr(plan, relocated_mcp) && !strstr(plan, relocated_agent); - yyjson_doc_free(doc); free(plan); cbm_unsetenv("PI_CODING_AGENT_DIR"); + doc = plan ? yyjson_read(plan, strlen(plan), 0) : NULL; + root = doc ? yyjson_doc_get_root(doc) : NULL; + bool relocated_ok = + plan && strstr(plan, "\"omp\"") && + test_json_string_array_contains(root, "skill_files_planned", relocated_skill) && + !strstr(plan, relocated_mcp) && !strstr(plan, relocated_agent); + yyjson_doc_free(doc); + free(plan); + cbm_unsetenv("PI_CODING_AGENT_DIR"); cbm_setenv("OMP_PROFILE", "bad;name", 1); plan = cbm_build_install_plan_json(tmpdir, "/opt/codebase-memory-cli"); - doc = plan ? yyjson_read(plan, strlen(plan), 0) : NULL; root = doc ? yyjson_doc_get_root(doc) : NULL; - bool fallback_ok = plan && strstr(plan, "\"omp\"") && - test_json_string_array_contains(root, "skill_files_planned", default_skill) && - !strstr(plan, profile_skill); - yyjson_doc_free(doc); free(plan); cbm_unsetenv("OMP_PROFILE"); + doc = plan ? yyjson_read(plan, strlen(plan), 0) : NULL; + root = doc ? yyjson_doc_get_root(doc) : NULL; + bool fallback_ok = + plan && strstr(plan, "\"omp\"") && + test_json_string_array_contains(root, "skill_files_planned", default_skill) && + !strstr(plan, profile_skill); + yyjson_doc_free(doc); + free(plan); + cbm_unsetenv("OMP_PROFILE"); struct stat state; bool plan_clean = stat(default_skill, &state) != 0 && stat(profile_skill, &state) != 0 && @@ -4819,22 +4966,22 @@ TEST(cli_registry_routes_omp_via_profile_and_pi_coding_agent_dir) { restore_test_env(env_names[i], saved_env[i]); test_rmdir_r(tmpdir); if (!default_ok || !profile_ok || !relocated_ok || !fallback_ok || !plan_clean) { - fprintf(stderr, "OMP CLI-first plan diag default=%d profile=%d relocated=%d fallback=%d clean=%d\n", + fprintf(stderr, + "OMP CLI-first plan diag default=%d profile=%d relocated=%d fallback=%d clean=%d\n", default_ok, profile_ok, relocated_ok, fallback_ok, plan_clean); FAIL("OMP plan must resolve effective agent directories for its CLI-first skill only"); } PASS(); } - TEST(cli_registry_omp_named_profile_install_and_uninstall_preserve_user_content) { char tmpdir[256]; snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-registry-omp-lifecycle-XXXXXX"); if (!cbm_mkdtemp(tmpdir)) FAIL("cbm_mkdtemp failed"); - const char *const env_names[] = {"HOME", "PATH", "OMP_PROFILE", "PI_CODING_AGENT_DIR", - "XDG_CONFIG_HOME", "APPDATA"}; + const char *const env_names[] = { + "HOME", "PATH", "OMP_PROFILE", "PI_CODING_AGENT_DIR", "XDG_CONFIG_HOME", "APPDATA"}; char *saved_env[sizeof(env_names) / sizeof(env_names[0])]; for (size_t i = 0U; i < sizeof(env_names) / sizeof(env_names[0]); i++) { saved_env[i] = save_test_env(env_names[i]); @@ -4870,10 +5017,10 @@ TEST(cli_registry_omp_named_profile_install_and_uninstall_preserve_user_content) bool installed = install_rc == 0 && (!installed_mcp || !strstr(installed_mcp, "codebase-memory-mcp")) && installed_instructions && strcmp(installed_instructions, user_instructions) == 0 && - test_file_contains_all(skill_path, - (const char *const[]){"codebase-memory-cli search", - "codebase-memory-cli coverage"}, - 2U) && + test_file_contains_all( + skill_path, + (const char *const[]){"codebase-memory-cli search", "codebase-memory-cli coverage"}, + 2U) && stat(scout_path, &state) != 0 && stat(verify_path, &state) != 0 && stat(auditor_path, &state) != 0; free(installed_mcp); @@ -4890,12 +5037,12 @@ TEST(cli_registry_omp_named_profile_install_and_uninstall_preserve_user_content) char *preserved_instructions = read_test_file_alloc(instructions_path); char *preserved_verify = read_test_file_alloc(verify_path); char *mcp_after = read_test_file_alloc(mcp_path); - bool uninstalled = - uninstall_rc == 0 && preserved_instructions && - strcmp(preserved_instructions, user_instructions) == 0 && preserved_verify && - strcmp(preserved_verify, modified_verify) == 0 && stat(skill_path, &state) != 0 && - stat(scout_path, &state) != 0 && stat(auditor_path, &state) != 0 && - (!mcp_after || !strstr(mcp_after, "codebase-memory-mcp")); + bool uninstalled = uninstall_rc == 0 && preserved_instructions && + strcmp(preserved_instructions, user_instructions) == 0 && preserved_verify && + strcmp(preserved_verify, modified_verify) == 0 && + stat(skill_path, &state) != 0 && stat(scout_path, &state) != 0 && + stat(auditor_path, &state) != 0 && + (!mcp_after || !strstr(mcp_after, "codebase-memory-mcp")); free(preserved_instructions); free(preserved_verify); free(mcp_after); @@ -4904,7 +5051,8 @@ TEST(cli_registry_omp_named_profile_install_and_uninstall_preserve_user_content) restore_test_env(env_names[i], saved_env[i]); test_rmdir_r(tmpdir); if (!installed || !uninstalled) - FAIL("OMP CLI-first lifecycle must install only the skill and preserve user instructions/modified legacy profiles"); + FAIL("OMP CLI-first lifecycle must install only the skill and preserve user " + "instructions/modified legacy profiles"); PASS(); } @@ -4914,8 +5062,8 @@ TEST(cli_registry_omp_relocated_dry_run_is_non_mutating) { if (!cbm_mkdtemp(tmpdir)) FAIL("cbm_mkdtemp failed"); - const char *const env_names[] = {"HOME", "PATH", "OMP_PROFILE", "PI_CODING_AGENT_DIR", - "XDG_CONFIG_HOME", "APPDATA"}; + const char *const env_names[] = { + "HOME", "PATH", "OMP_PROFILE", "PI_CODING_AGENT_DIR", "XDG_CONFIG_HOME", "APPDATA"}; char *saved_env[sizeof(env_names) / sizeof(env_names[0])]; for (size_t i = 0U; i < sizeof(env_names) / sizeof(env_names[0]); i++) { saved_env[i] = save_test_env(env_names[i]); @@ -4969,7 +5117,8 @@ TEST(cli_registry_installs_gitlab_and_devin_lifecycle_context) { if (!cbm_mkdtemp(tmpdir)) FAIL("cbm_mkdtemp failed"); - const char *const env_names[] = {"HOME", "PATH", "XDG_CONFIG_HOME", "GLAB_CONFIG_DIR", "APPDATA"}; + const char *const env_names[] = {"HOME", "PATH", "XDG_CONFIG_HOME", "GLAB_CONFIG_DIR", + "APPDATA"}; char *saved_env[sizeof(env_names) / sizeof(env_names[0])]; for (size_t i = 0U; i < sizeof(env_names) / sizeof(env_names[0]); i++) { saved_env[i] = save_test_env(env_names[i]); @@ -5033,45 +5182,53 @@ TEST(cli_registry_installs_gitlab_and_devin_lifecycle_context) { bool hook_plan_ok = test_plan_hook_contains(hook_root, "GitLab Duo CLI", gitlab_hooks) && test_plan_hook_contains(hook_root, "Devin CLI / Local", devin_config); #endif - bool plan_ok = plan && hook_plan_ok && - !test_json_string_array_contains(plan_root, "config_files_planned", gitlab_mcp) && - !test_json_string_array_contains(plan_root, "config_files_planned", devin_config) && - test_json_string_array_contains(plan_root, "instruction_files_planned", devin_agents) && - test_json_string_array_contains(plan_root, "skill_files_planned", devin_skill); + bool plan_ok = + plan && hook_plan_ok && + !test_json_string_array_contains(plan_root, "config_files_planned", gitlab_mcp) && + !test_json_string_array_contains(plan_root, "config_files_planned", devin_config) && + test_json_string_array_contains(plan_root, "instruction_files_planned", devin_agents) && + test_json_string_array_contains(plan_root, "skill_files_planned", devin_skill); yyjson_doc_free(plan_doc); yyjson_doc_free(hook_doc); free(plan); free(hook_plan); int first_rc = cbm_install_agent_configs(tmpdir, binary_path, false, false); - if (first_rc == 0) first_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); + if (first_rc == 0) + first_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); int second_rc = cbm_install_agent_configs(tmpdir, binary_path, false, false); - if (second_rc == 0) second_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); + if (second_rc == 0) + second_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false); char *gitlab_data = read_test_file_alloc(gitlab_hooks); char *devin_data = read_test_file_alloc(devin_config); char *devin_agents_data = read_test_file_alloc(devin_agents); char *gitlab_mcp_data = read_test_file_alloc(gitlab_mcp); #ifdef _WIN32 bool gitlab_hook_installed = gitlab_data && strcmp(gitlab_data, gitlab_original) == 0; - bool devin_hooks_installed = devin_data && strstr(devin_data, "theme_mode") && - !strstr(devin_data, "--dialect devin"); + bool devin_hooks_installed = + devin_data && strstr(devin_data, "theme_mode") && !strstr(devin_data, "--dialect devin"); #else bool gitlab_hook_installed = gitlab_data && strstr(gitlab_data, "/usr/bin/user-hook") && strstr(gitlab_data, "hook-augment") && test_count_substring(gitlab_data, "hook-augment") == 1U; - bool devin_hooks_installed = devin_data && strstr(devin_data, "theme_mode") && - strstr(devin_data, "SessionStart") && - strstr(devin_data, "UserPromptSubmit") && - strstr(devin_data, "PostCompaction") && - test_count_substring(devin_data, "--dialect devin") == 3U; + bool devin_hooks_installed = + devin_data && strstr(devin_data, "theme_mode") && strstr(devin_data, "SessionStart") && + strstr(devin_data, "UserPromptSubmit") && strstr(devin_data, "PostCompaction") && + test_count_substring(devin_data, "--dialect devin") == 3U; #endif bool installed = first_rc == 0 && second_rc == 0 && gitlab_hook_installed && - devin_hooks_installed && (!gitlab_mcp_data || !strstr(gitlab_mcp_data, "codebase-memory-mcp")) && + devin_hooks_installed && + (!gitlab_mcp_data || !strstr(gitlab_mcp_data, "codebase-memory-mcp")) && devin_agents_data && strstr(devin_agents_data, devin_personal) && strstr(devin_agents_data, "codebase-memory-cli search") && test_file_contains_all(devin_skill, - (const char *const[]){"codebase-memory-cli search", "codebase-memory-cli coverage"}, 2U); - free(gitlab_data); free(devin_data); free(devin_agents_data); free(gitlab_mcp_data); + (const char *const[]){"codebase-memory-cli search", + "codebase-memory-cli coverage"}, + 2U); + free(gitlab_data); + free(devin_data); + free(devin_agents_data); + free(gitlab_mcp_data); char *argv[] = {"uninstall", "--yes"}; int uninstall_rc = cli_test_cmd_uninstall(2, argv); @@ -5083,21 +5240,24 @@ TEST(cli_registry_installs_gitlab_and_devin_lifecycle_context) { bool gitlab_clean = gitlab_data && strcmp(gitlab_data, gitlab_original) == 0; #else bool gitlab_clean = gitlab_data && strstr(gitlab_data, "/usr/bin/user-hook") && - strstr(gitlab_data, "\"keep\":true") && !strstr(gitlab_data, "hook-augment"); + strstr(gitlab_data, "\"keep\":true") && + !strstr(gitlab_data, "hook-augment"); #endif - bool devin_clean = devin_data && strstr(devin_data, "theme_mode") && - !strstr(devin_data, "--dialect devin") && devin_agents_data && - strstr(devin_agents_data, devin_personal) && - !strstr(devin_agents_data, "codebase-memory-cli search") && - stat(devin_skill, &state) != 0; + bool devin_clean = + devin_data && strstr(devin_data, "theme_mode") && !strstr(devin_data, "--dialect devin") && + devin_agents_data && strstr(devin_agents_data, devin_personal) && + !strstr(devin_agents_data, "codebase-memory-cli search") && stat(devin_skill, &state) != 0; bool cleaned = uninstall_rc == 0 && gitlab_clean && devin_clean; - free(gitlab_data); free(devin_data); free(devin_agents_data); + free(gitlab_data); + free(devin_data); + free(devin_agents_data); for (size_t i = 0U; i < sizeof(env_names) / sizeof(env_names[0]); i++) restore_test_env(env_names[i], saved_env[i]); test_rmdir_r(tmpdir); if (!plan_ok || !installed || !cleaned) - FAIL("GitLab/Devin CLI-first integration must plan/install lifecycle context without fresh MCP registration and clean only owned content"); + FAIL("GitLab/Devin CLI-first integration must plan/install lifecycle context without fresh " + "MCP registration and clean only owned content"); PASS(); } @@ -5120,8 +5280,10 @@ TEST(cli_registry_hook_cleanup_is_independent_from_mcp_ownership) { snprintf(qoder_settings, sizeof(qoder_settings), "%s/settings.json", qoder_dir); snprintf(devin_config, sizeof(devin_config), "%s/config.json", devin_dir); snprintf(binary_path, sizeof(binary_path), "%s/.local/bin/codebase-memory-cli", tmpdir); - test_mkdirp(qoder_dir); test_mkdirp(devin_dir); - const char *foreign = "{\"mcpServers\":{\"codebase-memory-mcp\":{\"command\":\"/foreign/cbm\"}}}\n"; + test_mkdirp(qoder_dir); + test_mkdirp(devin_dir); + const char *foreign = + "{\"mcpServers\":{\"codebase-memory-mcp\":{\"command\":\"/foreign/cbm\"}}}\n"; write_test_file(qoder_settings, foreign); write_test_file(devin_config, foreign); cbm_setenv("HOME", tmpdir, 1); @@ -5147,7 +5309,8 @@ TEST(cli_registry_hook_cleanup_is_independent_from_mcp_ownership) { write_test_file(qoder_settings, "{}\n"); write_test_file(devin_config, "{}\n"); - bool modified_ready = cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false) == 0; + bool modified_ready = + cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false) == 0; const char *const modified_dialects[] = {"--dialect Xoder", "--dialect Xevin"}; for (size_t i = 0U; i < 2U; i++) { char *data = read_test_file_alloc(paths[i]); @@ -5171,7 +5334,8 @@ TEST(cli_registry_hook_cleanup_is_independent_from_mcp_ownership) { restore_test_env(env_names[i], saved_env[i]); test_rmdir_r(tmpdir); if (!ready || !independent_cleanup || !modified_ready || !modified_preserved) - FAIL("Qoder/Devin hook ownership must be exact and independent from preserved foreign MCP data"); + FAIL("Qoder/Devin hook ownership must be exact and independent from preserved foreign MCP " + "data"); PASS(); } #endif @@ -5246,7 +5410,10 @@ TEST(cli_registry_installs_codebuddy_bob_and_pochi_durable_context) { FAIL("cbm_mkdtemp failed"); const char *const env_names[] = {"HOME", "PATH", "XDG_CONFIG_HOME"}; char *saved_env[3]; - for (size_t i = 0U; i < 3U; i++) { saved_env[i] = save_test_env(env_names[i]); cbm_unsetenv(env_names[i]); } + for (size_t i = 0U; i < 3U; i++) { + saved_env[i] = save_test_env(env_names[i]); + cbm_unsetenv(env_names[i]); + } char bin_dir[512], bob_command[640], codebuddy_dir[512], bob_dir[512], pochi_dir[512]; snprintf(bin_dir, sizeof(bin_dir), "%s/bin", tmpdir); @@ -5258,7 +5425,10 @@ TEST(cli_registry_installs_codebuddy_bob_and_pochi_durable_context) { #else snprintf(bob_command, sizeof(bob_command), "%s/bob", bin_dir); #endif - test_mkdirp(bin_dir); test_mkdirp(codebuddy_dir); test_mkdirp(bob_dir); test_mkdirp(pochi_dir); + test_mkdirp(bin_dir); + test_mkdirp(codebuddy_dir); + test_mkdirp(bob_dir); + test_mkdirp(pochi_dir); write_test_file(bob_command, "#!/bin/sh\nexit 0\n"); #ifndef _WIN32 chmod(bob_command, 0700); @@ -5269,8 +5439,10 @@ TEST(cli_registry_installs_codebuddy_bob_and_pochi_durable_context) { char pochi_mcp[640], pochi_rules[640], pochi_skill[640], pochi_agent[640]; snprintf(codebuddy_mcp, sizeof(codebuddy_mcp), "%s/.mcp.json", codebuddy_dir); snprintf(codebuddy_memory, sizeof(codebuddy_memory), "%s/CODEBUDDY.md", codebuddy_dir); - snprintf(codebuddy_skill, sizeof(codebuddy_skill), "%s/skills/codebase-memory-cli/SKILL.md", codebuddy_dir); - snprintf(codebuddy_agent, sizeof(codebuddy_agent), "%s/agents/codebase-memory.md", codebuddy_dir); + snprintf(codebuddy_skill, sizeof(codebuddy_skill), "%s/skills/codebase-memory-cli/SKILL.md", + codebuddy_dir); + snprintf(codebuddy_agent, sizeof(codebuddy_agent), "%s/agents/codebase-memory.md", + codebuddy_dir); snprintf(bob_ide_mcp, sizeof(bob_ide_mcp), "%s/mcp.json", bob_dir); snprintf(bob_shell_mcp, sizeof(bob_shell_mcp), "%s/mcp_settings.json", bob_dir); snprintf(bob_rule, sizeof(bob_rule), "%s/rules/codebase-memory.md", bob_dir); @@ -5286,10 +5458,13 @@ TEST(cli_registry_installs_codebuddy_bob_and_pochi_durable_context) { const char *pochi_personal = "# Personal Pochi rule\n"; write_test_file(codebuddy_memory, codebuddy_personal); write_test_file(bob_ide_mcp, "{\"keep\":\"bob-ide\"}\n"); - char bob_rules_dir[640]; snprintf(bob_rules_dir, sizeof(bob_rules_dir), "%s/rules", bob_dir); test_mkdirp(bob_rules_dir); + char bob_rules_dir[640]; + snprintf(bob_rules_dir, sizeof(bob_rules_dir), "%s/rules", bob_dir); + test_mkdirp(bob_rules_dir); write_test_file(bob_rule, bob_personal); write_test_file(pochi_rules, pochi_personal); - cbm_setenv("HOME", tmpdir, 1); cbm_setenv("PATH", bin_dir, 1); + cbm_setenv("HOME", tmpdir, 1); + cbm_setenv("PATH", bin_dir, 1); char binary_path[640]; #ifdef _WIN32 snprintf(binary_path, sizeof(binary_path), "%s/.local/bin/codebase-memory-cli.exe", tmpdir); @@ -5301,47 +5476,65 @@ TEST(cli_registry_installs_codebuddy_bob_and_pochi_durable_context) { bool plan_ok = plan && strstr(plan, codebuddy_memory) && strstr(plan, codebuddy_skill) && strstr(plan, bob_rule) && strstr(plan, bob_skill) && strstr(plan, pochi_rules) && strstr(plan, pochi_skill) && !strstr(plan, codebuddy_mcp) && - !strstr(plan, bob_ide_mcp) && !strstr(plan, bob_shell_mcp) && !strstr(plan, pochi_mcp) && - !strstr(plan, codebuddy_agent) && !strstr(plan, bob_agent) && !strstr(plan, pochi_agent); + !strstr(plan, bob_ide_mcp) && !strstr(plan, bob_shell_mcp) && + !strstr(plan, pochi_mcp) && !strstr(plan, codebuddy_agent) && + !strstr(plan, bob_agent) && !strstr(plan, pochi_agent); free(plan); int first_rc = cbm_install_agent_configs(tmpdir, binary_path, false, false); int second_rc = cbm_install_agent_configs(tmpdir, binary_path, false, false); struct stat state; char *bob_legacy = read_test_file_alloc(bob_ide_mcp); - bool codebuddy_installed = test_file_contains_all(codebuddy_memory, + bool codebuddy_installed = + test_file_contains_all( + codebuddy_memory, (const char *const[]){codebuddy_personal, "codebase-memory-cli search"}, 2U) && - test_file_contains_all(codebuddy_skill, - (const char *const[]){"codebase-memory-cli search", "codebase-memory-cli coverage"}, 2U) && + test_file_contains_all( + codebuddy_skill, + (const char *const[]){"codebase-memory-cli search", "codebase-memory-cli coverage"}, + 2U) && stat(codebuddy_mcp, &state) != 0 && stat(codebuddy_agent, &state) != 0; - bool bob_installed = bob_legacy && strstr(bob_legacy, "bob-ide") && !strstr(bob_legacy, "codebase-memory-mcp") && + bool bob_installed = + bob_legacy && strstr(bob_legacy, "bob-ide") && !strstr(bob_legacy, "codebase-memory-mcp") && stat(bob_shell_mcp, &state) != 0 && - test_file_contains_all(bob_rule, (const char *const[]){bob_personal, "codebase-memory-cli search"}, 2U) && - test_file_contains_all(bob_skill, (const char *const[]){"codebase-memory-cli coverage"}, 1U) && + test_file_contains_all( + bob_rule, (const char *const[]){bob_personal, "codebase-memory-cli search"}, 2U) && + test_file_contains_all(bob_skill, (const char *const[]){"codebase-memory-cli coverage"}, + 1U) && stat(bob_agent, &state) != 0; - bool pochi_installed = stat(pochi_mcp, &state) != 0 && - test_file_contains_all(pochi_rules, (const char *const[]){pochi_personal, "codebase-memory-cli search"}, 2U) && - test_file_contains_all(pochi_skill, (const char *const[]){"codebase-memory-cli coverage"}, 1U) && + bool pochi_installed = + stat(pochi_mcp, &state) != 0 && + test_file_contains_all( + pochi_rules, (const char *const[]){pochi_personal, "codebase-memory-cli search"}, 2U) && + test_file_contains_all(pochi_skill, (const char *const[]){"codebase-memory-cli coverage"}, + 1U) && stat(pochi_agent, &state) != 0; free(bob_legacy); - bool installed = first_rc == 0 && second_rc == 0 && codebuddy_installed && bob_installed && pochi_installed; + bool installed = + first_rc == 0 && second_rc == 0 && codebuddy_installed && bob_installed && pochi_installed; char *argv[] = {"uninstall", "--yes"}; int uninstall_rc = cli_test_cmd_uninstall(2, argv); char *codebuddy_after = read_test_file_alloc(codebuddy_memory); char *bob_after = read_test_file_alloc(bob_rule); char *pochi_after = read_test_file_alloc(pochi_rules); - bool cleaned = uninstall_rc == 0 && codebuddy_after && strstr(codebuddy_after, codebuddy_personal) && - !strstr(codebuddy_after, "codebase-memory-cli search") && bob_after && strstr(bob_after, bob_personal) && - !strstr(bob_after, "codebase-memory-cli search") && pochi_after && strstr(pochi_after, pochi_personal) && - !strstr(pochi_after, "codebase-memory-cli search") && stat(codebuddy_skill, &state) != 0 && - stat(bob_skill, &state) != 0 && stat(pochi_skill, &state) != 0; - free(codebuddy_after); free(bob_after); free(pochi_after); - - for (size_t i = 0U; i < 3U; i++) restore_test_env(env_names[i], saved_env[i]); + bool cleaned = + uninstall_rc == 0 && codebuddy_after && strstr(codebuddy_after, codebuddy_personal) && + !strstr(codebuddy_after, "codebase-memory-cli search") && bob_after && + strstr(bob_after, bob_personal) && !strstr(bob_after, "codebase-memory-cli search") && + pochi_after && strstr(pochi_after, pochi_personal) && + !strstr(pochi_after, "codebase-memory-cli search") && stat(codebuddy_skill, &state) != 0 && + stat(bob_skill, &state) != 0 && stat(pochi_skill, &state) != 0; + free(codebuddy_after); + free(bob_after); + free(pochi_after); + + for (size_t i = 0U; i < 3U; i++) + restore_test_env(env_names[i], saved_env[i]); test_rmdir_r(tmpdir); if (!plan_ok || !installed || !cleaned) - FAIL("CodeBuddy, Bob, and Pochi must use CLI-first durable instructions/skills without fresh MCP or tier-profile creation"); + FAIL("CodeBuddy, Bob, and Pochi must use CLI-first durable instructions/skills without " + "fresh MCP or tier-profile creation"); PASS(); } @@ -5395,7 +5588,8 @@ TEST(cli_codex_respects_codex_home) { cbm_detected_agents_t agents = cbm_detect_agents(tmpdir); char *json = cbm_build_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); - char *hook_json = cbm_build_hook_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); + char *hook_json = + cbm_build_hook_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); char expected_config[640]; char expected_instructions[640]; snprintf(expected_config, sizeof(expected_config), "%s/config.toml", codex_home); @@ -5448,7 +5642,8 @@ TEST(cli_grok_respects_grok_home) { if (!agents.grok) FAIL("Grok Build detection must honor GROK_HOME"); if (!planned || !no_hooks) - FAIL("Grok install plan must place CLI-first rules and skill under GROK_HOME and never plan a hook"); + FAIL("Grok install plan must place CLI-first rules and skill under GROK_HOME and never " + "plan a hook"); PASS(); } @@ -5548,7 +5743,8 @@ TEST(cli_opencode_honors_custom_config) { char *json = cbm_build_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); char expected_instructions[640]; char expected_skill[640]; - snprintf(expected_instructions, sizeof(expected_instructions), "%s/.config/opencode/AGENTS.md", tmpdir); + snprintf(expected_instructions, sizeof(expected_instructions), "%s/.config/opencode/AGENTS.md", + tmpdir); snprintf(expected_skill, sizeof(expected_skill), "%s/.config/opencode/skills/codebase-memory-cli/SKILL.md", tmpdir); bool plans_custom = agents.opencode && json && strstr(json, expected_instructions) && @@ -5585,17 +5781,19 @@ TEST(cli_opencode_prefers_existing_jsonc_config_discussion1560) { cbm_unsetenv("OPENCODE_CONFIG"); char *json = cbm_build_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); - bool targets_jsonc = json && strstr(json, "/.config/opencode/AGENTS.md") != NULL && - strstr(json, "/.config/opencode/skills/codebase-memory-cli/SKILL.md") != NULL && - strstr(json, "/.config/opencode/opencode.jsonc") == NULL && - strstr(json, "/.config/opencode/opencode.json") == NULL; + bool targets_jsonc = + json && strstr(json, "/.config/opencode/AGENTS.md") != NULL && + strstr(json, "/.config/opencode/skills/codebase-memory-cli/SKILL.md") != NULL && + strstr(json, "/.config/opencode/opencode.jsonc") == NULL && + strstr(json, "/.config/opencode/opencode.json") == NULL; free(json); restore_test_env("PATH", saved_path); restore_test_env("OPENCODE_CONFIG", saved_file); test_rmdir_r(tmpdir); if (!targets_jsonc) - FAIL("OpenCode CLI-first install must preserve existing JSONC config and plan only durable context"); + FAIL("OpenCode CLI-first install must preserve existing JSONC config and plan only durable " + "context"); PASS(); } @@ -5675,7 +5873,8 @@ TEST(cli_opencode_config_dir_detects_without_retargeting_global_json) { restore_test_env("OPENCODE_CONFIG_DIR", saved_dir); test_rmdir_r(tmpdir); if (!correct) - FAIL("OPENCODE_CONFIG_DIR may detect OpenCode but must not retarget CLI-first durable writes"); + FAIL("OPENCODE_CONFIG_DIR may detect OpenCode but must not retarget CLI-first durable " + "writes"); PASS(); } @@ -5760,14 +5959,15 @@ TEST(cli_relative_kiro_and_hermes_homes_never_target_root) { cbm_setenv("HERMES_HOME", "relative-hermes", 1); char *json = cbm_build_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); - char *hook_json = cbm_build_hook_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); + char *hook_json = + cbm_build_hook_install_plan_json(tmpdir, "/usr/local/bin/codebase-memory-cli"); char expected_kiro[512]; char expected_hermes[512]; snprintf(expected_kiro, sizeof(expected_kiro), "%s/.kiro/steering/codebase-memory.md", tmpdir); snprintf(expected_hermes, sizeof(expected_hermes), "%s/.hermes/config.yaml", tmpdir); - bool safe = json && hook_json && strstr(json, expected_kiro) && strstr(hook_json, expected_hermes) && - !strstr(json, "\"/steering/codebase-memory.md\"") && - !strstr(hook_json, "\"/config.yaml\""); + bool safe = + json && hook_json && strstr(json, expected_kiro) && strstr(hook_json, expected_hermes) && + !strstr(json, "\"/steering/codebase-memory.md\"") && !strstr(hook_json, "\"/config.yaml\""); free(json); free(hook_json); @@ -5809,20 +6009,22 @@ TEST(cli_fresh_cli_only_yaml_and_toml_agents_create_parent_dirs) { path, (const char *const[]){"pre_llm_call:", "hook-augment", "--dialect hermes"}, 3); snprintf(path, sizeof(path), "%s/.config/goose/.goosehints", tmpdir); installed = installed && test_file_contains_all( - path, (const char *const[]){"codebase-memory-cli", "coverage"}, 2); + path, (const char *const[]){"codebase-memory-cli", "coverage"}, 2); snprintf(path, sizeof(path), "%s/.vibe/AGENTS.md", tmpdir); installed = installed && test_file_contains_all( - path, (const char *const[]){"codebase-memory-cli", "search"}, 2); + path, (const char *const[]){"codebase-memory-cli", "search"}, 2); snprintf(path, sizeof(path), "%s/.vibe/skills/codebase-memory-cli/SKILL.md", tmpdir); - installed = installed && test_file_contains_all( - path, (const char *const[]){"Codebase Memory", "codebase-memory-cli"}, 2); + installed = + installed && test_file_contains_all( + path, (const char *const[]){"Codebase Memory", "codebase-memory-cli"}, 2); for (size_t i = 0; i < sizeof(env_names) / sizeof(env_names[0]); i++) { restore_test_env(env_names[i], saved_env[i]); } test_rmdir_r(tmpdir); if (!installed) - FAIL("CLI-first Hermes, Goose, and Vibe installs must create parents for their current durable artifacts"); + FAIL("CLI-first Hermes, Goose, and Vibe installs must create parents for their current " + "durable artifacts"); PASS(); } @@ -5884,15 +6086,18 @@ TEST(cli_augment_installs_session_context_and_subagent) { #else snprintf(binary, sizeof(binary), "%s/codebase-memory-cli", bin_dir); #endif - test_mkdirp(augment_dir); test_mkdirp(bin_dir); + test_mkdirp(augment_dir); + test_mkdirp(bin_dir); write_test_file(binary, "#!/bin/sh\nexit 0\n"); #ifndef _WIN32 chmod(binary, 0755); #endif char *saved_home = save_test_env("HOME"), *saved_path = save_test_env("PATH"); - cbm_setenv("HOME", tmpdir, 1); cbm_setenv("PATH", tmpdir, 1); + cbm_setenv("HOME", tmpdir, 1); + cbm_setenv("PATH", tmpdir, 1); int install_rc = cbm_install_agent_configs(tmpdir, binary, false, false); - if (install_rc == 0) install_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary, false, false); + if (install_rc == 0) + install_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary, false, false); char settings_path[640], rule_path[640], scout_path[640], agent_path[640], auditor_path[640]; char session_script_path[640], coverage_script_path[640]; @@ -5900,50 +6105,73 @@ TEST(cli_augment_installs_session_context_and_subagent) { snprintf(rule_path, sizeof(rule_path), "%s/rules/codebase-memory.md", augment_dir); snprintf(scout_path, sizeof(scout_path), "%s/agents/codebase-memory-scout.md", augment_dir); snprintf(agent_path, sizeof(agent_path), "%s/agents/codebase-memory.md", augment_dir); - snprintf(auditor_path, sizeof(auditor_path), "%s/agents/codebase-memory-auditor.md", augment_dir); + snprintf(auditor_path, sizeof(auditor_path), "%s/agents/codebase-memory-auditor.md", + augment_dir); #ifdef _WIN32 - snprintf(session_script_path, sizeof(session_script_path), "%s/hooks/codebase-memory-cli-session.ps1", augment_dir); - snprintf(coverage_script_path, sizeof(coverage_script_path), "%s/hooks/codebase-memory-cli-coverage.ps1", augment_dir); + snprintf(session_script_path, sizeof(session_script_path), + "%s/hooks/codebase-memory-cli-session.ps1", augment_dir); + snprintf(coverage_script_path, sizeof(coverage_script_path), + "%s/hooks/codebase-memory-cli-coverage.ps1", augment_dir); #else - snprintf(session_script_path, sizeof(session_script_path), "%s/hooks/codebase-memory-cli-session.sh", augment_dir); - snprintf(coverage_script_path, sizeof(coverage_script_path), "%s/hooks/codebase-memory-cli-coverage.sh", augment_dir); + snprintf(session_script_path, sizeof(session_script_path), + "%s/hooks/codebase-memory-cli-session.sh", augment_dir); + snprintf(coverage_script_path, sizeof(coverage_script_path), + "%s/hooks/codebase-memory-cli-coverage.sh", augment_dir); #endif char *settings = read_test_file_alloc(settings_path), *rule = read_test_file_alloc(rule_path); - char *session_script = read_test_file_alloc(session_script_path), *coverage_script = read_test_file_alloc(coverage_script_path); + char *session_script = read_test_file_alloc(session_script_path), + *coverage_script = read_test_file_alloc(coverage_script_path); struct stat state; - bool settings_ok = settings && !strstr(settings, "mcpServers") && strstr(settings, "SessionStart") && - strstr(settings, "PostToolUse") && strstr(settings, "\"matcher\": \"view\""); + bool settings_ok = settings && !strstr(settings, "mcpServers") && + strstr(settings, "SessionStart") && strstr(settings, "PostToolUse") && + strstr(settings, "\"matcher\": \"view\""); bool context_ok = rule && strstr(rule, "codebase-memory-cli search") && - strstr(rule, "codebase-memory-cli coverage") && session_script && strstr(session_script, binary) && - strstr(session_script, "hook-augment") && coverage_script && strstr(coverage_script, binary) && - strstr(coverage_script, "--dialect augment") && stat(scout_path, &state) != 0 && - stat(agent_path, &state) != 0 && stat(auditor_path, &state) != 0; + strstr(rule, "codebase-memory-cli coverage") && session_script && + strstr(session_script, binary) && strstr(session_script, "hook-augment") && + coverage_script && strstr(coverage_script, binary) && + strstr(coverage_script, "--dialect augment") && + stat(scout_path, &state) != 0 && stat(agent_path, &state) != 0 && + stat(auditor_path, &state) != 0; #ifndef _WIN32 - context_ok = context_ok && stat(session_script_path, &state) == 0 && (state.st_mode & S_IXUSR) != 0 && - stat(coverage_script_path, &state) == 0 && (state.st_mode & S_IXUSR) != 0; + context_ok = context_ok && stat(session_script_path, &state) == 0 && + (state.st_mode & S_IXUSR) != 0 && stat(coverage_script_path, &state) == 0 && + (state.st_mode & S_IXUSR) != 0; #endif - free(settings); free(rule); free(session_script); free(coverage_script); + free(settings); + free(rule); + free(session_script); + free(coverage_script); char *asset_plan = cbm_build_install_plan_json(tmpdir, binary); char *hook_plan = cbm_build_hook_install_plan_json(tmpdir, binary); - size_t plan_len = (asset_plan ? strlen(asset_plan) : 0U) + (hook_plan ? strlen(hook_plan) : 0U) + 2U; + size_t plan_len = + (asset_plan ? strlen(asset_plan) : 0U) + (hook_plan ? strlen(hook_plan) : 0U) + 2U; char *plan = calloc(plan_len, 1U); - if (plan) snprintf(plan, plan_len, "%s\n%s", asset_plan ? asset_plan : "", hook_plan ? hook_plan : ""); - free(asset_plan); free(hook_plan); + if (plan) + snprintf(plan, plan_len, "%s\n%s", asset_plan ? asset_plan : "", + hook_plan ? hook_plan : ""); + free(asset_plan); + free(hook_plan); bool plan_ok = plan && strstr(plan, settings_path) && strstr(plan, rule_path) && strstr(plan, session_script_path) && strstr(plan, coverage_script_path) && - !strstr(plan, scout_path) && !strstr(plan, agent_path) && !strstr(plan, auditor_path); + !strstr(plan, scout_path) && !strstr(plan, agent_path) && + !strstr(plan, auditor_path); free(plan); char *args[] = {"-n"}; int uninstall_rc = cli_test_cmd_uninstall(1, args); char *settings_after = read_test_file_alloc(settings_path); - bool removed = (!settings_after || (!strstr(settings_after, "SessionStart") && !strstr(settings_after, "PostToolUse"))) && - stat(session_script_path, &state) != 0 && stat(coverage_script_path, &state) != 0; + bool removed = + (!settings_after || + (!strstr(settings_after, "SessionStart") && !strstr(settings_after, "PostToolUse"))) && + stat(session_script_path, &state) != 0 && stat(coverage_script_path, &state) != 0; free(settings_after); - restore_test_env("HOME", saved_home); restore_test_env("PATH", saved_path); test_rmdir_r(tmpdir); + restore_test_env("HOME", saved_home); + restore_test_env("PATH", saved_path); + test_rmdir_r(tmpdir); if (install_rc != 0 || uninstall_rc != 0 || !settings_ok || !context_ok || !plan_ok || !removed) - FAIL("Augment must use CLI-first instructions plus SessionStart/PostToolUse coverage hooks without fresh MCP or agents"); + FAIL("Augment must use CLI-first instructions plus SessionStart/PostToolUse coverage hooks " + "without fresh MCP or agents"); PASS(); } @@ -6106,34 +6334,40 @@ TEST(cli_hook_ownership_requires_exact_command_identity) { snprintf(settings, sizeof(settings), "%s/settings.json", claude_dir); test_mkdirp(claude_dir); char *saved_home = save_test_env("HOME"), *saved_claude = save_test_env("CLAUDE_CONFIG_DIR"); - cbm_setenv("HOME", tmpdir, 1); cbm_setenv("CLAUDE_CONFIG_DIR", claude_dir, 1); - ASSERT_EQ(cbm_resolve_claude_hook_command_for_testing("codebase-memory-cli-discovery-gate", false, - exact_command, sizeof(exact_command)), + cbm_setenv("HOME", tmpdir, 1); + cbm_setenv("CLAUDE_CONFIG_DIR", claude_dir, 1); + ASSERT_EQ(cbm_resolve_claude_hook_command_for_testing("codebase-memory-cli-discovery-gate", + false, exact_command, + sizeof(exact_command)), 0); snprintf(foreign, sizeof(foreign), "{\"hooks\":{" "\"PreToolUse\":[{\"matcher\":\"Grep|Glob|Read\",\"hooks\":[{" "\"type\":\"command\",\"command\":\"echo %s user-owned-claude\"}]}]," "\"BeforeTool\":[{\"matcher\":\"google_web_search|grep_search\",\"hooks\":[{" - "\"type\":\"command\",\"command\":\"echo codebase-memory-mcp search_graph user-owned-gemini\"}]}]}}\n", + "\"type\":\"command\",\"command\":\"echo codebase-memory-mcp search_graph " + "user-owned-gemini\"}]}]}}\n", exact_command); write_test_file(settings, foreign); int install_claude = cbm_upsert_claude_hooks(settings); int install_gemini = cbm_upsert_gemini_hooks(settings); char *after_install = read_test_file_alloc(settings); bool install_preserved = after_install && strstr(after_install, "user-owned-claude") && - strstr(after_install, "user-owned-gemini") && strstr(after_install, "Code discovery: prefer codebase-memory-cli"); + strstr(after_install, "user-owned-gemini") && + strstr(after_install, "Code discovery: prefer codebase-memory-cli"); free(after_install); int remove_claude = cbm_remove_claude_hooks(settings); int remove_gemini = cbm_remove_gemini_hooks(settings); char *after_remove = read_test_file_alloc(settings); bool remove_preserved = after_remove && strstr(after_remove, "user-owned-claude") && - strstr(after_remove, "user-owned-gemini") && - strstr(after_remove, exact_command) && - !strstr(after_remove, "Code discovery: prefer codebase-memory-cli"); + strstr(after_remove, "user-owned-gemini") && + strstr(after_remove, exact_command) && + !strstr(after_remove, "Code discovery: prefer codebase-memory-cli"); free(after_remove); - restore_test_env("HOME", saved_home); restore_test_env("CLAUDE_CONFIG_DIR", saved_claude); test_rmdir_r(tmpdir); + restore_test_env("HOME", saved_home); + restore_test_env("CLAUDE_CONFIG_DIR", saved_claude); + test_rmdir_r(tmpdir); if (install_claude != 0 || install_gemini != 0 || remove_claude != 0 || remove_gemini != 0 || !install_preserved || !remove_preserved) FAIL("hook ownership must require exact command identity, not a matching substring"); @@ -6178,8 +6412,8 @@ TEST(cli_gemini_hook_upgrade_migrates_released_exact_commands) { int session_upsert = cbm_upsert_gemini_session_hooks(settings); char *upgraded = read_test_file_alloc(settings); bool preserved_on_upsert = upgraded && strstr(upgraded, legacy_before_commands[i]) && - strstr(upgraded, "grep/file-read; run index_repository first") && - strstr(upgraded, "codebase-memory-cli"); + strstr(upgraded, "grep/file-read; run index_repository first") && + strstr(upgraded, "codebase-memory-cli"); free(upgraded); write_test_file(settings, legacy_json); @@ -6187,16 +6421,18 @@ TEST(cli_gemini_hook_upgrade_migrates_released_exact_commands) { int session_remove = cbm_remove_gemini_session_hooks(settings); char *removed = read_test_file_alloc(settings); bool legacy_preserved = removed && strstr(removed, legacy_before_commands[i]) && - strstr(removed, "grep/file-read; run index_repository first") && - !strstr(removed, "Code discovery: prefer codebase-memory-cli"); + strstr(removed, "grep/file-read; run index_repository first") && + !strstr(removed, "Code discovery: prefer codebase-memory-cli"); free(removed); all_preserved = all_preserved && before_upsert == 0 && session_upsert == 0 && - before_remove == 0 && session_remove == 0 && preserved_on_upsert && legacy_preserved; + before_remove == 0 && session_remove == 0 && preserved_on_upsert && + legacy_preserved; } test_rmdir_r(tmpdir); if (!all_preserved) - FAIL("Gemini CLI hooks must preserve MCP-era command identities while owning only CLI commands"); + FAIL("Gemini CLI hooks must preserve MCP-era command identities while owning only CLI " + "commands"); PASS(); } @@ -6209,9 +6445,11 @@ TEST(cli_uninstall_preserves_hook_script_with_modified_binary) { char script_path[640]; snprintf(claude_dir, sizeof(claude_dir), "%s/.claude", tmpdir); #ifdef _WIN32 - snprintf(script_path, sizeof(script_path), "%s/hooks/codebase-memory-cli-session-reminder.cmd", claude_dir); + snprintf(script_path, sizeof(script_path), "%s/hooks/codebase-memory-cli-session-reminder.cmd", + claude_dir); #else - snprintf(script_path, sizeof(script_path), "%s/hooks/codebase-memory-cli-session-reminder", claude_dir); + snprintf(script_path, sizeof(script_path), "%s/hooks/codebase-memory-cli-session-reminder", + claude_dir); #endif test_mkdirp(claude_dir); @@ -6395,8 +6633,9 @@ TEST(cli_claude_subagent_hook) { const char *d = read_test_file(cfg); ASSERT_NOT_NULL(d); ASSERT(strstr(d, "SubagentStart") != NULL); - ASSERT(strstr(d, "\"*\"") != NULL); /* match-all matcher */ - ASSERT(strstr(d, "codebase-memory-cli-subagent-reminder") != NULL); /* points at the hook script */ + ASSERT(strstr(d, "\"*\"") != NULL); /* match-all matcher */ + ASSERT(strstr(d, "codebase-memory-cli-subagent-reminder") != + NULL); /* points at the hook script */ /* Idempotent: a second upsert must not duplicate our entry. */ ASSERT_EQ(cbm_upsert_claude_subagent_hooks(cfg), 0); @@ -6437,8 +6676,9 @@ TEST(cli_claude_hook_mutation_converges_mixed_owned_duplicates) { char *saved_config = save_test_env("CLAUDE_CONFIG_DIR"); cbm_setenv("CLAUDE_CONFIG_DIR", config_dir, 1); - ASSERT_EQ(cbm_resolve_claude_hook_command_for_testing("codebase-memory-cli-subagent-reminder", false, - current_command, sizeof(current_command)), + ASSERT_EQ(cbm_resolve_claude_hook_command_for_testing("codebase-memory-cli-subagent-reminder", + false, current_command, + sizeof(current_command)), 0); snprintf(original, sizeof(original), "{\"hooks\":{\"SubagentStart\":[" @@ -6451,10 +6691,11 @@ TEST(cli_claude_hook_mutation_converges_mixed_owned_duplicates) { write_test_file(cfg, original); int upsert_rc = cbm_upsert_claude_subagent_hooks(cfg); char *after_upsert = read_test_file_alloc(cfg); - bool converged = upsert_rc == 0 && after_upsert && - test_count_substring(after_upsert, "codebase-memory-cli-subagent-reminder") == 1U && - strstr(after_upsert, "cbm-subagent-reminder") && - strstr(after_upsert, "echo user-subagent-hook"); + bool converged = + upsert_rc == 0 && after_upsert && + test_count_substring(after_upsert, "codebase-memory-cli-subagent-reminder") == 1U && + strstr(after_upsert, "cbm-subagent-reminder") && + strstr(after_upsert, "echo user-subagent-hook"); free(after_upsert); write_test_file(cfg, original); @@ -6494,15 +6735,15 @@ TEST(cli_claude_subagent_hook_preserves_user_entry) { ASSERT_EQ(cbm_upsert_claude_subagent_hooks(cfg), 0); const char *d = read_test_file(cfg); ASSERT_NOT_NULL(d); - ASSERT(strstr(d, "echo user-subagent-hook") != NULL); /* user's hook untouched */ - ASSERT(strstr(d, "codebase-memory-cli-subagent-reminder") != NULL); /* ours added */ + ASSERT(strstr(d, "echo user-subagent-hook") != NULL); /* user's hook untouched */ + ASSERT(strstr(d, "codebase-memory-cli-subagent-reminder") != NULL); /* ours added */ /* Remove CMM's hook: the user's entry must still be intact, ours gone. */ ASSERT_EQ(cbm_remove_claude_subagent_hooks(cfg), 0); d = read_test_file(cfg); ASSERT_NOT_NULL(d); - ASSERT(strstr(d, "echo user-subagent-hook") != NULL); /* user's hook preserved */ - ASSERT_NULL(strstr(d, "codebase-memory-cli-subagent-reminder")); /* only ours removed */ + ASSERT(strstr(d, "echo user-subagent-hook") != NULL); /* user's hook preserved */ + ASSERT_NULL(strstr(d, "codebase-memory-cli-subagent-reminder")); /* only ours removed */ test_rmdir_r(tmpdir); PASS(); @@ -6573,12 +6814,15 @@ TEST(cli_claude_lifecycle_hooks_delegate_to_augmenter) { char subagent_path[640]; char settings_path[640]; #ifdef _WIN32 - snprintf(session_path, sizeof(session_path), "%s/hooks/codebase-memory-cli-session-reminder.cmd", config_dir); - snprintf(subagent_path, sizeof(subagent_path), "%s/hooks/codebase-memory-cli-subagent-reminder.cmd", - config_dir); + snprintf(session_path, sizeof(session_path), + "%s/hooks/codebase-memory-cli-session-reminder.cmd", config_dir); + snprintf(subagent_path, sizeof(subagent_path), + "%s/hooks/codebase-memory-cli-subagent-reminder.cmd", config_dir); #else - snprintf(session_path, sizeof(session_path), "%s/hooks/codebase-memory-cli-session-reminder", config_dir); - snprintf(subagent_path, sizeof(subagent_path), "%s/hooks/codebase-memory-cli-subagent-reminder", config_dir); + snprintf(session_path, sizeof(session_path), "%s/hooks/codebase-memory-cli-session-reminder", + config_dir); + snprintf(subagent_path, sizeof(subagent_path), "%s/hooks/codebase-memory-cli-subagent-reminder", + config_dir); #endif snprintf(settings_path, sizeof(settings_path), "%s/settings.json", config_dir); char *session = read_test_file_alloc(session_path); @@ -6740,13 +6984,20 @@ TEST(cli_vscode_only_installs_copilot_durable_context) { #endif test_mkdirp(code_user); char *saved_home = save_test_env("HOME"), *saved_path = save_test_env("PATH"); - char *saved_copilot = save_test_env("COPILOT_HOME"), *saved_xdg = save_test_env("XDG_CONFIG_HOME"); + char *saved_copilot = save_test_env("COPILOT_HOME"), + *saved_xdg = save_test_env("XDG_CONFIG_HOME"); char *saved_appdata = save_test_env("APPDATA"); - cbm_setenv("HOME", tmpdir, 1); cbm_setenv("PATH", tmpdir, 1); cbm_unsetenv("COPILOT_HOME"); + cbm_setenv("HOME", tmpdir, 1); + cbm_setenv("PATH", tmpdir, 1); + cbm_unsetenv("COPILOT_HOME"); #if !defined(__APPLE__) && !defined(_WIN32) - char xdg[512]; snprintf(xdg, sizeof(xdg), "%s/.config", tmpdir); cbm_setenv("XDG_CONFIG_HOME", xdg, 1); + char xdg[512]; + snprintf(xdg, sizeof(xdg), "%s/.config", tmpdir); + cbm_setenv("XDG_CONFIG_HOME", xdg, 1); #elif defined(_WIN32) - char appdata[512]; snprintf(appdata, sizeof(appdata), "%s/AppData/Roaming", tmpdir); cbm_setenv("APPDATA", appdata, 1); + char appdata[512]; + snprintf(appdata, sizeof(appdata), "%s/AppData/Roaming", tmpdir); + cbm_setenv("APPDATA", appdata, 1); #endif char binary[640]; #ifdef _WIN32 @@ -6757,22 +7008,27 @@ TEST(cli_vscode_only_installs_copilot_durable_context) { cbm_install_agent_configs(tmpdir, binary, false, false); cbm_install_agent_hooks_for_testing(tmpdir, binary, false, false); int second_install_rc = cbm_install_agent_configs(tmpdir, binary, false, false); - if (second_install_rc == 0) second_install_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary, false, false); + if (second_install_rc == 0) + second_install_rc = cbm_install_agent_hooks_for_testing(tmpdir, binary, false, false); - char hook_path[640], skill_path[640], agents_dir[640], agent_path[640], mcp_path[640], instructions_path[640]; + char hook_path[640], skill_path[640], agents_dir[640], agent_path[640], mcp_path[640], + instructions_path[640]; snprintf(hook_path, sizeof(hook_path), "%s/.copilot/hooks/codebase-memory-cli.json", tmpdir); - snprintf(skill_path, sizeof(skill_path), "%s/.copilot/skills/codebase-memory-cli/SKILL.md", tmpdir); + snprintf(skill_path, sizeof(skill_path), "%s/.copilot/skills/codebase-memory-cli/SKILL.md", + tmpdir); snprintf(agents_dir, sizeof(agents_dir), "%s/.copilot/agents", tmpdir); snprintf(agent_path, sizeof(agent_path), "%s/codebase-memory.agent.md", agents_dir); snprintf(mcp_path, sizeof(mcp_path), "%s/.copilot/mcp-config.json", tmpdir); - snprintf(instructions_path, sizeof(instructions_path), "%s/.copilot/copilot-instructions.md", tmpdir); + snprintf(instructions_path, sizeof(instructions_path), "%s/.copilot/copilot-instructions.md", + tmpdir); char *hook = read_test_file_alloc(hook_path), *skill = read_test_file_alloc(skill_path); struct stat state; bool installed = second_install_rc == 0 && hook && strstr(hook, "sessionStart") && - strstr(hook, "subagentStart") && strstr(hook, "--dialect copilot") && skill && - strstr(skill, "codebase-memory-cli search") && stat(agent_path, &state) != 0 && - stat(mcp_path, &state) != 0 && stat(instructions_path, &state) != 0; - free(hook); free(skill); + strstr(hook, "subagentStart") && strstr(hook, "--dialect copilot") && skill && + strstr(skill, "codebase-memory-cli search") && stat(agent_path, &state) != 0 && + stat(mcp_path, &state) != 0 && stat(instructions_path, &state) != 0; + free(hook); + free(skill); /* Verify legacy tier cleanup preserves content that is not an exact shipped profile. */ test_mkdirp(agents_dir); @@ -6785,11 +7041,15 @@ TEST(cli_vscode_only_installs_copilot_durable_context) { preserved && strcmp(preserved, modified) == 0; free(preserved); - restore_test_env("HOME", saved_home); restore_test_env("PATH", saved_path); - restore_test_env("COPILOT_HOME", saved_copilot); restore_test_env("XDG_CONFIG_HOME", saved_xdg); - restore_test_env("APPDATA", saved_appdata); test_rmdir_r(tmpdir); + restore_test_env("HOME", saved_home); + restore_test_env("PATH", saved_path); + restore_test_env("COPILOT_HOME", saved_copilot); + restore_test_env("XDG_CONFIG_HOME", saved_xdg); + restore_test_env("APPDATA", saved_appdata); + test_rmdir_r(tmpdir); if (rc != 0 || !installed || !ownership_safe) - FAIL("VS Code-only installs must receive current Copilot skill/hooks without fresh MCP or tier profiles, and legacy cleanup must preserve modified files"); + FAIL("VS Code-only installs must receive current Copilot skill/hooks without fresh MCP or " + "tier profiles, and legacy cleanup must preserve modified files"); PASS(); } @@ -6925,15 +7185,15 @@ TEST(cli_installer_follows_trusted_symlinked_agent_roots) { char outside_junie_agent[512]; snprintf(outside_qoder_settings, sizeof(outside_qoder_settings), "%s/settings.json", qoder_target); - snprintf(outside_qoder_skill, sizeof(outside_qoder_skill), "%s/skills/codebase-memory-cli/SKILL.md", - qoder_target); + snprintf(outside_qoder_skill, sizeof(outside_qoder_skill), + "%s/skills/codebase-memory-cli/SKILL.md", qoder_target); snprintf(outside_junie_mcp, sizeof(outside_junie_mcp), "%s/mcp/mcp.json", junie_target); snprintf(outside_junie_agent, sizeof(outside_junie_agent), "%s/agents/codebase-memory.md", junie_target); struct stat state; bool followed = stat(outside_qoder_settings, &state) == 0 || - stat(outside_qoder_skill, &state) == 0 || stat(outside_junie_mcp, &state) == 0 || - stat(outside_junie_agent, &state) == 0; + stat(outside_qoder_skill, &state) == 0 || + stat(outside_junie_mcp, &state) == 0 || stat(outside_junie_agent, &state) == 0; restore_test_env("HOME", saved_home); restore_test_env("PATH", saved_path); @@ -7207,8 +7467,8 @@ TEST(cli_codex_preflight_reports_heading_and_reason) { redirected = dup2(fileno(capture), STDOUT_FILENO) >= 0 && dup2(fileno(capture), STDERR_FILENO) >= 0; if (redirected) { - install_rc = - cbm_install_agent_hooks_for_testing(tmpdir, "/opt/codebase-memory-mcp", false, false); + install_rc = cbm_install_agent_hooks_for_testing(tmpdir, "/opt/codebase-memory-mcp", + false, false); } fflush(NULL); (void)dup2(saved_stdout, STDOUT_FILENO); @@ -7287,10 +7547,10 @@ TEST(cli_hook_augment_context_tracks_search_json_shape) { ASSERT_NOT_NULL(srv); /* The exact request ha_build_args produces: format:"json". */ - char *envelope = - cbm_test_operation_execute(srv, "search_graph", - "{\"project\":\"hookproj\",\"name_pattern\":\".*someIndexedSymbol.*\"," - "\"limit\":5,\"format\":\"json\"}"); + char *envelope = cbm_test_operation_execute( + srv, "search_graph", + "{\"project\":\"hookproj\",\"name_pattern\":\".*someIndexedSymbol.*\"," + "\"limit\":5,\"format\":\"json\"}"); ASSERT_NOT_NULL(envelope); bool is_error = true; @@ -7348,8 +7608,8 @@ TEST(cli_hook_augment_bash_pretooluse_reaches_augmenter) { cbm_setenv("CBM_CACHE_DIR", cache, 1); char input[1400]; snprintf(input, sizeof(input), - "{\"hook_event_name\":\"PreToolUse\",\"tool_name\":\"Bash\"," - "\"cwd\":\"%s\",\"tool_input\":{" + "{\"hook_event_name\":\"PreToolUse\",\"tool_name\":\"Bash\"," + "\"cwd\":\"%s\",\"tool_input\":{" "\"command\":\"rg -n someIndexedSymbol .\"}}", repo); char *output = cbm_hook_augment_process(NULL, input); @@ -7939,11 +8199,11 @@ TEST(cli_upgrade_migrates_released_claude_hook_scripts) { char *subagent = read_test_file_alloc(subagent_path); char *settings = read_test_file_alloc(settings_path); bool preserved = rc == 0 && gate && strcmp(gate, legacy_gate) == 0 && session && - strcmp(session, legacy_session) == 0 && subagent && - strcmp(subagent, legacy_subagent) == 0 && - (!settings || (!strstr(settings, "cbm-code-discovery-gate") && - !strstr(settings, "cbm-session-reminder") && - !strstr(settings, "cbm-subagent-reminder"))); + strcmp(session, legacy_session) == 0 && subagent && + strcmp(subagent, legacy_subagent) == 0 && + (!settings || (!strstr(settings, "cbm-code-discovery-gate") && + !strstr(settings, "cbm-session-reminder") && + !strstr(settings, "cbm-subagent-reminder"))); free(gate); free(session); free(subagent); @@ -8423,30 +8683,18 @@ TEST(cli_detect_agents_none_found) { * Group B: MCP Config Upsert — Codex TOML * ═══════════════════════════════════════════════════════════════════ */ - - - - - /* ═══════════════════════════════════════════════════════════════════ * Group B: MCP Config Upsert — Zed (corrected format) * ═══════════════════════════════════════════════════════════════════ */ - - /* ═══════════════════════════════════════════════════════════════════ * Group B: MCP Config Upsert — OpenCode * ═══════════════════════════════════════════════════════════════════ */ - - - /* ═══════════════════════════════════════════════════════════════════ * Group B: MCP Config Upsert — Antigravity * ═══════════════════════════════════════════════════════════════════ */ - - /* ═══════════════════════════════════════════════════════════════════ * Group C: Instructions File Upsert * ═══════════════════════════════════════════════════════════════════ */ @@ -8738,10 +8986,11 @@ TEST(cli_hook_gate_script_no_predictable_tmp_issue384) { char script_path[512]; #ifdef _WIN32 - snprintf(script_path, sizeof(script_path), "%s/.claude/hooks/codebase-memory-cli-discovery-gate.cmd", - tmpdir); + snprintf(script_path, sizeof(script_path), + "%s/.claude/hooks/codebase-memory-cli-discovery-gate.cmd", tmpdir); #else - snprintf(script_path, sizeof(script_path), "%s/.claude/hooks/codebase-memory-cli-discovery-gate", tmpdir); + snprintf(script_path, sizeof(script_path), + "%s/.claude/hooks/codebase-memory-cli-discovery-gate", tmpdir); #endif const char *data = read_test_file(script_path); ASSERT_NOT_NULL(data); @@ -8789,7 +9038,8 @@ TEST(cli_hook_scripts_platform_shape_issue929) { char script_path[512]; #ifdef _WIN32 - snprintf(script_path, sizeof(script_path), "%s/codebase-memory-cli-discovery-gate.cmd", hooks_dir); + snprintf(script_path, sizeof(script_path), "%s/codebase-memory-cli-discovery-gate.cmd", + hooks_dir); const char *data = read_test_file(script_path); ASSERT_NOT_NULL(data); ASSERT(strncmp(data, "@echo off", 9) == 0); /* cmd, not bash */ @@ -8869,16 +9119,22 @@ TEST(cli_windows_claude_lifecycle_migrates_only_exact_owned_legacy_state) { char subagent_previous[1024] = {0}; char subagent_released[1024] = {0}; bool commands_ready = - cbm_resolve_claude_hook_command_for_testing( - "codebase-memory-cli-session-reminder.cmd", true, session_current, sizeof(session_current)) == 0 && - cbm_resolve_claude_hook_command_for_testing("codebase-memory-cli-session-reminder", false, session_previous, + cbm_resolve_claude_hook_command_for_testing("codebase-memory-cli-session-reminder.cmd", + true, session_current, + sizeof(session_current)) == 0 && + cbm_resolve_claude_hook_command_for_testing("codebase-memory-cli-session-reminder", false, + session_previous, sizeof(session_previous)) == 0 && - cbm_resolve_claude_hook_command_for_testing( - "codebase-memory-cli-subagent-reminder.cmd", true, subagent_current, sizeof(subagent_current)) == 0 && - cbm_resolve_claude_hook_command_for_testing( - "codebase-memory-cli-subagent-reminder", false, subagent_previous, sizeof(subagent_previous)) == 0; - snprintf(session_released, sizeof(session_released), "%s/codebase-memory-cli-session-reminder", hooks_dir); - snprintf(subagent_released, sizeof(subagent_released), "%s/codebase-memory-cli-subagent-reminder", hooks_dir); + cbm_resolve_claude_hook_command_for_testing("codebase-memory-cli-subagent-reminder.cmd", + true, subagent_current, + sizeof(subagent_current)) == 0 && + cbm_resolve_claude_hook_command_for_testing("codebase-memory-cli-subagent-reminder", false, + subagent_previous, + sizeof(subagent_previous)) == 0; + snprintf(session_released, sizeof(session_released), "%s/codebase-memory-cli-session-reminder", + hooks_dir); + snprintf(subagent_released, sizeof(subagent_released), + "%s/codebase-memory-cli-subagent-reminder", hooks_dir); const char *foreign_command = "cmd.exe /d /s /c user-owned-hook.cmd"; yyjson_mut_doc *initial_doc = yyjson_mut_doc_new(NULL); @@ -8908,7 +9164,8 @@ TEST(cli_windows_claude_lifecycle_migrates_only_exact_owned_legacy_state) { free(initial_json); yyjson_mut_doc_free(initial_doc); - int install_rc = seeded ? cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false) : -1; + int install_rc = + seeded ? cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false) : -1; char *installed_settings = read_test_file_alloc(settings_path); yyjson_doc *installed_doc = installed_settings ? yyjson_read(installed_settings, strlen(installed_settings), 0) : NULL; @@ -8979,7 +9236,7 @@ TEST(cli_windows_claude_hook_scripts_migrate_and_uninstall_all_owned_shapes) { snprintf(config_dir, sizeof(config_dir), "%s/.claude", tmpdir); snprintf(hooks_dir, sizeof(hooks_dir), "%s/hooks", config_dir); snprintf(appdata, sizeof(appdata), "%s/AppData/Roaming", tmpdir); - snprintf(binary_path, sizeof(binary_path), "%s/.local/bin/codebase-memory-mcp.exe", tmpdir); + snprintf(binary_path, sizeof(binary_path), "%s/.local/bin/codebase-memory-cli.exe", tmpdir); test_mkdirp(hooks_dir); const char *const env_names[] = {"HOME", "PATH", "CLAUDE_CONFIG_DIR", @@ -9022,7 +9279,9 @@ TEST(cli_windows_claude_hook_scripts_migrate_and_uninstall_all_owned_shapes) { } int current_upgrade_rc = - current_scripts_ready ? cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false) : -1; + current_scripts_ready + ? cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false) + : -1; bool current_legacy_removed = current_upgrade_rc == 0; for (size_t i = 0U; i < sizeof(legacy_names) / sizeof(legacy_names[0]); i++) { char current_path[768]; @@ -9049,7 +9308,8 @@ TEST(cli_windows_claude_hook_scripts_migrate_and_uninstall_all_owned_shapes) { } int released_upgrade_rc = - released_ready ? cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false) : -1; + released_ready ? cbm_install_agent_hooks_for_testing(tmpdir, binary_path, false, false) + : -1; bool released_legacy_removed = released_upgrade_rc == 0; for (size_t i = 0U; i < sizeof(legacy_names) / sizeof(legacy_names[0]); i++) { char legacy_path[768]; @@ -9301,9 +9561,10 @@ TEST(cli_upsert_claude_hook_replace) { char settingspath[512]; snprintf(settingspath, sizeof(settingspath), "%s/settings.json", tmpdir); /* Pre-existing CMM hook with an OLD matcher (pre-#963) + old message */ - write_test_file(settingspath, "{\"hooks\":{\"PreToolUse\":[{\"matcher\":\"Grep|Glob\"," - "\"hooks\":[{\"type\":\"command\"," - "\"command\":\"~/.claude/hooks/codebase-memory-cli-discovery-gate\"}]}]}}"); + write_test_file(settingspath, + "{\"hooks\":{\"PreToolUse\":[{\"matcher\":\"Grep|Glob\"," + "\"hooks\":[{\"type\":\"command\"," + "\"command\":\"~/.claude/hooks/codebase-memory-cli-discovery-gate\"}]}]}}"); int rc = cbm_upsert_claude_hooks(settingspath); ASSERT_EQ(rc, 0); @@ -10623,7 +10884,6 @@ TEST(cli_update_only_names_an_installer_that_exists_issue1632) { PASS(); } - TEST(cli_cp78_install_and_hook_plans_are_disjoint) { char tmpdir[256]; snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-cp78-plan-XXXXXX"); @@ -10638,15 +10898,14 @@ TEST(cli_cp78_install_and_hook_plans_are_disjoint) { char *assets = cbm_build_install_plan_json(tmpdir, "/opt/codebase-memory-cli"); char *hooks = cbm_build_hook_install_plan_json(tmpdir, "/opt/codebase-memory-cli"); - bool ok = assets && hooks && strstr(assets, "\"surface\": \"assets\"") && - strstr(hooks, "\"surface\": \"hooks\"") && - strstr(assets, "codebase-memory-cli/SKILL.md") && - !strstr(assets, "codebase-memory-cli-discovery-gate") && - !strstr(assets, "codebase-memory-cli-session-reminder") && - strstr(hooks, "codebase-memory-cli-discovery-gate") && - strstr(hooks, "codebase-memory-cli-session-reminder") && - !strstr(hooks, "codebase-memory-cli/SKILL.md") && - !strstr(hooks, "codebase-memory-mcp"); + bool ok = + assets && hooks && strstr(assets, "\"surface\": \"assets\"") && + strstr(hooks, "\"surface\": \"hooks\"") && strstr(assets, "codebase-memory-cli/SKILL.md") && + !strstr(assets, "codebase-memory-cli-discovery-gate") && + !strstr(assets, "codebase-memory-cli-session-reminder") && + strstr(hooks, "codebase-memory-cli-discovery-gate") && + strstr(hooks, "codebase-memory-cli-session-reminder") && + !strstr(hooks, "codebase-memory-cli/SKILL.md") && !strstr(hooks, "codebase-memory-mcp"); free(assets); free(hooks); diff --git a/tests/test_daemon_runtime.c b/tests/test_daemon_runtime.c index 3859a52c14..4771d58a96 100644 --- a/tests/test_daemon_runtime.c +++ b/tests/test_daemon_runtime.c @@ -1026,9 +1026,9 @@ static void *runtime_application_client_request_thread(void *opaque) { static void *runtime_real_application_detect_changes_thread(void *opaque) { runtime_real_application_call_t *call = opaque; uint32_t timeout_ms = call->timeout_ms ? call->timeout_ms : RUNTIME_TEST_TIMEOUT_MS; - call->status = - cbm_daemon_application_client_operation(call->client, "detect_changes", call->arguments, - &call->response, &call->response_length, timeout_ms); + call->status = cbm_daemon_application_client_operation(call->client, "detect_changes", + call->arguments, &call->response, + &call->response_length, timeout_ms); atomic_store_explicit(&call->completed, true, memory_order_release); return NULL; } @@ -1037,9 +1037,8 @@ static bool runtime_real_application_ingest_probe(cbm_daemon_runtime_client_t *c uint32_t timeout_ms) { uint8_t *response = NULL; uint32_t response_length = 0; - cbm_daemon_runtime_application_status_t status = - cbm_daemon_application_client_operation(client, "ingest_traces", "{\"traces\":[]}", &response, - &response_length, timeout_ms); + cbm_daemon_runtime_application_status_t status = cbm_daemon_application_client_operation( + client, "ingest_traces", "{\"traces\":[]}", &response, &response_length, timeout_ms); bool usable = status == CBM_DAEMON_RUNTIME_APPLICATION_OK && response && response_length > 0 && strstr((const char *)response, "traces_received"); free(response); @@ -1365,6 +1364,8 @@ static bool runtime_test_fixture_start_failed(const char *tag, const char *stage } static bool runtime_test_fixture_permanent = false; +/* Nonzero overrides the server request (hello) deadline for one fixture. */ +static uint64_t runtime_test_fixture_request_timeout_ms = 0; static bool runtime_test_fixture_start_configured( runtime_test_fixture_t *fixture, const char *tag, const cbm_daemon_build_identity_t *identity, @@ -1412,7 +1413,9 @@ static bool runtime_test_fixture_start_configured( .conflict_log_cap_bytes = 64U * 1024U, .max_clients = max_clients, .lease_timeout_ms = lease_timeout_ms, - .request_timeout_ms = RUNTIME_TEST_TIMEOUT_MS, + .request_timeout_ms = runtime_test_fixture_request_timeout_ms + ? runtime_test_fixture_request_timeout_ms + : RUNTIME_TEST_TIMEOUT_MS, .shutdown_timeout_ms = RUNTIME_TEST_TIMEOUT_MS, /* Default false: every teardown-latency fixture depends on prompt * last-client-exit. Only the permanent-lifecycle tests flip this. */ @@ -2882,7 +2885,12 @@ TEST(daemon_runtime_connection_cap_covers_slow_hello_and_stopping_is_terminal) { cbm_daemon_build_identity_t identity = runtime_test_identity("2.4.0", runtime_test_self_build()); runtime_test_fixture_t fixture; + /* slow_hello must keep its slot until the test closes it. With the default + * 2 s hello deadline, two full client handshakes on a slow sanitized runner + * can outlast it, freeing the slot before the overflow connect. */ + runtime_test_fixture_request_timeout_ms = 30000; bool started = runtime_test_fixture_start_limited(&fixture, "connection-cap", &identity, 2); + runtime_test_fixture_request_timeout_ms = 0; cbm_daemon_ipc_connection_t *slow_hello = NULL; cbm_daemon_runtime_client_t *accepted = NULL; cbm_daemon_runtime_client_t *overflow = NULL; @@ -3887,20 +3895,20 @@ TEST(daemon_runtime_disconnect_cancels_blocked_non_index_child_and_preserves_oth first ? cbm_daemon_runtime_client_connect(fixture.endpoint, &identity, RUNTIME_TEST_TIMEOUT_MS, &second_result) : NULL; - bool contexts_set = - first && second && - cbm_daemon_application_client_set_context(first, root, root, NULL, NULL, - RUNTIME_TEST_TIMEOUT_MS) == - CBM_DAEMON_RUNTIME_APPLICATION_OK && - cbm_daemon_application_client_set_context(second, root, root, NULL, NULL, - RUNTIME_TEST_TIMEOUT_MS) == - CBM_DAEMON_RUNTIME_APPLICATION_OK; + bool contexts_set = first && second && + cbm_daemon_application_client_set_context(first, root, root, NULL, NULL, + RUNTIME_TEST_TIMEOUT_MS) == + CBM_DAEMON_RUNTIME_APPLICATION_OK && + cbm_daemon_application_client_set_context(second, root, root, NULL, NULL, + RUNTIME_TEST_TIMEOUT_MS) == + CBM_DAEMON_RUNTIME_APPLICATION_OK; #ifdef CBM_SANITIZED_BUILD const uint32_t second_session_probe_timeout_ms = 5000; #else const uint32_t second_session_probe_timeout_ms = RUNTIME_TEST_TIMEOUT_MS; #endif - bool second_ingest_before = contexts_set && runtime_real_application_ingest_probe(second, second_session_probe_timeout_ms); + bool second_ingest_before = contexts_set && runtime_real_application_ingest_probe( + second, second_session_probe_timeout_ms); bool second_heartbeat_before = second_ingest_before && cbm_daemon_runtime_client_heartbeat(second, second_session_probe_timeout_ms); @@ -3977,9 +3985,10 @@ TEST(daemon_runtime_disconnect_cancels_blocked_non_index_child_and_preserves_oth first = NULL; } - bool second_usable_after = second && child_cleanup_complete && - runtime_real_application_ingest_probe(second, RUNTIME_TEST_TIMEOUT_MS) && - cbm_daemon_runtime_client_heartbeat(second, RUNTIME_TEST_TIMEOUT_MS); + bool second_usable_after = + second && child_cleanup_complete && + runtime_real_application_ingest_probe(second, RUNTIME_TEST_TIMEOUT_MS) && + cbm_daemon_runtime_client_heartbeat(second, RUNTIME_TEST_TIMEOUT_MS); bool second_closed = false; if (second) { second_closed = cbm_daemon_runtime_client_close(second, RUNTIME_TEST_TIMEOUT_MS);