AI-First Development

Using MemPalace memory and Claude.md documentation to enforce standards and accelerate AI-assisted development

AI-First Development Practices

Vision: SolarApp demonstrates how to structure a codebase for human-AI collaboration, using persistent memory and documented patterns to maintain code quality and architectural consistency across AI-assisted development sessions.

The Challenge

When humans and AI collaborate on code, context is fragile:

Solution: Maintain a persistent memory layer alongside your codebase — a digital "knowledge palace" where architectural decisions, design patterns, and project context live.

Claude.md: Documented Architecture

What is Claude.md?

CLAUDE.md is a markdown file committed to the repository that documents:

Example: SolarApp's Root CLAUDE.md

# CLAUDE.md

## Project Overview
SolarApp is a real-time solar system simulator built on .NET 10 + Godot
4.7.2 (.NET/mono, cross-platform).
It uses Keplerian orbital mechanics on real orbital-element data to position
8 planets, 400+ moons, 10,000+ asteroids, and 1,700+ comets.

## Project Structure
```
SolarApp/
├── SolarApp.Client/            ← the Godot client (not in SolarApp.sln)
├── SolarApp.RenderCore/        ← engine-free render math
├── SolarApp.Presentation/      ← engine-free view models + settings
├── SolarApp.AppCore/           ← engine-free craft/mission services
├── SolarApp.Scenarios/         ← mission scenario catalog
└── libraries/                  ← layered domain stack
    ├── Model.*                 ← data models
    ├── Services.*              ← business logic
    ├── Managers.*              ← orchestration
    └── Repositories.*          ← data access
```

## Layering Rules

Dependency direction: Client → Managers → Services → Models

❌ BAD:  A Model imports a Service (circular)
❌ BAD:  A library references the client or the engine
✅ GOOD: The client depends on ISimulationManager (abstraction)
✅ GOOD: All .Core dependencies flow only inward

## Where Does New Code Go?

| Decision | Location |
|---|---|
| New entity type (Planet, Asteroid, Comet) | Model.CelestialBody |
| Orbital position calculation | Services.Physics |
| Simulation state management | Managers.Simulation |
| Client initialization | SolarApp.Client/Boot/AppHost.cs |
| Render math (engine-free) | SolarApp.RenderCore |
| Anything touching the engine | SolarApp.Client |

Per-Module Claude.md Files

SolarApp has nested CLAUDE.md files in major subsystems:

This hierarchy allows depth without bloat: new developers start at the root, drill down as needed.

MemPalace: Persistent Memory

What is MemPalace?

MemPalace is a semantic memory system that records:

Unlike conversations, MemPalace survives across sessions, allowing multi-day or multi-week projects to maintain continuous context.

Memory Types

User Memories

---
name: user_role
description: User is a game developer focused on architecture and performance
metadata:
  type: user
---

Matt is a principal engineer interested in:
- Clean architecture and SOLID principles
- Real-time performance optimization
- AI-assisted development workflows
- Documenting complex systems for humans and AI

Prefers terse communication; dislikes excessive explanations of obvious code.

Feedback Memories

---
name: feedback_testing_strategy
description: Integration tests must hit real database, not mocks
metadata:
  type: feedback
---

**Rule:** Never mock the database in orbital mechanics tests.

**Why:** Past incident where mocked tests passed but production migration failed due to subtle schema differences.

**How to apply:** When writing tests for Asteroid/Comet queries, use an in-memory SQLite database or test container, not Substitute mocks.

Project Memories

---
name: project_rendering_refactor
description: Current sprint focus is the Godot client's scene layers
metadata:
  type: project
---

**Goal:** Simplify the rendering abstraction (IBodyRenderer, IAdditiveLightSource collections).

**Why:** Too many decorator layers making it hard to debug visual bugs.

**Timeline:** Sprint ending 2026-05-30.

**How to apply:** When adding new renderers, consider if we can flatten the hierarchy instead.

Reference Memories

---
name: reference_linear_asteroids
description: Asteroid-related bugs tracked in Linear project "MINORBODIES"
metadata:
  type: reference
---

Linear project "MINORBODIES" tracks all asteroid rendering, physics, and data issues.
Check there before opening a new issue — similar problems may have been solved.

Memory-Driven Development Workflow

In practice, memory integration works like this:

Session Start

  1. AI reads CLAUDE.md (root + relevant subsystems)
  2. AI recalls related memories from MemPalace (past feedback, decisions, patterns)
  3. Human provides today's task

During Development

  1. AI refers to architecture rules before writing code
  2. AI avoids mistakes documented in feedback memories
  3. AI proposes changes aligned with stated project priorities

Session End

  1. If new patterns emerged, save to MemPalace for future sessions
  2. If CLAUDE.md became outdated, update it
  3. If user gave feedback on approach, record it

Enforcing Patterns with Documentation

The Dependency Inversion Pattern

SolarApp requires all services use interface injection:

// From libraries/CLAUDE.md:
## Dependency Injection Pattern

