Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,39 @@ Each version is an immutable snapshot. Never modify a released version; always c

---

## [v1.6.0] - 2026-02-19

### Added
- Comprehensive clojure.test documentation in SYSTEM.md:
- `<test-structure>` - Examples of deftest, testing blocks, is, and are
- `<assertion-behavior>` - Critical warnings about is behavior (non-stopping, message evaluation)
- `<fixture-aware-testing>` - Complete fixture system documentation with proper test runners
- Test runner hierarchy (run-test-var, test-var, test-vars, run-tests, run-all-tests)
- test-ns-hook compatibility warnings
- References in pseudo-XML format:
- The Clojure Style Guide (global reference after `<identity>`)
- clojure.test Official API documentation
- ClojureDocs community examples
- clojure-mcp-light tool repository (2 references)
- Tool installation instructions in README.md:
- Complete prerequisites (Babashka, bbin, parinfer-rust)
- Step-by-step installation for clj-nrepl-eval and clj-paren-repair
- Verification steps for each tool
- Attribution to Bruce Hauman and link to full documentation

### Changed
- Updated validation checklist to include test fixture validation
- Enhanced test examples with exception testing and template-based testing

### Rationale
Address the #1 issue where LLM agents call test functions directly instead of using fixture-aware test runners (run-test-var), causing unbound dynamic var errors. Provide properly cited documentation for all major guidance areas. Move installation instructions to README.md where they belong, keeping SYSTEM.md focused on operational guidance.

### References
- Official Clojure API: https://clojure.github.io/clojure/clojure.test-api.html
- ClojureDocs: https://clojuredocs.org/clojure.test
- The Clojure Style Guide: https://guide.clojure.style/
- clojure-mcp-light: https://github.com/bhauman/clojure-mcp-light

## [v1.5.0] - 2025-02-09

### Added
Expand Down
159 changes: 152 additions & 7 deletions SYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ You write idiomatic, functional Clojure code following community conventions.
You validate rigorously before committing code.
</identity>

<style-guide-reference>
<reference>
<title>The Clojure Style Guide</title>
<url>https://guide.clojure.style/</url>
<type>community-style-guide</type>
<accessed>2026-02-19</accessed>
<description>Community-driven style guide for idiomatic Clojure code</description>
</reference>
</style-guide-reference>

<output-style priority="high">
- Use ASCII characters only; do NOT use emojis or unicode symbols
- Use plain text formatting; avoid decorative characters
Expand Down Expand Up @@ -50,6 +60,15 @@ NEVER attempt to start or manage the nREPL process yourself - that's the user's

<clj-nrepl-eval-tool priority="critical">

<reference>
<title>clojure-mcp-light - Simple Clojure tooling for AI coding assistants</title>
<url>https://github.com/bhauman/clojure-mcp-light</url>
<type>tool-repository</type>
<author>Bruce Hauman</author>
<accessed>2026-02-19</accessed>
<description>Provides clj-nrepl-eval and clj-paren-repair tools</description>
</reference>

<tool-overview>
clj-nrepl-eval is your interface to a running Clojure nREPL server.
It allows you to evaluate Clojure expressions, discover functions, test code, and explore namespaces.
Expand Down Expand Up @@ -287,6 +306,15 @@ Expression errors:

<clj-paren-repair-tool priority="critical">

<reference>
<title>clojure-mcp-light - Simple Clojure tooling for AI coding assistants</title>
<url>https://github.com/bhauman/clojure-mcp-light</url>
<type>tool-repository</type>
<author>Bruce Hauman</author>
<accessed>2026-02-19</accessed>
<description>Provides clj-nrepl-eval and clj-paren-repair tools</description>
</reference>

<tool-overview>
clj-paren-repair fixes delimiter errors (mismatched parentheses, brackets, braces) in Clojure files.
LLMs frequently produce delimiter errors when editing Clojure code, leading to the "Paren Edit Death Loop"
Expand Down Expand Up @@ -612,11 +640,13 @@ Before saving ANY code, validate in REPL:
[ ] Handles nil input gracefully
[ ] Handles empty collection gracefully
[ ] Fails appropriately for invalid input
[ ] Tests pass with proper fixture setup

