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
25 changes: 25 additions & 0 deletions cmd/mxcli/cmd_brain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -207,3 +208,27 @@ func TestChangedShardsMapsPathsToShards(t *testing.T) {
}

func day() time.Time { return time.Date(2026, 9, 3, 0, 0, 0, 0, time.UTC) }

// The store's project.md is documented as loaded every session, and its cap —
// the tightest in the store — is justified by exactly that. Routing to it
// through the skill alone does not make it true: a skill is triggered by
// symptom, so a session that never hits the symptom never reads the project's
// decisions. The generated CLAUDE.md is the only thing that makes the claim
// mechanical, which is why it is asserted here rather than left to review.
func TestGeneratedClaudeMDRoutesToTheBrainAtSessionStart(t *testing.T) {
md := generateClaudeMD("Demo", "Demo.mpr")
for _, want := range []string{
"docs/brain/project.md", // the unconditional read
"docs/brain/modules/<Module>", // the on-demand shards
"brain plan", // how to pick work up
} {
if !strings.Contains(md, want) {
t.Errorf("generated CLAUDE.md does not mention %q — project.md is then loaded only when a symptom happens to trigger the skill, and the cap that assumes otherwise is unfounded", want)
}
}
// It has to come before the bulk of the file, or "read this first" is a
// claim the document's own ordering contradicts.
if i := strings.Index(md, "docs/brain/project.md"); i < 0 || i > len(md)/3 {
t.Errorf("the brain section is at byte %d of %d; it is meant to be read first", i, len(md))
}
}
19 changes: 19 additions & 0 deletions cmd/mxcli/init_claudemd.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,25 @@ func generateClaudeMD(projectName, mprFile string) string {
w("This is a Mendix project configured for AI-assisted development using mxcli and MDL (Mendix Definition Language).\n\n")

// ── Communication Style ────────────────────────────────────────
// The brain's project.md is described as "loaded every session", and this
// is the only thing that makes that true. Routing to it through the skill
// alone does not: a skill is triggered by symptom, so a session that never
// hits the symptom never learns the project's own decisions — and the
// tightest cap in the store was justified by an unconditional load that
// nothing actually performed.
w("## Project Brain — read this first\n\n")
w("If " + bt + "docs/brain/" + bt + " exists, read " + bt + "docs/brain/project.md" + bt + " before doing anything\n")
w("else. It holds the decisions this project has already made — things no command can\n")
w("tell you, and that are cheap to contradict by accident.\n\n")
w("Then, depending on what you are doing:\n\n")
w("- **Building in a module** — also read " + bt + "docs/brain/modules/<Module>.md" + bt + " for the\n")
w(" modules you are about to touch. Not the whole directory; only those.\n")
w("- **Planning, or picking work up** — run " + bt + "./mxcli brain plan -p " + mprPath + bt + ".\n")
w(" It reports what is built from the model itself, so it cannot be out of date.\n\n")
w("Record what you learn with " + bt + "./mxcli brain capture" + bt + ". Read\n")
w(bt + ".ai-context/skills/project-brain/SKILL.md" + bt + " for what belongs there and what does not —\n")
w("the short version is that anything mxcli can answer must never be written down.\n\n")

w("## Communication Style\n\n")
w("When discussing changes with the user:\n\n")
w("- **Never show raw MDL scripts in chat.** Instead, describe changes in plain language as a numbered list.\n")
Expand Down
Loading