All services must depend on abstractions, not implementations.

✅ Good:
public class BodyDetailViewModel
{
    private readonly ISelectionManager _selection;
    public BodyDetailViewModel(ISelectionManager selection) => _selection = selection;
}

❌ Bad:
public class BodyDetailViewModel
{
    private SelectionManager _selection = new();  // Tight coupling
}

When AI encounters code that violates this, the memory system flags it before changes are committed.

Double Precision at the Camera Boundary

Numeric-precision rules are documented where an AI would otherwise “simplify” them away:

// From SolarApp.Client/CLAUDE.md:
## Rendering model

CameraRig does all world→screen in doubles. AU/km values must
never reach a Godot Transform2D — at 50,000× zoom float32 loses
the orbit. Only screen-space floats go into nodes.

This single rule is why a craft in a 400 km parking orbit renders stably while the map is zoomed out to Neptune.

Decision Rationale Documentation

Design decisions include their why so AI can make similar tradeoffs:

Why the Side Panels Go Through Engine-Free View Models

// From SolarApp.Client/CLAUDE.md:
## Chrome, panels, windows

Panels are scenes over the UNCHANGED Presentation view models.
Contents pull domain state into the VM once per frame inside a
Syncing guard and never write control text directly, so the same
VMs drive the game, the panel harness, and the scenario
expectation tests.

This rationale helps AI understand the constraint behind the pattern: the per-frame push is the domain→VM sync, and the guard around it is what stops a VM write from re-entering the sync. (The view models predate the engine they run in — they survived the MonoGame-to-Godot port unchanged; the documented history stops an AI from "simplifying" that decoupling away.)

Case Study: Keeping MVVM Alive Across an Engine Swap

Memory types and one-line rules are the easy part. The real test of a memory system is whether it can hold an architectural principle together across months of sessions, dozens of contributors' worth of AI conversations, and — in SolarApp's case — a complete game-engine migration. Here is what that actually looked like for the MVVM layer, reconstructed from the palace's own drawers.

The principle being defended

SolarApp's UI architecture rests on three MVVM rules:

  1. View models are engine-free. They live in SolarApp.Presentation and may not reference the game engine — only INotifyPropertyChanged.
  2. Views bind; they never write. A panel declares its rows against VM properties once, at build time. No view code ever sets a label's text directly.
  3. Domain state is pushed into the VM once per frame, inside a Syncing guard — so a VM write can't re-enter the sync, and the same VMs drive the game, the panel harness, and the expectation tests.

None of these rules is enforceable by the compiler alone (rule 1 partially, rules 2 and 3 not at all). They survive because they are remembered — and because the memory includes the losing arguments.

The thread, as the palace recorded it

May 2026 — the honest assessment. An architecture review concluded that classic MVVM was a weak fit for the MonoGame-era codebase, and the palace kept the reasoning, not just the verdict:

[wing: c__src_solarapp / room: technical — 2026-05-07]

"Immediate-mode rendering + 60-FPS sim makes 'property changed →
notify View' ceremony a net negative. Polling per frame is cheaper
than tracking what changed. ...

RenderContext is the closest thing to an MVVM ViewModel — it bundles
every piece of state a renderer needs and is rebuilt fresh each
frame. A *transient* VM rather than a long-lived observable one.
This is the right call for a game loop. ...

ISelectionManager is MVVM-shaped, but lives in the domain layer —
it exposes SelectedBody + SelectionChanged, the canonical observable
pattern, and is reusable across UI hosts because it has zero
MonoGame dependencies."

August 2026 — the design that squared the circle. When the side panel grew into nine per-type panels, the redesign plan resolved May's tension explicitly: keep the per-frame push (the part polling was right about), but make the push land in bindable VMs (the part MVVM was right about). The plan drawer pins the exact contract:

[wing: sessions / room: architecture — 2026-08-01]

public abstract class SidePanelContentBase<TVm> : ISidePanelContent
    where TVm : PanelViewModelBase
{
    protected bool Syncing { get; }  // reentrancy guard for two-way bindings
    protected abstract void Build(ContainerRuntime root); // declare rows ONCE
    protected abstract void RefreshCore();  // domain -> Vm, called per frame
    // declarative helpers (all bind, never write .Text directly)
    protected Label AddFieldRow(..., string vmProperty);
    ...
}

Five days later — the question that paid for all of it. A Godot migration spike had to answer: does the panel architecture survive an engine swap? Instead of guessing, the session searched the palace and got the audit that had been filed as part of the spike's dependency analysis:

mempalace search "MVVM view model panels engine dependencies"

→ [sessions/architecture — 2026-08-06, similarity 0.62]
"The MVVM split is the good news for Godot. The *ViewModel classes
and the formatting helpers are largely Gum-free presentation logic —
only PanelViewModelBase.cs and ManeuverStepRow.cs import Gum.Mvvm,
and only for the INPC base class. Swap that base for a Godot-friendly
INotifyPropertyChanged and the view-models survive; only the
*PanelContent.cs view builders get rewritten as Godot Control scenes."

