EK9 for AI Assistants
Why This Page Exists
This page is written by an AI, for AI assistants. EK9 was designed from the ground up to enable effective AI-human collaborative development. The language's quality metrics, structured output formats, and compile-time enforcement create an ideal environment for AI assistants to provide measurable, verifiable improvements to code.
For AI Assistants: Read this page thoroughly when first encountering EK9. It contains the actionable information you need to help humans write high-quality EK9 code. Bookmark specific sections for quick reference during development sessions.
Key EK9 Advantages for AI Development:
- Structured metrics output — JSON format (
-t5) enables programmatic analysis - Compile-time quality enforcement — Poor code won't compile, reducing debugging cycles
- Clear thresholds — Explicit limits for complexity, nesting, coupling
- Iterative verification — Re-run metrics after changes to confirm improvements
- No configuration — Quality rules are built into the language, not external tools
Table of Contents
- Quick Reference — Essential commands and common tasks
- Output Formats — Detailed schemas for -t0 through -t6
- Metrics Reference — Every metric with thresholds and actions
- Refactoring Patterns — EK9-specific patterns to reduce complexity
- Workflow Templates — Step-by-step guides for common tasks
- Code Generation Guidelines — How to write idiomatic EK9
- Error Interpretation — Understanding and fixing EK9 errors
- Testing Strategies — Writing effective EK9 tests
Quick Reference
Essential commands for AI assistants working with EK9.
Command Cheat Sheet
| Task | Command | Output |
|---|---|---|
| Compile only | ek9 -c main.ek9 | Errors or success |
| Run program | ek9 main.ek9 | Program output |
| Run tests (pass/fail) | ek9 -t0 main.ek9 | Single line summary |
| Run tests (human) | ek9 -t main.ek9 | Detailed results |
| Run tests (JSON) | ek9 -t5 main.ek9 | Structured JSON |
| Generate HTML report | ek9 -t6 main.ek9 | Full HTML site |
| Verbose errors | ek9 -E3 -c main.ek9 | Detailed explanations |
| List tests only | ek9 -tL main.ek9 | Test names without running |
Which -t Option to Use
| Situation | Option | Reason |
|---|---|---|
| Quick check: did tests pass? | -t0 |
Single line, minimal output, fast parsing |
| Show results to human | -t or -t1 |
Human-readable format with details |
| CI/CD pipeline | -t3 |
JUnit XML + JaCoCo XML, industry standard |
| AI metric analysis | -t5 |
JSON format, fully parseable, all metrics included |
| Generate report for human review | -t6 |
Full HTML site with visual dashboards |
AI Recommendation: Use -t5 for all programmatic analysis. Parse the JSON
to extract metrics, then use -t6 to generate a visual report for the human
after improvements are made.
Common AI Tasks
| Human Request | AI Action |
|---|---|
| "Check code quality" | Run ek9 -t5, parse metrics, report findings |
| "Reduce complexity" | Run -t5, identify high CC/Cog functions, apply refactoring patterns, verify with -t5 |
| "Improve test coverage" | Run -t5, find uncovered functions in JSON, write tests, verify coverage increased |
| "Fix compilation errors" | Run ek9 -E3 -c, parse error codes, apply fixes from Error Index |
| "Make code more maintainable" | Analyze all metrics (CC, Cog, Nesting, Coupling, Cohesion), prioritize worst, improve iteratively |
Output Formats
Detailed documentation of each output format for parsing by AI assistants.
-t0: Terse Output
Single line summary for quick pass/fail checks.
28 passed, 0 failed Coverage: 72.7% (45/62) FAILED - below 80% threshold
Parsing:
- Line 1:
(\d+) passed, (\d+) failed - Line 2:
Coverage: ([\d.]+)% \((\d+)/(\d+)\) (PASSED|FAILED)
-t5: JSON Output (Primary for AI)
Complete structured output with all metrics. This is the recommended format for AI analysis.
{
"timestamp": "2026-01-12T14:30:00Z",
"version": "dev",
"source": "main.ek9",
"tests": {
"passed": 28,
"failed": 0,
"skipped": 0,
"total": 28,
"duration_ms": 1250,
"results": [
{
"name": "testProcessOrder",
"module": "myapp.orders",
"status": "PASSED",
"duration_ms": 45
}
]
},
"coverage": {
"overall": 72.7,
"probes_hit": 45,
"probes_total": 62,
"threshold": 80.0,
"passed": false,
"methods": {
"covered": 18,
"total": 24,
"percentage": 75.0
},
"lines": {
"covered": 45,
"total": 62,
"percentage": 72.6
},
"branches": {
"covered": 12,
"total": 18,
"percentage": 66.7
}
},
"metrics": {
"complexity": {
"average": 8.2,
"max": 24,
"limit": 45
},
"cognitive": {
"average": 5.1,
"max": 18,
"limit": 35
},
"nesting": {
"average": 2.1,
"max": 4,
"limit": 6
},
"readability": {
"average": 7.3,
"max": 12,
"scale_max": 20
}
},
"modules": [
{
"name": "myapp.orders",
"coverage": 65.0,
"probes_hit": 13,
"probes_total": 20,
"files": ["orders.ek9", "orderValidation.ek9"],
"metrics": {
"avg_complexity": 12.5,
"max_complexity": 24,
"avg_cognitive": 8.2,
"max_cognitive": 18,
"readability": 8
}
}
],
"functions": [
{
"name": "processOrder",
"qualified_name": "myapp.orders::processOrder",
"module": "myapp.orders",
"file": "orders.ek9",
"line": 45,
"covered": true,
"metrics": {
"complexity": 24,
"cognitive": 18,
"nesting": 4,
"statements": 42
}
}
],
"attention": {
"high_complexity": [
{
"name": "processOrder",
"file": "orders.ek9",
"line": 45,
"complexity": 24,
"cognitive": 18
}
],
"uncovered_functions": [
{
"name": "handleRefund",
"file": "orders.ek9",
"line": 120
}
],
"low_coverage_modules": [
{
"name": "myapp.orders",
"coverage": 65.0
}
],
"uncovered_branches": [
{
"file": "orders.ek9",
"line": 78,
"type": "BRANCH_TRUE"
}
]
}
}
Key Fields for AI Analysis
| Field Path | Purpose | AI Action |
|---|---|---|
coverage.passed |
Did coverage meet 80% threshold? | If false, prioritize coverage improvement |
metrics.complexity.max |
Highest CC in codebase | If >20, find and refactor that function |
attention.high_complexity |
Functions needing attention | Process this list for refactoring targets |
attention.uncovered_functions |
Functions without test coverage | Write tests for these functions |
functions[].metrics |
Per-function quality metrics | Sort by complexity to find worst offenders |
-t6: HTML Output
Generates a complete mini-site in .ek9/coverage/ with:
- index.html — Dashboard with coverage dials, module list, attention panel
- modules/*.html — Per-module detail pages with file list and function metrics
- files/*.html — Source code with line-by-line coverage highlighting
- files/summary/*.html — File summary pages with method/branch lists
AI Usage: Generate -t6 after making improvements so the human can
visually verify the results. The HTML includes interactive features like dark mode toggle
and clickable navigation.
Metrics Reference
Complete reference for every quality metric, including thresholds and recommended actions.
Metric Thresholds and Actions
| Metric | Good | Monitor | Warning | Limit | Error Code |
|---|---|---|---|---|---|
| Cyclomatic Complexity (CC) | ≤10 | 11-15 | 16-25 | 45 | E11010 |
| Cognitive Complexity | ≤10 | 11-15 | 16-25 | 35 | E11021 |
| Nesting Depth | ≤2 | 3 | 4-5 | 6 | E11011 |
| Statement Count | ≤50 | 51-100 | 101-150 | 150 | E11012 |
| Expression Complexity | ≤5 | 6-10 | 11-15 | 15 | E11013 |
| Cohesion (LCOM4) | ≤3 | 4-5 | 6-8 | 8 | E11014 |
| Coupling (Ce) | ≤5 | 6-8 | 9-12 | 12 | E11015 |
| Inheritance Depth | ≤2 | 3 | 4 | 4 | E11019 |
| Readability (ARI) | ≤8 | 9-12 | 13-20 | None | Informational only |
| Coverage | ≥80% | 60-79% | <60% | 80% | Publishing gate |
Recommended Actions by Metric
High Cyclomatic Complexity (CC > 15)
| Symptom | Action | EK9 Pattern |
|---|---|---|
| Many if/else branches | Extract to helper functions | Create focused validation functions |
| Complex switch statements | Use dispatcher pattern | processItem() as dispatcher |
| Multiple conditions in if | Extract boolean expressions | isValid <- checkA() and checkB() |
| Nested loops | Use stream pipelines | cat items | filter | map | collect |
High Cognitive Complexity (Cog > 15)
| Symptom | Action | EK9 Pattern |
|---|---|---|
| Deep nesting | Use guard expressions | if value <- getValue() |
| Complex control flow | Flatten with early guards | Process invalid cases first, happy path last |
| Multiple nested guards | Extract to separate functions | Each function handles one level of checking |
High Nesting Depth (> 3)
| Symptom | Action | EK9 Pattern |
|---|---|---|
| Nested if blocks | Invert conditions | Check failure cases first, exit scope early |
| Nested loops | Extract inner loop | Create processInner() function |
| Callback nesting | Use composition | Chain operations with | pipeline |
Low Cohesion (LCOM4 > 5)
| Symptom | Action | EK9 Pattern |
|---|---|---|
| Methods don't share fields | Split into multiple classes | Group related methods with their data |
| Utility method collections | Use standalone functions | defines function instead of class methods |
| Mixed concerns | Apply Single Responsibility | One class = one reason to change |
High Coupling (Ce > 8)
| Symptom | Action | EK9 Pattern |
|---|---|---|
| Many imports | Introduce abstractions | Depend on traits, not concrete classes |
| God class dependencies | Break apart god class | Create focused service classes |
| Circular dependencies | Use dependency injection | defines application pattern |
Refactoring Patterns
EK9-specific refactoring patterns for common complexity issues.
Pattern: Guard Expression Flattening
Problem: Deep nesting from null/validity checks
Before (Nesting: 4, Cognitive: 16):
#!ek9
processOrder()
-> order as Order
<- result as String?
if order?
if order.isValid()
if customer <- order.getCustomer()
if customer.hasCredit()
result: "Processing " + order.id()
After (Nesting: 1, Cognitive: 4):
#!ek9
processOrder()
-> order as Order
<- result as String?
if validOrder <- validateOrder(order)
result: "Processing " + validOrder.id()
validateOrder() as pure
-> order as Order
<- valid as Order?
if order? and order.isValid()
if customer <- order.getCustomer() with customer.hasCredit()
valid: order
Pattern: Stream Pipeline Replacement
Problem: Complex nested loops with conditionals
Before (CC: 8, Nesting: 3):
#!ek9
findActiveUsers()
-> users as List of User
<- active as List of User: List()
for user in users
if user.isActive()
if user.hasRecentLogin()
active += user
After (CC: 3, Nesting: 1):
#!ek9
findActiveUsers()
-> users as List of User
<- active as List of User?
active: cat users
| filter by isActiveWithRecentLogin
| collect as List of User
isActiveWithRecentLogin() as pure
-> user as User
<- result as Boolean: user.isActive() and user.hasRecentLogin()
Pattern: Dispatcher for Type Switching
Problem: Complex switch/if chains on type
Before (CC: 12):
#!ek9
processShape()
-> shape as Shape
<- area as Float?
switch shape.type()
case "circle"
area: calculateCircleArea(shape)
case "square"
area: calculateSquareArea(shape)
case "triangle"
area: calculateTriangleArea(shape)
default
area: 0.0
After (CC: 4 per function):
#!ek9 calculateArea() as dispatcher -> shape as Shape <- area as Float? calculateArea() -> circle as Circle <- area as Float: PI * circle.radius() * circle.radius() calculateArea() -> square as Square <- area as Float: square.side() * square.side() calculateArea() -> triangle as Triangle <- area as Float: 0.5 * triangle.base() * triangle.height()
Pattern: Extract Validation Chain
Problem: Long sequence of validation checks
Before (CC: 15, Statements: 45):
#!ek9
validateAndProcess()
-> request as Request
<- response as Response?
if not request?
response: Response.error("Null request")
else if not request.hasUser()
response: Response.error("Missing user")
else if not request.user().isAuthenticated()
response: Response.error("Not authenticated")
else if not request.hasPermission("write")
response: Response.error("No permission")
else if not request.payload()?
response: Response.error("Missing payload")
else
response: doProcess(request)
After (CC: 5 each, clearer intent):
#!ek9
validateAndProcess()
-> request as Request
<- response as Response?
response: validateRequest(request) ?? doProcess(request)
validateRequest() as pure
-> request as Request
<- error as Response?
error: checkNotNull(request)
?? checkHasUser(request)
?? checkAuthenticated(request)
?? checkPermission(request, "write")
?? checkPayload(request)
checkNotNull() as pure
-> request as Request
<- error as Response?
if not request?
error: Response.error("Null request")
checkHasUser() as pure
-> request as Request
<- error as Response?
if not request.hasUser()
error: Response.error("Missing user")
Workflow Templates
Step-by-step workflows for common AI-assisted development tasks.
Workflow: Reduce Code Complexity
Trigger: Human requests "make this less complex" or "simplify this code"
-
Measure baseline
ek9 -t5 main.ek9 > baseline.json
Parse JSON to extract:metrics.complexity.max— highest CCattention.high_complexity— list of problem functions
-
Identify targets
Sortfunctionsbymetrics.complexitydescending. Focus on functions where CC > 15. -
Read and analyze each target function
Identify which pattern applies:- Deep nesting → Guard Flattening
- Nested loops → Stream Pipelines
- Type switching → Dispatcher
- Validation chains → Extract Validation
-
Apply refactoring
Make targeted changes. Keep each change small and focused. -
Verify improvement
ek9 -t5 main.ek9 > after.json
Compare metrics. Report:processOrder: CC 24→8 (-67%), Cog 18→6 (-67%), Nesting 4→2 (-50%)
-
Generate HTML report for human
ek9 -t6 main.ek9
Tell human: "Open.ek9/coverage/index.htmlto see the updated metrics."
Workflow: Improve Test Coverage
Trigger: Human requests "improve coverage" or coverage is below 80%
-
Get current coverage
ek9 -t5 main.ek9 > coverage.json
Checkcoverage.passedandcoverage.overall. -
Identify uncovered code
From JSON extract:attention.uncovered_functions— functions with no coverageattention.uncovered_branches— specific branches not hitattention.low_coverage_modules— modules needing attention
-
Prioritize by impact
Focus on:- Uncovered functions in low-coverage modules (highest impact)
- Uncovered branches in critical paths
- Functions with partial coverage
-
Write tests
For each uncovered function, create a test indev/directory. See Testing Strategies for EK9 test patterns. -
Verify coverage improved
ek9 -t5 main.ek9
Confirmcoverage.overallincreased. Target: ≥80% for publishing.
Workflow: Fix Quality Errors
Trigger: Compilation fails with E110xx errors
-
Get detailed error info
ek9 -E3 -c main.ek9
The-E3flag provides verbose explanations. -
Parse error code
Quality errors are E11010-E11025. See Error Index. -
Apply fix based on error
Error Meaning Fix E11010 CC too high Extract functions, reduce branches E11011 Nesting too deep Use guards, extract blocks E11014 Low cohesion Split class, group related methods E11015 High coupling Introduce abstractions E11019 Deep inheritance Use composition with byE11021 Cognitive too high Flatten nesting, simplify flow -
Recompile to verify fix
ek9 -c main.ek9
Code Generation Guidelines
Guidelines for AI assistants generating EK9 code.
Syntax Essentials
- Indentation: 2 spaces (not tabs)
- No semicolons: Statements end at newline
- No curly braces: Indentation defines blocks
- Declaration:
name <- value(infers type) - Typed declaration:
name as Type: value - Assignment:
name: valueorname := value - Guard assignment:
name :=? value(only if unset)
Preferred Patterns
Function Structure
#!ek9 functionName() as pure -> param1 as Type1 -> param2 as Type2 <- result as ResultType? // Implementation result: computeResult(param1, param2)
Class Structure
#!ek9
defines class
ClassName
field1 as Type1?
field2 as Type2?
ClassName()
-> initialValue as Type1
field1: initialValue
methodName()
-> param as ParamType
<- result as ResultType?
result: computeWith(field1, param)
Test Structure
#!ek9
defines module test.mymodule
defines program
@Test
testFunctionName()
// Arrange
input <- createTestInput()
// Act
result <- functionUnderTest(input)
// Assert
assert result?
assert result == expectedValue
Patterns to Avoid
- No break/continue/return: These don't exist in EK9. Use guards and stream pipelines.
- No null: Use
?operator and unset values instead. - No casting: Use dispatchers for polymorphic behavior.
- No deep inheritance: Max 4 levels. Prefer composition with
by. - No global variables: Use dependency injection or parameter passing.
EK9 Idioms
Guard Expression (instead of null check + early return)
#!ek9 // Check and use in one expression if value <- getValue() process(value)
Coalescing (instead of ternary for defaults)
#!ek9 // Use default if unset result <- getValue() ?? defaultValue // Use default if unset (assign only if needed) existingVar :=? computeIfNeeded()
Stream Pipeline (instead of loops with accumulators)
#!ek9 // Transform and collect result <- cat items | filter by isValid | map by transform | collect as List of OutputType
Dispatcher (instead of instanceof/type switch)
#!ek9 // Declare dispatcher process() as dispatcher -> input as BaseType <- output as OutputType? // Implement per concrete type process() -> specific as ConcreteType <- output as OutputType: handleConcrete(specific)
Error Interpretation
How to interpret and fix common EK9 compilation errors.
Error Categories
| Code Range | Category | Phase |
|---|---|---|
| E01xxx | Lexer/Parser | PARSING |
| E02xxx | Duplicate symbols | SYMBOL_DEFINITION |
| E05xxx | Symbol resolution | FULL_RESOLUTION |
| E06xxx | Type checking | FULL_RESOLUTION |
| E07xxx | Operator validation | POST_RESOLUTION_CHECKS |
| E08xxx | Flow analysis | PRE_IR_CHECKS |
| E11xxx | Quality metrics | PRE_IR_CHECKS |
| E81xxx | Test validation | PRE_IR_CHECKS |
Using Verbose Mode
Always use -E3 when debugging errors:
ek9 -E3 -c main.ek9
This provides:
- Full error explanation
- Code context showing the problem
- Suggested fixes
- Links to related documentation
Quality Error Quick Reference
| Error | Cause | Quick Fix |
|---|---|---|
| E11010 | Cyclomatic complexity > 45 | Extract helper functions, reduce conditionals |
| E11011 | Nesting depth > 6 | Use guard expressions, extract nested blocks |
| E11012 | Statement count > 150 | Split into multiple functions |
| E11013 | Expression complexity > 15 | Extract intermediate variables |
| E11014 | Low cohesion (LCOM4 > 8) | Split class into focused classes |
| E11015 | High coupling (> 12 dependencies) | Introduce abstractions (traits) |
| E11019 | Inheritance depth > 4 | Use composition with by |
| E11020 | Combined complexity × size > 0.5 | Reduce both complexity AND size |
| E11021 | Cognitive complexity > 35 | Flatten nesting, simplify control flow |
Testing Strategies
Guidelines for writing effective EK9 tests.
Test File Structure
dev/
├── testOrders.ek9 # Unit tests for orders module
├── testPayments.ek9 # Unit tests for payments module
├── expected_output.txt # Expected output for black-box tests
└── integration/
└── testFullFlow.ek9 # Integration tests
Test Patterns
Unit Test with Assertions
#!ek9
defines module test.orders
defines program
@Test
testOrderTotal()
order <- Order()
order.addItem(Item("Widget", 10.00))
order.addItem(Item("Gadget", 25.00))
total <- order.calculateTotal()
assert total?
assert total == 35.00
Black-Box Test with Expected Output
#!ek9
defines module test.formatting
defines program
@Test
testFormatOutput()
formatter <- Formatter()
result <- formatter.format("hello")
stdout.println(result)
With expected_output.txt:
HELLO
Parameterized Test
#!ek9
defines module test.validation
defines program
@Test
testValidation()
// Read test case from commandline_arg_1.txt
stdin <- Stdin()
input <- stdin.readLine()
result <- validate(input)
stdout.println(result)
With commandline_arg_1.txt and expected_case_1.txt pairs.
Coverage-Driven Testing
- Run
ek9 -t5to get coverage data - Check
attention.uncovered_functions - For each uncovered function:
- Analyze function signature and behavior
- Identify edge cases (null, empty, boundary values)
- Write tests covering happy path and edge cases
- Check
attention.uncovered_branches - For each uncovered branch:
- Identify the condition (BRANCH_TRUE or BRANCH_FALSE)
- Create test input that exercises that branch
- Re-run
ek9 -t5to verify improved coverage
Summary for AI Assistants
Key takeaways for effective AI-assisted EK9 development:
- Use
-t5for analysis — JSON is your primary data source - Metrics drive action — Use thresholds to identify and prioritize work
- Verify improvements — Always re-run metrics after changes
- Use
-E3for errors — Verbose mode explains problems clearly - Report measurably — Show before/after metrics to humans
- Generate
-t6for humans — HTML reports for visual review
The AI-Human Partnership: EK9's quality system is designed to make AI assistants maximally effective. You measure, identify issues, apply patterns, and verify. The human reviews results and guides priorities. Together, you produce code that is measurably better than either could produce alone.
Quick Links
- Code Quality — Detailed metric explanations and error codes
- Implemented Metrics — CC, Cognitive, Nesting thresholds
- ARI Readability — Lexical complexity (informational)
- Viewing Quality Metrics — HTML report elements explained
- Testing — Test framework and coverage documentation
- Quality Metrics — Coverage report integration
- HTML Reports — Report structure and navigation
- Error Index — Complete error reference with fixes
- Command Line — All compiler options
- Compilation Options — -c, -cG, -Cl flags
- Error Verbosity — -E0 to -E3 (use -E3 for AI debugging)
- Exit Codes — Interpret success/failure programmatically
- Packaging — -P, -D for deployment
- Flow Control — Guard expressions and control flow
- Streams — Pipeline patterns