Conventions
- Tests
- Use
Fnord.TestCaseand helpers (mock_project,tmpdir). - Prefer
async: falsefor most tests due to global state and GenServers. - Always use
Settings.get_user_home()for paths in production and tests; do not useSystem.user_home!().
- Use
- Compile-time vs runtime
- This is an escript app, so there is no built-in elixir app config.
- Module attributes evaluate at compile time.
- Use
defpfunctions for values that must reflect runtime or test settings.
- Persistence and concurrency
- Use atomic writes (temp file + rename). See
Settings.write_atomic!. - Use
FileLockfor concurrent access; prefer per-file locks. - Separate concerns into distinct files.
- Use atomic writes (temp file + rename). See
- Build / quality
- ALWAYS run
make checkbefore finalizing your response. - Validation rules configured for this project will run automatically after code-modifying tools, but
make checkremains your responsibility as a final gate. - Treat compilation warnings as errors. Public functions should have
@spec. - This is not a library; there is no need to worry about any external API stability.
- NEVER commit changes yourself unless explicitly instructed
- NEVER push changes yourself unless explicitly instructed
- ALWAYS run
- Functions should do one thing well
- prefer small, pure functions with clear behavior
- use
|>to chain transformations - prefer function heads over complex conditionals
- prefer function heads over complex or nested
Enumiterators (egeach,reduce) - prefer pattern matching over guards when possible
- Integration points
- integration points are where different abstraction levels meet
- it's integration levels all the way down; there is no single "lowest" level
- everything workflow is an integration of smaller, more focused pieces
- at the very least, entry points into a module are integration points
- at significant integration points, use
withto transition between lower-level concerns and higher-level logic- translate errors into domain-specific errors; what matters to the caller at this level of abstraction?
- special cases should be handled at integration points, not buried in lower-level functions
- integration points always get
@docs explaining how they fit into the bigger picture/larger feature - integration points should have basic positive and negative path tests for the expected/intended use cases
- add tests as edge cases manifest
- integration points are where different abstraction levels meet
AI.Agentis for implementations of that behaviourAI.Toolsis for implementations of that behaviourServicesis for genservers- Prefer context modules that get called by integration/feature/behavior layers
- Do not use in-line conditionals (eg
if ..., do: ..., else: ...) - Do not use
aliass unless required @doc falsefollowed by a comment explaining a function is silly; just give the function a@doc- Avoid
@doc falseentirely - Do not use type guards unless required for functionality; they add complexity and can confuse
dialyzer - When refactoring, always consider whether the organization of the code must change to reflect ownership of concerns
- If you centralize a concern, does the location of the code reflect that ownership?
- dialyzer warnings should be treated as errors, even if they say "legacy" (elixir is in the process of incorporating many dialyzer identifications into the compiler; ones noted "legacy" are actually just in the process of being ported)
- you can perform an independent review of your changes using
claude -p "<review request prompt>" --allowedTools "Read,Grep,Glob"- this is useful because it is a different LLM with a fresh context window
- Comments should describe the current behavior; they should NEVER describe or identify a change that is currently being made
- Comments should walk the reader through how the code behaves, including how it fits into the bigger picture, and how it relates to the feature or behavior it supports
- Comments should not be used to identify or describe bugs or issues; instead, they should be used to describe the current, intended behavior, and should always match the code. If there is a bug, the code should be adjusted and then the comments brought into line with the new behavior.
- Comment style should be literary; if you hide all of the code in a file, the comments should still present the reader with an outline of the code, how it behaves, and how it fits into the next level of abstraction up.
- This repository is the runtime for the assistant.
- Edits you make here change how the assistant behaves when operating inside this project.
- Keep diffs small and covered by tests.