Feature: ConfigService coverage round 3 — path accessors, write_scoped_config, set_value, and resolve local Exercises uncovered lines in config_service.py: - Line 1195: project_root property - Line 1201: project_config_path returns None - Line 1208: local_config_path returns None - Lines 1284-1285, 1289: write_scoped_config ValueError for missing project root - Lines 1297-1298: write_scoped_config reads existing target file - Lines 1336-1337: set_value redacts sensitive keys - Lines 1351-1352: set_value catches event_bus emit failure - Lines 1507-1508, 1510-1511: resolve verbose with local config winning Background: Given cfscov3 a fresh temporary directory # -- project_root property (line 1195) -- Scenario: project_root returns the configured project root path Given cfscov3 a ConfigService with an explicit project root Then cfscov3 project_root should return the configured path Scenario: project_root returns None when no project root is set Given cfscov3 a ConfigService with no project root Then cfscov3 project_root should return None # -- project_config_path returns None (line 1201) -- Scenario: project_config_path returns None when project root is absent Given cfscov3 a ConfigService with no project root Then cfscov3 project_config_path should return None # -- local_config_path returns None (line 1208) -- Scenario: local_config_path returns None when project root is absent Given cfscov3 a ConfigService with no project root Then cfscov3 local_config_path should return None # -- write_scoped_config raises ValueError (lines 1284-1285, 1289) -- Scenario: write_scoped_config raises ValueError for PROJECT scope without project root Given cfscov3 a ConfigService with no project root When cfscov3 I call write_scoped_config with PROJECT scope Then cfscov3 a ValueError mentioning "no project root" should be stored Scenario: write_scoped_config raises ValueError for LOCAL scope without project root Given cfscov3 a ConfigService with no project root When cfscov3 I call write_scoped_config with LOCAL scope Then cfscov3 a ValueError mentioning "no project root" should be stored # -- write_scoped_config reads existing target (lines 1297-1298) -- Scenario: write_scoped_config merges into existing project config.toml Given cfscov3 a ConfigService with a project root containing an existing config.toml When cfscov3 I call write_scoped_config with PROJECT scope adding a new key Then cfscov3 the project config.toml should contain both the old and new keys Scenario: write_scoped_config merges into existing local config.local.toml Given cfscov3 a ConfigService with a project root containing an existing config.local.toml When cfscov3 I call write_scoped_config with LOCAL scope adding a new key Then cfscov3 the local config.local.toml should contain both the old and new keys # -- set_value redacts sensitive keys (lines 1336-1337) -- Scenario: set_value redacts old and new values for a sensitive key Given cfscov3 a ConfigService with a mock event bus When cfscov3 I call set_value with sensitive key "provider.openai.api-key" and value "sk-secret-123" Then cfscov3 the event bus should have received redacted values # -- set_value catches event_bus emit failure (lines 1351-1352) -- Scenario: set_value swallows exception when event_bus.emit raises Given cfscov3 a ConfigService with a failing event bus When cfscov3 I call set_value with key "core.log.level" and value "DEBUG" Then cfscov3 no error should have been raised # -- resolve verbose with local config winning (lines 1507-1508, 1510-1511) -- Scenario: resolve in verbose mode returns local config value with chain entry Given cfscov3 a ConfigService with a local config containing "core.log.level" set to "TRACE" When cfscov3 I resolve "core.log.level" with verbose True Then cfscov3 the resolved value should be "TRACE" from LOCAL source And cfscov3 the chain should contain a local entry with value "TRACE" and a path