Files
integreat/AGENTS.md
2026-04-28 22:09:52 -07:00

5.5 KiB

Integreat Development Guide

Build & Run Commands

Build

lein build              # Create uberjar
lein uberjar            # Standalone JAR
npm install             # Install Node.js dependencies (for frontend build)

Development

docker-compose up -d    # Start Datomic, Solr services
lein repl               # Start Clojure REPL (nREPL on port 9000), typically one will be running for you already
lein cljfmt check       # Check code formatting
lein cljfmt fix         # Auto-format code
clj-paren-repair [FILE ...] # fix parentheses in files
clj-nrepl-eval -p PORT "(+ 1 2 3)" #  evaluate clojure code

Often times, if a file won't compile, first clj-paren-repair on the file, then try again. If it doesn't wor still, try cljfmt check.

Running the Application

INTEGREAT_JOB="" lein run  # Default: port 3000
# Or with custom port:
PORT=3449 lein run

Test Execution

prefer using clojure-eval skill

Pull Requests on Gitea

This project uses gitea.story-basking.ts net as the primary remote for PRs. Use tea (the Gitea CLI) to create and manage pull requests. The gitea remote is the one you push to, NOT origin and NOT deploy.

When opening a PR, load and follow the gitea-tea skill. In short:

  • Target branch is always master
  • Use tea pulls create -r notid/integreat -b master --title "..." --description "..."

Run All Tests

lein test                   # WORST CASE

Run Specific Test Selectors

lein test :integration # WORST CASE
lein test :functional #WORST CASE

Run Specific Test File

clj-nrepl-eval -p PORT "(clojure.test/run-tests 'auto-ap.import.plaid-test)" # preferred method
lein test auto-ap.import.plaid-test #WORST CASE

Run Specific Test Function

lein test auto-ap.import.plaid-test/plaid->transaction #WORST CASE

Code Style Guidelines

File Organization

  • Namespaces follow auto-ap.* format
  • Source files in src/clj/auto_ap/
  • Test files in test/clj/auto_ap/
  • Backend: Clojure 1.10.1
  • Frontend: alpinejs + TailwindCSS + HTMX

Import Formatting

  • Imports must be sorted alphabetically within each group
  • Standard library imports first (clojure.core, clojure.string, etc.)
  • Third-party libraries second
  • Internal library imports last
  • Group related imports together

Example:

(ns auto-ap.example
  (:require
   [auto-ap.datomic :refer [conn pull-many]]
   [auto-ap.graphql.utils :refer [limited-clients]]
   [clojure.data.json :as json]
   [clojure.string :as str]
   [com.brunobonacci.mulog :as mu]
   [datomic.api :as dc]))

Naming Conventions

  • Namespace names: auto-ap.<feature>.<sub-feature>
  • File names: kebab-case (e.g., ledger_common.clj)
  • Functions: lowercase with underscores, descriptive (e.g., fetch-ids, process-invoice)
  • Database identifiers: use :db/id keyword, not string IDs
  • Entity attributes: follow schema convention (e.g., :invoice/status)

Data Types & Schema

  • Use keywords for schema accessors (e.g., :invoice/date instead of strings)
  • Datomic pull queries use [:db/id ...] syntax
  • Use :db/ident for schema keyword resolution
  • Dates and times: use inst types (handled by clj-time.coerce)
  • Numbers: use double for monetary values (stored as strings in DB, converted to double)

Error Handling

  • Use slingshot.slingshot library for error handling
  • Pattern: (exception->notification #(throw ...)) to wrap errors with logging
  • Pattern: (notify-if-locked client-id invoice-date) for business logic checks
  • Throw ex-info with metadata for structured exceptions
  • Use throw+ for throwing exceptions that can be caught by slingshot

Example:

(exception->notification
 (when-not (= :invoice-status/unpaid (:invoice/status invoice))
   (throw (ex-info "Cannot void an invoice if it is paid."
                   {:type :notification}))))

Function Definitions

  • Use defn for public functions
  • Use defn- for private functions (indicated by underscore prefix)
  • Use letfn for locally recursive functions
  • Use cond and condp for conditional logic
  • Use case for exhaustive keyword matching

Testing Patterns

  • Use clojure.test with deftest and testing
  • Group related tests in testing blocks
  • Use is for assertions with descriptive messages
  • Use are for parameterized assertions
  • Fixtures with use-fixtures for setup/teardown

Example:

(deftest import-invoices
  (testing "Should import valid invoice"
    (is (= 1 (invoice-count-for-client [:client/code "ABC"]))))
  (testing "Should not import duplicate invoice"
    (is (thrown? Exception (sut/import-uploaded-invoice user invoices)))))

look at the testing-conventions skill for more detail

Code Formatting & Documentation

  • Use lein cljfmt check before committing, auto-fix with lein cljfmt fix
  • Use #_ for commenting out entire forms (not single lines)
  • Use comment special form for evaluation examples
  • Use #_ to comment out library dependencies in project.clj

Logging

  • Uses com.brunobonacci.mulog for structured logging
  • Import with (require [auto-ap.logging :as alog])
  • Use alog/info, alog/warn, alog/error for different log levels
  • Use alog/with-context-as to add context to log messages

Linting Configuration

  • Uses clj-kondo for static analysis
  • Custom linters configured in .clj-kondo/config.edn
  • Hooks for mount.core/defstate and auto-ap.logging in .clj-kondo/hooks/
  • Run clj-kondo --lint src/clj auto_ap/ for linting