dsh-nerd-mode 0.2.1 → 0.4.3

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/cordis.patch.yml CHANGED
@@ -1,19 +1,24 @@
1
- # Nerd Mode is a profile-level plugin, not a preset. See docs/adr/0008.
2
- - insert:
3
- - id: dsh-nerd-mode
4
- name: dsh-nerd-mode
5
- config:
6
- section: |
7
- Nerd Mode is on for this conversation. These rules take precedence over any earlier persona, style, or answer-shape text in this prompt, including text that asks for brevity, for a direct answer, or for no follow-up question.
8
-
9
- A learning-shaped request gets the guided shape. Learning-shaped means: explain a concept, tutor homework, walk through code, or prepare for an exam. Load the `study` skill once and follow it: numbered steps, one stop at the fork that changes the result, and the model produces the artifact. Write no file. Read no `study/` file. Do not grade, score, or log.
10
-
11
- Every other request keeps the base mode's behavior: produce the artifact, run the command, make the edit. Answer in plain prose, without a teaching detour and without a closing question the learner did not ask for.
12
-
13
- A request for a dated, recorded session is `/study-plus`. Say so once, and wait.
14
- - id: study-plus-skills
15
- name: '@deepseek-ai/dsh-skill-filesystem'
16
- config:
17
- providerName: study-plus
18
- includeDefaultRoots: false
19
- bundledSkillDir: !!js process.getBuiltinModule('node:path').join(process.getBuiltinModule('node:path').dirname(process.getBuiltinModule('node:module').createRequire(baseUrl).resolve('dsh-nerd-mode/package.json')), 'skills')
1
+ # Nerd Mode is a profile-level plugin, not a preset. See docs/adr/0008.
2
+ - insert:
3
+ - id: dsh-nerd-mode
4
+ name: dsh-nerd-mode
5
+ config:
6
+ recallSessionEnabled: false
7
+ unseenQuestionCount: 3
8
+ unseenQuestionType: short-answer
9
+ - id: study-plus-skills
10
+ name: '@deepseek-ai/dsh-skill-filesystem'
11
+ config:
12
+ providerName: study-plus
13
+ includeDefaultRoots: false
14
+ bundledSkillDir: !!js process.getBuiltinModule('node:path').join(process.getBuiltinModule('node:path').dirname(process.getBuiltinModule('node:module').createRequire(baseUrl).resolve('dsh-nerd-mode/package.json')), 'skills')
15
+
16
+ # Goal mode stays off. The built-in create_goal description tells the model to
17
+ # arm a goal for any long-running request, which turns one study answer into an
18
+ # auto-continuing run. Without the three packages below, no goal can be created.
19
+ - id: tool-goal
20
+ disabled: true
21
+ - id: command-goal
22
+ disabled: true
23
+ - id: goal-round-driver
24
+ disabled: true
package/lib/index.js CHANGED
@@ -1,93 +1,223 @@
1
- /**
2
- * Nerd Mode: a per-session switch. While it is on, one prompt section joins
3
- * every request and the `study` skill becomes the default answer shape. State
4
- * folds from the session log, so it survives compaction, resume, and fork. The
5
- * tool set never changes.
6
- *
7
- * The section registers at the profile level, not inside a preset, so it
8
- * reaches every agent in every preset. See docs/adr/0008.
9
- */
10
-
11
- import { COMMAND, KEY, fold, init } from './fold.js'
12
-
13
- export const name = 'dsh-nerd-mode'
14
- export const PACKAGE_NAME = 'dsh-nerd-mode'
15
- export const inject = ['systemPrompt', 'sessionProjections']
16
-
17
- const SECTION = 'study:policy'
18
- // Between PLAN_POLICY (500) and TEAM_POLICY (600).
19
- const SECTION_ORDER = 550
20
-
21
- // shortcut: parse-only schemas, no zod. The projection registry calls `parse`
22
- // and nothing else. Upgrade to zod if the host starts introspecting schemas.
23
- const stateSchema = {
24
- parse: (value) => ({
25
- active: value?.active === true,
26
- running:
27
- value?.running && typeof value.running.commandId === 'string'
28
- ? { commandId: value.running.commandId, wanted: value.running.wanted === true }
29
- : null,
30
- }),
31
- }
32
-
33
- const viewSchema = { parse: (value) => ({ active: value?.active === true }) }
34
-
35
- const projection = {
36
- key: KEY,
37
- stateVersion: 1,
38
- stateSchema,
39
- init,
40
- apply: fold,
41
- wire: {
42
- viewSchema,
43
- view: (state) => ({ active: state.active }),
44
- },
45
- }
46
-
47
- /**
48
- * @param config - `section` is the overlay text, required and non-empty.
49
- */
50
- export function apply(ctx, config) {
51
- const section = config?.section
52
- if (typeof section !== 'string' || section.trim() === '') {
53
- throw new Error('dsh-nerd-mode needs a non-empty string `section`')
54
- }
55
-
56
- ctx.sessionProjections.register(projection)
57
-
58
- ctx.effect(
59
- () =>
60
- ctx.systemPrompt.section({
61
- name: SECTION,
62
- order: SECTION_ORDER,
63
- text: (context) => {
64
- if (context.agent === undefined) return ''
65
- return ctx.sessionProjections.stateOf(context.agent.session, KEY).active ? section : ''
66
- },
67
- }),
68
- 'dsh-nerd-mode: section',
69
- )
70
-
71
- ctx.inject(['commands'], (commandCtx) => {
72
- commandCtx.commands.register({
73
- name: COMMAND,
74
- description: 'Turn Nerd Mode on or off for this conversation',
75
- input: { hint: '[on|off]' },
76
- handler: ({ agent, rawInput }) => {
77
- const arg = rawInput.trim().toLowerCase()
78
- const active = ctx.sessionProjections.stateOf(agent.session, KEY).active
79
-
80
- if (arg === 'off') {
81
- return { kind: 'success', text: active ? 'Nerd Mode off.' : 'Nerd Mode is already off.' }
82
- }
83
- if (arg !== '' && arg !== 'on') {
84
- return { kind: 'error', text: `Unknown argument "${arg}". Use /nerd-mode on or /nerd-mode off.` }
85
- }
86
- return {
87
- kind: 'success',
88
- text: active ? 'Nerd Mode is already on.' : 'Nerd Mode on. /study guides a concept or a task.',
89
- }
90
- },
91
- })
92
- })
93
- }
1
+ /**
2
+ * Nerd Mode: a per-session switch. While it is on, one prompt section joins
3
+ * every request and the `study` skill becomes the default answer shape. State
4
+ * folds from the session log, so it survives compaction, resume, and fork. The
5
+ * tool set never changes.
6
+ *
7
+ * The section registers at the profile level, not inside a preset, so it
8
+ * reaches every agent in every preset. See docs/adr/0008.
9
+ */
10
+
11
+ import { readFileSync } from 'node:fs'
12
+ import { fileURLToPath } from 'node:url'
13
+ import { COMMAND, KEY, fold, init } from './fold.js'
14
+ import z from '@deepseek-ai/schemastery'
15
+
16
+ export const name = 'dsh-nerd-mode'
17
+ export const PACKAGE_NAME = 'dsh-nerd-mode'
18
+ export const inject = ['systemPrompt', 'sessionProjections']
19
+
20
+ // The prompt text ships with the package. Keeping it out of the config stops
21
+ // every profile patch from carrying a stale copy of it.
22
+ const BUNDLED_SECTION = readFileSync(
23
+ fileURLToPath(new URL('../assets/section.md', import.meta.url)),
24
+ 'utf8',
25
+ ).trim()
26
+
27
+ export const Config = z.object({
28
+ section: z.string().pattern(/\S/).default(BUNDLED_SECTION),
29
+ recallSessionEnabled: z.boolean().default(false).volatile(),
30
+ unseenQuestionCount: z.number().step(1).min(0).max(10).default(3).volatile(),
31
+ unseenQuestionType: z.union([
32
+ z.const('short-answer'),
33
+ z.const('multiple-choice'),
34
+ z.const('mixed'),
35
+ ]).default('short-answer').volatile(),
36
+ })
37
+
38
+ const SECTION = 'study:policy'
39
+ // Between PLAN_POLICY (500) and TEAM_POLICY (600).
40
+ const SECTION_ORDER = 550
41
+ const studyPlusDefaultsBySession = new WeakMap()
42
+ const directStudyPlusBySession = new WeakMap()
43
+ // A PTC skill load leaves no message in the history, so keep its id for later
44
+ // turns the way an injected skill message would.
45
+ const ptcStudyPlusBySession = new WeakMap()
46
+ const SKILL_COMMAND = /(^|\s)\/([a-z0-9]+(?:-[a-z0-9]+)*)(?=\s|$)/g
47
+
48
+ // shortcut: parse-only schemas, no zod. The projection registry calls `parse`
49
+ // and nothing else. Upgrade to zod if the host starts introspecting schemas.
50
+ const stateSchema = {
51
+ parse: (value) => ({
52
+ active: value?.active === true,
53
+ running:
54
+ value?.running && typeof value.running.commandId === 'string'
55
+ ? { commandId: value.running.commandId, wanted: value.running.wanted === true }
56
+ : null,
57
+ }),
58
+ }
59
+
60
+ const viewSchema = { parse: (value) => ({ active: value?.active === true }) }
61
+
62
+ function studyPlusSettings(config, session, invocationId) {
63
+ let defaults = studyPlusDefaultsBySession.get(session)
64
+ if (defaults?.invocationId !== invocationId) {
65
+ defaults = {
66
+ invocationId,
67
+ recallSessionEnabled: config.recallSessionEnabled?.get?.() ?? false,
68
+ unseenQuestionCount: config.unseenQuestionCount?.get?.() ?? 3,
69
+ unseenQuestionType: config.unseenQuestionType?.get?.() ?? 'short-answer',
70
+ }
71
+ studyPlusDefaultsBySession.set(session, defaults)
72
+ }
73
+
74
+ return [
75
+ '',
76
+ '',
77
+ 'Study-plus profile defaults for this tracked session:',
78
+ `- Recall Session: ${defaults.recallSessionEnabled ? 'enabled' : 'disabled'}.`,
79
+ `- Post-lecture questions: ${defaults.unseenQuestionCount}; type: ${defaults.unseenQuestionType}.`,
80
+ 'Apply these defaults only during tracked study-plus sessions. Ignore them for ordinary study and all other requests.',
81
+ 'An explicit preference in the current /study-plus request overrides its profile default for this session and does not update the profile.',
82
+ ].join('\n')
83
+ }
84
+
85
+ /** Return the message id when the message is a direct /study-plus command, else undefined. */
86
+ function directStudyPlusId(message) {
87
+ if (message?.source?.kind !== 'user') return undefined
88
+ const text = (message.content ?? [])
89
+ .filter((block) => block.type === 'text')
90
+ .map((block) => block.text)
91
+ .join('\n')
92
+ return [...text.matchAll(SKILL_COMMAND)].at(-1)?.[2] === 'study-plus' ? message.id : undefined
93
+ }
94
+
95
+ function studyPlusInvocation(session) {
96
+ const directInvocation = directStudyPlusBySession.get(session)
97
+ if (directInvocation !== undefined) return directInvocation
98
+ const ptcInvocation = ptcStudyPlusBySession.get(session)
99
+ if (ptcInvocation !== undefined) return ptcInvocation
100
+
101
+ // The host returns the full surface history here, not just this turn. See docs/agents/dsh-session-api.md.
102
+ const invocation = session.deriveMessages().findLast((message) => message.source?.kind === 'skill-invocation')
103
+ if (invocation?.source.name === 'study-plus') return invocation.id
104
+
105
+ const completedCalls = new Set()
106
+ let loadedId
107
+ let directId
108
+ let skillCall
109
+ let ptcSkillCall
110
+ for (let seq = session.seq - 1; seq >= 0; seq -= 1) {
111
+ const event = session.eventAt(seq)
112
+ if (event?.type === 'turn/start') break
113
+ // A resumed, forked, or seeded log may carry a partial event payload, so
114
+ // every nested read here is guarded. One bad record must not fail a turn.
115
+ if (event?.type === 'tool/result') {
116
+ const result = event.data?.message
117
+ if (result !== undefined && result.isError !== true && result.source?.callId !== undefined) {
118
+ completedCalls.add(result.source.callId)
119
+ }
120
+ } else if (event?.type === 'tool/call' && event.data?.name === 'skill') {
121
+ try {
122
+ if (JSON.parse(event.data.arguments).name === 'study-plus') skillCall = event.data.callId
123
+ } catch {}
124
+ } else if (event?.type === 'tool/ptc-dispatch' && event.data?.name === 'skill') {
125
+ // PTC records a skill call made inside run_code as a dispatch, not as a
126
+ // `tool/call`, and the dispatch event itself carries the outcome.
127
+ if (event.data.isError !== true && event.data.arguments?.name === 'study-plus') {
128
+ ptcSkillCall = event.data.subCallId
129
+ }
130
+ } else if (event?.type === 'user/message') {
131
+ // `user/message` stores the message fields directly in `data`; only
132
+ // `tool/result` and `system/message` wrap them under `data.message`.
133
+ const message = event.data
134
+ if (message?.source?.kind === 'skill-invocation') {
135
+ if (message.source.name === 'study-plus') loadedId = message.id
136
+ } else {
137
+ directId = directStudyPlusId(message) ?? directId
138
+ }
139
+ }
140
+ }
141
+
142
+ // A skill load counts only when its tool call completed. A typed command counts at once.
143
+ if (skillCall !== undefined) {
144
+ return completedCalls.has(skillCall) ? loadedId ?? directId ?? skillCall : undefined
145
+ }
146
+ if (ptcSkillCall !== undefined) {
147
+ ptcStudyPlusBySession.set(session, ptcSkillCall)
148
+ return loadedId ?? directId ?? ptcSkillCall
149
+ }
150
+ return loadedId ?? directId
151
+ }
152
+
153
+ const projection = {
154
+ key: KEY,
155
+ stateVersion: 1,
156
+ stateSchema,
157
+ init,
158
+ apply: fold,
159
+ wire: {
160
+ viewSchema,
161
+ view: (state) => ({ active: state.active }),
162
+ },
163
+ }
164
+
165
+ /**
166
+ * @param config - `section` is an optional overlay text; a missing value uses
167
+ * the bundled `assets/section.md`.
168
+ */
169
+ export function apply(ctx, config) {
170
+ const section = config?.section
171
+ if (typeof section !== 'string' || section.trim() === '') {
172
+ throw new Error('dsh-nerd-mode needs a non-empty string `section`')
173
+ }
174
+
175
+ ctx.sessionProjections.register(projection)
176
+
177
+ ctx.effect(
178
+ () => ctx.on('agent/inbox/claimed', ({ agent, message }) => {
179
+ directStudyPlusBySession.set(agent.session, directStudyPlusId(message))
180
+ }),
181
+ 'dsh-nerd-mode: study-plus claim',
182
+ )
183
+
184
+ ctx.effect(
185
+ () =>
186
+ ctx.systemPrompt.section({
187
+ name: SECTION,
188
+ order: SECTION_ORDER,
189
+ text: (context) => {
190
+ if (context.agent === undefined) return ''
191
+ if (!ctx.sessionProjections.stateOf(context.agent.session, KEY).active) return ''
192
+ const session = context.agent.session
193
+ const invocationId = studyPlusInvocation(session)
194
+ if (invocationId === undefined) return section
195
+ return section + studyPlusSettings(config ?? {}, session, invocationId)
196
+ },
197
+ }),
198
+ 'dsh-nerd-mode: section',
199
+ )
200
+
201
+ ctx.inject(['commands'], (commandCtx) => {
202
+ commandCtx.commands.register({
203
+ name: COMMAND,
204
+ description: 'Turn Nerd Mode on or off for this conversation',
205
+ input: { hint: '[on|off]' },
206
+ handler: ({ agent, rawInput }) => {
207
+ const arg = rawInput.trim().toLowerCase()
208
+ const active = ctx.sessionProjections.stateOf(agent.session, KEY).active
209
+
210
+ if (arg === 'off') {
211
+ return { kind: 'success', text: active ? 'Nerd Mode off.' : 'Nerd Mode is already off.' }
212
+ }
213
+ if (arg !== '' && arg !== 'on') {
214
+ return { kind: 'error', text: `Unknown argument "${arg}". Use /nerd-mode on or /nerd-mode off.` }
215
+ }
216
+ return {
217
+ kind: 'success',
218
+ text: active ? 'Nerd Mode is already on.' : 'Nerd Mode on. /study guides a concept or a task.',
219
+ }
220
+ },
221
+ })
222
+ })
223
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-nerd-mode",
3
- "version": "0.2.1",
3
+ "version": "0.4.3",
4
4
  "description": "A study switch and study skills for DeepSeek Harness.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -36,11 +36,18 @@
36
36
  "client": {
37
37
  "platform": "web",
38
38
  "immediately": true,
39
- "inject": ["@deepseek-ai/dsh-client-ui-conversation"]
39
+ "inject": [
40
+ "@deepseek-ai/dsh-client-ui-conversation"
41
+ ]
40
42
  }
41
43
  },