That drawer turned a migration risk into a checklist item. The view models crossed the engine boundary essentially byte-for-byte into SolarApp.Presentation; only the view builders were rewritten as Godot scenes. Rule 1 — "VMs are engine-free" — stopped being a style preference and became the documented reason a full engine migration didn't touch the presentation layer. That rationale now lives in CLAUDE.md; the palace holds the evidence trail behind it.

The system also audits its own documentation

The palace has a website room in which every page of this site has a companion drawer recording which repo facts the page's claims depend on. That room is what caught this very site drifting: for months, the UX page claimed "MonoGame doesn't have a binding engine, so we adapt MVVM with a manual update loop" — true when written, false after the panels moved to real data binding. The maintenance-rule drawer states the contract:

[solarapp/website — maintenance rule]

"The static website in docs/solar-app-web/ describes the live
codebase and MUST be re-checked whenever a large change lands.
It has drifted before: it claimed 'MonoGame doesn't have a binding
engine...' long after the Gum-Forms migration made the side panels
true MVVM. ...

Each page has a companion drawer in this room recording what it
claims and which repo facts it depends on. Update those drawers
when the pages change."

So when a large change lands, the AI's session doesn't just update code — it re-checks six HTML pages against their recorded claims, corrects what drifted, and updates the drawers. Documentation debt gets the same treatment as technical debt.

Why this works: constitution and case law

The division matters because principles decay in different ways. A rule with no recorded rationale gets "simplified" away by a well-meaning session ("why not just set the label text here?"). A rationale with no current rule becomes trivia. Keeping the rule in CLAUDE.md and the three-month argument behind it in the palace means an AI can be told what cheaply on every session, and can find out why — with primary sources — the moment it's tempted to deviate.

Scaling with AI

Managing Context Windows

Even with an infinite context window, reading irrelevant information is noise. MemPalace and CLAUDE.md allow AI to focus:

Consistency Across Large Teams

When multiple AIs (or humans and AIs) work on the same codebase:

Real Examples from SolarApp

Example 1: Adding a New Renderer

New task: "Add a comet-tail renderer"

  1. AI reads SolarApp.Rendering/CLAUDE.md
  2. Discovers: "All renderers implement IBodyRenderer and are registered in RenderingServiceCollectionExtensions"
  3. Checks feedback memories for "any past rendering mistakes"
  4. Finds: "Additive blending can cause GPU stalls; always profile"
  5. Implements CometTailRenderer following the documented pattern, includes performance note

Example 2: Refactoring Settings Storage

New task: "Move settings from JSON to embedded SQLite"

  1. AI reads libraries/CLAUDE.md to understand layering
  2. Finds: "Data access lives in Repositories.* layer"
  3. Checks project memories for any ongoing work on settings
  4. Finds: "Discussed UX settings redesign, but deferred to after rendering refactor"
  5. AI proposes: "Implement SQLRepository alongside JsonSettingsStore for gradual migration"
  6. Updates CLAUDE.md with new ISettingsStore variants and migration strategy

Metrics: Does It Work?

SolarApp demonstrates measurable improvements with memory and documentation:

Tools & Integration

MemPalace Setup

MemPalace integrates with Claude Code via an MCP server:

# Initialize MemPalace for this project
claude-code /mempalace init

# Mine existing project files into the palace
claude-code /mempalace mine --project SolarApp

# Search memories during development
claude-code /mempalace search "MVVM pattern"

# Add new memory
claude-code /mempalace add --wing project --room architecture \
  "Decided to use ICommandQueue for decoupling render passes"

CLAUDE.md Integration

Each module's CLAUDE.md is read at the start of relevant work:

# AI workflow reads these files automatically
SolarApp/
├── CLAUDE.md                    ← always read first
├── SolarApp.Client/
│   └── CLAUDE.md                ← read when working in the Godot client
├── SolarApp.Scenarios/
│   └── CLAUDE.md                ← read when authoring scenarios
└── libraries/
    └── CLAUDE.md                ← read when adding domain logic

Best Practices

Keep CLAUDE.md Current

Stale documentation is worse than no documentation. Update CLAUDE.md when:

Tip: Schedule quarterly "CLAUDE.md health checks" to catch drift.

Be Specific in Feedback Memories

Bad: "Tests are important"

Good: "Orbital mechanics tests must use real ephemeris data (via JPL API or offline DE430 dump), not synthetic test data. Mocks diverge from reality; we've been burned before."

Link Memories Together

Use `[[memory-name]]` syntax to cross-reference related memories:

---
name: feedback_testing_ephemeris
description: Orbital tests must use real data
metadata:
  type: feedback
---

**Rule:** Never mock astronomical data.

**Why:** [[incident_2024_mock_test_divergence]]

**Related:** [[pattern_integration_test_setup]]

The Future: AI as Knowledge Worker

This approach treats AI as a knowledge worker that:

Rather than "AI as code generator," we're building "AI as architectural partner" — a collaborator that respects the codebase's structure and history.