Skip to content
Draft
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
14 changes: 14 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Collaboration Rules

generic project rules: see [`agents/rules.md`](agents/rules.md).
when assisting in architecture analysis/review:
strictly follow [`agents/architecture_assistance.md`](agents/architecture_assistance.md).

## What this is

See [system overview](agents/context.md)

13 changes: 13 additions & 0 deletions agents/architecture_assistance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
When acting as an assistant to architect/analyst:

- Role: assistant to a senior software architect/developer — analysis, code inspection, suggestions, and other cognitive tasks by default.
- When broad picture of architecture is needed: Check doc/development/ (especially [overview](doc/development/overview.md), [internals](doc/development/internals/) and [conventions](doc/development/conventions/) prior to reverse-engineering the codebase
- File edits are allowed when requested; the user reviews all changes and handles commits/stashes.
- Only make local edits of files beyond doc/ when explicitly asked to.
- No git commits, no GitHub interaction, no agent mode.
- Apply project coding rules: see [`agents/rules.md`](agents/rules.md).
- grep, bash, sed and similar tools are allowed unless prohibited by other rules
- tampering with git history (altering .git directory) is prohibited
- read-only git operations allowed, staging is allowed with confirmation
- git reset/unstage/commit/rebase/merge/pull/push and similar -- only when explicitly asked
- read-only access to public git repositories and to raw.githubusercontent.com is allowed
68 changes: 68 additions & 0 deletions agents/context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
## Reference Material

**Do not load these files proactively** — read on demand only.

- `doc/development/conventions/code.md` — formatting and style conventions
- `doc/development/conventions/architecture_principles.md` — design philosophy
- `doc/development/conventions/git.md` — commit message conventions
- `doc/development/overview.md` — MVC structure, OOP patterns, modes, evaluation pipeline
- `doc/development/drawing_system.md` — virtual canvas, pen-and-paper vs real-time draw
- `doc/development/internals/` — console, editor, user input, examples (detailed subsystem docs)

## What This Is

**Compy** is a console-based, Lua-programmable fantasy computer for children, built on the [LÖVE2D](https://love2d.org) framework (v11.5). MVC architecture, classes as Lua globals, Lua 5.1/LuaJIT. Runs as an IDE, a project player, or a test harness. See `doc/development/overview.md` for architecture details.

## Commands

### Running

```sh
love src # IDE mode
love src test [--auto] [--size] [--draw] # test mode
love src play <proj> # run a project or .compy zip
love src harmony # screenshot testing mode
```

### Tests

```sh
busted tests # run all
busted tests --tags <tag> # by tag (common: ast, src, parser, analyzer)
just ut <TAG> # run once for one tag
just ut_all # run all once
just unit_test # all, with nodemon auto-reload
just unit_test_tag <TAG> # one tag, auto-reload
```

### Development

```sh
just dev # run app with auto-reload on .lua changes
just dev-atest # run app with --auto test on each change
```

### Packaging & Setup

```sh
just package # dist/game.love
just package-js # web version via love.js
just setup-web-dev # install web tooling (npm)
just deploy-examples # copy examples to ~/Documents/compy/projects
git clone --recurse-submodules # required: two submodules (metalua, stringutils)
luarocks --local --lua-version 5.1 install busted luautf8 luafilesystem
```

### Environment Variables

| Variable | Effect |
|---|---|
| `DEBUG=1` | Debug mode (extra keybindings, draw overlays) |
| `HIDPI=true` | Double-scale display |
| `COMPY_PROF=1` | Enable profiler |
| `COMPY_WRAP=<n>` | Override drawable char width (debug mode) |
| `COMPY_LINES=<n>` | Override visible line count (debug mode) |

### Pre-commit Hook

`just setup-hooks` installs a hook that runs `busted tests` before every commit.
133 changes: 133 additions & 0 deletions agents/rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Compy — Coding Rules

Audience: LLM assistant. Apply when writing, reviewing, or analysing code in this repo.
Source: compiled from [`doc/development/conventions/code.md`](../doc/development/conventions/code.md) and [`doc/development/conventions/architecture_principles.md`](../doc/development/conventions/architecture_principles.md).

---

## Architecture analysis

When broad picture required (not a focused check of specific behaviour):
Use `./doc/development/*` as the initial source of syntetic pre-extracted knowledge prior to direct codebase inspection (assume minor mistakes though)

Load those as needed for the current task, not everything upfront.

Mapping of reference docs:
./doc/development/overview.md -- bird-eye overview of the architecture
`./doc/development/conventions/*` -- fundamental dev-facing rules
./doc/development/conventions/architecture_principles.md -- fundamental conventions and constraints applying to the whole project
./doc/development/conventions/code.md -- code style
./doc/development/conventions/git.md -- conventional commits usage
`./doc/development/internals/*` -- current implementation essentials, derived from codebase isnpection -- currently cover editor, console, examples
./doc/development/internals/user_input.md -- cross-component usage of user input in different modes
./doc/development/drawing_system.md -- unobvious wiring of drawing system, enabling paper-and-pen vs realtime drawing modes

Some (not all) essential excerpts from these docs are inlined below (they may be sufficient in many contexts)

## Hard Limits (coding)

| Constraint | Limit |
|---|---|
| Line length | 64 chars |
| Function body | 14 lines |
| Parameters | 4 |
| Nesting depth | 4 |

When a limit is approached, redesign — don't raise the limit.

---

## Formatting (editor-enforced)

- Metalua auto-prettyprints; don't fight it. Published examples must be idempotent under the built-in editor.
- `end` closes on the same line as the last statement in its block.
- Empty table: `{ }` (spaces inside braces).
- Table/array literals: one element per line.
- Comments: own line only, never inline.
- At most one blank line between non-blank lines.

---

## Scope & Naming

- Locals scoped to the smallest enclosing block. No variable outlives its purpose.
- Extract compound conditions to named locals — name the intent, not the mechanics:
```lua
local done = count >= max or timed_out
if done then ... end
```
- Standard aliases at module top: `local gfx = love.graphics`, `local sfx = compy.audio`.

---

## Design

**File = console equivalence.** Code in a `.lua` file and the same code typed into the Compy REPL must behave identically. No load-order side effects; no implicit global state that the REPL wouldn't have.

**Prefer functional style.** Closures, iterators, and immutable-by-convention data are preferred. They compose cleanly and stay readable.

**Avoid deep OOP.** Inheritance chains, manager-of-manager classes, and factory abstractions are anti-patterns here. If a student with six months of experience couldn't follow the call graph, it's too abstract.

**No C accent.** Don't use string/integer tags as surrogate function pointers dispatched via if-chains. Store and pass the function itself.
```lua
-- wrong
local dir = 'up'
if dir == 'up' then go_up() elseif dir == 'down' then go_down() end

-- right
local dir = go_up
dir()
```
Same applies to dispatch tables: `actions[key]()` beats `if key == 'x' then ... end`. The framework has accumulated some of this; new code and all examples must not.

**Pedagogical test.** Before adding an abstraction: *could a motivated student understand and modify this?* If not, look for a simpler path.

---

## Tone in Analytic Notes and Specs

Tech debt and internal inconsistencies are expected in any system that evolved organically under real constraints. Treat them as such — not as failures to highlight, but as context to note.

When writing analysis, specs, or review notes:
- State what is, no blame tone. "This module has accumulated several responsibilities" not "this is bad/wrong"".
- Flag debt matter-of-factly without judgement: "likely a historic tradeoff", "has known complexity here", "worth revisiting when X", "may prevent future Y unless resolved".
- The audience is the same people who built the system. They are senior, aware of the tradeoffs, and acting in good faith. They do not need to be told something is "wrong" — they need a clear picture of what is and what the options are.
- That said, no unsolicited flattery or patronism, no false reassurance. Collegial, humble, accurate and direct is the target register.

---

## Summaries for Stakeholders

When writing a stakeholder-facing summary or "in brief" section:

- **Mirror the stakeholders' own tone and everyday vocabulary** from the source input — their words ("get rid of the legacy API", "only text fields", "before 1.0", "showcase", "trivial to convert"), not internal jargon (milestone IDs, "PERT", "reftable", "facade", "sink"). Spell a term or figure out only the first time it is genuinely unavoidable.
- **Keep it short without losing meaning.** A summary is a statement, not a dialogue — no second-person address.
- **Do not re-explain stakeholders' own requests back to them in the same words.** They already know what they asked for. In a feedback-check context, answer what they actually want to know: whether any architectural regression resulted, whether price / risk / order changed, and what the new plan is — and point them to the updated `roadmap.md` / `summaries/roadmap.md`, which is where they will look.
- **Read whether they are confirming or exploring.** Judge the register of the ask first: are they *assuming* something and looking to have it confirmed or disproved, or are they *unsure* and want more information? When they are clearly assuming (a leading question, a stated presumption), frame a matching finding as a confirmation ("Confirmed: …", "as expected") rather than presenting it as a surprise — and disprove just as directly when the assumption is wrong. When they are genuinely unsure, lead with the information plainly.

---

## Commit Messages

Conventional Commits, driven by semver semantics. Full guide: `doc/development/conventions/git.md`.

| Type | When |
|---|---|
| `feat` | New capability |
| `fix` | Bug fix |
| `refactor` | Restructuring, no behavior change intended |
| `style` | Cosmetic source changes only (whitespace, comments) — **not** CSS |
| `test` | Test code only |
| `chore` | Build system, scripts, tooling — **not** "small annoying task" |
| `docs` | Documentation only |

Breaking change: append `!` to type (`feat!:`) or add `BREAKING CHANGE:` footer.
Scope: project-specific, not yet formally documented — omit rather than guess.

---

## Memory / GC

- Avoid allocation in hot paths (`update`, `draw`). Reuse tables and values where the pattern is clear.
- **Mandatory in student-facing examples.** Encouraged in new framework code. Existing framework code may have known debt here and there — just don't add more.
- Prefer iterators over intermediate tables. Prefer preallocated buffers over per-frame construction.
82 changes: 82 additions & 0 deletions doc/development/conventions/architecture_principles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Architecture Principles

Compy is an educational artifact as much as a software project. Its architecture reflects that: the goal is to model good habits for students, not to maximize abstraction or minimize developer friction.

---

## Transparency

**Code in a file and code typed into the console must have the same effect.**

This is not just a technical constraint — it is the foundational design principle. It means modules must not rely on load-order side effects, implicit global state should be minimal, and the runtime environment seen by student code should feel like a natural extension of the REPL they work in daily.

A corollary: if a piece of code can only be understood in the context of its call stack, its class hierarchy, or its module graph, it is already too complex.

---

## Cognitive Budget

The structural limits in the code conventions (64-char lines, 14-line functions, 4 parameters, 4 nesting levels) are not style preferences — they are a budget. They exist to keep code within the working memory of a child reading it. This applies doubly to student-facing examples.

When a piece of code is approaching a limit, the right response is usually to rethink the design, not to raise the limit.

---

## Scope Discipline

Locals belong to the smallest block that needs them. No variable should outlive its purpose. This reduces cognitive load (fewer names in scope at any point) and avoids subtle bugs from state leaking across logical boundaries.

Extract compound conditions to named locals — this is not just style, it is a form of inline documentation that names the *intent* of a test.

---

## Prefer Functional Style

Lightweight closures, Lua iterators, and immutable-by-convention data structures are preferred over stateful objects wherever readability is not harmed. Closures are cheap, local, and self-documenting in Lua. Iterators compose cleanly. Immutable data is easy to reason about.

Deeply layered OOP — inheritance chains, manager classes, factory-of-factory patterns — is an anti-pattern here. It hides behavior, inflates the call graph, and produces code that is hard for a student (or a teacher) to follow. Our metric is not "is this extensible?" but "can someone who has been programming for six months read this?"

---

## Write Lua, Not C with Lua Syntax

A recurring C habit is using a string or integer tag as a surrogate function pointer, then dispatching on it with an if-chain:

```lua
-- C-accented Lua
local dir = 'up'
if dir == 'up' then go_up()
elseif dir == 'down' then go_down() end
```

In Lua, functions are values. The tag and the if-chain are unnecessary:

```lua
-- idiomatic Lua
local dir = go_up
dir()
```

The same applies to dispatch tables: rather than a string key checked at call time, store the function directly and call it. The result is shorter, has no conditional, and makes the intent structurally visible rather than textually described.

This matters especially in student-facing code. Students learning Lua should build Lua intuitions — treating functions as first-class values, not as something to be named with a string and dispatched manually. The C habit, once acquired, is hard to unlearn and tends to produce code that is both more verbose and harder to reason about than the idiomatic alternative.

The framework has accumulated some of this pattern in places (it is organic code); new code and all examples should avoid it.

---

## Memory Consciousness

Avoid unnecessary allocation. In a GC-managed runtime on modest hardware, object churn adds up — in tight loops especially. Prefer reusing tables and values over creating new ones when the logic permits.

This principle is **mandatory in student-facing examples** (which students read, copy, and run on real hardware) and **encouraged in new framework code**. The organically grown parts of the framework are not always clean on this front; that is acceptable technical debt, not a license to add more.

Concrete habits: avoid allocating in `update`/`draw` hot paths; prefer iterators over intermediate tables; reuse preallocated buffers where the pattern is clear.

---

## The Pedagogical Test

Before introducing a new abstraction, pattern, or dependency, ask: *would a motivated student be able to understand and modify this?* If the honest answer is no, look for a simpler approach.

The project is not a software factory. It is a teaching instrument that also happens to run.
78 changes: 78 additions & 0 deletions doc/development/conventions/code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Code Conventions

> These conventions are not fully fixed — they evolve with experience. Some are unique to this project by design.
> Further discussion: https://github.com/compy-toys/compy/issues/54

---

## Formatting

Formatting is handled automatically by the metalua pretty-printer. No manual formatting rules are enforced — the built-in Compy editor auto-reformats on save. **Published examples must be idempotent under the editor** (run the editor over the code before publishing; it should produce no changes).

### Style examples

```lua
-- if/for/function: `end` on the same line as the last statement in the block
if condition then
...end

for iteration do
...end

function outer(args)
local inner = function(args)
... end
end

-- empty table: spaces inside braces
empty = { }

-- array/table: each element on its own line
array = {
1,
2,
3
}

-- all comments on their own line (not inline)

-- at most one blank line between non-blank lines
```

---

## Structural Limits

| Rule | Limit |
|---|---|
| Line length | 64 characters (fits the Compy screen) |
| Function length | 14 lines |
| Parameters per function | 4 |
| Nesting depth | 4 levels |

---

## Code Style

**Extract compound conditions to named locals:**
```lua
-- avoid
if a and b or c then end

-- prefer
local cond = a and b or c
if cond then end
```

**Do not use local variables outside their block.** A local should be scoped as tightly as possible.

**File = console equivalence.** Code in a `.lua` file and the same code typed into the Compy console must have identical effect. This guides how modules and scripts are structured.

---

## Standard Abbreviations

```lua
local gfx = love.graphics
local sfx = compy.audio
```
Loading
Loading