بنقرة واحدة
zen-rules
How to write zen rules for new SIGNAL levels — idiomatic Go detection, jolts, suggestions, testing
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
How to write zen rules for new SIGNAL levels — idiomatic Go detection, jolts, suggestions, testing
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Use this skill whenever introducing a new Go programming concept that needs to be explained visually to beginners. Triggers when the user asks to explain, illustrate, animate, or teach any Go concept — including but not limited to: variables, functions, packages, imports, structs, interfaces, goroutines, channels, error handling, maps, slices, pointers, closures, or methods. Also triggers when the user says things like "add a new scene", "introduce X concept", "animate how X works", or "show the next concept". Always use this skill before writing any animation code — it defines the entire design system, analogy model, and scene structure that all Go animations must follow.
How to write and extend beginner mode content — pre-level concept briefings for new players
How to add new game content (chapters, bosses, story) to SIGNAL
Audit engine output patterns for common Go pitfalls — ensures every step has targeted feedback for Printf/Println, whitespace, format mismatches, and other gotchas
How to write and tune AI system prompts for Maya's 3-tier LLM backend
How to build boss fight encounters in SIGNAL — combat loop, visual effects, jeopardy, and content authoring
| name | zen-rules |
| description | How to write zen rules for new SIGNAL levels — idiomatic Go detection, jolts, suggestions, testing |
| allowed-tools | Read, Write, Edit, Glob, Grep, Bash |
Zen rules live in src/lib/game/zen.ts. They detect idiomatic Go patterns in player code, award bonus XP, and trigger Maya's "memory jolt" narrative. Every new challenge step needs corresponding zen rules.
interface ZenRule {
id: string; // snake_case, unique across all rules
principle: string; // short label, e.g. "grouped imports"
check: (code: string) => boolean; // heuristic — regex or string ops
isRelevant?: (code: string) => boolean; // skip rule if irrelevant to player's code
bonusXP: number; // 5-15 per rule
jolt: string; // Maya's memory returning (player followed the rule)
suggestion: string; // Maya's hint (player didn't follow the rule)
getSuggestion?: (code: string) => string; // context-aware suggestion (overrides static suggestion)
}
STEP_ZEN_RULES — key is the step ID (e.g. "chapter-04:errors").zen.test.ts for the new step.Checks are heuristic — regex and string ops only. No AST parsing.
// GOOD — simple, targeted regex
check: (code) => /fmt\.Printf/.test(code) && /%[sdvf]/.test(code),
// GOOD — line-by-line analysis for structural patterns
check: (code) => {
const lines = code.split("\n");
for (let i = 0; i < lines.length - 1; i++) {
if (/^\s*package\s/.test(lines[i])) {
return i + 1 < lines.length && lines[i + 1].trim() === "";
}
}
return false;
},
// GOOD — scoped to a specific function body
check: (code) => {
const funcBody = code.match(/func\s+sumCodes[^{]*\{([\s\S]*?)\n\}/);
if (!funcBody) return false;
return !funcBody[1].includes("fmt.");
},
// BAD — too broad, false positives on string literals
check: (code) => code.includes("const"),
// BAD — bundles two independent checks (all-or-nothing)
check: (code) => hasBlankAfterPackage(code) && hasBlankAfterImport(code),
/(?:const|var|:=)\s*(\w+)/) not raw includes.false for the unmodified starter code (including any // TODO comments).Jolts fire when the player follows the rule. They are Maya remembering something about Go.
\n\n for paragraph breaks. Messages are rendered in a chat bubble.// GOOD jolt
jolt: "...wait. i remember something.\n\nyou grouped the import with parentheses — `import ( ... )`. that's how go does it. even with one import. when you add more later, the diff stays clean.\n\nmy professor called it... \"plan for the code that comes after yours.\""
// BAD jolt — doesn't affirm, too generic
jolt: "imports are important in go. you should always think about how to organize them."
// BAD jolt — uses caps and exclamation
jolt: "Great job! You used Printf correctly!"
"...wait.", "...the fog is lifting.", "...fragments coming back."Suggestions fire when the player doesn't follow the rule. They nudge toward the idiom.
// GOOD suggestion
suggestion: "try grouping your import: `import ( \"fmt\" )` — parentheses. even for one. it's the go way... i think i remember that much."
// GOOD suggestion
suggestion: "use `i++` — it's the go way. not `i += 1`, not `i = i + 1`. one operation, one form. simplicity matters."
// BAD suggestion — no code example
suggestion: "you should think about how go handles increments."
// BAD suggestion — doesn't start with action verb
suggestion: "your increment could be more idiomatic."
getSuggestion)Static suggestion strings can mislead players. If a player declared const cell = "B-09" but didn't reference it in their print call, the suggestion "try declaring named values" is confusing — they already did. Use getSuggestion to inspect the player's code and return the right message.
// BAD — static suggestion that assumes the player did nothing
suggestion: "try declaring named values — `cell := \"B-09\"` instead of hardcoding."
// GOOD — context-aware, diagnoses what's actually wrong
getSuggestion: (code) => {
const decls = collectDeclarations(code);
if (decls.length > 0) {
return "you declared variables — now use them in your print call. the whole point of a name is to use it.";
}
return "try declaring named values — `cell := \"B-09\"` instead of hardcoding.";
},
getSuggestionWhen getSuggestion is defined, analyzeZen uses it instead of the static suggestion. The static suggestion field is still required (used as fallback in library recording and content quality tests).
Add to the STEP_ZEN_RULES record in zen.ts:
const STEP_ZEN_RULES: Record<string, ZenRule[]> = {
// existing...
"chapter-04:errors": [errorsReturned, wrapErrors, checkErrImmediately],
};
Every step's zen rules need tests in zen.test.ts. Follow this structure:
describe("chapter-04:errors", () => {
const stepId = "chapter-04:errors";
// Test each rule independently with minimal code
test("errorsReturned — detects error return", () => {
const code = `func divide(a, b int) (int, error) {\n if b == 0 { return 0, fmt.Errorf("divide by zero") }\n return a / b, nil\n}`;
const result = analyzeZen(stepId, code);
expect(result.jolts.length).toBeGreaterThanOrEqual(1);
});
// Test that starter code doesn't trigger rules
test("starter code triggers no rules", () => {
const starter = `package main\n\nimport "fmt"\n\n// TODO: implement divide`;
const result = analyzeZen(stepId, starter);
expect(result.bonusXP).toBe(0);
});
// Test partial credit
test("following some rules gives partial XP", () => {
// code that follows errorsReturned but not wrapErrors
const result = analyzeZen(stepId, partialCode);
expect(result.bonusXP).toBeGreaterThan(0);
expect(result.suggestions.length).toBeGreaterThan(0);
});
});
The test file includes cross-cutting content quality checks for ALL rules:
test("jolts affirm what the player did (not suggest)", () => {
// jolts should NOT start with "try", "use", "add" etc.
});
test("suggestions contain actionable advice", () => {
// suggestions MUST start with action verbs or transitional patterns like "but", "instead"
});
test("suggestions contain code examples", () => {
// every suggestion must include backtick-wrapped code
});
New rules must pass these tests. Run npx vitest zen to verify.
Base all rules on established Go idioms:
code.includes("const") matches fmt.Println("constant value"). Use declaration patterns.// TODO lines. Make sure your regex doesn't false-positive on those.buildZenMessage behavior. It shows ALL jolts (every good practice acknowledged) but only ONE suggestion (focused improvement). Don't assume only one jolt is shown.