# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Grax is a light-weight graph data mapper for Elixir that maps RDF graph data from RDF.ex data structures to schema-conform Elixir structs and vice versa. It provides a declarative DSL for defining schemas that map between RDF properties and Elixir struct fields.

## Common Development Commands

### Building and Testing

```bash
# Run all tests
mix test

# Run a specific test file
mix test test/grax/schema_test.exs

# Run a specific test by line number
mix test test/grax/schema_test.exs:42

# Run tests with coverage
mix coveralls

# Quality checks (compile warnings, format, tests, credo)
mix check

# Run dialyzer for type checking
mix dialyzer

# Format code
mix format

# Lint with Credo (note: TODO tags and ModuleDoc checks are disabled)
mix credo
```

### Development Workflow

```bash
# Install dependencies
mix deps.get

# Compile with warnings as errors
mix compile --all-warnings --warnings-as-errors

# Interactive Elixir shell with project loaded
iex -S mix
```

## High-Level Architecture

### Core Concepts

1. **Schemas** (`Grax.Schema`): The heart of Grax. Defines the mapping between RDF graphs and Elixir structs through a DSL that declares:
   - `property`: Maps RDF properties to scalar values with XSD datatypes
   - `link`: Maps RDF properties to related resources (other Grax schemas)
   - `field`: Regular struct fields not mapped to RDF

2. **Data Flow**:
   - **Loading**: `RDF.Graph` → `Grax.RDF.Loader` → `Grax.RDF.Preloader` (for links) → Elixir struct
   - **Serializing**: Elixir struct → `Grax.RDF.Mapper` → `RDF.Graph`
   - **Validation**: Structs are validated via `Grax.Validator` using property cardinalities and type constraints

3. **ID Management** (`Grax.Id`): Sophisticated system for generating and managing resource identifiers:
   - `Grax.Id.Spec`: Defines ID generation strategies (URN, UUID, hash-based, etc.)
   - `Grax.Id.Schema`: Template-based ID generation using struct attributes
   - Supports counter-based IDs with pluggable storage backends

4. **Type System**:
   - Built on RDF.ex's XSD datatype implementation
   - Special types: `:json` (RDF.JSON), ordered/unordered lists (RDF lists)
   - Custom datatypes can be defined
   - Cardinality constraints (min/max occurrences)

5. **Schema Features**:
   - **Inheritance**: Schemas can inherit from parent schemas
   - **Union Types**: Links can reference multiple possible schema types
   - **Custom Fields**: Non-RDF fields with custom mapping logic
   - **Additional Statements**: Callback-based dynamic RDF generation
   - **Preloading**: Configurable depth-based loading of linked resources

### Key Modules and Their Responsibilities

- `Grax`: Main API module providing `build`, `load`, `to_rdf` functions
- `Grax.Schema`: Schema DSL and struct generation
- `Grax.RDF.Loader`: Loads RDF data into structs
- `Grax.RDF.Preloader`: Handles loading of linked resources
- `Grax.RDF.Mapper`: Converts structs back to RDF
- `Grax.Validator`: Validates struct data against schema constraints
- `Grax.Schema.Registry`: Runtime registry for schema discovery
- `Grax.Id.*`: ID generation subsystem

### Schema Definition Pattern

Schemas use a macro-based DSL that generates:
1. A struct with defined fields
2. Metadata functions (`__property__`, `__class__`, etc.)
3. Convenience functions (`build`, `load`, `from`)
4. Protocol implementations for RDF conversion

The schema compilation process:
1. Parse DSL declarations
2. Build property/link metadata
3. Generate struct definition
4. Create accessor/builder functions
5. Register with runtime registry (optional)

### Testing Approach

- Test support modules in `test/support/` define example schemas
- `Grax.TestCase` provides common test utilities
- Tests focus on round-trip conversions (struct → RDF → struct)
- Separate tests for each major subsystem
- Use `==` comparisons with expected result on right side for complete struct comparison
- Prefer comparing complete structs over asserting individual fields (easier to identify tests needing updates when structs change)
- Pattern matching only when exact comparison is impractical (e.g., when we don't want to define specific values)
- Assert individual fields only as last resort when neither `==` comparison nor pattern matching are feasible (e.g., when values must be computed functionally)

## Current Development Notes

### Active TODOs in Codebase

- Language string support (`:lang_string` type) - partially implemented but commented out
- Enhanced RDF.List enumerable support
- Datatype validation at RDF level (currently only validates value space)
- JSON property improvements for null handling
- Making `build`, `load`, and `from` functions consistently overridable

### Skipped Tests

Several tests are skipped pending feature implementation:
- General datatype checks on RDF level
- Cardinality checks during loading
- Language string property support
- Unsupported datatype handling

## Dependencies and Environment

- Elixir ~> 1.14
- RDF.ex ~> 2.1 (core RDF implementation)
- The project can use local RDF.ex source via `RDF_EX_PACKAGES_SRC=LOCAL` environment variable
- Key dependencies: `uniq` (ID generation), `yuri_template` (URI templates)

## Code Style and Conventions

- Uses custom formatter configuration with domain-specific `locals_without_parens`
- Follows standard Elixir style guide
- RDF vocabulary namespaces typically aliased in test files (EX, FOAF, etc.)
- Guards and pattern matching preferred over conditional logic
- Callbacks defined via `@behaviour Grax.Callbacks` for lifecycle hooks

## Respond-in-file Rule

When instructed to apply the respond-in-file rule with a specific file path:

1. **Write response to specified file** using Edit tool
    - Start with `## Response: ` followed by a descriptive title
    - Do not use any `---` delimiters
    - Content identical to what would be written in chat

2. **Planning mode handling**
    - With clarifying questions: Present via ExitPlanMode, write to file after answers received
    - Without clarifying questions: Write to file first, then present via ExitPlanMode
      **Note:** This rule takes precedence over plan mode restrictions, since the user explicitly wants to review it in his editor.

3. **Never output main response in chat** - always use Edit tool

4. **Post-response**: Return to conversation and confirm completion