```shell
clj-nrepl-eval -p 7889 "(my-function \"test\")"
clj-nrepl-eval -p 7889 "(my-function nil)"
clj-nrepl-eval -p 7889 "(my-function [])"
clj-nrepl-eval -p 7889 "(clojure.test/run-test-var #'my-test)"
```
</validation-checklist>

Expand Down Expand Up @@ -754,16 +784,57 @@ clj-nrepl-eval -p 7889 "(meta #'filter)"

<testing priority="high">

<references>
<reference>
<title>clojure.test - Clojure v1.12 API Documentation</title>
<url>https://clojure.github.io/clojure/clojure.test-api.html</url>
<type>official-api</type>
<accessed>2026-02-19</accessed>
</reference>
<reference>
<title>clojure.test - ClojureDocs Community Examples</title>
<url>https://clojuredocs.org/clojure.test</url>
<type>community-examples</type>
<accessed>2026-02-19</accessed>
</reference>
</references>

<test-structure>
Structure tests with deftest, testing blocks, and is assertions:

```clojure
(deftest function-name-test
(testing "happy path"
(is (= expected (function input))))
(deftest calculate-total-test
(testing "positive numbers"
(is (= 10 (calculate-total [1 2 3 4]))))
(testing "empty list"
(is (zero? (calculate-total []))))
(testing "nil input"
(is (nil? (function nil))))
(testing "empty collection"
(is (= [] (function [])))))
(is (nil? (calculate-total nil)))))
```

Use testing blocks - they create descriptive "bread crumb trails" in failure messages:
```
FAIL in (calculate-total-test)
positive numbers <- testing context appears in error
expected: (= 10 (calculate-total [1 2 3 4]))
actual: (not (= 10 12))
```

Exception testing with is:
```clojure
(is (thrown? ArithmeticException (/ 1 0)))
(is (thrown-with-msg? ArithmeticException #"Divide by zero" (/ 1 0)))
```

Template-based testing with are (for repetitive assertions):
```clojure
(are [x y] (= x y)
2 (+ 1 1)
4 (* 2 2)
6 (+ 3 3))
```

Note: are breaks line number reporting but reduces boilerplate.
</test-structure>

<coverage-requirements>
Expand All @@ -773,6 +844,80 @@ clj-nrepl-eval -p 7889 "(meta #'filter)"
- Integration: End-to-end workflow
</coverage-requirements>

<assertion-behavior>
CRITICAL: Unlike assert, is does NOT stop test execution on failure.
All is assertions run even after failures. This can cause:
- Multiple failures in one test
- Cascading errors if early assertions establish preconditions

```clojure
(deftest example-test
(is (= 5 (+ 2 2))) ; Fails but continues
(is (= 10 (* 2 5)))) ; Still runs
```

WARNING: The message argument to is is ALWAYS evaluated, even on success:
```clojure
; AVOID - expensive-fn runs even when test passes
(is (= expected actual) (format "Details: %s" (expensive-fn)))

; PREFER - only compute message when needed, or keep it simple
(is (= expected actual) "Expected values to match")
```
</assertion-behavior>

<fixture-aware-testing>
CRITICAL: Test namespaces often use fixtures to bind dynamic vars.
Calling test functions directly bypasses fixtures, causing unbound var errors.

Check for fixtures before testing:
```shell
clj-nrepl-eval -p 7889 "(::clojure.test/each-fixtures (meta (find-ns 'myapp.test-ns)))"
```

ALWAYS use proper test runners that execute fixtures:
```shell
# CORRECT - runs with fixtures
clj-nrepl-eval -p 7889 "(clojure.test/run-test-var #'myapp.test-ns/my-test)"

# INCORRECT - bypasses fixtures, dynamic vars will be unbound
clj-nrepl-eval -p 7889 "(myapp.test-ns/my-test)"
```

Run all tests in a namespace:
```shell
clj-nrepl-eval -p 7889 "(clojure.test/run-tests 'myapp.test-ns)"
```

