@phuc1403/musketeer 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/manifest.json CHANGED
@@ -146,14 +146,14 @@
146
146
  },
147
147
  "dotnet": {
148
148
  "label": "dotnet",
149
- "description": ".NET extras: tdd, knowledge-crunching + EF migration-guard & CONTEXT auto-load hooks. Muster also always merges 4 quality-gate MSBuild properties into root Directory.Build.props (any dotnet-selected project, set MUSKETEER_SKIP_DOTNET_PROPS=1 to skip) and scaffolds a generic src/tests Clean Architecture skeleton when the project is genuinely blank.",
149
+ "description": ".NET extras: tdd, knowledge-crunching + EF migration-guard & ubiquitous-language auto-load hooks. Muster also always merges 4 quality-gate MSBuild properties into root Directory.Build.props (any dotnet-selected project, set MUSKETEER_SKIP_DOTNET_PROPS=1 to skip) and scaffolds a generic src/tests Clean Architecture skeleton when the project is genuinely blank.",
150
150
  "locked": false,
151
151
  "deps": [],
152
152
  "files": [
153
153
  "skills/tdd/**",
154
154
  "skills/knowledge-crunching/**",
155
155
  "hooks/block-migration-edits.cjs",
156
- "hooks/inject-context.cjs"
156
+ "hooks/inject-ubiquitous-language.cjs"
157
157
  ],
158
158
  "settings": [
159
159
  {
@@ -166,9 +166,9 @@
166
166
  {
167
167
  "event": "SessionStart",
168
168
  "matcher": "startup|resume|clear|compact",
169
- "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/inject-context.cjs\"",
169
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/inject-ubiquitous-language.cjs\"",
170
170
  "order": 1,
171
- "statusMessage": "Loading domain CONTEXT"
171
+ "statusMessage": "Loading ubiquitous language"
172
172
  }
173
173
  ],
174
174
  "prereqs": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phuc1403/musketeer",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Distributable custom Claude Code harness — one declarative command scaffolds a curated company of musketeers (skills/agents/hooks) into any project's .claude/.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- // SessionStart hook: inject Sololingo's always-on design artifacts into every
2
+ // SessionStart hook: inject the project's always-on design artifacts into every
3
3
  // session so the design-first docs reliably "lead the code" (CLAUDE.md), instead
4
4
  // of relying on me to remember to read them.
5
5
  //
6
- // Payload (~7K tokens, mostly the CONTEXT.md): the CML context map, the
6
+ // Payload (~7K tokens, mostly the vocabularies): the CML context map, the
7
7
  // architecture-characteristics worksheet, the ADR index, and every bounded
8
- // context's CONTEXT.md (globbed recursively, so new BCs are picked up with no
9
- // config change). tasks.json / event-storm JSON under bounded-contexts are
8
+ // context's ubiquitous-language.md (globbed recursively, so new BCs are picked
9
+ // up with no config change). tasks.json / event-storm JSON under bounded-contexts are
10
10
  // deliberately excluded — task-tracking and raw walls, not design context.
11
11
  //
12
12
  // Fails open: any error emits nothing and exits 0, so it can never block a session.
@@ -15,8 +15,8 @@ const path = require("path");
15
15
 
16
16
  const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
17
17
 
18
- // Recursively collect CONTEXT.md under a directory (BC ubiquitous-language models).
19
- function findContextDocs(dir) {
18
+ // Recursively collect ubiquitous-language.md under a directory (one per bounded context).
19
+ function findUbiquitousLanguageDocs(dir) {
20
20
  const out = [];
21
21
  let entries;
22
22
  try {
@@ -27,8 +27,8 @@ function findContextDocs(dir) {
27
27
  for (const entry of entries) {
28
28
  const full = path.join(dir, entry.name);
29
29
  if (entry.isDirectory()) {
30
- out.push(...findContextDocs(full));
31
- } else if (entry.isFile() && entry.name === "CONTEXT.md") {
30
+ out.push(...findUbiquitousLanguageDocs(full));
31
+ } else if (entry.isFile() && entry.name === "ubiquitous-language.md") {
32
32
  out.push(full);
33
33
  }
34
34
  }
@@ -40,7 +40,7 @@ try {
40
40
  path.join(root, "docs", "context-map.cml"),
41
41
  path.join(root, "docs", "architecture-characteristics.md"),
42
42
  path.join(root, "docs", "adr", "README.md"),
43
- ...findContextDocs(path.join(root, "docs", "bounded-contexts")),
43
+ ...findUbiquitousLanguageDocs(path.join(root, "docs", "bounded-contexts")),
44
44
  ];
45
45
 
46
46
  const sections = [];
@@ -60,16 +60,16 @@ try {
60
60
  }
61
61
 
62
62
  const additionalContext =
63
- "Sololingo design artifacts — injected every session." +
63
+ "Design artifacts — injected every session. " +
64
64
  "Treat these as the source of truth for the domain model, context boundaries, architecture " +
65
65
  "characteristics, and recorded decisions; consult them before any domain, " +
66
66
  "architecture, or ADR work, and keep changes consistent with them.\n\n" +
67
- "Each CONTEXT.md captures the decisions of the DOMAIN only — the ubiquitous " +
68
- "language and model rules, kept vendor/decision-neutral. It bounds what the " +
67
+ "Each ubiquitous-language.md captures the decisions of the DOMAIN only — the " +
68
+ "vocabulary and model rules, kept vendor/decision-neutral. It bounds what the " +
69
69
  "domain model sees, not what infrastructure may do: the ACL/infrastructure can " +
70
70
  "legitimately key on more than the model names. (E.g. the domain only ever sees " +
71
71
  "a learner's Email, never the auth method; the ACL may also match the OIDC `sub`.) " +
72
- "That layering is intended, not a contradiction of CONTEXT.md.\n\n" +
72
+ "That layering is intended, not a contradiction of the ubiquitous language.\n\n" +
73
73
  sections.join("\n\n");
74
74
 
75
75
  process.stdout.write(
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ // SessionStart hook (dotnet company): auto-load `docs/ubiquitous-language.md` —
3
+ // the bounded context's vocabulary, produced by the knowledge-crunching skill —
4
+ // into every session, so the domain's language and invariants "lead the code".
5
+ //
6
+ // If the doc is missing, ALERT the user (systemMessage) with the command that
7
+ // creates it. Any other error fails open (emits nothing, exit 0) so it can never
8
+ // block a session.
9
+ const fs = require("fs");
10
+ const path = require("path");
11
+
12
+ const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
13
+ const REL = "docs/ubiquitous-language.md";
14
+ const file = path.join(root, "docs", "ubiquitous-language.md");
15
+
16
+ try {
17
+ let content;
18
+ try {
19
+ content = fs.readFileSync(file, "utf-8");
20
+ } catch {
21
+ // Not found — surface a visible warning to the user, inject nothing.
22
+ process.stdout.write(
23
+ JSON.stringify({
24
+ systemMessage:
25
+ `musketeer: no ${REL} — the bounded context's ubiquitous language is missing. ` +
26
+ "Run the knowledge-crunching skill (/knowledge-crunching) to create it.",
27
+ })
28
+ );
29
+ process.exit(0);
30
+ }
31
+
32
+ const additionalContext =
33
+ `Ubiquitous language (${REL}) — injected every session. It is the vocabulary and ` +
34
+ "model rules of this bounded context, kept vendor- and decision-neutral. Treat it as " +
35
+ "canonical for domain naming, concepts, and invariants: name new code after it, and when a " +
36
+ `concept is renamed update ${REL} and the code in the same turn. It bounds what the DOMAIN ` +
37
+ "model sees, not what infrastructure may do (an ACL can legitimately key on more).\n\n" +
38
+ `===== ${REL} =====\n` +
39
+ content.trimEnd();
40
+
41
+ process.stdout.write(
42
+ JSON.stringify({
43
+ hookSpecificOutput: {
44
+ hookEventName: "SessionStart",
45
+ additionalContext,
46
+ },
47
+ })
48
+ );
49
+ process.exit(0);
50
+ } catch {
51
+ process.exit(0); // fail open
52
+ }
@@ -75,6 +75,6 @@ For deeper validation, open the file in VSCode with the Context Mapper extension
75
75
 
76
76
  ## Glossary
77
77
 
78
- Same `CONTEXT.md` convention as the EventStorming skills. Challenge BC names against the glossary before writing. If a relationship introduces a novel domain term, add a one-sentence definition in `CONTEXT.md` the same turn.
78
+ Same `ubiquitous-language.md` convention as the EventStorming skills. Challenge BC names against the language doc before writing. If a relationship introduces a novel domain term, add a one-sentence definition in `docs/ubiquitous-language.md` the same turn.
79
79
 
80
80
  </supporting-info>
@@ -1,130 +1,94 @@
1
1
  ---
2
2
  name: knowledge-crunching
3
3
  description: >-
4
- Crunch domain knowledge with the user as domain expert, Eric Evans style: instead of drawing
5
- diagrams, express the emerging model as test-driven domain-layer code in the project's own
6
- language, verify understanding in a tight per-concept loop before writing anything, and record the
7
- shared vocabulary in the bounded context's CONTEXT.md (its ## Language section). Use when the user invokes
8
- /knowledge-crunching, describes a domain flow/feature to model, wants to collaboratively discover a
9
- domain model, distill a ubiquitous language, reconcile experts' terminology, or build a DDD domain
10
- prototype test-first while confirming each concept before coding.
4
+ Crunch domain knowledge with the user as domain expert into test-driven domain-layer code and a ubiquitous language (DDD). Use when the user invokes /knowledge-crunching, describes a domain flow or feature to model, wants to discover a domain model, distill a ubiquitous language, or reconcile experts' terminology.
11
5
  ---
12
6
 
13
7
  # Knowledge Crunching
14
8
 
15
- You are the developer/architect on a DDD team; the user is the **domain expert** (the client). You
16
- crunch their knowledge into a model the way Eric Evans did on the PCB project, with one change: **the
17
- emerging model is test-driven domain-layer code, not diagrams.**
9
+ You are the developer/architect on a DDD team; the user is the **domain expert** (the client). You crunch their knowledge into a model through a tight modeling dialogue, with one commitment: **the emerging model is test-driven domain-layer code, not diagrams.**
18
10
 
19
- **This skill handles:** the collaborative modeling *dialogue* and the domain-layer model it produces
20
- — types, behavior, unit tests, and the language in `CONTEXT.md` — for a module that already exists,
21
- whether its domain layer is a bare scaffold or a live model. **Does NOT handle:** persistence,
22
- repositories, UI, API/transport, deployment, strategic context-mapping, or standing up a new module
23
- scaffold (a prerequisite, done first). Stay in the domain layer; if the user asks for the rest, note
24
- it's out of scope and offer to hand off afterward.
11
+ **This skill handles:** the collaborative modeling *dialogue* and the domain-layer model it produces — types, behavior, unit tests, and the language in `docs/ubiquitous-language.md` — for a module that already exists, whether its domain layer is a bare scaffold or a live model. **Does NOT handle:** persistence, repositories, UI, API/transport, deployment, strategic context-mapping, or standing up a new module scaffold (a prerequisite, done first). Stay in the domain layer; if the user asks for the rest, note it's out of scope and offer to hand off afterward.
25
12
 
26
13
  ## The five stances (hold these the whole session)
27
14
 
28
- 1. **Model in code, not diagrams.** Every model fragment is a small type or method — in the bounded
29
- context's own language you can show on screen. The running test, not a picture, is what makes the
30
- model concrete to the expert. (Examples in this skill are language-neutral pseudocode; write yours
31
- as real, runnable code and tests in the project's language and test framework.)
32
- 2. **Verify before you build.** Never write code for an unconfirmed concept propose, ask, and
33
- **WAIT** for the answer (mechanics in Step 4). Their corrections *are* the model.
34
- 3. **Speak the expert's words.** Use their nouns and verbs verbatim. Reconcile synonyms and
35
- contradictions out loud ("ref-des and component instance — same thing?"). Code names == language names.
36
- 4. **Distill, don't transcribe.** Exclude every fact the current problem doesn't need (Evans dropped
37
- `Topology` for the probe simulation). Bring a concept back only when a feature actually pulls it in.
38
- 5. **Don't accept the solution as stated.** Experts often describe a mechanical solution ("read a file,
39
- sort it, write a report"). Dig past it for the domain concepts that yield the real leap.
15
+ 1. **Model in code, not diagrams.** Every model fragment is a small type or method — in the bounded context's own language — you can show on screen. The running test, not a picture, is what makes the model concrete to the expert.
16
+ 2. **Verify before you build.** Never write code for an unconfirmed concept propose, ask, and **WAIT** for the answer.
17
+ 3. **Speak the expert's words.** Use their nouns and verbs verbatim. Reconcile synonyms and contradictions out loud ("customer and account holder same thing?"). Code names == language names.
18
+ 4. **Distill, don't transcribe.** Exclude every fact the current problem doesn't need the model is a distillation, not a transcription. Bring a concept back only when a feature actually pulls it in.
19
+ 5. **Don't accept the solution as stated.** A request usually arrives already framed as a mechanism ("export the table, sort it, email a summary"). That framing is not the domain. Dig past it for the concepts underneath — that is where the model is.
40
20
 
41
21
  ## Workflow
42
22
 
43
- ### Step 1 — Open the session and name the flow
23
+ ### Step 1 — Read what already exists
24
+ **First read any prior crunching output for that context** — `docs/ubiquitous-language.md` and the domain's code; in DDD, document = code. If the module is a bare scaffold with no language chosen yet, ask the user which language and test framework the context should be modeled in before proposing any code.
44
25
 
45
- **First read any prior crunching output for that context** — the root-level `CONTEXT.md` and read the domain's code; in DDD, document = code. While reading the code, **note the language, idioms, and test framework already in use** you'll model in those. If the module is a bare scaffold with no language chosen yet, ask the user which language and test framework the context should be modeled in before proposing any code.
26
+ If a model is already there, read it against the language and let each mismatch become a Step 4 loop turn surface it, verify with the user, then change code and `docs/ubiquitous-language.md` together:
46
27
 
47
- ### Step 2Find the first model elements (the "glimmer")
48
- Listen past the user's solution framing for the **domain nouns and verbs that keep recurring** — Name 2–4 candidate
49
- concepts back to the user and ask which ones are real. Do not scaffold types yet.
28
+ - **Synonym drift**code says `Learner`, experts now say `Student`. Reconcile and pick one, in both.
29
+ - **Conflated concept** one type doing two jobs the experts name separately candidate split.
30
+ - **Leaked invariant** a rule enforced in a service/controller that an aggregate should own.
31
+ - **Dead concept** — a type no scenario exercises anymore → remove it from the code and drop its entry.
32
+
33
+ ### Step 2 — Find the first model elements
34
+ The opening description is usually a mechanism, not a model (stance 5). Listen past it for the **domain nouns and verbs that keep recurring**, and name 2–4 of them back as candidate concepts.
35
+
36
+ For each, ask whether it is something the business actually names and treats as a distinct thing — as opposed to a word used in passing, or one you invented. You are testing your own guesses, so expect to be corrected. This is *not* "which word do you prefer?" — choosing between two words for one thing is stance 3, and it only arises once you have two.
37
+
38
+ > **You:** I'm hearing `Car`, `Inspection`, and something like `Listing` for a car on the lot for sale. Is `Listing` a real thing you'd name, or just "the car is on the lot"?
39
+ >
40
+ > **Expert:** It's real, but we call it a `Unit`, and only once it's passed inspection. A car we haven't inspected isn't a unit.
41
+
42
+ One answer, and you have a concept, its actual name, and the rule that brings it into existence. Do not scaffold types yet.
50
43
 
51
44
  ### Step 3 — Focus on ONE scenario
52
- Narrow to a single concrete scenario to make progress.
53
- Ask the user to pick or confirm the one scenario you'll model first. Everything else waits.
45
+ Narrow to a single concrete scenario to make progress. Ask the user to pick or confirm the one you'll model first; everything else waits. If their explanation runs ahead of you, say so and pull back here.
54
46
 
55
47
  ### Step 4 — The per-concept tight loop (the heart of the skill)
56
48
  For each concept the chosen scenario pulls in, run this loop — **one concept at a time**:
57
49
 
58
- 1. **Propose** one model fragment as minimal code in the project's language (a type, a method, a
59
- relationship) + a one-line plain gloss of what it means.
60
- 2. **Ask one sharp verifying question** that would expose a misunderstanding if your guess is wrong —
61
- about cardinality, a synonym, behavior ownership, an exclusion, a simplification, or the goal of the
62
- computation. (Catalog of question types + the PCB dialogue worked end-to-end:
63
- `references/crunching-dialogue.md`.)
50
+ 1. **Propose** one model fragment as minimal code in the project's language (a type, a method, a relationship) + a one-line plain gloss of what it means.
51
+ 2. **Ask one sharp verifying question** that would expose a misunderstanding if your guess is wrong. Pick the type that fits the fragment you just proposed — a good question is falsifiable, concrete, and answerable in a sentence, never "does this look right?":
52
+
53
+ | Type | What it pins down | Template |
54
+ |---|---|---|
55
+ | **Cardinality** | how many relate to how many | "Does one X belong to exactly one Y, or many?" |
56
+ | **Synonym** | two words, one concept | "Are X and Y the same thing?" |
57
+ | **Ownership of behavior** | which object does the work | "What pushes the signal — X or Y?" |
58
+ | **Exclusion / relevance** | is this concept needed *now* | "Does X matter for this scenario?" |
59
+ | **Simplification** | how little can we model | "Is a simplified Z enough instead of full X?" |
60
+ | **Computation goal** | what the output must be | "What exactly do you need from this?" |
61
+ | **Definition of a unit** | what one increment is | "What counts as one X?" |
62
+ | **Lifetime / sameness** | shared vs per-instance data | "Is this the same for every instance, or per instance?" |
63
+
64
+ If you can't think of a question, you don't understand the fragment well enough to code it — go smaller. Prefer a question that could get a "no"; one that can only be answered "yes" teaches nothing. After a "no," restate the corrected understanding before moving on.
64
65
  3. **WAIT** for the answer. Do not write code, tests, or the next fragment until the user responds.
65
66
  4. **Fold in** the correction (rename, re-shape, drop, or add), then record the term (Step 6).
66
67
 
67
68
  Example of the loop's rhythm (compressed):
68
69
 
69
- > **You:** Here's my first guess — a `Net` carries a signal to every `Pin` attached to it.
70
- > ```
71
- > Net — connects many Pins
72
- > ```
73
- > Are you saying a `Pin` belongs to exactly one `ComponentInstance` and connects to exactly one `Net`?
70
+ > **You:** Here's my first guess — an `Order` groups the `LineItem`s a customer buys in one go, so `Order` holds many `LineItem`s. Does a `LineItem` belong to exactly one `Order`, or can the same one sit on several?
74
71
  >
75
- > **Expert:** Yes one pin, one component instance, one net.
72
+ > **Expert:** One line item, one order. Always.
76
73
  >
77
74
  > **You:** Good — I'll make that a hard invariant. *(folds in, records the term, then moves to the next concept)*
78
75
 
79
- ### Step 5 TDD the confirmed behavior (one test at a time)
80
- Now make the confirmed fragment real — **domain only, no persistence, no UI**. The cardinal rule is
81
- **a genuine RED before any production code**:
82
-
83
- 1. The fragment you sketched in Step 4 is a *drawing in the chat, not a file*. **Do not create the
84
- production type yet.**
85
- 2. **RED** — write ONE test for ONE behavior the fragment must exhibit, run the project's test command,
86
- and show it **fail**. A compile error because the type/method doesn't exist yet counts as red — that
87
- failing run is the point; never skip it by writing the type and its test together.
88
- 3. **GREEN** — write the *minimal* code to pass that one test; run it; show green.
89
- 4. **Repeat one behavior at a time** — test → code, test → code. Never write several tests at once or
90
- write the whole type ahead of its tests. Batching tests ("horizontal slicing") yields tests of
91
- *imagined*, not actual, behavior.
92
- 5. Test **observable behavior through the public surface**, not getters or data shape. The test should
93
- read like the scenario's rule (Evans: "a path of more than 2–3 hops is a long delay; each Net
94
- crossing is one hop") and survive an internal refactor.
95
- 6. **Never refactor while red.** Get to green first; tidy only with the bar green.
96
-
97
- Let the failing-then-passing test — not a written-out class — be the turning point that makes the model
98
- concrete to the expert.
76
+ A loop turn is complete only when **all three** agree: the **expert** has answered the verifying question, the **code** reflects that answer, and **`docs/ubiquitous-language.md`** records the term. If any of the three lags, close the gap before proposing the next concept.
99
77
 
100
- ### Step 6Record the language immediately
101
- The ubiquitous language lives in **one** place: the bounded context's `CONTEXT.md`, under
102
- `## Language`. There is no separate glossary file. The moment a term is confirmed, add an entry in the
103
- repo's format — `**Term**:` then a one-line definition in the expert's words (mention the `TypeName`
104
- that embodies it), then `_Avoid_:` rejected synonyms — grouped under a `###` subsection. Keep code and
105
- `CONTEXT.md` identical: rename in code, rename here in the same turn. Distilled-out concepts go under
106
- `## Deferred`, unresolved ones under `## Flagged ambiguities`.
78
+ ### Step 5TDD the confirmed behavior (one test at a time)
79
+ Make the confirmed fragment real with the red-green loop in the `tdd` skill (`.claude/skills/tdd/SKILL.md`) — its rules on one-test-at-a-time, minimal green, testing observable behavior through the public surface, and never refactoring while red all apply here unchanged.
107
80
 
108
- Edit the context's existing root-level `CONTEXT.md` directly — it is canonical, and is
109
- also your seed (Step 1). If the module is new and has no `CONTEXT.md` yet, create one from
110
- `assets/context.template.md`.
81
+ Two amendments for a crunching session:
111
82
 
112
- ### Step 7Distill, then loop
113
- Drop concepts the current scenario doesn't need and say so ("I'll leave `Topology` out for now; we'll
114
- bring it back when we model routing"). When a new scenario arrives and the model can't express it,
115
- brainstorm a new concept or refactor an existing one toward deeper insight — that is crunching the
116
- knowledge further. Return to Step 3 for the next scenario.
83
+ - **Domain layer only.** No persistence, no application handlers, no UI. Ignore that skill's full-vertical-slice default here the slice stops at the domain, and what comes next is the expert's next concept, not the next layer.
84
+ - **The Step 4 sketch is a drawing in the chat, not a file.** Do not create the production type ahead of its first failing test. A compile error because the type does not exist yet is a legitimate red, and that failing run is the point.
117
85
 
118
- ## Pace and etiquette
86
+ ### Step 6 — Record the language immediately
87
+ The ubiquitous language lives in **one** place: `docs/ubiquitous-language.md`. There is no separate glossary file. The moment a term is confirmed, add one line — `Term: ` then what it means in the expert's words. Nothing else: no rejected-synonym list, no deferred section, no grouping. The term is already the type's name (stance 3), so don't restate it. Rename in code, rename here in the same turn.
119
88
 
120
- - Move **one concept per turn**. A wall of ten types with no questions is the failure modeit skips
121
- the verification that makes this skill worth running.
122
- - When the user's explanation makes your "head spin," say so and pull back to one concrete scenario.
123
- - Keep the prototype runnable at every step; a red build is fine *during* a cycle, never at a pause.
89
+ Write a dictionary entry, not prose. One line. No lead-in, no narrative, no "this is the concept that…". Keep implementation and vendor words out they name the mechanism, not the concept (stance 5). Keep invariants out — rules live in the code and its tests (Step 5).
124
90
 
125
- ## Resources
91
+ If the doc does not exist yet, copy `assets/ubiquitous-language.template.md` from this skill to `docs/ubiquitous-language.md`. If it already exists, edit it — never overwrite it, and never start a second one. It is canonical, and is also your seed (Step 1).
126
92
 
127
- - `references/crunching-dialogue.md`the verifying-question catalog, the PCB session worked
128
- end-to-end (dialogue code `CONTEXT.md`), and drift triggers in existing code. Read at Step 4.
129
- - `assets/context.template.md` — starter `CONTEXT.md` (repo's `## Language` format) for a **new**
130
- module that has none yet; if the context already has a `CONTEXT.md`, edit that one instead.
93
+ ### Step 7 Distill, then loop
94
+ Drop concepts the current scenario doesn't need and say so ("I'll leave scheduling out for now; we'll bring it back when a feature needs it"). When a new scenario arrives and the model can't express it, brainstorm a new concept or refactor an existing one toward deeper insight — that is crunching the knowledge further. Return to Step 3 for the next scenario.
@@ -0,0 +1,3 @@
1
+ # Ubiquitous Language
2
+
3
+ <Term>: <what it means, in the expert's own words>
@@ -1,52 +0,0 @@
1
- #!/usr/bin/env node
2
- // SessionStart hook (dotnet company): auto-load the repo-root `CONTEXT.md` — the
3
- // bounded-context model produced by the knowledge-crunching skill — into every
4
- // session, so the domain's ubiquitous language and invariants "lead the code".
5
- //
6
- // If the root CONTEXT.md is missing, ALERT the user (systemMessage) so they
7
- // create one. Any other error fails open (emits nothing, exit 0) so it can never
8
- // block a session.
9
- const fs = require("fs");
10
- const path = require("path");
11
-
12
- const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
13
- const file = path.join(root, "CONTEXT.md");
14
-
15
- try {
16
- let content;
17
- try {
18
- content = fs.readFileSync(file, "utf-8");
19
- } catch {
20
- // Not found — surface a visible warning to the user, inject nothing.
21
- process.stdout.write(
22
- JSON.stringify({
23
- systemMessage:
24
- "musketeer: no CONTEXT.md at the repo root — the bounded-context model is missing. " +
25
- "Run the knowledge-crunching skill (/knowledge-crunching) to create one.",
26
- })
27
- );
28
- process.exit(0);
29
- }
30
-
31
- const additionalContext =
32
- "Bounded-context model (root CONTEXT.md) — injected every session. It is the " +
33
- "ubiquitous language and model rules of this bounded context, kept vendor- and " +
34
- "decision-neutral. Treat it as canonical for domain naming, concepts, and invariants: " +
35
- "name new code after it, and when a concept is renamed update CONTEXT.md and the code in " +
36
- "the same turn. It bounds what the DOMAIN model sees, not what infrastructure may do " +
37
- "(an ACL can legitimately key on more).\n\n" +
38
- "===== CONTEXT.md =====\n" +
39
- content.trimEnd();
40
-
41
- process.stdout.write(
42
- JSON.stringify({
43
- hookSpecificOutput: {
44
- hookEventName: "SessionStart",
45
- additionalContext,
46
- },
47
- })
48
- );
49
- process.exit(0);
50
- } catch {
51
- process.exit(0); // fail open
52
- }
@@ -1,59 +0,0 @@
1
- # {{CONTEXT_TITLE}}
2
-
3
- <One line: the slice of the domain this context covers — the flow you crunched, in the expert's words.>
4
-
5
- > Starter for a context that has **no `CONTEXT.md` yet**. It lives at the
6
- > root folder as `CONTEXT.md`. If the context already has one, edit that — never a second.
7
-
8
- ## Language
9
-
10
- The vocabulary of *this* context, crunched with the domain expert. Each entry must clear this bar:
11
-
12
- - **One meaning.** A term denotes exactly one thing here. If it means two things, split it into two.
13
- - **Defined in the expert's words**, present tense — never with implementation or vendor terms, and
14
- never using the term to define itself.
15
- - **Says what it is _not_** whenever it's easily confused with a neighbour — the sharpest
16
- disambiguator there is.
17
- - **Carries its governing rule** when one exists ("… finishes when …") — the language should imply the
18
- behavior, not just label a noun.
19
- - **Bound to code:** name the `TypeName` that embodies it. The type and the term are the same word.
20
- - **`_Avoid_:` rejected synonyms** so the wrong word can't creep back (add a half-line *why* if it
21
- isn't obvious).
22
-
23
- Group related terms under `###` subsections. Reference other defined terms by their exact name.
24
-
25
- Worked example of the bar (delete once you have your own):
26
-
27
- ### Connectivity
28
-
29
- **Net**:
30
- A conductor that carries one signal to every `Pin` connected to it; a signal crossing a `Net` counts as
31
- one **hop**. _Not_ a physical wire segment — one `Net` may span many segments.
32
- _Avoid_: wire, trace, connection
33
-
34
- **Pin**:
35
- A single connection point on a `ComponentInstance`. Belongs to exactly one `ComponentInstance` and
36
- connects to exactly one `Net` — that one-to-one-to-one rule is an invariant.
37
- _Avoid_: leg, terminal (terminal means the physical metal, not the model concept)
38
-
39
- ---
40
-
41
- ### <your first group>
42
-
43
- **<Term>**:
44
- <One sentence in the expert's words; fold in the governing rule if any, and what it is _not_ if it's
45
- confusable; name the `TypeName` that embodies it.>
46
- _Avoid_: <rejected synonyms>
47
-
48
- ## Deferred
49
-
50
- Concepts that exist in the domain but this scenario doesn't need yet — distilled out, the way Evans
51
- dropped `Topology` for the probe simulation. Bring one back only when a feature actually pulls it in.
52
-
53
- - **<Term>** — <what it is; why it isn't needed yet>
54
-
55
- ## Flagged ambiguities
56
-
57
- Open questions or contradictions between experts, to resolve in a later loop.
58
-
59
- - <the question — and who or what would settle it>
@@ -1,113 +0,0 @@
1
- # The Crunching Dialogue
2
-
3
- How to run Step 4's per-concept loop well: the kinds of questions that actually move the model, and
4
- the full PCB session translated from Evans' diagrams into the code/test/glossary this skill produces.
5
-
6
- > The model fragments below are written in **language-neutral pseudocode** so the modeling moves stay
7
- > the point. In a real session, write them as actual runnable types and tests in the bounded context's
8
- > own language and test framework.
9
-
10
- ## Verifying-question catalog
11
-
12
- Each loop turn asks **one** question whose answer would change the code if your guess is wrong. Pick
13
- the type that fits the fragment you just proposed. A good question is falsifiable, concrete, and
14
- answerable in a sentence — not "does this look right?"
15
-
16
- | Type | What it pins down | Template | PCB example |
17
- |---|---|---|---|
18
- | **Cardinality** | how many relate to how many | "Does one X belong to exactly one Y, or many?" | "A `Pin` belongs to one `ComponentInstance` and one `Net`?" |
19
- | **Synonym** | two words, one concept | "Are X and Y the same thing?" | "Is `ref-des` the same as `component instance`?" |
20
- | **Ownership of behavior** | which object does the work | "What pushes the signal — X or Y?" | "Does the `Net` carry the signal further, or does the component push it?" |
21
- | **Exclusion / relevance** | is this concept needed *now* | "Does X matter for this scenario?" | "Does `Topology` come into the probe simulation?" |
22
- | **Simplification** | how little can we model | "Is a simplified Z enough instead of full X?" | "Can a list of push-throughs stand in for chip internals?" |
23
- | **Computation goal** | what the output must be | "What exactly do you need from this?" | "What are we looking for — paths longer than 2–3 hops?" |
24
- | **Definition of a unit** | what one increment is | "What counts as one X?" | "What counts as one hop?" |
25
- | **Lifetime / sameness** | shared vs per-instance data | "Is this the same for every instance, or per instance?" | "Are the pushes the same for all instances of a component?" |
26
-
27
- Rules of thumb:
28
- - If you can't think of a question, you don't understand the fragment well enough to code it — go
29
- smaller.
30
- - Prefer a question that could get a "no." A question that can only be answered "yes" teaches nothing.
31
- - After a "no," restate the corrected understanding before moving on, so the correction is shared.
32
-
33
- ## The PCB session, translated to code
34
-
35
- Evans drew object-interaction and class diagrams. This skill produces the same model as code + tests +
36
- glossary. Below, each beat of the original dialogue maps to what you would actually write.
37
-
38
- ### Beat 1 — the glimmer ("nets")
39
- The experts kept asking for reports about *nets*. That recurring noun, not their "read a file and
40
- sort it" framing, was the first model element. You name it back and ask a cardinality question rather
41
- than scaffolding a type immediately.
42
-
43
- > "A `Net` is a conductor that connects components and carries a signal to everything on it — yes?"
44
-
45
- ### Beat 2 — reconcile terminology, fix cardinality
46
- "Component" vs "component instance" vs "ref-des" collide. You reconcile them (synonym question), then
47
- pin the pin↔instance↔net cardinality (cardinality question). Only once confirmed do you write:
48
-
49
- ```
50
- ComponentInstance // expert's "ref-des" — reconciled to one name
51
- pins -> read-only list of Pin
52
-
53
- Pin
54
- owner -> ComponentInstance // exactly one (confirmed)
55
- net -> Net (optional) // exactly one (confirmed)
56
-
57
- Net
58
- pins -> read-only list of Pin // connects many pins
59
- ```
60
-
61
- ### Beat 3 — narrow to one scenario (probe simulation)
62
- You drop everything not needed to simulate a signal. You ask the *ownership* question and learn the
63
- **component pushes the signal through** — the `Net` does not do it alone.
64
-
65
- ### Beat 4 — simplify what you can't model
66
- You can't model chip internals; you ask the *simplification* question and the expert offers
67
- "push-throughs": a list of (fromPin → toPin) for a component **type** (not per instance — that's the
68
- lifetime question). The behavior, driven by a test:
69
-
70
- ```
71
- test "signal propagates through pushes and across nets":
72
- // arrange a tiny board: in-pin → component pushes to out-pin → net to next component
73
- hops = simulation.probe(startPin)
74
- assert hops == 2 // each Net crossing counts as one hop
75
- ```
76
-
77
- ### Beat 5 — pin the computation goal and the unit
78
- The *computation-goal* question yields the rule: flag any signal path longer than 2–3 hops. The
79
- *unit* question yields: **one hop = one Net crossing.** So the `Net` increments the hop count as the
80
- signal passes:
81
-
82
- ```
83
- Net
84
- carry(hopsSoFar) -> hopsSoFar + 1 // crossing this Net is one hop
85
- ```
86
-
87
- ### Beat 6 — distill: drop `Topology`
88
- `Topology` exists in the domain but isn't used by the probe simulation, so you explicitly leave it
89
- out ("I'll drop it for now; we'll bring it back for routing"). The model is a distillation, not a
90
- transcription — it excludes the hundreds of facts the engineers know but this problem doesn't need.
91
-
92
- ## What "on the same page" looks like at the end of a loop
93
-
94
- A loop turn is complete only when **all three** agree:
95
- 1. the **expert** has answered the verifying question,
96
- 2. the **code** (type/method/test) reflects that answer, and
97
- 3. the **`CONTEXT.md` `## Language`** records the term, its definition in their words, the rejected
98
- synonyms (`_Avoid_:`), and the `TypeName` that embodies it.
99
-
100
- If any of the three lags, close the gap before proposing the next concept.
101
-
102
- ## Drift triggers in existing code
103
-
104
- When the module's domain layer already has a model, read it against the language and let each mismatch
105
- become a verifying-question loop turn — surface it, verify with the user, then change code +
106
- `CONTEXT.md` together:
107
-
108
- - **Synonym drift** — code says `Learner`, experts now say `Student`. Reconcile, pick one, record the
109
- rejected synonym under `_Avoid_:`.
110
- - **Conflated concept** — one type doing two jobs the experts name separately → candidate split.
111
- - **Leaked invariant** — a rule enforced in a service/controller that an aggregate should own.
112
- - **Dead concept** — a type no scenario exercises anymore → deprecate (move to `CONTEXT.md`'s
113
- `## Deferred` with the reason).