6dfc2a9308
- T5.3: HAMT Persistent Map (lib/cljnim_pmap.nim)
O(log32 n) assoc/dissoc/get with structural sharing
16 unit tests, ckMap migrated from seq to HAMT
- T5.4: Persistent Set backed by HAMT map
ckSet with conj/disj/contains?/get, set literal #{}
- Fix: cljContains/cljCount return CljVal (not bool/int)
- Fix: equality in generics via explicit cljEq parameter
- Updated roadmap, task board, CI/Makefile
113 lines
6.6 KiB
Markdown
113 lines
6.6 KiB
Markdown
# Task Board for AI Agents
|
|
|
|
> Pick a task, implement it, test it, commit it. Each task is self-contained.
|
|
|
|
## Legend
|
|
- 🔴 Large — 1+ week
|
|
- 🟡 Medium — 2-5 days
|
|
- 🟢 Small — few hours
|
|
- ⬜ Not started / 🔄 In progress / ✅ Done
|
|
|
|
---
|
|
|
|
## Phase 4: AI-Native Tooling (Complete)
|
|
|
|
| ID | Task | Status | Complexity | Files | Acceptance Criteria |
|
|
|---|---|---|---|---|---|
|
|
| T4.1 | JSON REPL mode | ✅ | 🟡 | `src/repl.nim` | `./cljnim repl --json` works, structured I/O |
|
|
| T4.2 | Batch evaluation | ✅ | 🟢 | `src/repl.nim` | `{"op":"eval-batch","forms":["(defn f[x]x)","(f 42)"]}` works |
|
|
| T4.3 | File operations | ✅ | 🟡 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(file/read)`, `(file/write)`, `(file/ls)` work |
|
|
| T4.4 | Git operations | ✅ | 🟡 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(git/status)`, `(git/commit)`, `(git/push)` work |
|
|
| T4.5 | nREPL protocol | ⬜ | 🟡 | `src/repl.nim` | REPL speaks nREPL over TCP |
|
|
| T4.6 | Tool-call format | ⬜ | 🟢 | `src/repl.nim` | Accept `{"tool":"cljnim/eval","args":{...}}` |
|
|
|
|
---
|
|
|
|
## Phase 5: Persistent Data Structures
|
|
|
|
| ID | Task | Status | Complexity | Files | Acceptance Criteria |
|
|
|---|---|---|---|---|---|
|
|
| T5.1 | HAMT Vector — core structure | ✅ | 🔴 | New: `lib/cljnim_pvec.nim`, `lib/cljnim_runtime.nim` | `PersistentVector` with 32-way trie, structural sharing. `nth` in O(log₃₂ n). 14 tests. |
|
|
| T5.2 | HAMT Vector — integration | ✅ | 🔴 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `ckVector` uses HAMT. All 51 `vecData` usages migrated. |
|
|
| T5.3 | HAMT Map | ✅ | 🔴 | New: `lib/cljnim_pmap.nim`, `lib/cljnim_runtime.nim` | Persistent Hash Map with HAMT. `assoc`, `dissoc`, `get` in O(log₃₂ n). 16 tests. |
|
|
| T5.4 | Persistent Set | ✅ | 🟡 | `lib/cljnim_runtime.nim` | Set backed by Persistent Hash Map. `conj`, `disj`, `contains?`, `get`, `count`. Reader emits `(set [items])`. |
|
|
| T5.5 | Transients | ⬜ | 🟡 | `lib/cljnim_runtime.nim` | `transient`, `persistent!`, `conj!`, `assoc!` for batch mutations. |
|
|
|
|
**Why HAMT matters**: Current Vector is a Nim `seq` — every `conj` copies the entire array (O(n)). Real Clojure uses Hash Array Mapped Trie for O(log₃₂ n) updates with structural sharing.
|
|
|
|
**Starting point for T5.1**: Read `lib/cljnim_vector.nim` (if exists) or research Clojure's `PersistentVector.java`. Key concepts: 32-way branching, path copying, tail optimization.
|
|
|
|
---
|
|
|
|
## Phase 6: Clojure Core Library
|
|
|
|
| ID | Task | Status | Complexity | Files | Acceptance Criteria |
|
|
|---|---|---|---|---|---|
|
|
| T6.1 | `str` — string concatenation | ⬜ | 🟢 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(str "a" 1 true)` → `"a1true"`. Test in `tests/test_emitter.nim`. |
|
|
| T6.2 | `pr-str` — readable representation | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(pr-str [1 2 3])` → `"[1 2 3]"`. Escapes strings properly. |
|
|
| T6.3 | `slurp` — read file to string | ⬜ | 🟢 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(slurp "file.txt")` returns content. Already have `file/read` — just alias/wrap. |
|
|
| T6.4 | `spit` — write string to file | ⬜ | 🟢 | `lib/cljnim_runtime.nim`, `src/emitter.nim` | `(spit "file.txt" "content")` writes file. |
|
|
| T6.5 | `read-line` — read from stdin | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(read-line)` reads one line from stdin. |
|
|
| T6.6 | `range` — lazy number sequence | ⬜ | 🟡 | `lib/cljnim_runtime.nim` | `(range 10)`, `(range 1 10)`, `(range 1 10 2)`. Returns lazy seq. |
|
|
| T6.7 | `repeat`, `cycle`, `iterate` | ⬜ | 🟡 | `lib/cljnim_runtime.nim` | Infinite lazy sequences. |
|
|
| T6.8 | `take`, `drop` — seq slicing | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(take 5 (range 100))`, `(drop 5 [1 2 3 4 5])`. |
|
|
| T6.9 | `partition`, `interleave` | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(partition 2 [1 2 3 4])`, `(interleave [1 2] [3 4])`. |
|
|
| T6.10 | `meta`, `with-meta`, `vary-meta` | ⬜ | 🟡 | `lib/cljnim_runtime.nim`, `src/types.nim` | Metadata support on vars, functions, collections. |
|
|
| T6.11 | `type`, `instance?` | ⬜ | 🟢 | `lib/cljnim_runtime.nim` | `(type 42)` → `:int`. `(instance? :int 42)` → true. |
|
|
|
|
---
|
|
|
|
## Phase 7: Project Compilation
|
|
|
|
| ID | Task | Status | Complexity | Files | Acceptance Criteria |
|
|
|---|---|---|---|---|---|
|
|
| T7.1 | `ns` declaration parsing | ⬜ | 🟡 | `src/reader.nim`, `src/emitter.nim` | `(ns my.app (:require [other.lib :as lib]))` parses and compiles. |
|
|
| T7.2 | Multi-file compilation | ⬜ | 🔴 | `src/cljnim.nim`, `src/emitter.nim` | `./cljnim compile project.clj` finds all required files and compiles them together. |
|
|
| T7.3 | Module caching | ⬜ | 🟡 | `src/cljnim.nim` | Compiled `.nim` files cached in `nimcache/`. Rebuild only changed files. |
|
|
| T7.4 | Dependency resolution | ⬜ | 🔴 | New: `src/deps.nim` | Read `deps.edn` or `project.clj` format. Download deps from Git. |
|
|
|
|
---
|
|
|
|
## Phase 8: Self-Hosted REPL
|
|
|
|
| ID | Task | Status | Complexity | Files | Acceptance Criteria |
|
|
|---|---|---|---|---|---|
|
|
| T8.1 | In-memory compilation | ⬜ | 🔴 | `src/repl.nim`, `src/emitter.nim` | REPL compiles to Nim AST in memory, no temp files. |
|
|
| T8.2 | Fast REPL startup | ⬜ | 🟡 | `src/repl.nim` | REPL starts in < 100ms. Pre-compiled runtime loaded once. |
|
|
| T8.3 | Hot code reloading | ⬜ | 🟡 | `src/repl.nim` | Redefine a function, all callers use new version immediately. |
|
|
|
|
---
|
|
|
|
## Phase 9: Concurrency
|
|
|
|
| ID | Task | Status | Complexity | Files | Acceptance Criteria |
|
|
|---|---|---|---|---|---|
|
|
| T9.1 | Atoms (CAS) | ⬜ | 🟡 | `lib/cljnim_runtime.nim` | `(def a (atom 0))`, `(swap! a inc)`, `(reset! a 42)`, `(deref a)`. |
|
|
| T9.2 | Agents | ⬜ | 🟡 | `lib/cljnim_runtime.nim` | `(def ag (agent 0))`, `(send ag inc)`. Async execution. |
|
|
| T9.3 | core.async channels | ⬜ | 🔴 | New: `lib/cljnim_async.nim` | `(chan)`, `(>! ch val)`, `(<! ch)`, `(go ...)`. Simplified version. |
|
|
|
|
---
|
|
|
|
## Quick Wins (do these first!)
|
|
|
|
These tasks are **small, well-defined, and high impact**:
|
|
|
|
1. **T6.1 `str`** ✅ — Done. `cljStrConcat` in runtime, `str` mapping in emitter.
|
|
2. **T6.3 `slurp`** — Alias of existing `cljFileRead`. 1 line of work.
|
|
3. **T6.4 `spit`** — Alias of existing `cljFileWrite`. 1 line of work.
|
|
4. **T6.8 `take`, `drop`** ✅ — Done. `cljTake`, `cljDrop` in runtime, emitter mappings.
|
|
5. **T6.11 `type`, `instance?`** ✅ — Done. `cljType` in runtime, `type` mapping in emitter.
|
|
6. **T4.6 Tool-call format** — Wrap REPL input parser to accept `{"tool":...}`.
|
|
|
|
---
|
|
|
|
## How to claim a task
|
|
|
|
1. Read this file
|
|
2. Pick an unclaimed task (marked ⬜)
|
|
3. Implement it
|
|
4. Run `make test && make check`
|
|
5. Update this file: change ⬜ to ✅
|
|
6. Commit: `(git/commit "T6.1: Add str function")`
|
|
7. Push: `(git/push)`
|