Skip to content

Commit 2292051

Browse files
aksOpsclaude
andauthored
feat(config): unified config — Phase B (#51)
* feat(config): scaffold unified config record tree + ConfigLayer enum * fix(config): tighten empty() factories to all-null (ProjectConfig root) + DetectorOverride.empty() for symmetry * feat(config): add ConfigDefaults.builtIn() matching historical application.yml + CLI defaults * feat(config): add SnakeYAML-backed loader for codeiq.yml with file-anchored errors * feat(config): add CODEIQ_<SECTION>_<KEY> env var overlay * feat(config): layered merger with per-leaf provenance tracking * feat(config): add ConfigValidator with explicit, actionable field errors * feat(config): add ConfigResolver façade (defaults + file + env + CLI, with provenance) * feat(cli): add code-iq config validate * fix(cli): address review findings for config validate subcommand * feat(cli): add code-iq config explain with per-field provenance * fix(cli): address review findings for config explain subcommand * chore(cli): drop deprecated org.springframework.lang.Nullable from ConfigExplainSubcommand Spring 7.0 deprecated this annotation. Fields are already private with javadoc comments that document the nullable contract, so the annotation is pure ceremony here. No behavior change; 11 config CLI tests still green. * feat(config): UnifiedConfigAdapter — bridge unified tree to legacy CodeIqConfig API * fix(config): close unified schema gaps blocking legacy adapter parity * chore(test): drop unused imports in UnifiedConfigAdapterTest Neo4jConfig and Map imports were left over from an earlier draft. Build green; 6/6 adapter tests still pass. * feat(config): wire CodeIqUnifiedConfig as the Spring source of truth; legacy CodeIqConfig adapted from it * fix(config): close Task 11 review gaps — clean application.yml + guard startup * feat(config): deprecation shim — load .osscodeiq.yml with WARN, prefer codeiq.yml * fix(config): translate legacy .osscodeiq.yml flat keys into unified overlay * refactor(config): normalize codeiq.yml keys to snake_case; camelCase accepted as deprecated alias Canonical keys are now snake_case across the board (matches CODEIQ_* env var casing and the legacy .osscodeiq.yml schema users are migrating from). camelCase spellings continue to load for one release as deprecated aliases; each alias produces at most one WARN per file. When both spellings appear for the same leaf, snake_case wins and a conflict WARN fires naming the camelCase form as deprecated. Full suite: 3275 tests pass, 0 failures (up from 3271 baseline). * docs(config): document codeiq.yml, resolution order, and migration from .osscodeiq.yml * docs(phase-b): exit-gate verification — Phase B complete Records the Task 14 verification pass against spec §3.6 (Pillar 1 — Unified Config). All four acceptance criteria met; 3275/0/31 tests green; no release blockers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2f398fb commit 2292051

52 files changed

Lines changed: 3098 additions & 103 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CLAUDE.md

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -367,18 +367,46 @@ mvn dependency-check:check
367367

368368
## Configuration
369369

370-
### Application properties (`application.yml`)
371-
- `codeiq.root-path` -- codebase root (default: `.`)
372-
- `codeiq.cache-dir` -- cache directory name (default: `.code-intelligence`)
373-
- `codeiq.graph.path` -- Neo4j graph path (default: `.osscodeiq/graph.db`)
374-
- `codeiq.max-radius` -- max ego graph radius (default: 10)
375-
- `codeiq.max-depth` -- max impact trace depth (default: 10)
376-
- `codeiq.batch-size` -- files per H2 flush batch (default: 500)
377-
- `codeiq.neo4j.enabled` -- Neo4j conditional toggle (default: `true`, overridden to `false` in `indexing` profile)
378-
- `spring.ai.mcp.server.protocol` -- MCP protocol (STREAMABLE)
379-
380-
### Project-level overrides (`.osscodeiq.yml`)
381-
Placed in the codebase root, loaded by `ProjectConfigLoader` before analysis.
370+
Single source of truth: **`codeiq.yml`** at the repo root. See
371+
`docs/codeiq.yml.example` for the full schema (snake_case throughout;
372+
camelCase accepted as a deprecated alias for one release). Resolution order
373+
(last wins):
374+
375+
1. Built-in defaults (`ConfigDefaults.builtIn()`)
376+
2. `~/.codeiq/config.yml` (user-global)
377+
3. `./codeiq.yml` (project)
378+
4. `CODEIQ_<SECTION>_<KEY>` env vars (e.g. `CODEIQ_SERVING_PORT=9090`)
379+
5. CLI flags on `code-iq <command>`
380+
381+
Validate and introspect with:
382+
383+
```bash
384+
code-iq config validate
385+
code-iq config explain
386+
```
387+
388+
### Spring-owned keys (stay in `application.yml`)
389+
390+
A small set of keys still lives in `src/main/resources/application.yml`
391+
because they drive Spring's `@ConditionalOnProperty` / `@Value` wiring and
392+
have not been migrated into `codeiq.yml`:
393+
394+
- `codeiq.neo4j.enabled` -- profile-conditional toggle (`false` in the
395+
`indexing` profile, `true` in `serving`).
396+
- `codeiq.neo4j.bolt.port` -- embedded Neo4j Bolt listener port.
397+
- `codeiq.cors.allowed-origin-patterns` -- CORS allow-list for the REST API.
398+
- `codeiq.ui.enabled` -- toggles the React SPA static resource handler.
399+
400+
`UnifiedConfigBeans` bridges the unified config to the legacy `CodeIqConfig`
401+
bean for code paths that haven't been ported yet.
402+
403+
### `.osscodeiq.yml` deprecation
404+
405+
`.osscodeiq.yml` is deprecated. `ProjectConfigLoader` still loads it for one
406+
release, translates its legacy flat keys into the unified nested shape, and
407+
logs a one-time WARN per canonical path. Rename to `codeiq.yml` and migrate
408+
flat keys into the `project:` / `indexing:` / `serving:` / `mcp:` /
409+
`observability:` / `detectors:` sections.
382410

383411
## Gotchas & Lessons Learned
384412

README.md

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -157,31 +157,100 @@ java -jar code-iq-*-cli.jar serve /shared
157157

158158
## Configuration
159159

160-
Create `.osscodeiq.yml` in your repo root to customize the pipeline:
160+
code-iq is configured by a single YAML file at the repo root: **`codeiq.yml`**.
161+
Every field is optional; omitted fields fall back to the in-code defaults
162+
(`ConfigDefaults.builtIn()`). See
163+
[`docs/codeiq.yml.example`](docs/codeiq.yml.example) for the full reference
164+
with inline documentation. All keys are **snake_case**; camelCase spellings
165+
are accepted as deprecated aliases for one release and log a WARN on load.
166+
167+
### Resolution order (last wins)
168+
169+
1. Built-in defaults
170+
2. `~/.codeiq/config.yml` (user-global)
171+
3. `./codeiq.yml` (project)
172+
4. Environment variables: `CODEIQ_<SECTION>_<KEY>` (e.g. `CODEIQ_SERVING_PORT=9090`,
173+
`CODEIQ_MCP_AUTH_MODE=bearer`, `CODEIQ_INDEXING_BATCH_SIZE=1000`). Nested
174+
keys are flattened with underscores; values parse as YAML scalars.
175+
5. CLI flags on `code-iq <command>`
176+
177+
### Commands
178+
179+
```bash
180+
code-iq config validate # Validate ./codeiq.yml, exit 1 on error
181+
code-iq config validate -p custom.yml
182+
code-iq config explain # Print each effective value + its source layer
183+
```
184+
185+
### Minimal example
161186

162187
```yaml
188+
project:
189+
name: my-service
190+
root: .
191+
192+
indexing:
193+
exclude: ['**/node_modules/**', '**/build/**', '**/dist/**']
194+
cache_dir: .code-iq/cache
195+
batch_size: 500
196+
197+
serving:
198+
port: 8080
199+
bind_address: 0.0.0.0
200+
201+
mcp:
202+
enabled: true
203+
transport: http
204+
```
205+
206+
### Spring-owned keys (stay in `application.yml`)
207+
208+
A handful of keys drive Spring's `@ConditionalOnProperty` / `@Value` wiring
209+
and have not been migrated into `codeiq.yml`. Keep them in
210+
`src/main/resources/application.yml`:
211+
212+
- `codeiq.neo4j.enabled` -- profile-conditional Neo4j toggle (`false` under
213+
the `indexing` profile, `true` under `serving`).
214+
- `codeiq.neo4j.bolt.port` -- embedded Neo4j Bolt listener port.
215+
- `codeiq.cors.allowed-origin-patterns` -- CORS allow-list for the REST API.
216+
- `codeiq.ui.enabled` -- toggles the React SPA static resource handler.
217+
218+
Everything else belongs in `codeiq.yml`. `UnifiedConfigBeans` bridges the
219+
two worlds for values that exist in both.
220+
221+
### Migration from `.osscodeiq.yml`
222+
223+
`.osscodeiq.yml` is deprecated. code-iq still loads it for one release via
224+
`ProjectConfigLoader`, translates its legacy flat keys into the unified
225+
shape, and logs a one-time WARN per path. Rename the file to `codeiq.yml`
226+
and restructure flat keys into the nested sections.
227+
228+
**Before** (`.osscodeiq.yml`, legacy flat schema):
229+
230+
```yaml
231+
languages: [java, typescript, yaml]
232+
exclude:
233+
- '**/node_modules/**'
234+
- '**/build/**'
163235
pipeline:
164236
parallelism: 4
165237
batch-size: 500
238+
batch_size: 500
239+
```
166240

167-
languages:
168-
- java
169-
- typescript
170-
- yaml
171-
172-
detectors:
173-
categories:
174-
- endpoints
175-
- entities
176-
- auth
177-
- config
241+
**After** (`codeiq.yml`, unified snake_case schema):
178242

179-
exclude:
180-
- "**/node_modules/**"
181-
- "**/build/**"
243+
```yaml
244+
indexing:
245+
languages: [java, typescript, yaml]
246+
exclude:
247+
- '**/node_modules/**'
248+
- '**/build/**'
249+
parallelism: 4
250+
batch_size: 500
182251
```
183252

184-
Or auto-generate a config: `code-iq plugins suggest /path/to/repo`
253+
See `docs/codeiq.yml.example` for the full schema.
185254

186255
## Graph Model
187256

docs/codeiq.yml.example

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# docs/codeiq.yml.example
2+
#
3+
# Authoritative reference for `codeiq.yml` (Phase B unified config).
4+
#
5+
# - Place this file as `codeiq.yml` at the repo root.
6+
# - Every field is optional; omitted fields fall back to the built-in defaults
7+
# (see `ConfigDefaults.builtIn()`).
8+
# - All keys are snake_case. camelCase spellings (e.g. `cacheDir`, `batchSize`,
9+
# `bindAddress`, `pageCacheMb`, `perToolTimeoutMs`, `logFormat`) are accepted
10+
# as deprecated aliases for one release and emit a WARN on load. Do not use
11+
# camelCase in new config.
12+
# - Run `code-iq config validate` to type-check your file, and
13+
# `code-iq config explain` to print every effective value and the layer it
14+
# was resolved from.
15+
16+
# ---------------------------------------------------------------------------
17+
# project
18+
# ---------------------------------------------------------------------------
19+
project:
20+
name: my-service # human-readable identifier; defaults to null
21+
root: . # codebase root, relative to this file (default: ".")
22+
service_name: my-service # override for the emitted SERVICE node name
23+
modules: [] # optional list of sub-modules (Phase C). Example:
24+
# - path: services/api
25+
# type: maven # maven | gradle | npm | pnpm | pip | go | cargo | ...
26+
# name: api
27+
# kind: service # service | library | tool | infra
28+
29+
# ---------------------------------------------------------------------------
30+
# indexing
31+
# ---------------------------------------------------------------------------
32+
indexing:
33+
languages: [] # allow-list; empty = detect all supported languages
34+
include: [] # glob allow-list; empty = include everything discovered
35+
exclude: # glob deny-list; applied after `include`
36+
- '**/node_modules/**'
37+
- '**/build/**'
38+
- '**/dist/**'
39+
- '**/generated/**'
40+
incremental: true # reuse H2 cache when file hashes match
41+
cache_dir: .code-iq/cache # H2 analysis cache directory
42+
parallelism: auto # "auto" or a positive integer
43+
batch_size: 500 # files per H2 flush batch (default: 500)
44+
max_depth: 10 # max impact-trace depth
45+
max_radius: 10 # max ego-graph radius
46+
max_files: null # null = no cap; positive int to bound discovery
47+
max_snippet_lines: null # null = use CodeIqConfig default
48+
49+
# ---------------------------------------------------------------------------
50+
# serving
51+
# ---------------------------------------------------------------------------
52+
serving:
53+
port: 8080 # HTTP port for REST + MCP + UI
54+
bind_address: 0.0.0.0 # interface to bind; 127.0.0.1 for localhost-only
55+
read_only: false # must be false in non-prod; CI gate enforces this
56+
neo4j:
57+
dir: .code-iq/graph/graph.db # embedded Neo4j data directory
58+
page_cache_mb: 256 # Neo4j page cache (MB)
59+
heap_initial_mb: 256 # JVM -Xms for Neo4j (MB)
60+
heap_max_mb: 1024 # JVM -Xmx for Neo4j (MB)
61+
62+
# ---------------------------------------------------------------------------
63+
# mcp (Model Context Protocol server)
64+
# ---------------------------------------------------------------------------
65+
mcp:
66+
enabled: true # expose MCP tools via the serving layer
67+
transport: http # http | stdio
68+
base_path: /mcp # HTTP path prefix when transport=http
69+
auth:
70+
mode: none # none (default) | bearer | mtls
71+
token_env: CODEIQ_MCP_TOKEN # env var read when mode=bearer
72+
limits:
73+
per_tool_timeout_ms: 15000 # hard cap per tool invocation
74+
max_results: 500 # cap on result rows returned per tool
75+
max_payload_bytes: 2000000 # cap on single response body (bytes)
76+
rate_per_minute: 300 # per-client rate limit
77+
tools:
78+
enabled: ['*'] # allow-list of tool names; '*' = all
79+
disabled: [] # deny-list wins over `enabled`
80+
81+
# ---------------------------------------------------------------------------
82+
# observability
83+
# ---------------------------------------------------------------------------
84+
observability:
85+
metrics: true # expose Micrometer/Prometheus metrics
86+
tracing: false # emit OTLP spans (off by default)
87+
log_format: json # json | text
88+
log_level: info # trace | debug | info | warn | error
89+
90+
# ---------------------------------------------------------------------------
91+
# detectors
92+
# ---------------------------------------------------------------------------
93+
detectors:
94+
profiles: [default] # named detector bundles to activate
95+
overrides: # per-detector feature flags, keyed by SimpleClassName
96+
SpringRestDetector: { enabled: true }
97+
QuarkusRestDetector: { enabled: true }
98+
# MicronautRestDetector: { enabled: false }
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Phase B Exit-Gate Verification — 2026-04-22
2+
3+
**Branch:** `phase-b/unified-config`
4+
**Head commit:** `5356630 docs(config): document codeiq.yml, resolution order, and migration from .osscodeiq.yml`
5+
**Final test count:** **3275 pass / 0 fail / 0 errors / 31 skipped** (`mvn -B test`, BUILD SUCCESS)
6+
7+
## Gate status
8+
9+
| # | Gate | Status | Evidence |
10+
|---|---|---|---|
11+
| 1 | Single source of truth — `codeiq.yml` is authoritative; `application.yml` no longer duplicates migrated keys | PASS | `src/main/resources/application.yml` contains zero instances of `codeiq.root-path`, `codeiq.cache-dir`, `codeiq.graph.path`, `codeiq.max-depth`, `codeiq.max-radius`, `codeiq.batch-size`. Remaining `codeiq.*` keys are exactly: `codeiq.ui.enabled` (L33-35), `codeiq.neo4j.enabled` (L56-58 indexing profile, L94-96 serving profile). `codeiq.neo4j.bolt.port` and `codeiq.cors.allowed-origin-patterns` consume `@Value` defaults with no YAML override (documented at L25-32). |
12+
| 2 | Layered resolution — defaults → user-global → project → env → CLI | PASS | `src/main/java/io/github/randomcodespace/iq/config/unified/ConfigLayer.java` enumerates `BUILT_IN, USER_GLOBAL, PROJECT, ENV, CLI`. `ConfigResolver.resolve()` appends layers in that exact order into `ConfigMerger.Input` list; "last wins" semantics are documented in the class Javadoc. |
13+
| 3 | Provenance — `config explain` prints per-field source | PASS | `ConfigExplainSubcommand` row format `FIELD(40) LAYER(12) SOURCE(40) VALUE`. `ConfigExplainSubcommandTest.printsProvenanceForEachLeaf` asserts stdout contains `serving.port`, value `9000`, layer `PROJECT`, plus `ENV` layer for env-overridden `mcp.limits.per_tool_timeout_ms=30000`, and at least one `BUILT_IN` leaf. `cliOverlayWinsOverEnv` test asserts CLI > ENV precedence on the explain output. |
14+
| 4 | Validation — `config validate` returns exit 0/1 on valid/invalid | PASS | `ConfigValidateSubcommand` returns `1` on validation errors (sorted by `fieldPath`, written to stderr) or load failure. `ConfigValidateSubcommandTest` covers: `invalidFileReturnsOneAndListsErrorsOnStderr` (port 99999 → exit 1, stderr contains `serving.port`), `missingFileReturnsOneAndPrintsLoadErrorToStderr`, `malformedYamlReturnsOneAndReportsLoadError`, `emptyFileIsValidAndReturnsZero`. |
15+
| 5 | Env var overlay — `CODEIQ_<SECTION>_<KEY>` works across sections | PASS | `EnvVarOverlayTest` covers 6 cases: `readsServingPort`, `readsNestedMcpLimit` (`CODEIQ_MCP_LIMITS_PERTOOLTIMEOUTMS`), `parsesBooleansAndLists` (`CODEIQ_INDEXING_LANGUAGES=java,typescript,python`), `unknownVarIsIgnored`, `nonCodeiqVarsIgnored`, `malformedIntThrowsWithVarName`. |
16+
| 6 | Schema documented — `docs/codeiq.yml.example` exists, snake_case throughout | PASS | File exists with 6 sections matching `CodeIqUnifiedConfig` record: `project`, `indexing`, `serving`, `mcp`, `observability`, `detectors`. Only "camelCase" hits in real content are `SpringRestDetector`/`QuarkusRestDetector` — Java SimpleClassName keys under `detectors.overrides`, which is the documented convention, not config key casing. Header explicitly calls out camelCase as deprecated alias. |
17+
| 7 | `.osscodeiq.yml` deprecation — WARN once per path; legacy flat keys translated | PASS | `ProjectConfigLoader.loadFrom` uses `ConcurrentHashMap.newKeySet()` (`WARNED_PATHS`) at L70; WARN emitted only on first `add(canonical)`. `ProjectConfigLoaderTest` (14 tests) covers: `preferCodeiqYmlWhenBothPresent` (new file wins, no WARN), `fallsBackToOsscodeIqWithWarn`, `fallbackOsscodeiqWithFlatKeysTranslatesToUnifiedOverlay`, `fallbackOsscodeiqWithNewShapeStillWorks`, `mixedLegacyFlatAndNestedKeysPrefersLegacyPath`, `neitherFilePresentReturnsEmptyConfig`. Per-path dedupe test is **not explicitly covered** (see follow-ups). |
18+
| 8 | `CodeIqConfig` API unchanged | PASS | All legacy getters present with original signatures: `getRootPath`/`setRootPath` (L62-66), `getCacheDir` (L70), `getMaxDepth` (L78), `getMaxFiles` (L86), `getMaxRadius` (L94), `getBatchSize` (L102), `getServiceName` (L118), `getGraph` (L126), `getMaxSnippetLines` (L142). Inner `Graph.getPath`/`setPath` (L50-51). 27 source files still reference `CodeIqConfig`. |
19+
| 9 | Test count baseline — 3275+ tests, 0 failures | PASS | `mvn -B test``Tests run: 3275, Failures: 0, Errors: 0, Skipped: 31``BUILD SUCCESS`. |
20+
| 10 | No regressions — `.osscodeiq.yml` still loads for legacy users | PASS | Covered by `ProjectConfigLoaderTest.fallsBackToOsscodeIqWithWarn` and `fallbackOsscodeiqWithNewShapeStillWorks`. SpotBugs / frontend build not re-verified in this gate pass (neither is a §3.6 requirement; see follow-ups for any outstanding Phase-A items). |
21+
22+
## Spec §3.6 acceptance criteria (direct mapping)
23+
24+
| Spec criterion | Plan task | Verified via |
25+
|---|---|---|
26+
| One file controls pipeline end-to-end; no CLI flag for default run | Task 14 gate 1 | `CodeIqUnifiedConfig` + `UnifiedConfigBeans` wire the full tree; all previously-required CLI overrides now read from `codeiq.yml` via `ConfigResolver`. Full pipeline smoke (`java -jar ... index .`) deferred to release candidate (jar not built in this verification pass) — all unit + integration paths pass. |
27+
| `code-iq config explain` prints effective config + source per value | Task 14 gate 2 | `ConfigExplainSubcommand` + passing tests above. |
28+
| Deprecation warning fires when `.osscodeiq.yml` is used | Task 14 gate 3 | `ProjectConfigLoader.loadFrom` L107 `log.warn(...)`; `fallsBackToOsscodeIqWithWarn` test asserts `r.deprecationWarningEmitted() == true`. |
29+
| Invalid config yields a clear, file-anchored error | Task 14 gate 4 | `ConfigValidateSubcommand` sorts `ConfigError.fieldPath()` to stderr with `field.path: message` format; `invalidFileReturnsOneAndListsErrorsOnStderr` asserts `serving.port` appears in stderr for out-of-range port. |
30+
31+
## Docs-vs-implementation sync
32+
33+
- `README.md` §Configuration (L158-218) — documents `codeiq.yml` as single source, resolution order (5 layers, last wins), `config validate` / `config explain` commands, minimal example (snake_case), and the 4 Spring-owned keys. Matches implementation.
34+
- `CLAUDE.md` §Configuration (L368-409) — same structure, including `.osscodeiq.yml` deprecation section pointing to `ProjectConfigLoader`. Matches implementation.
35+
36+
## Release blockers
37+
38+
**None.** Phase B meets all §3.6 acceptance criteria. All code paths exercised by 3275 passing tests.
39+
40+
## Post-release follow-ups
41+
42+
Tracked issues (priority: post-release):
43+
44+
- **#47** — Detector taxonomy refactor (post)
45+
- **#48** — SQL / migration detector (post)
46+
- **#49** — Freeze `CodeIqConfig` setters (post — setter mutability does not affect Phase B's contract; unified config is the write path)
47+
- **#50** — Slice `UnifiedConfigBeansTest` (post)
48+
- **#52** — Retire legacy `ProjectConfigLoader` static methods + migrate Analyzer/CliOutput (post — static methods are marked `@Deprecated since 0.2.0, for removal` in javadoc, and `loadFrom` is the new instance path; they can be removed once Analyzer/CliOutput are migrated in a follow-up, without breaking Phase B's single-source-of-truth gate)
49+
50+
Minor gaps noted (not blockers):
51+
52+
- `ProjectConfigLoaderTest` covers WARN emission via `LoadResult.deprecationWarningEmitted()` but has no explicit test that calling `loadFrom` twice against the same canonical path emits the WARN only once. The logic (`WARNED_PATHS.add(canonical)`) is correct; a unit test asserting dedupe would be a belt-and-braces addition. **Recommend: add as a trivial follow-up, not a release blocker.**
53+
- Frontend build and SpotBugs not re-executed in this verification pass — neither is a §3.6 criterion. Phase A baseline covered them; no Phase B changes touched frontend or triggered new SpotBugs findings.
54+
- End-to-end smoke test with packaged jar (`java -jar .../code-iq-*-cli.jar index .`) was not run because the packaged jar is not a §3.6 artifact — the four acceptance criteria are fully covered by the subcommand unit tests plus the unified-config loader/merger/resolver tests. Recommended as a final pre-tag sanity check when the release candidate is built.
55+
56+
## Final verdict
57+
58+
**APPROVED TO MERGE.** Phase B (Pillar 1 — Unified Config) has met every §3.6 acceptance criterion with passing, deterministic test coverage. Single source of truth (`codeiq.yml`) verified; 5-layer resolution (`BUILT_IN → USER_GLOBAL → PROJECT → ENV → CLI`) verified; provenance surfaced by `config explain`; validation errors are file-anchored and exit 1; `.osscodeiq.yml` deprecation shim translates legacy flat keys and emits a per-path WARN; legacy `CodeIqConfig` API surface preserved; `application.yml` reduced to Spring-framework-consumed keys only (all 4 documented). Full suite green: 3275/0/31.

src/main/java/io/github/randomcodespace/iq/cli/CodeIqCli.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
FlowCommand.class,
2727
BundleCommand.class,
2828
CacheCommand.class,
29+
ConfigCommand.class,
2930
StatsCommand.class,
3031
TopologyCommand.class,
3132
PluginsCommand.class,

0 commit comments

Comments
 (0)