Files
marfrit 7b5d58686e docs: codify contribution flow — issues for features, PRs for review
Captures two carve-outs to aish's "non-PR-flow repo" default:

- Feature requests and bugs go to git.reauktion.de/marfrit/aish/issues
  rather than direct-implement-in-band. Tag `architecture` for cross-
  phase concerns. Aligns with the fleet-wide bug-filing convention from
  the `his` cheatsheet; this row extends it to features for aish.
- Review-required iteration opens a PR (authored as claude-<host>,
  marfrit reviews, self-approval forbidden). PR #1 was the precedent.

Both are opt-in; direct-to-main remains the default for autonomous
work that doesn't need a feedback loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 11:04:51 +00:00

8.8 KiB

CLAUDE.md — aish project handoff

You are continuing work on aish, an AI-augmented conversational shell implemented in LuaJIT. This file is auto-loaded when a Claude session opens a clone of this repo. Read it once at session start.


1. Read these first, in order

  1. docs/PHASE0.md — locked substrate. Every architectural decision is here. Do not contradict it without amending it.
  2. README.md — human-facing project summary and orientation.
  3. config.lua — the runtime model registry. Endpoints reflect a specific LAN; the user may have already adapted yours.

If a question seems open after reading those three, ask the user a single focused question rather than guessing. Cost of asking is one turn; cost of building on a wrong assumption is half a phase.


2. Where you are in the phase loop

aish follows an 8(+1) phase loop:

0 substrate → 1 formulate → 2 analyze → 3 baseline → 4 plan
              → 5 review → 6 implement → 7 verify → 8 memory-update
                                                 (+1 reflect)

Loopbacks:

  • 3 → 1 if baseline data invalidates the formulation
  • 7 → 4 if verification fails — fix the plan, not the substrate
  • any → 0 if a substrate fact turns out to be wrong

Don't skip phases. Phase 0 is done (the manifest is locked); your starting position depends on what's already in the tree.

Check the state by reading docs/ and git log --oneline. If only PHASE0.md exists in docs/ and every module raises NotImplemented, you are at Phase 0 → Phase 1 transition: the substrate is locked, no module is implemented, your job is to write the Phase 0 implementation per the manifest.

If you find phase docs docs/PHASE1.md, docs/PHASE2.md etc., follow their state.


3. Source-of-truth invariants — do not violate without amending PHASE0.md

These are not stylistic preferences; they are decisions that downstream phases depend on:

Invariant Where it's locked
LuaJIT 2.x only — no PUC-Rio Lua-only constructs §3
FFI only — no compiled C extensions, no luarocks packages §3
Module file names in §4 are stable across phases §4
/v1/chat/completions is the broker contract §6
CMD: (exact prefix, single space) is the command-extraction marker §6
Config is plain Lua loaded with dofile, not JSON/YAML §3, §10
cd is intercepted via libc chdir, not delegated to popen §7
Phase 0 has no disk I/O for history (memory only) §8

If a Phase N implementation needs to break one of these, amend PHASE0.md in the same commit and call out the change in the commit message. Don't silently diverge.


4. Implementation order for Phase 0

Bottom-up beats top-down for this codebase. Suggested ordering:

  1. ffi/libc.luachdir, strerror work; verifiable in isolation.
  2. ffi/readline.luareadline(), add_history(), free() work; test with a tiny REPL stub before wiring repl.lua.
  3. ffi/curl.lua — easy interface, blocking POST with response capture into a Lua string. Test against any local llama-server with a curl one-liner side-by-side.
  4. context.lua — pure data structure, trivial to unit-test.
  5. executor.luapopen wrapper, cd interception (uses libc.chdir), CMD: line extraction.
  6. router.lua — pure function; classify(line, config) → (kind, payload).
  7. broker.lua — uses ffi/curl.lua + JSON encode/decode. You will need a JSON library — see §6 below.
  8. renderer.lua — output formatting; trivial.
  9. repl.lua — wires everything via the dispatch table.
  10. main.lua — already mostly there; finalize once repl.run exists.

Don't write all ten in one commit. One commit per module, build the chain by passing through; each commit should leave the tree in a state where luajit main.lua either runs further than the previous commit or fails with the next NotImplemented.


5. Testing approach