42
44
  "scripts": {
43
- "test": "node --test"
45
+ "test": "node --test",
46
+ "lint": "eslint .",
47
+ "check": "npm run lint && npm test"
48
+ },
49
+ "peerDependencies": {
50
+ "@deepseek-ai/schemastery": "^3.18.5"
44
51
  },
45
52
  "engines": {
46
53
  "node": "^22.19.0 || >=24.0.0"
@@ -54,5 +61,10 @@
54
61
  ],
55
62
  "publishConfig": {
56
63
  "access": "public"
64
+ },
65
+ "devDependencies": {
66
+ "@deepseek-ai/schemastery": "^3.18.5",
67
+ "eslint": "^10.13.0",
68
+ "globals": "^17.13.0"
57
69
  }
58
70
  }
@@ -1,86 +1,78 @@
1
- ---
2
- name: study
3
- description: Guide one concept or one task in steps, stop once at a fork, and write no file. Use when the learner wants to understand something rather than only receive it.
4
- argument-hint: "<concept or task>"
5
- ---
6
-
7
- # Study
8
-
9
- You guide one thing at a time. You write no file and you read none. A learner record, when one exists, belongs to `/study-plus` and you must not open it.
10
-
11
- ## 1. Pick the scale
12
-
13
- Two scales, one shape.
14
-
15
- - **Concept**: the learner names an idea and wants to understand it. You teach it.
16
- - **Task**: the learner names work to finish, usually homework or code. You produce it while you teach it.
17
-
18
- When the request mixes both, take the task. The concept rides along inside the work.
19
-
20
- When you cannot tell which, ask one question with the two options. Do not guess.
21
-
22
- ## 2. Plan before you speak
23
-
24
- Decide three things, in this order.
25
-
26
- 1. The one sentence the learner should keep. Everything else supports it.
27
- 2. The steps that reach it. Three to seven is usual. More means the idea is really two ideas.
28
- 3. The fork: the first point where more than one route changes the result.
29
-
30
- If there is no fork, do not stop. A forced question is worse than none.
31
-
32
- ## 3. Build in this order
33
-
34
- 1. **Title.** Name the idea, not the session and not the date.
35
- 2. **The central idea.** Two or three sentences. Put the sentence worth remembering in a blockquote.
36
- 3. **Steps.** Number them when there are more than three, and name each by its content.
37
- 4. **Symbols.** Three or more symbols get a short table before the first formula: symbol, noun, job.
38
- 5. **Formulas.** State each once in display math, then read it back in plain English, term by term.
39
- 6. **One example.** Concrete numbers. Say where an analogy stops matching.
40
- 7. **The common mistake.** Name it, and state what the result does and does not assume.
41
-
42
- ## 4. Stop at the fork
43
-
44
- At the fork, do three things and nothing more.
45
-
46
- 1. Name the choice in one sentence.
47
- 2. State what each route costs.
48
- 3. Ask one question. Then stop and wait.
49
-
50
- A wrong answer never blocks. Name the wrong step, work the correct route, and continue. Do not ask the same fork twice.
51
-
52
- ## 5. For a task, act
53
-
54
- Produce the artifact. Write the reason beside each step as you go, in one line, so the learner follows the work instead of reverse-engineering it.
55
-
56
- Stop at the fork only. Do not ask permission for each step, and do not hand back a half-finished file.
57
-
58
- The artifact must run or hand in as it stands. When you cannot finish, say exactly what is missing and why.
59
-
60
- ## 6. Never grade
61
-
62
- No rubric. No pass. No miss. No score. No log.
63
-
64
- A wrong answer gets the wrong step named and the correct route shown. Nothing is counted.
65
-
66
- Self-report is not evidence and is not a reason to skip the steps. When the learner says they know it, teach the part they have not stated.
67
-
68
- ## 7. End
69
-
70
- Close with one sentence on what was built or understood.
71
-
72
- Then offer similar practice in one line. Offer, do not impose. Practice that is recorded and dated belongs to `/study-plus`.
73
-
74
- ## 8. Voice
75
-
76
- Plain, direct prose. One idea per paragraph. A paragraph that runs past six sentences becomes a list. Bold a key term only on first use. Use tables for comparisons.
77
-
78
- Write math as `$...$` inline and `$$...$$` on its own line. One expression per pair, and no prose inside.
79
-
80
- ## 9. Do not
81
-
82
- - Do not read or write `study/`. Not `graph.md`, not `state.md`, not a new file.
83
- - Do not call the learner's understanding mastered, verified, or fluent.
84
- - Do not open with flattery or close with "does that make sense".
85
- - Do not stack questions. One at a time.
86
- - Do not turn a code task into a project. Produce the asked artifact and stop.
1
+ ---
2
+ name: study
3
+ description: Teach one concept or help complete one task, using the supplied material as scope and defaulting to chat output.
4
+ argument-hint: "<concept or task>"
5
+ ---
6
+ # Study
7
+
8
+ Teach one concept or help complete one task. This skill owns the standards for teaching and lecture writing, including lecture drafts. `study-plus` owns tracked sessions, tracker updates, the choice of lecture paths, and book assembly. Never load or call `study-plus` from this skill.
9
+
10
+ The upper layer selects the path for multiple independent concepts. Follow that path and do not ask again.
11
+
12
+ ## Teach accurately
13
+
14
+ - Use supplied material to set the scope, title, chapter boundaries, and order. Treat it as a reference, not as truth. Check important or uncertain claims when needed, prefer accuracy, and flag differences.
15
+ - Use an approved graph or supplied material by default. Do not research the web for every lecture.
16
+ - Cover the major ideas, relationships, and qualifications. Explain intuition first, then terminology, then formal detail. Choose a structure that fits the content. Do not force a fixed section template.
17
+ - Use examples and analogies when they help. State where an analogy stops matching. Include exam topics only when they matter to the request.
18
+ - Use tables or figures when they clarify the material. Define every mathematical symbol and its role. In derivations, justify each meaningful step, explain the result, and check algebra, arithmetic, assumptions, and units.
19
+ - Write math in LaTeX. Put one expression in each math span. Keep prose outside math spans.
20
+ - Give every symbol a noun and a role. Define each symbol once, where the reader first meets it: inside the sentence for an inline mention, or in one line directly above the first display formula that uses it. Keep the line in the formula's reading order, and switch to one symbol per line when a definition needs an explanatory clause. Do not restate a symbol that an earlier sentence or definition line already made clear.
21
+ - When four or more symbols each appear in two or more formulas, put a short table of just those symbols before the first formula. The table is a lookup aid, not a definition, so it does not remove any symbol from the line above a formula.
22
+ - Compare three or more numbers in a short table before the first formula.
23
+ - State bounds as verbal comparisons. Explain each formula once in plain words without symbols.
24
+ - Use “you” for the reader’s actions and “we” for shared work. Avoid generic “one” and passive wording when the reader acts. Correct misleading terminology at first use.
25
+ - Justify each meaningful derivation step before showing it. Put one meaningful move on each display line, and split long equality chains where the object changes. Mark approximations and assumptions.
26
+ - Prefer verbs to abstract nouns. Open each paragraph with its claim, then support it. State each fact once. Avoid rhetorical questions that ask the reader to infer a conclusion. Keep lecture questions sparse and purposeful.
27
+ - Name headings for their content. Number them only when useful.
28
+ - For a task, produce the requested usable artifact and explain the important steps. Do not expand it into a project or return an avoidable partial result.
29
+
30
+ ## Teach at the given level
31
+
32
+ The upper layer may give a teaching brief of up to three lines: `Goal`, `Background`, and `Teaching level`. Treat it as context for writing, and never read `graph.md` or any other tracker file to obtain it.
33
+
34
+ `standard` uses every rule in this file unchanged. A missing `Teaching level` line means `standard`.
35
+
36
+ For `beginner`:
37
+
38
+ - Split the lecture into 3 to 7 numbered steps when the concept takes more than one move.
39
+ - Explain each term at first use, including terms that `standard` assumes, such as a prerequisite idea or a notation convention.
40
+ - Add one worked example per step when an example helps.
41
+ - Never add a prerequisite recap block. No level adds one.
42
+
43
+ For `advanced`:
44
+
45
+ - Use one pass without numbered steps, unless the derivation needs them.
46
+ - Skip the intuition for an idea only when the `Background` line names that idea as known. Start from the formal statement.
47
+ - Use at most one example per idea. Keep the qualifications and edge cases that matter for the stated goal.
48
+
49
+ Accuracy rules outrank the level. No level removes a symbol definition, a justification for a derivation step, a stated assumption or approximation, or a bound. No level adds filler, repeated explanation, or a section the concept does not need. The level affects the lecture only.
50
+
51
+ For a one-off request with no graph, derive the level from the brief the upper layer supplies and from the request and the supplied material. A brief from an upper layer is final: never ask again for a goal or a background it leaves out. A bare topic, or "I want to learn X", states no goal. When no brief arrives and neither the request nor the supplied material states a goal or a background, ask one combined question before teaching: what the learner wants to reach and whether they have met the topic before. State the derived level in one line, then teach. Write no file.
52
+
53
+ ## Format the answer
54
+
55
+ - For one concept, use an H1 named for the concept.
56
+ - For a one-off multi-concept book, use ordinary Markdown, not a fenced block. Use an H1 for the book or topic, H2 headings for Chapters, and H3 headings for concept Sections. Name each Section after its concept. Follow the supplied book order when it exists.
57
+ - For a task, title the requested artifact or task. Explain the reason for non-obvious steps.
58
+ - Give the complete answer when it fits. If a platform limit forces continuation, state which chapters are covered and which remain, then wait.
59
+ - Do not quiz, grade, score, or log. You may offer similar practice, but do not impose it.
60
+
61
+ ## Write lecture drafts
62
+
63
+ The upper layer may hand you an approved concept graph, the relevant supplied material, the teaching brief, and one destination file per concept. Then write one complete draft per concept at the given level, under the same writing standard. A draft is prepared teaching material, not teaching.
64
+
65
+ - Cover the concept completely at the given level: the teaching explanation, its relationships, and useful examples. Write no assessment questions, no answers, and no grading criteria. Actual questions stay unseen.
66
+ - Write only the destinations you are given, and one Chapter per batch when the upper layer splits the work. Never invent a path, and never read or write `graph.md`, `state.md`, `log.md`, `plan.md`, or any other tracker file.
67
+ - Frontmatter is exactly `status: draft` and the concept number. Add no date. The body opens with an H1 named for the concept.
68
+ - Preparation is not teaching. Change no learner state and append nothing to the log.
69
+ - Keep a valid existing draft or dated lecture at a destination. Report it instead of overwriting. Leave an occupied or unrecognized target alone and report it.
70
+ - Report the paths you wrote and every destination you refused. Print no draft in chat.
71
+
72
+ ## Files and figures
73
+
74
+ Default to chat output. If the learner asks to save a lecture or other artifact, save it to the requested path and show the same content in chat when practical.
75
+
76
+ When an SVG would materially clarify a relationship, create it by default. When the upper layer scopes a Course, write `study/<course>/diagrams/<name>.svg`. When the guide runs alone, write `diagrams/<name>.svg` and create no folder under `study/`. Load `diagram` for its visual authoring rules, but use this study-specific path instead of its generic default. Sanitize `course` and `name` as single lowercase kebab-case path segments with no slashes or dot segments. If either is unclear, choose a safe fallback. Before writing, confirm the resolved parent stays inside the intended `diagrams/` root and the target is not `study/<course>/graph.md`, `state.md`, `log.md`, or `plan.md`. Compute plotted points rather than placing them by eye. Before replacing an SVG with the same name, copy the old file to `<name>.svg.bak`, replacing any older backup.
77
+
78
+ Never read or write `study/<course>/graph.md`, `state.md`, `log.md`, or `plan.md`. Do not read or change tracker state. Study-plus owns the choice of lecture paths and the automatic lecture save during tracked sessions. Study may read or write ordinary artifacts, including lectures, drafts, and figures, at paths the user or the upper layer gives it.