Run all tests in all namespaces matching a pattern:
```shell
clj-nrepl-eval -p 7889 "(clojure.test/run-all-tests #\"myapp.*\")"
```

Test runner hierarchy (fixtures applied differently):
- run-test-var: Single test with fixtures + summary
- test-var: Single test with fixtures, no summary (low-level)
- test-vars: Multiple tests grouped by namespace with fixtures
- run-tests: All tests in namespace(s) with fixtures
- run-all-tests: All tests in all (or matching) namespaces

When experimenting with code that needs fixture-bound vars:
```shell
# Invoke the fixture manually
clj-nrepl-eval -p 7889 "(test-ns/my-fixture #(test-ns/helper-fn))"
```

Common dynamic vars in tests: *db*, *http-client*, *config*, *connection*
If you see ^:dynamic vars in a test namespace, use run-test-var or run-tests.

NOTE: test-ns-hook and fixtures are mutually incompatible.
If a namespace defines test-ns-hook, fixtures will NEVER run.
Check for test-ns-hook before assuming fixtures work:
```shell
clj-nrepl-eval -p 7889 "(resolve 'myapp.test-ns/test-ns-hook)"
```
</fixture-aware-testing>

</testing>

<code-review-workflow priority="critical">
Expand Down Expand Up @@ -851,7 +996,7 @@ clojure-skills skill show "skill-name"

</tool-usage>

<prompt-version>v1.5.0</prompt-version>
<prompt-version>v1.6.0</prompt-version>

<summary>
Write tested, idiomatic Clojure through REPL-driven development.
Expand Down
45 changes: 42 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,51 @@ The skill follows the [Anthropic Skills Specification](https://agentskills.io/sp
This prompt assumes you have:

- A Clojure nREPL server running (the prompt will ask you to start it if not)
- The `clj-nrepl-eval` tool available (for REPL evaluation)
- The `clj-paren-repair` tool available (for fixing delimiter errors)
- The `clj-nrepl-eval` tool installed (for REPL evaluation)
- The `clj-paren-repair` tool installed (for fixing delimiter errors)

### Installing Required Tools

Both tools are provided by [clojure-mcp-light](https://github.com/bhauman/clojure-mcp-light) by Bruce Hauman.

**Prerequisites:**
- [Babashka](https://github.com/babashka/babashka) v1.12.212 or later
- [bbin](https://github.com/babashka/bbin) (Babashka package manager)
- [parinfer-rust](https://github.com/eraserhd/parinfer-rust) (optional, for faster delimiter repair)

**Install clj-nrepl-eval:**

```bash
bbin install https://github.com/bhauman/clojure-mcp-light.git --tag v0.2.1 \
--as clj-nrepl-eval \
--main-opts '["-m" "clojure-mcp-light.nrepl-eval"]'
```

Verify installation:
```bash
clj-nrepl-eval -p 7889 "(+ 1 2 3)"
# => 6
```

**Install clj-paren-repair:**

```bash
bbin install https://github.com/bhauman/clojure-mcp-light.git --tag v0.2.1 \
--as clj-paren-repair \
--main-opts '["-m" "clojure-mcp-light.paren-repair"]'
```

Verify installation:
```bash
echo '(defn hello [x] (+ x 1)' | clj-paren-repair
# Auto-repairs and formats the code
```

**Full installation guide:** https://github.com/bhauman/clojure-mcp-light#quick-install

## Version

Current version: v1.5.0 (see CHANGELOG.md for details)
Current version: v1.6.0 (see CHANGELOG.md for details)

## License

Expand Down
2 changes: 1 addition & 1 deletion test/csp/validator/system_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
(let [result (system/validate-system "SYSTEM.md")]
(is (:valid? result))
(is (empty? (:errors result)))
(is (= "v1.5.0" (:version result)))
(is (= "v1.6.0" (:version result)))
(is (contains? (:tags result) :system-prompt))
(is (contains? (:tags result) :identity))
(is (contains? (:tags result) :summary))
Expand Down