There is no test framework dependency by design. Testing is per-module ad-hoc with luajit -e 'local m = require("module"); ...' from the repo root, or a smoke luajit main.lua after each module lands.

For broker testing without burning model time: any of the local llama-servers in config.lua will respond to a hand-crafted POST. Use curl -sS http://dirac.fritz.box:8081/v1/chat/completions -d '{...}' to generate a known-good reference, then compare your FFI output.


6. JSON encode/decode — undecided

The manifest doesn't pick a JSON library. LuaJIT 2.x has no built-in JSON. Options:

  • dkjson — pure Lua, single file, vendor it under vendor/dkjson.lua. Slow but no dependency.
  • cjson — fast, but it's a C extension. Violates §3 (no compiled extensions). Skip unless you amend the manifest.
  • Hand-rolled minimal encoder — viable since the broker payload shape is small and well-defined; ~50 LOC.

Recommend dkjson vendored. Decide and add a note to PHASE0.md §3 in the same commit.


7. When you hit ambiguity

The user (mfritsche) prefers being asked over being assumed-about. The phrase "Ask readily — prefer quick question over 3+ guessing attempts" applies. Concrete pattern:

  • If the question affects only the current commit: ask.
  • If it affects a future phase: log it in docs/PHASE0.md §13 (Open Questions) with target phase, keep working.
  • If it affects multiple existing modules: stop, ask before refactoring.

Don't suggest pausing the session. Don't suggest "let's pick this up tomorrow". Don't pre-emptively defer work the user hasn't asked you to defer. The user controls pace.


8. Commit style

Imperative subject, file-scoped where possible. Examples:

  • executor: implement io.popen wrapper with stderr merge
  • ffi/curl: blocking POST with header list and response buffer
  • repl: wire router → executor → broker dispatch
  • phase0 amendment: vendor dkjson under vendor/

Body explains the why if non-obvious. Reference PHASE0.md sections by number when relevant.

Co-Authored-By trailer on Claude-authored commits:

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

(or whichever model you actually are; check before substituting.)


9. What aish does NOT do — out of scope, all phases

These are listed in PHASE0.md §12. Briefly:

  • Does not manage llama.cpp lifecycle (assumed externally running)
  • Does not implement model inference
  • Is not a multiplexer (no tmux semantics)
  • Is not a sandbox (no namespaces, no seccomp)

If you find yourself implementing any of those, stop — that's a different project.


10. The model serving aish (typically)

aish targets local llama.cpp endpoints. The committed config.lua references the user's home network (dirac.fritz.box, hossenfelder.fritz.box). The user's other Claude sessions have established that small Q4_K_M models (Qwen2.5-Coder-7B and similar) have variance issues on code generation tasks — the same prompt can yield both correct and broken code across rolls. Practical implications for aish:

  • Default temperature to 0.2 or lower for code tasks.
  • Don't assume the model output is correct — validate before exec.
  • The confirm_cmd = true default in config.shell is there for this reason; don't disable it without a deliberate UX change.

This isn't paranoia, it's a measured property of the local model class the user runs.


11. Identity

If this is a session running on a fleet host other than the user's primary Claude window, your Gitea identity is claude-<hostname>. For aish (a non-PR-flow repo), commits as that identity are fine without a PR. If you need to push and lack credentials, use a Gitea Personal Access Token in the URL: git push https://<user>:<token>@git.reauktion.de/marfrit/aish.git main.

The user has marfrit-level credentials available via a separate channel if needed for repo-admin operations.


12. Contribution flow

Default for direct work: commit straight to main. No PR, no issue gate. This is what "non-PR-flow repo" means in §11.

Two opt-in carve-outs:

  • Feature requests and bugs → Gitea issues at git.reauktion.de/marfrit/aish/issues. Don't implement feature requests in-band; file the issue, let marfrit triage. Tag architecture for cross-phase concerns. (Bug-filing convention is fleet-wide per the his cheatsheet; this row extends it to features for aish specifically.)
  • Review-required iteration → PR. When the medium needs to be the diff (inline comments per finding, refinable wording), open a PR authored as claude-<host> and let marfrit review. Self-approval forbidden. PR #1 (marfrit/aish#1, 2026-05-10) set the precedent — the MCP phase-2 question batch surfaced by review of 013c625.

When in doubt whether something is a feature request vs. an in-band fix, ask. Cheaper than the alternatives.