feat: dependency resolver (Phase 1) + formatter + doctor + assertion evaluation

Resolver (Phase 1):
- resolve::resolve() takes SystemConfig + Registry → BuildPlan
- Three-layer feature/config merge (uses config::resolve_package)
- Feature-gated dependencies: deps with feature=X skipped if feature disabled
- Topological sort via Kahn's algorithm (BFS on in-degree)
- Cycle detection, missing package warnings
- CLI: kappa resolve <config> [<package>]

Formatter:
- format_package() + format_config() → canonical output
- Consistent 4-space indent, canonical declaration order

Doctor:
- check_package() + check_config() → warnings for common issues
- Missing fields, empty configs, root shell, feature warnings

Assertion evaluation:
- evaluate_assertions() resolves dotted field paths
- Supports == and != operators for config validation
- kappa validate now runs assertion checks

Review fixes:
- plan.steps.empty() exit code corrected to 0
- Removed unused <unordered_set> include
- Eliminated duplicate merge logic (uses config::resolve_package)
This commit is contained in:
2026-07-30 00:36:38 -04:00
parent 0c1e82d66b
commit ee9f280346
10 changed files with 746 additions and 23 deletions
+8 -2
View File
@@ -44,9 +44,15 @@ void SysParser::skip_newlines() {
Token SysParser::consume(TokenType type) {
if (!at(type)) {
if (type == TokenType::Rbrace && at(TokenType::Eof)) {
throw ParseError(current_.line, current_.col,
msg_unclosed_block("block"));
}
if (type == TokenType::String && at(TokenType::Ident)) {
throw ParseError(current_.line, current_.col, msg_not_a_string());
}
throw ParseError(current_.line, current_.col,
std::format("expected '{}', got '{}'",
token_name(type), token_name(current_.type)));
msg_expected(token_name(type), token_name(current_.type)));
}
Token t = std::move(current_);
advance();