@g3nomaly/g3n-skills 0.1.4 → 0.1.6
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/.claude-plugin/marketplace.json +14 -0
- package/README.md +8 -1
- package/package.json +2 -2
- package/skills/coding/code/SKILL.md +4 -1
- package/skills/coding/code/references/cpp.md +43 -0
- package/skills/coding/code/references/python.md +28 -0
- package/skills/coding/code/references/rust.md +26 -0
- package/skills/coding/document/SKILL.md +10 -0
- package/skills/coding/document/hooks/install-highlighting.ts +136 -0
- package/skills/coding/document/references/cpp.md +105 -0
- package/skills/coding/document/references/python.md +95 -0
- package/skills/coding/document/references/rust.md +122 -0
- package/skills/coding/document/vscode/package.json +28 -0
- package/skills/coding/document/vscode/syntaxes/python.json +56 -0
- package/skills/coding/document/vscode/syntaxes/rust.json +25 -0
- package/skills/teach/.claude-plugin/plugin.json +7 -0
- package/skills/teach/architecture/SKILL.md +19 -0
- package/skills/teach/architecture/agents/openai.yaml +5 -0
- package/skills/teach/architecture/references/examples.md +16 -0
- package/skills/teach/deep-dive/SKILL.md +19 -0
- package/skills/teach/deep-dive/agents/openai.yaml +5 -0
- package/skills/teach/deep-dive/references/examples.md +15 -0
- package/skills/teach/explain/SKILL.md +19 -0
- package/skills/teach/explain/agents/openai.yaml +5 -0
- package/skills/teach/explain/references/examples.md +9 -0
- package/skills/teach/guided-debugging/SKILL.md +25 -0
- package/skills/teach/guided-debugging/agents/openai.yaml +5 -0
- package/skills/teach/guided-debugging/references/debugging-loop.md +18 -0
- package/skills/teach/guided-debugging/references/examples.md +24 -0
- package/skills/teach/guided-review/SKILL.md +20 -0
- package/skills/teach/guided-review/agents/openai.yaml +5 -0
- package/skills/teach/guided-review/references/examples.md +18 -0
- package/skills/teach/learn-by-building/SKILL.md +19 -0
- package/skills/teach/learn-by-building/agents/openai.yaml +5 -0
- package/skills/teach/learn-by-building/references/examples.md +15 -0
- package/skills/teach/teaching/SKILL.md +22 -0
- package/skills/teach/teaching/agents/openai.yaml +5 -0
- package/skills/teach/teaching/references/learning-model.md +24 -0
|
@@ -56,6 +56,20 @@
|
|
|
56
56
|
"./decisions"
|
|
57
57
|
]
|
|
58
58
|
},
|
|
59
|
+
{
|
|
60
|
+
"name": "teach",
|
|
61
|
+
"source": "./skills/teach",
|
|
62
|
+
"description": "Teach through real work: build, explain, dive deep, decide architecture, debug and review while the user learns the reasoning.",
|
|
63
|
+
"skills": [
|
|
64
|
+
"./teaching",
|
|
65
|
+
"./learn-by-building",
|
|
66
|
+
"./explain",
|
|
67
|
+
"./deep-dive",
|
|
68
|
+
"./architecture",
|
|
69
|
+
"./guided-debugging",
|
|
70
|
+
"./guided-review"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
59
73
|
{
|
|
60
74
|
"name": "using-hooks",
|
|
61
75
|
"source": "./skills/using-hooks",
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @g3nomaly/g3n-skills
|
|
2
2
|
|
|
3
|
-
The g3n skills for Claude Code and Codex:
|
|
3
|
+
The g3n skills for Claude Code and Codex: 38 skills in 9 groups. This package holds the skills and has no CLI. Install them with [`@g3nomaly/skills`](https://www.npmjs.com/package/@g3nomaly/skills), which reads this package as its built-in source, or read it as a Claude Code plugin marketplace.
|
|
4
4
|
|
|
5
5
|
Some skills carry their own hooks. A hook is a TypeScript file in the skill's `hooks/` folder, and the agent runs it with `bun`. The task-manager hooks keep plans, phases and tasks as Markdown files that the agent reaches only through those hooks. The workflow hooks decide when a step of a phase is finished.
|
|
6
6
|
|
|
@@ -65,6 +65,13 @@ Skills marked _manual_ run only when you type their command, such as `/grill-me`
|
|
|
65
65
|
| `speech` | `no-questions` | Replies end without questions or offers; a Stop hook refuses a reply that asks one |
|
|
66
66
|
| `task-manager` | `task-manager` | Query plans, phases and tasks and change their status through hooks |
|
|
67
67
|
| `task-manager` | `decisions` | Record and look up a plan's decisions as numbered files inside the plan |
|
|
68
|
+
| `teach` | `teaching` | The teaching principles every teach skill follows: real work, causality, mental models and guidance per concept |
|
|
69
|
+
| `teach` | `learn-by-building` | Learn a subject while completing a real task on the user's own system |
|
|
70
|
+
| `teach` | `explain` | Explain existing material through its purpose, mental model, flow and failure modes |
|
|
71
|
+
| `teach` | `deep-dive` | Trace the layers beneath an abstraction, only as deep as the question needs |
|
|
72
|
+
| `teach` | `architecture` | Reason from forces and options to an architecture decision and its consequences |
|
|
73
|
+
| `teach` | `guided-debugging` | Find a failure's root cause through hypotheses and small experiments, teaching the method |
|
|
74
|
+
| `teach` | `guided-review` | Review work by consequence and explain the principle behind each finding |
|
|
68
75
|
| `using-hooks` | `hook-pre-tool-use` | _Manual._ Writing a `PreToolUse` hook, which can allow, deny or escalate a tool call |
|
|
69
76
|
| `using-hooks` | `hook-post-tool-use` | _Manual._ Writing a `PostToolUse` hook, which reacts to a finished tool call |
|
|
70
77
|
| `using-hooks` | `hook-stop` | _Manual._ Writing a `Stop` hook, which can refuse to let the agent end its turn |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@g3nomaly/g3n-skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "The g3n skills, each with its own hooks: a skill source for @g3nomaly/skills and a Claude Code plugin marketplace.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"test:clean": "CLEAN_TMP=1 bun test"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@g3nomaly/core": "^0.3.
|
|
18
|
+
"@g3nomaly/core": "^0.3.5",
|
|
19
19
|
"@types/bun": "^1.4.0",
|
|
20
20
|
"typescript": "~7.0.2",
|
|
21
21
|
"zod": "^4.4.3"
|
|
@@ -9,7 +9,7 @@ disable-model-invocation: false
|
|
|
9
9
|
Apply SOLID principles, DRY, and KISS. Use descriptive names for variables, functions, and classes.
|
|
10
10
|
|
|
11
11
|
- **One domain per file**: schemas, selectors and business logic live in separate files. Split god objects.
|
|
12
|
-
- **Strict typing**: per the language reference.
|
|
12
|
+
- **Strict typing**: per the language reference.
|
|
13
13
|
- **Production grade**: handle errors explicitly, validate external input at the boundary, leave no dead code.
|
|
14
14
|
- **No shims**: on a rename or refactor, update every call site. Leave no aliases.
|
|
15
15
|
- **Config from the environment**: read configuration from environment variables through a schema.
|
|
@@ -26,3 +26,6 @@ Each language has a reference that names its toolchain, its commands and its rul
|
|
|
26
26
|
| Language | Reference | Files |
|
|
27
27
|
|---|---|---|
|
|
28
28
|
| TypeScript | [`references/typescript.md`](references/typescript.md) | `.ts` `.tsx` |
|
|
29
|
+
| Python | [`references/python.md`](references/python.md) | `.py` |
|
|
30
|
+
| C++ | [`references/cpp.md`](references/cpp.md) | `.cpp` `.cc` `.hpp` `.h` |
|
|
31
|
+
| Rust | [`references/rust.md`](references/rust.md) | `.rs` |
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# C++
|
|
2
|
+
|
|
3
|
+
CMake configures and builds with clang. vcpkg supplies dependencies in manifest mode, from `vcpkg.json`. Run commands from inside the project directory, where `CMakeLists.txt` lives.
|
|
4
|
+
|
|
5
|
+
| Task | Command |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Configure | `cmake --preset <preset>` when `CMakePresets.json` exists, otherwise `cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_TOOLCHAIN_FILE="$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON` |
|
|
8
|
+
| Add a dependency | `vcpkg add port <pkg>`, then `find_package` and `target_link_libraries` in `CMakeLists.txt` |
|
|
9
|
+
| Build | `cmake --build build` |
|
|
10
|
+
| Run | the target's binary under `build/` |
|
|
11
|
+
|
|
12
|
+
## Checks
|
|
13
|
+
|
|
14
|
+
| Check | Command | When |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Build | `cmake --build build` | always |
|
|
17
|
+
| Format | `clang-format --dry-run --Werror <files>`, or `clang-format -i <files>` to fix | always |
|
|
18
|
+
| Lint | see [clang-tidy](#clang-tidy) | always |
|
|
19
|
+
| Test | `ctest --test-dir build --output-on-failure` | when the project has tests |
|
|
20
|
+
|
|
21
|
+
## clang-tidy
|
|
22
|
+
|
|
23
|
+
Its output is long enough to be cut off in a tool result, and it exits `0` on warnings. Never judge it from the terminal output.
|
|
24
|
+
|
|
25
|
+
1. Run it in the foreground, all output to a log:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
clang-tidy -p build --quiet --warnings-as-errors='*' <files> > build/clang-tidy.log 2>&1; echo "exit $?"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
2. Count the findings: `grep -cE ':[0-9]+:[0-9]+: (warning|error):' build/clang-tidy.log`.
|
|
32
|
+
3. List them: `grep -nE ':[0-9]+:[0-9]+: (warning|error):' build/clang-tidy.log`. When the list itself is long, page through it with `sed -n '1,100p'`, then the next 100, until the line count matches step 2.
|
|
33
|
+
|
|
34
|
+
The check passes only when the exit code is `0` **and** the count is `0`. Never pipe clang-tidy into `head` or `tail`, and never run it in the background.
|
|
35
|
+
|
|
36
|
+
## Rules
|
|
37
|
+
|
|
38
|
+
- Never install libraries with `apt` or vendor them by hand. vcpkg owns dependencies.
|
|
39
|
+
- `.clang-format` and `.clang-tidy` at the project root set the style. Fix the code, not the config, to pass a check.
|
|
40
|
+
- The C++ standard is set once in `CMakeLists.txt`. Do not raise it without asking.
|
|
41
|
+
- Targets compile with `-Wall -Wextra -Wpedantic -Werror`.
|
|
42
|
+
- RAII for every resource. Ownership goes through `std::unique_ptr` or `std::shared_ptr`, never a raw `new` or `delete`.
|
|
43
|
+
- External input is parsed into typed objects at the boundary.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
|
|
3
|
+
uv handles install, run and build. Run commands from inside the project directory, where `pyproject.toml` lives.
|
|
4
|
+
|
|
5
|
+
| Task | Command |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Install | `uv sync` |
|
|
8
|
+
| Add a dependency | `uv add <pkg>`, or `uv add --dev <pkg>` for dev |
|
|
9
|
+
| Run a file | `uv run <file>` |
|
|
10
|
+
| Run a tool once | `uvx <tool>` |
|
|
11
|
+
| Build | `uv build` |
|
|
12
|
+
|
|
13
|
+
## Checks
|
|
14
|
+
|
|
15
|
+
| Check | Command | When |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Typecheck | `uv run ty check` | always |
|
|
18
|
+
| Lint | `uv run ruff check --fix`, then fix what remains by hand | always |
|
|
19
|
+
| Format | `uv run ruff format --check`, or `uv run ruff format` to fix | always |
|
|
20
|
+
| Test | `uv run pytest` | when the project has tests |
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
|
|
24
|
+
- Never `pip`, `python -m venv` or `poetry`. uv owns the environment.
|
|
25
|
+
- Ruff and ty read their settings from `pyproject.toml`. Fix the code, not the config, to pass a check.
|
|
26
|
+
- Every function signature has type hints, including the return type.
|
|
27
|
+
- `object` and narrowing in place of `Any`.
|
|
28
|
+
- External input is parsed into typed objects at the boundary.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Rust
|
|
2
|
+
|
|
3
|
+
Cargo handles install, run, test and build. Run commands from inside the crate or workspace directory, where `Cargo.toml` lives.
|
|
4
|
+
|
|
5
|
+
| Task | Command |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Install | `cargo fetch`, or let `cargo build` fetch |
|
|
8
|
+
| Add a dependency | `cargo add <crate>`, or `cargo add --dev <crate>` for dev |
|
|
9
|
+
| Run | `cargo run` |
|
|
10
|
+
| Build | `cargo build`, or `cargo build --release` |
|
|
11
|
+
|
|
12
|
+
## Checks
|
|
13
|
+
|
|
14
|
+
| Check | Command | When |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Typecheck | `cargo check` | always |
|
|
17
|
+
| Lint | `cargo clippy --all-targets -- -D warnings` | always |
|
|
18
|
+
| Format | `cargo fmt --check`, or `cargo fmt` to fix | always |
|
|
19
|
+
| Test | `cargo test` | when the crate has tests |
|
|
20
|
+
|
|
21
|
+
## Rules
|
|
22
|
+
|
|
23
|
+
- Fix the code, not the lint config, to pass a check. No `#[allow(...)]` to silence clippy.
|
|
24
|
+
- Errors return as `Result` and travel with `?`. No `unwrap` or `expect` outside tests.
|
|
25
|
+
- No `unsafe` without asking.
|
|
26
|
+
- External input is parsed into typed structs at the boundary, e.g. with `serde`.
|
|
@@ -54,3 +54,13 @@ A decision is a choice **the user** made, with its _why_, never the _how_. A cho
|
|
|
54
54
|
The syntax for each language:
|
|
55
55
|
|
|
56
56
|
- TypeScript: [`references/typescript.md`](references/typescript.md)
|
|
57
|
+
- Python: [`references/python.md`](references/python.md)
|
|
58
|
+
- C++: [`references/cpp.md`](references/cpp.md)
|
|
59
|
+
- Rust: [`references/rust.md`](references/rust.md)
|
|
60
|
+
|
|
61
|
+
## Editor highlighting
|
|
62
|
+
|
|
63
|
+
Only when the user asks for docstring highlighting in VS Code. `<document-dir>` is the folder holding this `SKILL.md`.
|
|
64
|
+
|
|
65
|
+
1. **Install**: `bun <document-dir>/hooks/install-highlighting.ts`. It installs the extension in `vscode/`, which colours Rust doc sections and Python docstring sections like JSDoc tags, and turns off rust-analyzer's comment tokens that would cover them. On exit `1`, report its message.
|
|
66
|
+
2. **Reload**: tell the user to run `Developer: Reload Window`. Done when they see the colours.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// packages/g3n-skills/skills/coding/document/hooks/install-highlighting.ts
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
/** The editor setting that lets rust-analyzer paint over every grammar inside comments. */
|
|
7
|
+
const RUST_COMMENT_TOKENS = "rust-analyzer.semanticHighlighting.comments.enable";
|
|
8
|
+
|
|
9
|
+
/** Folder of the grammar-only extension this hook installs. */
|
|
10
|
+
const EXTENSION_DIR = path.join(import.meta.dir, "..", "vscode");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @brief Identity of the extension, read from its `package.json`.
|
|
14
|
+
*
|
|
15
|
+
* @property id - `publisher.name`, e.g. `g3nomaly.g3n-doc-tags`.
|
|
16
|
+
* @property version - Version from `package.json`, e.g. `0.1.0`.
|
|
17
|
+
*/
|
|
18
|
+
type Extension = { id: string; version: string };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @brief Reads the id and version of the extension in `EXTENSION_DIR`.
|
|
22
|
+
*
|
|
23
|
+
* @returns The extension's identity.
|
|
24
|
+
* @throws Error when `package.json` lacks `publisher`, `name` or `version`.
|
|
25
|
+
*/
|
|
26
|
+
function readExtension(): Extension {
|
|
27
|
+
const manifest: unknown = JSON.parse(fs.readFileSync(path.join(EXTENSION_DIR, "package.json"), "utf8"));
|
|
28
|
+
if (typeof manifest !== "object" || manifest === null) throw new Error("vscode/package.json is not an object");
|
|
29
|
+
const { publisher, name, version } = manifest as Record<string, unknown>;
|
|
30
|
+
if (typeof publisher !== "string" || typeof name !== "string" || typeof version !== "string") {
|
|
31
|
+
throw new Error("vscode/package.json needs publisher, name and version");
|
|
32
|
+
}
|
|
33
|
+
return { id: `${publisher}.${name}`, version };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @brief Runs a command and fails with its output when it exits non-zero.
|
|
38
|
+
*
|
|
39
|
+
* @param command - Program and arguments, e.g. `["code", "--install-extension", "x.vsix"]`.
|
|
40
|
+
* @param cwd - Working directory of the command.
|
|
41
|
+
* @throws Error with the command's stderr when it exits non-zero.
|
|
42
|
+
*/
|
|
43
|
+
function run(command: string[], cwd: string): void {
|
|
44
|
+
const result = Bun.spawnSync(command, { cwd, stdout: "pipe", stderr: "pipe" });
|
|
45
|
+
if (result.exitCode !== 0) {
|
|
46
|
+
throw new Error(`${command.join(" ")} failed:\n${result.stderr.toString() || result.stdout.toString()}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @brief Packages the extension into a `.vsix` in a fresh temporary folder, so the skill folder stays untouched.
|
|
52
|
+
*
|
|
53
|
+
* @param extension - Identity of the extension, which names the file.
|
|
54
|
+
* @returns Absolute path of the `.vsix`.
|
|
55
|
+
*/
|
|
56
|
+
function packageExtension(extension: Extension): string {
|
|
57
|
+
const work = fs.mkdtempSync(path.join(os.tmpdir(), "g3n-doc-tags-"));
|
|
58
|
+
const source = path.join(work, "extension");
|
|
59
|
+
fs.cpSync(EXTENSION_DIR, source, { recursive: true });
|
|
60
|
+
|
|
61
|
+
const vsix = path.join(work, `${extension.id}-${extension.version}.vsix`);
|
|
62
|
+
run(["bunx", "@vscode/vsce@3", "package", "--no-dependencies", "--skip-license", "--allow-missing-repository", "--out", vsix], source);
|
|
63
|
+
return vsix;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @brief Finds the settings file VS Code reads on this machine: the WSL machine settings when a VS Code server
|
|
68
|
+
* runs here, otherwise the user settings.
|
|
69
|
+
*
|
|
70
|
+
* @returns Absolute path of the settings file, which may not exist yet.
|
|
71
|
+
*/
|
|
72
|
+
function settingsPath(): string {
|
|
73
|
+
const home = os.homedir();
|
|
74
|
+
if (fs.existsSync(path.join(home, ".vscode-server"))) return path.join(home, ".vscode-server", "data", "Machine", "settings.json");
|
|
75
|
+
if (process.platform === "darwin") return path.join(home, "Library", "Application Support", "Code", "User", "settings.json");
|
|
76
|
+
return path.join(home, ".config", "Code", "User", "settings.json");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @brief Turns off rust-analyzer's comment tokens in `file`, so the extension's colours show. Writes the file
|
|
81
|
+
* only when the setting is not already `false`.
|
|
82
|
+
*
|
|
83
|
+
* @param file - Absolute path of a VS Code settings file.
|
|
84
|
+
* @returns True when the file was written.
|
|
85
|
+
* @throws Error when the file is not plain JSON, such as a file with comments, naming the setting to add by hand.
|
|
86
|
+
*/
|
|
87
|
+
function disableRustCommentTokens(file: string): boolean {
|
|
88
|
+
const text = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "{}";
|
|
89
|
+
let settings: unknown;
|
|
90
|
+
try {
|
|
91
|
+
settings = JSON.parse(text.trim() === "" ? "{}" : text);
|
|
92
|
+
} catch {
|
|
93
|
+
settings = null;
|
|
94
|
+
}
|
|
95
|
+
if (typeof settings !== "object" || settings === null || Array.isArray(settings)) {
|
|
96
|
+
throw new Error(`${file} is not plain JSON. Add by hand: "${RUST_COMMENT_TOKENS}": false`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const record = settings as Record<string, unknown>;
|
|
100
|
+
if (record[RUST_COMMENT_TOKENS] === false) return false;
|
|
101
|
+
record[RUST_COMMENT_TOKENS] = false;
|
|
102
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
103
|
+
fs.writeFileSync(file, `${JSON.stringify(record, null, 2)}\n`);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @brief Installs the doc tag highlighting: packages the extension, installs it with VS Code's `code` command,
|
|
109
|
+
* and turns off rust-analyzer's comment tokens in the VS Code settings. Writes the settings file.
|
|
110
|
+
*
|
|
111
|
+
* @param argv - Arguments of the hook. `--pretty` indents the JSON.
|
|
112
|
+
* @throws Error when `code` is not on the PATH, packaging or installing fails, or the settings are not plain JSON.
|
|
113
|
+
*/
|
|
114
|
+
function main(argv: readonly string[]): void {
|
|
115
|
+
const code = Bun.which("code");
|
|
116
|
+
if (code === null) throw new Error("VS Code's `code` command is not on the PATH. Run this from a VS Code terminal.");
|
|
117
|
+
|
|
118
|
+
const extension = readExtension();
|
|
119
|
+
const vsix = packageExtension(extension);
|
|
120
|
+
run([code, "--install-extension", vsix, "--force"], path.dirname(vsix));
|
|
121
|
+
fs.rmSync(path.dirname(vsix), { recursive: true, force: true });
|
|
122
|
+
|
|
123
|
+
const settings = settingsPath();
|
|
124
|
+
const settingsChanged = disableRustCommentTokens(settings);
|
|
125
|
+
|
|
126
|
+
const result = { extension: `${extension.id}@${extension.version}`, settings, settingsChanged };
|
|
127
|
+
console.log(JSON.stringify(result, null, argv.includes("--pretty") ? 2 : undefined));
|
|
128
|
+
console.error("Run `Developer: Reload Window` in VS Code to load the highlighting.");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
main(process.argv.slice(2));
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# C++
|
|
2
|
+
|
|
3
|
+
Doxygen: a `/** */` block directly above the declaration, in the header.
|
|
4
|
+
|
|
5
|
+
- **Line 1**: `// src/users/user_store.hpp`, the relative path. No `@file` block.
|
|
6
|
+
- **Declarations with a docstring**: functions, classes, structs, enums, type aliases and concepts. The header declaration carries it; the definition in the `.cpp` does not repeat it.
|
|
7
|
+
- **Summary**: `/** Adds two numbers. */` on one line. A longer block opens with `@brief` and the summary.
|
|
8
|
+
- **Tags**: `@param name text`, `@return text`, `@throws Type When ...`, and for the members of a class, struct or enum `@property name text`. Any other tag that fits is fine: `@tparam T text`, `@example`, `@see`, `@deprecated`, `@since`.
|
|
9
|
+
- **Throws**: a function that can throw has one `@throws` tag per exception type, with its full name, e.g. `std::filesystem::filesystem_error`.
|
|
10
|
+
- **Example**: `@example` with the code on the lines below it. Not `@code` ... `@endcode`, which analyze does not read.
|
|
11
|
+
|
|
12
|
+
```cpp
|
|
13
|
+
/** Adds two numbers. */
|
|
14
|
+
[[nodiscard]] int add(int a, int b);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @brief Retrieves a user by id.
|
|
18
|
+
*
|
|
19
|
+
* @param user_id Surrogate key from the `users` table, not the auth provider's id.
|
|
20
|
+
* @return Custom User class used by the authentication service, or `std::nullopt` when no row matches.
|
|
21
|
+
*/
|
|
22
|
+
[[nodiscard]] std::optional<User> get_user(std::int64_t user_id);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @brief Reads a file and checks it against its schema.
|
|
26
|
+
*
|
|
27
|
+
* @param path Absolute path of the file, e.g. `/home/me/app/docs/auth.md`.
|
|
28
|
+
* @return Custom Tree struct with one node per section of the file.
|
|
29
|
+
* @throws FileValidationError When a field does not match the schema.
|
|
30
|
+
* @throws std::filesystem::filesystem_error When the file cannot be read.
|
|
31
|
+
*/
|
|
32
|
+
[[nodiscard]] Tree parse_file(const std::filesystem::path& path);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @brief Error for a file on disk that does not match its schema.
|
|
36
|
+
*
|
|
37
|
+
* Holds the path of the file and one issue per rejected field, and `what()`
|
|
38
|
+
* lists them all. Thrown by `parse_file` and by the readers in `filetree/`,
|
|
39
|
+
* `decisions/` and `vocabulary/` that check a file's layout by hand. Callers
|
|
40
|
+
* tell it apart from other errors with `catch (const FileValidationError&)`.
|
|
41
|
+
*
|
|
42
|
+
* @property file Absolute path of the file that failed, e.g. `/home/me/app/docs/auth.md`.
|
|
43
|
+
* @property issues Rejected fields, e.g. `{ "phases.0.status", "is required" }`.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* throw FileValidationError("/home/me/app/docs/auth.md", {{"status", "is \"done\""}});
|
|
47
|
+
*/
|
|
48
|
+
class FileValidationError : public std::runtime_error {
|
|
49
|
+
public:
|
|
50
|
+
/** Builds the message from the file path and every issue. */
|
|
51
|
+
FileValidationError(std::filesystem::path file, std::vector<FileValidationIssue> issues);
|
|
52
|
+
|
|
53
|
+
const std::filesystem::path file;
|
|
54
|
+
const std::vector<FileValidationIssue> issues;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @brief Entries a set of requests resolved to, and the requests that did not resolve.
|
|
59
|
+
*
|
|
60
|
+
* @property entries Resolved entries by key, e.g. `emphasise`.
|
|
61
|
+
* @property unknown Requests no source offers, e.g. `ghost@mine`.
|
|
62
|
+
* @property notes Why an entry nobody named is in the set, e.g. `{ "vocabulary", "dep of plan" }`.
|
|
63
|
+
*/
|
|
64
|
+
struct Resolution {
|
|
65
|
+
std::unordered_map<std::string, SourceOffer> entries;
|
|
66
|
+
std::vector<std::string> unknown;
|
|
67
|
+
std::unordered_map<std::string, std::string> notes;
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Bad
|
|
72
|
+
|
|
73
|
+
Each one next to what is wrong with it. The examples above show the fix.
|
|
74
|
+
|
|
75
|
+
```cpp
|
|
76
|
+
struct Resolution {
|
|
77
|
+
std::vector<std::string> unknown; ///< Requests no source offers.
|
|
78
|
+
};
|
|
79
|
+
```
|
|
80
|
+
A trailing `///<` docstring on a member. Members go in the struct's docstring as `@property` tags.
|
|
81
|
+
|
|
82
|
+
```cpp
|
|
83
|
+
// Retrieves a user by id.
|
|
84
|
+
std::optional<User> get_user(std::int64_t user_id);
|
|
85
|
+
```
|
|
86
|
+
A plain `//` comment in place of a docstring. Doxygen and clangd only read `/** */`.
|
|
87
|
+
|
|
88
|
+
```cpp
|
|
89
|
+
/**
|
|
90
|
+
* @brief Reads a file and checks it against its schema.
|
|
91
|
+
*
|
|
92
|
+
* @return The parsed tree. Throws when the file is invalid.
|
|
93
|
+
*/
|
|
94
|
+
[[nodiscard]] Tree parse_file(const std::filesystem::path& path);
|
|
95
|
+
```
|
|
96
|
+
The exception is prose inside `@return`, with no type. Each exception gets its own `@throws` tag, as in `parse_file` above.
|
|
97
|
+
|
|
98
|
+
```cpp
|
|
99
|
+
/**
|
|
100
|
+
* Resolves the dependencies of one entry against every offer.
|
|
101
|
+
*
|
|
102
|
+
* @param preferred Source to take a key from when several offer it.
|
|
103
|
+
*/
|
|
104
|
+
```
|
|
105
|
+
More than one line without `@brief`. It opens with `@brief Resolves the dependencies...`.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
|
|
3
|
+
Google-style docstrings: a `"""` block as the first statement inside the declaration.
|
|
4
|
+
|
|
5
|
+
- **Line 1**: `# src/app/users.py`, the relative path. No module docstring, so ruff's `D100` and `D104` stay off.
|
|
6
|
+
- **Declarations with a docstring**: functions, methods, classes, dataclasses, `TypedDict`s, `Protocol`s and enums.
|
|
7
|
+
- **Summary**: `"""Adds two numbers."""` on one line. The first line is the `@brief`: a longer block opens with the summary, then a blank line.
|
|
8
|
+
- **Tags**: sections `Args:`, `Returns:`, `Raises:` and `Example:`. The fields of a class, dataclass, `TypedDict` or enum go under `Attributes:` in its docstring.
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
def add(a: int, b: int) -> int:
|
|
12
|
+
"""Adds two numbers."""
|
|
13
|
+
return a + b
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def get_user(user_id: int) -> User | None:
|
|
17
|
+
"""Retrieves a user by id.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
user_id: Surrogate key from the `users` table, not the auth provider's id.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Custom User class used by the authentication service, or `None` when no row matches.
|
|
24
|
+
"""
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FileValidationError(Exception):
|
|
29
|
+
"""Error for a file on disk that does not match its schema.
|
|
30
|
+
|
|
31
|
+
Holds the path of the file and one issue per rejected field, and its message
|
|
32
|
+
lists them all. Raised by `parse_file` and by the readers in `filetree/`,
|
|
33
|
+
`decisions/` and `vocabulary/` that check a file's layout by hand. Callers
|
|
34
|
+
tell it apart from other errors with `except FileValidationError`.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
file: Absolute path of the file that failed, e.g. `/home/me/app/docs/auth.md`.
|
|
38
|
+
issues: Rejected fields, e.g. `FileValidationIssue(field="phases.0.status", message="is required")`.
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
raise FileValidationError("/home/me/app/docs/auth.md", [
|
|
42
|
+
FileValidationIssue(field="status", message='is "done"'),
|
|
43
|
+
])
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, file: str, issues: list[FileValidationIssue]) -> None:
|
|
47
|
+
"""Builds the message from the file path and every issue."""
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class Resolution:
|
|
53
|
+
"""Entries a set of requests resolved to, and the requests that did not resolve.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
entries: Resolved entries by key, e.g. `emphasise`.
|
|
57
|
+
unknown: Requests no source offers, e.g. `ghost@mine`.
|
|
58
|
+
notes: Why an entry nobody named is in the set, e.g. `{"vocabulary": "dep of plan"}`.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
entries: dict[str, SourceOffer]
|
|
62
|
+
unknown: list[str]
|
|
63
|
+
notes: dict[str, str]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`__init__` gets a summary only when the class docstring already tags its arguments as attributes.
|
|
67
|
+
|
|
68
|
+
## Bad
|
|
69
|
+
|
|
70
|
+
Each one next to what is wrong with it. The examples above show the fix.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
@dataclass
|
|
74
|
+
class Resolution:
|
|
75
|
+
"""Entries a set of requests resolved to."""
|
|
76
|
+
|
|
77
|
+
entries: dict[str, SourceOffer]
|
|
78
|
+
"""Resolved entries by key."""
|
|
79
|
+
```
|
|
80
|
+
A docstring on a field. Fields go in the class docstring under `Attributes:`.
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
def get_user(user_id: int) -> User | None:
|
|
84
|
+
# Looks the user up by surrogate key.
|
|
85
|
+
"""Retrieves a user by id."""
|
|
86
|
+
```
|
|
87
|
+
A comment that says what a tag can say. It goes under `Args:`.
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
def matches(request: EntryRequest, offers: Sequence[SourceOffer]) -> Match:
|
|
91
|
+
"""
|
|
92
|
+
Finds the entries one request names.
|
|
93
|
+
"""
|
|
94
|
+
```
|
|
95
|
+
The summary starts on line 2, and the custom types `EntryRequest`, `SourceOffer` and `Match` have no `Args:` or `Returns:` entry.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Rust
|
|
2
|
+
|
|
3
|
+
Rustdoc: a `/** */` block directly above the item, written in Markdown. Never `///`.
|
|
4
|
+
|
|
5
|
+
- **Line 1**: `// src/users/store.rs`, the relative path. No `//!` or `/*! */` module or crate docs.
|
|
6
|
+
- **Declarations with a docstring**: functions, methods, structs, enums, traits and type aliases.
|
|
7
|
+
- **Summary**: `/** Adds two numbers. */` on one line. The first line is the `@brief`: a longer block opens with the summary, then a blank ` *` line.
|
|
8
|
+
- **Tags**: Markdown sections `# Arguments`, `# Returns`, `# Errors`, `# Panics` and `# Examples`. Arguments are a list of `` * `name` - text ``.
|
|
9
|
+
- **Fields and variants**: Rust has no field tag, so this is the one exception to the owner rule. Each struct field and enum variant gets its own one-line `/** */`. Rustdoc and analyze both read it from there.
|
|
10
|
+
- **Examples**: a fenced block under `# Examples`. `cargo test` compiles and runs it, so it must build. Setup lines start with `# `, which hides them from the rendered docs.
|
|
11
|
+
|
|
12
|
+
```rust
|
|
13
|
+
/** Adds two numbers. */
|
|
14
|
+
pub fn add(a: i32, b: i32) -> i32 {
|
|
15
|
+
a + b
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Retrieves a user by id.
|
|
20
|
+
*
|
|
21
|
+
* # Arguments
|
|
22
|
+
*
|
|
23
|
+
* * `user_id` - Surrogate key from the `users` table, not the auth provider's id.
|
|
24
|
+
*
|
|
25
|
+
* # Returns
|
|
26
|
+
*
|
|
27
|
+
* Custom User struct used by the authentication service, or `None` when no row matches.
|
|
28
|
+
*
|
|
29
|
+
* # Errors
|
|
30
|
+
*
|
|
31
|
+
* `StoreError::Connection` when the database is unreachable.
|
|
32
|
+
*/
|
|
33
|
+
pub async fn get_user(user_id: i64) -> Result<Option<User>, StoreError> {
|
|
34
|
+
// ...
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Error for a file on disk that does not match its schema.
|
|
39
|
+
*
|
|
40
|
+
* Holds the path of the file and one issue per rejected field, and its
|
|
41
|
+
* `Display` lists them all. Returned by `parse_file` and by the readers in
|
|
42
|
+
* `filetree/`, `decisions/` and `vocabulary/` that check a file's layout by
|
|
43
|
+
* hand. Callers tell it apart from other errors by matching on the type.
|
|
44
|
+
*
|
|
45
|
+
* # Examples
|
|
46
|
+
*
|
|
47
|
+
* ```
|
|
48
|
+
* # use harbor::{FileValidationError, FileValidationIssue};
|
|
49
|
+
* let error = FileValidationError {
|
|
50
|
+
* file: "/home/me/app/docs/auth.md".into(),
|
|
51
|
+
* issues: vec![FileValidationIssue { field: "status".into(), message: r#"is "done""#.into() }],
|
|
52
|
+
* };
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
#[derive(Debug)]
|
|
56
|
+
pub struct FileValidationError {
|
|
57
|
+
/** Absolute path of the file that failed, e.g. `/home/me/app/docs/auth.md`. */
|
|
58
|
+
pub file: PathBuf,
|
|
59
|
+
/** Rejected fields, e.g. `FileValidationIssue { field: "phases.0.status".into(), message: "is required".into() }`. */
|
|
60
|
+
pub issues: Vec<FileValidationIssue>,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Entries a set of requests resolved to, and the requests that did not resolve. */
|
|
64
|
+
pub struct Resolution {
|
|
65
|
+
/** Resolved entries by key, e.g. `emphasise`. */
|
|
66
|
+
pub entries: HashMap<String, SourceOffer>,
|
|
67
|
+
/** Requests no source offers, e.g. `ghost@mine`. */
|
|
68
|
+
pub unknown: Vec<String>,
|
|
69
|
+
/** Why an entry nobody named is in the set, e.g. `("vocabulary", "dep of plan")`. */
|
|
70
|
+
pub notes: HashMap<String, String>,
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`harbor` in the example stands for the crate's own name. Doctests use the crate from outside, so `crate::` does not work there.
|
|
75
|
+
|
|
76
|
+
A function that returns `Result` always has `# Errors`. One that can panic always has `# Panics`.
|
|
77
|
+
|
|
78
|
+
## Bad
|
|
79
|
+
|
|
80
|
+
Each one next to what is wrong with it. The examples above show the fix.
|
|
81
|
+
|
|
82
|
+
```rust
|
|
83
|
+
/// Adds two numbers.
|
|
84
|
+
pub fn add(a: i32, b: i32) -> i32
|
|
85
|
+
```
|
|
86
|
+
A `///` comment. Use `/** Adds two numbers. */`.
|
|
87
|
+
|
|
88
|
+
```rust
|
|
89
|
+
/**
|
|
90
|
+
* Entries a set of requests resolved to.
|
|
91
|
+
*
|
|
92
|
+
* # Fields
|
|
93
|
+
*
|
|
94
|
+
* * `entries` - Resolved entries by key.
|
|
95
|
+
*/
|
|
96
|
+
pub struct Resolution {
|
|
97
|
+
pub entries: HashMap<String, SourceOffer>,
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
Fields listed in the struct's docstring. Neither rustdoc nor analyze reads them there. The `/** */` goes on the field.
|
|
101
|
+
|
|
102
|
+
```rust
|
|
103
|
+
/** Parses a file. */
|
|
104
|
+
pub fn parse_file(path: &Path) -> Result<Tree, FileValidationError>
|
|
105
|
+
```
|
|
106
|
+
Returns `Result` with no `# Errors` section.
|
|
107
|
+
|
|
108
|
+
```rust
|
|
109
|
+
/**
|
|
110
|
+
* # Examples
|
|
111
|
+
*
|
|
112
|
+
* ```
|
|
113
|
+
* let error = FileValidationError::new(path, vec![issue]);
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
```
|
|
117
|
+
`path` and `issue` are never defined and the type is never imported, so `cargo test` fails on it.
|
|
118
|
+
|
|
119
|
+
```rust
|
|
120
|
+
/*! User storage. */
|
|
121
|
+
```
|
|
122
|
+
A module docstring. Line 1 is the path comment, and the items carry the docs.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "g3n-doc-tags",
|
|
3
|
+
"displayName": "g3n Doc Tags",
|
|
4
|
+
"description": "Colours the sections and argument names of Rust doc comments and Python docstrings like JSDoc tags.",
|
|
5
|
+
"publisher": "g3nomaly",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"license": "Elastic-2.0",
|
|
8
|
+
"engines": {
|
|
9
|
+
"vscode": "^1.80.0"
|
|
10
|
+
},
|
|
11
|
+
"categories": [
|
|
12
|
+
"Programming Languages"
|
|
13
|
+
],
|
|
14
|
+
"contributes": {
|
|
15
|
+
"grammars": [
|
|
16
|
+
{
|
|
17
|
+
"scopeName": "g3n.doc-tags.rust",
|
|
18
|
+
"path": "./syntaxes/rust.json",
|
|
19
|
+
"injectTo": ["source.rust", "text.html.markdown"]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"scopeName": "g3n.doc-tags.python",
|
|
23
|
+
"path": "./syntaxes/python.json",
|
|
24
|
+
"injectTo": ["source.python", "text.html.markdown"]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scopeName": "g3n.doc-tags.python",
|
|
3
|
+
"injectionSelector": "L:string.quoted.docstring.multi.python, L:string.quoted.docstring.raw.multi.python",
|
|
4
|
+
"patterns": [
|
|
5
|
+
{ "include": "#named-section" },
|
|
6
|
+
{ "include": "#raises-section" },
|
|
7
|
+
{ "include": "#plain-section" }
|
|
8
|
+
],
|
|
9
|
+
"repository": {
|
|
10
|
+
"named-section": {
|
|
11
|
+
"comment": "`Args:` or `Attributes:` and the `name (type):` items under it. The section ends at the first line that is not indented deeper, or at the closing quotes.",
|
|
12
|
+
"begin": "^(\\s*)(Args|Arguments|Attributes|Keyword Args|Keyword Arguments|Other Parameters)(:)\\s*$",
|
|
13
|
+
"end": "(?=\"\"\"|''')|^(?!\\1\\s+\\S|\\s*$)",
|
|
14
|
+
"beginCaptures": {
|
|
15
|
+
"2": { "name": "storage.type.class.jsdoc" },
|
|
16
|
+
"3": { "name": "punctuation.separator.jsdoc" }
|
|
17
|
+
},
|
|
18
|
+
"patterns": [
|
|
19
|
+
{
|
|
20
|
+
"match": "^\\s+(\\*{0,2}[A-Za-z_]\\w*)(?:\\s*\\(([^)]*)\\))?(:)(?=\\s|$)",
|
|
21
|
+
"captures": {
|
|
22
|
+
"1": { "name": "variable.other.jsdoc" },
|
|
23
|
+
"2": { "name": "entity.name.type.instance.jsdoc" },
|
|
24
|
+
"3": { "name": "punctuation.separator.jsdoc" }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"raises-section": {
|
|
30
|
+
"comment": "`Raises:` and the `ErrorType:` items under it.",
|
|
31
|
+
"begin": "^(\\s*)(Raises)(:)\\s*$",
|
|
32
|
+
"end": "(?=\"\"\"|''')|^(?!\\1\\s+\\S|\\s*$)",
|
|
33
|
+
"beginCaptures": {
|
|
34
|
+
"2": { "name": "storage.type.class.jsdoc" },
|
|
35
|
+
"3": { "name": "punctuation.separator.jsdoc" }
|
|
36
|
+
},
|
|
37
|
+
"patterns": [
|
|
38
|
+
{
|
|
39
|
+
"match": "^\\s+([A-Za-z_][\\w.]*)(:)(?=\\s|$)",
|
|
40
|
+
"captures": {
|
|
41
|
+
"1": { "name": "entity.name.type.instance.jsdoc" },
|
|
42
|
+
"2": { "name": "punctuation.separator.jsdoc" }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"plain-section": {
|
|
48
|
+
"comment": "Sections whose body is prose or code, such as `Returns:` and `Example:`. Only the heading is coloured.",
|
|
49
|
+
"match": "^\\s*(Returns|Yields|Example|Examples|Note|Notes|Warning|Warnings|See Also|Todo)(:)\\s*$",
|
|
50
|
+
"captures": {
|
|
51
|
+
"1": { "name": "storage.type.class.jsdoc" },
|
|
52
|
+
"2": { "name": "punctuation.separator.jsdoc" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scopeName": "g3n.doc-tags.rust",
|
|
3
|
+
"injectionSelector": "L:comment.block.documentation.rust",
|
|
4
|
+
"patterns": [
|
|
5
|
+
{ "include": "#section" },
|
|
6
|
+
{ "include": "#argument" }
|
|
7
|
+
],
|
|
8
|
+
"repository": {
|
|
9
|
+
"section": {
|
|
10
|
+
"comment": "A section heading such as ` * # Errors`. Only known names, so a hidden doctest line such as `# use harbor::Tree;` stays plain.",
|
|
11
|
+
"match": "^\\s*\\*?\\s*(#)\\s+(Arguments|Returns|Errors|Panics|Safety|Examples)\\s*$",
|
|
12
|
+
"captures": {
|
|
13
|
+
"1": { "name": "punctuation.definition.block.tag.jsdoc" },
|
|
14
|
+
"2": { "name": "storage.type.class.jsdoc" }
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"argument": {
|
|
18
|
+
"comment": "An item of the Arguments list: ` * * `user_id` - text`.",
|
|
19
|
+
"match": "^\\s*\\*?\\s*[*-]\\s+(`)([A-Za-z_]\\w*)(`)\\s+(-)(?=\\s)",
|
|
20
|
+
"captures": {
|
|
21
|
+
"2": { "name": "variable.other.jsdoc" }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "teach",
|
|
3
|
+
"description": "Teach through real work: build, explain, dive deep, decide architecture, debug and review while the user learns the reasoning.",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"skills": ["./teaching", "./learn-by-building", "./explain", "./deep-dive", "./architecture", "./guided-debugging", "./guided-review"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architecture
|
|
3
|
+
description: Use when the user is deciding or wants to understand an architecture choice, such as system boundaries, ownership, dependencies, state, data flow, consistency, integration or deployment.
|
|
4
|
+
registryDependencies: [teaching]
|
|
5
|
+
disable-model-invocation: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Reason from the problem to the decision, and teach the user to reason the same way. Follow the `/teaching` skill throughout. A _pattern name_ describes a solution once the forces justify it. It is never the starting point.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Define the decision** and its scope in neutral terms, with no pattern name. Done when the user agrees it is the question.
|
|
13
|
+
2. **Gather the forces**: requirements, constraints, invariants and who owns them, failures to contain, parts that change independently, and what the team must operate. Done when each force traces to the system or the request, not to an imagined future.
|
|
14
|
+
3. **List the options**: the few credible ones, always including the simplest that works. Keep logical, runtime, deployment and data boundaries apart.
|
|
15
|
+
4. **Compare** the options on the forces that could change the decision. Drop an option whose cost solves a problem nobody has.
|
|
16
|
+
5. **Recommend** one, with your confidence and assumptions. When evidence is weak, prefer the option easiest to reverse.
|
|
17
|
+
6. **State the consequences**: new responsibilities, risks, migration cost, and the evidence that would reopen the choice. Done when the user could explain why the other options lost.
|
|
18
|
+
|
|
19
|
+
For worked cases, such as wrapping a provider SDK or splitting out a worker, read [`references/examples.md`](references/examples.md).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Architecture examples
|
|
2
|
+
|
|
3
|
+
## Call the storage SDK directly, or put a boundary in front of it?
|
|
4
|
+
|
|
5
|
+
Check whether the application has storage operations of its own, more than one provider, migration plans, special retry or authorization rules, or testing needs. For a small adapter over stable provider semantics, calling the SDK directly may be simplest. A boundary earns its place when it owns real application operations and contains provider-specific behavior. That provider replacement is _imaginable_ is no reason to add one.
|
|
6
|
+
|
|
7
|
+
## Should this worker be a microservice?
|
|
8
|
+
|
|
9
|
+
Separate logical ownership from deployment. Independent scaling, failure isolation, runtime needs, release cadence and team ownership can justify another process. Being called a "worker" does not.
|
|
10
|
+
|
|
11
|
+
## Failure modes
|
|
12
|
+
|
|
13
|
+
- _Hypothetical scale._ Paying permanent complexity for demand nobody has shown.
|
|
14
|
+
- _Boundary confusion._ Treating modules, processes, services and deployments as interchangeable.
|
|
15
|
+
- _Provider-shaped abstraction._ Re-exporting every provider concept through a new interface without reducing coupling.
|
|
16
|
+
- _Operation blindness._ Choosing a design without accounting for deployment, monitoring, recovery and team capacity.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deep-dive
|
|
3
|
+
description: Use when the user asks how a runtime, protocol, database, framework, operating-system feature, network operation or other abstraction works underneath.
|
|
4
|
+
registryDependencies: [teaching]
|
|
5
|
+
disable-model-invocation: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Build a model of the layers under an abstraction the user already uses. Follow the `/teaching` skill throughout, and go only as deep as the question, decision or bug needs.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Name the operation** being traced and why the user needs the depth. Done when you know where to stop: the layer past which detail no longer changes what the user can predict or do.
|
|
13
|
+
2. **Map the whole path** in a compact diagram before expanding any layer.
|
|
14
|
+
3. **Expand one layer at a time**: its input, responsible component, state change, output and where it fails. Explain the handoff to the next layer, not only the component's name.
|
|
15
|
+
4. **Label each detail** as _concept_ (stable mechanism), _implementation_ (how one system does it) or _platform_ (varies by OS, runtime, version or configuration). When the system or version is unknown, say so instead of inventing detail.
|
|
16
|
+
5. **Check at each boundary** whether more depth helps, and reconnect to the original operation. Offer a deeper branch rather than forcing it in.
|
|
17
|
+
6. **Finish with a compact model** the user can use to predict the behavior.
|
|
18
|
+
|
|
19
|
+
For worked cases, such as a Docker Compose connection or a `fetch()` call, read [`references/examples.md`](references/examples.md).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Deep-dive examples
|
|
2
|
+
|
|
3
|
+
## What happens when `api` connects to `postgres:5432` in Docker Compose?
|
|
4
|
+
|
|
5
|
+
Start with the path:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
service name → Docker DNS → container-network IP → TCP connection → PostgreSQL listener
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Compose provides service discovery on the shared network. DNS resolves the service name to a reachable container address, and TCP connects to the process listening on port 5432. Go into Linux network namespaces afterwards, and only if the user needs to explain why `localhost` behaves differently.
|
|
12
|
+
|
|
13
|
+
## What happens when JavaScript calls `fetch()`?
|
|
14
|
+
|
|
15
|
+
Map URL resolution, connection setup, TLS, the HTTP exchange, runtime scheduling and application handling. Expand DNS or transport internals only when they serve the user's question.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: explain
|
|
3
|
+
description: Use when the user asks to understand existing code, types, queries, configuration, workflows, tests, protocols or infrastructure.
|
|
4
|
+
registryDependencies: [teaching, deep-dive]
|
|
5
|
+
disable-model-invocation: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Explain why the important parts exist and how they interact, so the user can reason about the material later. Follow the `/teaching` skill throughout. Never paraphrase line by line.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Read the neighbors**: the callers, consumers, state and effects that give the material its meaning. Done when you can state its purpose in the larger system. Never invent an intent the evidence does not support.
|
|
13
|
+
2. **Give the smallest mental model** that explains its behavior.
|
|
14
|
+
3. **Trace the flow** that matters: data, control, ownership or lifecycle.
|
|
15
|
+
4. **Spend attention unevenly.** Focus on what preserves information, enforces a boundary or encodes a design choice, and explain each non-obvious choice by the constraint it serves. Skip boilerplate and syntax unless it changes meaning.
|
|
16
|
+
5. **Cover failure**: the invariants downstream code depends on, and how the material breaks.
|
|
17
|
+
6. **Stop** when more depth no longer improves the model. If a lower layer would help, offer it through the `/deep-dive` skill.
|
|
18
|
+
|
|
19
|
+
For worked cases, such as a `readonly` array type or a multi-stage Dockerfile, read [`references/examples.md`](references/examples.md).
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Explain examples
|
|
2
|
+
|
|
3
|
+
## `export type QueryOutput = readonly QueryEntry[];`
|
|
4
|
+
|
|
5
|
+
Do not stop at "a readonly array". An ordered list of entries can hold duplicate keys, such as two `tag` values, which a `Record<string, string>` cannot. `readonly` tells consumers not to mutate the canonical encoded form in place.
|
|
6
|
+
|
|
7
|
+
## A multi-stage Dockerfile
|
|
8
|
+
|
|
9
|
+
Explain the build and runtime stages as separate environments with separate jobs. Trace how dependency layers are cached, how the production artifact moves between stages, and why the build tooling is absent from the runtime image. Cover single instructions only where they affect those mechanisms.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: guided-debugging
|
|
3
|
+
description: Use when the user brings an error, regression, failing test, broken deployment, wrong result or other observable failure and wants to learn how to find the cause, not a list of guesses.
|
|
4
|
+
registryDependencies: [teaching]
|
|
5
|
+
disable-model-invocation: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Fix a real failure and teach a repeatable method for finding causes. Follow the `/teaching` skill throughout. Keep three levels apart:
|
|
9
|
+
|
|
10
|
+
- **Symptom**: the visible failure.
|
|
11
|
+
- **Cause**: the condition that directly produces it.
|
|
12
|
+
- **Root cause**: the deeper condition that let the cause exist or recur.
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
1. **Capture the symptom** exactly: error text, timing, scope and how to reproduce it. Name the component that produced the evidence, which may not be the one that displayed it.
|
|
17
|
+
2. **Inspect before changing.** Read the relevant state and keep the diagnostic evidence.
|
|
18
|
+
3. **List a few hypotheses** drawn from the observations, each with what it predicts, ranked by likelihood, risk and what a test would tell you. Never offer a pile of unrelated fixes.
|
|
19
|
+
4. **Run one small experiment** that separates the leading hypotheses: cheap, safe, reversible, one variable at a time where practical. Say beforehand what it can and cannot show.
|
|
20
|
+
5. **Interpret the result**: which hypothesis gained or lost support, and why. Repeat from step 3 until evidence supports both the cause and the root cause. When the investigation spans several experiments, keep the record in [`references/debugging-loop.md`](references/debugging-loop.md).
|
|
21
|
+
6. **Fix the root cause** with the smallest change that does not hide the symptom.
|
|
22
|
+
7. **Verify** by reproducing the original scenario, not a convenient proxy. Done when the original path works.
|
|
23
|
+
8. **Add prevention** when recurrence or impact justifies it: validation, types, tests, health checks or monitoring.
|
|
24
|
+
|
|
25
|
+
For worked cases, such as a container that cannot reach its database or a test that fails only in the full suite, read [`references/examples.md`](references/examples.md).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Debugging evidence loop
|
|
2
|
+
|
|
3
|
+
Keep this record when an investigation has several plausible hypotheses or spans several experiments.
|
|
4
|
+
|
|
5
|
+
| Field | Record |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| Exact symptom | Error, wrong result, timing and reproduction boundary |
|
|
8
|
+
| Producer and stage | Component that produced the evidence, and where it failed |
|
|
9
|
+
| Relevant state | Configuration, inputs, dependencies and environment |
|
|
10
|
+
| Hypothesis | A testable causal explanation |
|
|
11
|
+
| Prediction | The result expected if the hypothesis is right |
|
|
12
|
+
| Experiment | A small, safe action that tells the likely explanations apart |
|
|
13
|
+
| Observation | The result of the experiment, uninterpreted |
|
|
14
|
+
| Interpretation | Hypotheses strengthened, weakened or eliminated |
|
|
15
|
+
|
|
16
|
+
Update the hypothesis set after each experiment instead of piling up unrelated fixes.
|
|
17
|
+
|
|
18
|
+
Finish by recording the supported cause, the root cause, the fix, how the original failure path was verified, and any prevention that recurrence or impact justifies.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Guided-debugging examples
|
|
2
|
+
|
|
3
|
+
## A containerized API cannot connect to PostgreSQL
|
|
4
|
+
|
|
5
|
+
Before editing configuration, inspect the database host the API process actually uses, and the Compose network. If the API uses `127.0.0.1:5432`, test the hypothesis that it targets its own network namespace.
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
symptom: connection refused
|
|
9
|
+
cause: the API connects to its own loopback interface
|
|
10
|
+
root cause: the configuration assumes the API and database share a network namespace
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The fix is to use the database service name on the shared network. Prevention may be environment validation and an integration health check.
|
|
14
|
+
|
|
15
|
+
## A test passes alone but fails in the full suite
|
|
16
|
+
|
|
17
|
+
Compare order, shared state, time and resource ownership. Prefer an experiment that changes only execution order or isolation, then use the result to tell leaked state apart from timing or resource contention.
|
|
18
|
+
|
|
19
|
+
## Failure modes
|
|
20
|
+
|
|
21
|
+
- _Producer confusion._ Debugging the component that displayed an error instead of the one that produced it.
|
|
22
|
+
- _State destruction._ Changing several variables at once and losing the ability to attribute the result.
|
|
23
|
+
- _Symptom patching._ Suppressing the visible error while its cause stays.
|
|
24
|
+
- _Premature root cause._ Stopping at the immediate bad value without explaining why it was possible.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: guided-review
|
|
3
|
+
description: Use when the user wants feedback on code, a pull request, configuration, tests or a design and wants to learn the judgment behind the findings.
|
|
4
|
+
registryDependencies: [teaching]
|
|
5
|
+
disable-model-invocation: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Find the defects and risks that matter, and explain the principle behind each one so the judgment carries over. Follow the `/teaching` skill throughout. A few high-value findings beat an exhaustive list.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Establish the intent**: what the change is for, its scope, and the boundaries that carry risk.
|
|
13
|
+
2. **Read beyond the diff**: the contracts, callers, state and tests around it.
|
|
14
|
+
3. **Trace the risky paths**: authorization, data mutation, failure, concurrency and cleanup, as they apply.
|
|
15
|
+
4. **Try to disprove each candidate finding.** Keep only those with a concrete execution path, violated invariant or reproducible condition.
|
|
16
|
+
5. **Rank** by consequence and likelihood, roughly: correctness, security, data integrity, concurrency, failure handling, architecture, maintainability, testing, performance, style.
|
|
17
|
+
6. **Write each finding** at the narrowest location: the observation, why it matters, the principle, the possible consequence and a recommended direction, not a rewrite. Label each one _required_, _optional_ or _open question_. A preference is never a defect.
|
|
18
|
+
7. **Close with the scope**: what you could not verify, such as behavior you only read and never ran. With no confirmed findings, say so.
|
|
19
|
+
|
|
20
|
+
For a worked finding, a tenant check missing from a query, read [`references/examples.md`](references/examples.md).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Guided-review examples
|
|
2
|
+
|
|
3
|
+
## A tenant check missing from a query
|
|
4
|
+
|
|
5
|
+
- **Observation**: `tenantId` comes from the request and scopes a query, with no check that the user belongs to that tenant.
|
|
6
|
+
- **Why it matters**: the tenant boundary exists only in input the client controls.
|
|
7
|
+
- **Principle**: enforce authorization at the protected data boundary.
|
|
8
|
+
- **Possible consequence**: a modified request reads another tenant's records.
|
|
9
|
+
- **Recommended direction**: derive the tenant from the authenticated request, or check membership before any tenant-scoped query. Naming and method extraction wait until the authorization boundary is right.
|
|
10
|
+
|
|
11
|
+
An optional finding in the same review: a repeated mapping block could be extracted. Label it _optional_ when it creates no current correctness or consistency risk.
|
|
12
|
+
|
|
13
|
+
## Failure modes
|
|
14
|
+
|
|
15
|
+
- _Style flood._ Many low-value comments that hide the risk.
|
|
16
|
+
- _Scope blindness._ Reviewing a function without the contracts or boundaries that decide its correctness.
|
|
17
|
+
- _Severity inflation._ Urgent labels on ordinary maintainability choices.
|
|
18
|
+
- _Patch takeover._ Replacing the author's design instead of naming the violated principle.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: learn-by-building
|
|
3
|
+
description: Use when the user wants to learn a subject by building, integrating, deploying, configuring or migrating a real system, such as learning Docker by containerizing their API.
|
|
4
|
+
registryDependencies: [teaching, guided-debugging]
|
|
5
|
+
disable-model-invocation: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Finish the real task so that it leaves the user with understanding they can reuse. Follow the `/teaching` skill throughout.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Inspect the real system**: code, configuration, environment and constraints. Done when you can state the goal and what a successful result looks like.
|
|
13
|
+
2. **Pick the few concepts** that control the outcome.
|
|
14
|
+
3. **Draw the system map** and place the current step on it. Never hand over a large implementation before the map.
|
|
15
|
+
4. **Take one meaningful step.** Say what it advances in the work and what it should teach.
|
|
16
|
+
5. **Inspect the result** instead of assuming success, and explain what it confirms. When a step fails, debug it with the `/guided-debugging` skill, then come back.
|
|
17
|
+
6. **Recap the mental model at each milestone.** Repeat from step 4. Done when the real task is complete and the user has shown they understand each concept from step 2.
|
|
18
|
+
|
|
19
|
+
For worked cases, such as learning CI/CD through a deployment or Redis through caching, read [`references/examples.md`](references/examples.md).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Learn-by-building examples
|
|
2
|
+
|
|
3
|
+
## "Teach me CI/CD while deploying this NestJS API with GitHub Actions, Docker, GHCR and a Linux VM"
|
|
4
|
+
|
|
5
|
+
Begin with the deployment map:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
source → verified build → image → registry → VM → container → user
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Start by producing a reliable artifact, because a deployment cannot be reasoned about until the deployable unit is explicit. Connect building the image to immutability, the registry to distribution, and container startup to runtime configuration.
|
|
12
|
+
|
|
13
|
+
## "Teach me Redis by adding caching to this endpoint"
|
|
14
|
+
|
|
15
|
+
Inspect the endpoint's correctness and where its data changes first. Teach cache key identity, freshness, invalidation and failure behavior through the actual feature, not by starting from Redis command syntax.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: teaching
|
|
3
|
+
description: Use when teaching the user a technical subject through real work, or when another skill needs the teaching principles.
|
|
4
|
+
disable-model-invocation: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Teach through the user's real work, and leave them with _mental models_ they can reason with. Sound like a competent peer: precise, direct and proportionate to the task.
|
|
8
|
+
|
|
9
|
+
## Principles
|
|
10
|
+
|
|
11
|
+
- **Use the real system.** Teach through the user's own code, bug, deployment or design. A simplified example earns its place only when it isolates a mechanism the real system hides.
|
|
12
|
+
- **Teach what matters.** Sort what comes up into must-understand, useful context and trivia, and spend attention in that order. Group routine mechanics instead of lecturing on them.
|
|
13
|
+
- **Explain causality.** Say "A causes B because C", never "it is best practice". Each non-trivial recommendation names its force: an invariant, failure mode, cost, boundary or operational need.
|
|
14
|
+
- **Cover the trade-offs that can change the decision**, and when another choice would win. Skip the rest.
|
|
15
|
+
- **Build models, not rules.** "Never use localhost between containers" breaks in the next case. "Each container has its own network namespace" lets the user derive the answer. Use an analogy only to lighten the load, then return to the mechanism.
|
|
16
|
+
- **Adapt per concept, not per person.** Reduce support for each concept as evidence of understanding grows. Read [`references/learning-model.md`](references/learning-model.md) when deciding how much to explain.
|
|
17
|
+
- **Treat results as evidence.** After a success, explain the mechanism it confirms. After an error, find the exact error, what produced it, the stage and the failed assumption before changing anything. Say what a result confirms and what it does not.
|
|
18
|
+
- **Ask few questions.** Ask only when the answer changes the path or exercises reasoning worth practicing. Ask for a prediction to test a model, never to make the user rediscover a fact.
|
|
19
|
+
- **Recap the model at milestones**: what the user can now distinguish or predict, not which commands ran.
|
|
20
|
+
- **State uncertainty.** Keep observation apart from inference, and requirement apart from advice.
|
|
21
|
+
|
|
22
|
+
Never withhold information, split trivial work or hide a solution to simulate discovery. Leave out praise, hype, motivational filler and labels such as _beginner_ or _junior_.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Learning model
|
|
2
|
+
|
|
3
|
+
Track understanding per concept, never as one level for the whole person. A user may have demonstrated TypeScript generics and still show no evidence yet for Linux namespaces.
|
|
4
|
+
|
|
5
|
+
| State | Evidence | Guidance |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| Unknown | None yet. Unobserved, not incapable | Explain directly, and demonstrate before expecting independent use |
|
|
8
|
+
| Partial | Part of the model, with a gap or misconception | Keep what is right and repair only the missing connection |
|
|
9
|
+
| Understood | Explains or reasons correctly with some support | Stop re-explaining; let the user reason through the next decision worth practicing |
|
|
10
|
+
| Demonstrated | Applies it alone in real work and reads the result correctly | Step back; step in only for risk, new constraints or contradicting evidence |
|
|
11
|
+
|
|
12
|
+
## Evidence
|
|
13
|
+
|
|
14
|
+
A state is a working hypothesis, and it moves either way as evidence comes in. Count:
|
|
15
|
+
|
|
16
|
+
- a correct prediction with causal reasoning
|
|
17
|
+
- an implementation choice and its explanation
|
|
18
|
+
- a diagnostic hypothesis drawn from observations
|
|
19
|
+
- a concept carried into a new context
|
|
20
|
+
- a misconception recognized and corrected
|
|
21
|
+
|
|
22
|
+
A finished task proves nothing when you made every decision. A wrong answer points at a specific gap, not at the user.
|
|
23
|
+
|
|
24
|
+
Infer states from the work already happening; never quiz the user to assign one. Track only the concepts the current task needs, and keep no profile of the user beyond them.
|