From a17d8fa5b316bca297864e4867bec561e38f74a1 Mon Sep 17 00:00:00 2001 From: Ako Date: Fri, 4 Sep 2026 08:58:13 +0000 Subject: [PATCH] fix(brain): give project.md the session-start load its cap assumes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The store describes project.md as loaded every session, and justifies giving it the tightest cap in the store on exactly that basis. Nothing made it true. The only route to the brain in a generated project was a row in CLAUDE.md's skills table, and the skill's own description is symptom-triggered ("use before designing something that looks like it was decided before") — so a session that never hits the symptom never learns the project's decisions, and the cap was resting on a load that did not happen. The generated CLAUDE.md now names docs/brain/project.md as the first thing to read, with the module shards on demand and `brain plan` for picking work up. That is the mechanism; the skill remains the detail. Asserted by a test rather than left to review, including that the section sits in the first third of the file — "read this first" is otherwise a claim the document's own ordering contradicts. Control: removing the block fails the test with the two paths named. Noticed by comparing the design against Anthropic's AI-native SDLC playbook, which is explicit that CLAUDE.md is what gets read at the start of a session. The gap was ours: we had the policy without the mechanism. Co-Authored-By: Claude Opus 5 --- cmd/mxcli/cmd_brain_test.go | 25 +++++++++++++++++++++++++ cmd/mxcli/init_claudemd.go | 19 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/cmd/mxcli/cmd_brain_test.go b/cmd/mxcli/cmd_brain_test.go index a112929967..9331df42e8 100644 --- a/cmd/mxcli/cmd_brain_test.go +++ b/cmd/mxcli/cmd_brain_test.go @@ -6,6 +6,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "testing" "time" @@ -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/", // 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)) + } +} diff --git a/cmd/mxcli/init_claudemd.go b/cmd/mxcli/init_claudemd.go index 5cce90c5f8..5acd0fe10e 100644 --- a/cmd/mxcli/init_claudemd.go +++ b/cmd/mxcli/init_claudemd.go @@ -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/.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")