create-pathfinder 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +7 -0
- package/CLAUDE.md +71 -0
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/bin/create-pathfinder.mjs +8 -0
- package/context/ai-interaction.md +76 -0
- package/context/coding-standards.md +72 -0
- package/context/current-feature.md +46 -0
- package/context/features/example-feature-spec.md +99 -0
- package/context/history.md +14 -0
- package/context/learning/learner-profile.md +35 -0
- package/context/learning/lessons/.gitkeep +0 -0
- package/context/learning/progress.md +28 -0
- package/context/project-overview.md +198 -0
- package/copy-list.json +11 -0
- package/package.json +51 -0
- package/prompts/01-kickstart-project.md +1 -0
- package/prompts/01-teach-current-feature.md +9 -0
- package/prompts/02-debate-me.md +1 -0
- package/prompts/02-quiz-current-feature.md +7 -0
- package/prompts/03-challenge-current-feature.md +7 -0
- package/prompts/03-prototype.md +1 -0
- package/prompts/04-teach-current-architecture.md +7 -0
- package/prompts/04-to-specs.md +1 -0
- package/prompts/05-learning-review.md +5 -0
- package/prompts/05-load-feature.md +1 -0
- package/prompts/06-start-feature.md +1 -0
- package/prompts/07-review-feature.md +1 -0
- package/prompts/08-complete-feature.md +1 -0
- package/prompts/09-learn-feature.md +1 -0
- package/prompts/10-learn-codebase.md +1 -0
- package/prompts/11-handoff.md +1 -0
- package/prompts/12-skillsmith.md +1 -0
- package/prompts/13-reverse-engineer.md +18 -0
- package/prompts/14-reflect.md +13 -0
- package/skills/challenge-me/SKILL.md +81 -0
- package/skills/complete-feature/SKILL.md +17 -0
- package/skills/debate-me/SKILL.md +82 -0
- package/skills/handoff/SKILL.md +12 -0
- package/skills/kickstart-pathfinder/SKILL.md +49 -0
- package/skills/learn-codebase/SKILL.md +33 -0
- package/skills/learn-feature/SKILL.md +48 -0
- package/skills/learning-review/SKILL.md +76 -0
- package/skills/load-feature/SKILL.md +17 -0
- package/skills/prototype/SKILL.md +46 -0
- package/skills/quiz-me/SKILL.md +78 -0
- package/skills/reflect/SKILL.md +481 -0
- package/skills/reverse-engineer/SKILL.md +252 -0
- package/skills/review-feature/SKILL.md +25 -0
- package/skills/skillsmith/SKILL.md +14 -0
- package/skills/start-feature/SKILL.md +25 -0
- package/skills/teach-architecture/SKILL.md +79 -0
- package/skills/teach-feature/SKILL.md +110 -0
- package/skills/to-specs/SKILL.md +45 -0
- package/src/cli.mjs +156 -0
- package/src/install.mjs +99 -0
- package/src/kit.mjs +94 -0
- package/templates/CHANGELOG.template.md +11 -0
- package/templates/feature-spec.template.md +99 -0
- package/templates/lesson.template.md +71 -0
- package/templates/progress-entry.template.md +23 -0
- package/templates/project-overview.template.md +198 -0
package/src/cli.mjs
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command-line surface: parse arguments, refuse unsafe situations, report.
|
|
3
|
+
*
|
|
4
|
+
* No CLI framework. The flag set is four booleans and the whole parser is a
|
|
5
|
+
* loop; a dependency tree for that would be indefensible in a project whose
|
|
6
|
+
* identity is "not a framework."
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { findGitRoot, findKitRoot, COPY_LIST } from "./kit.mjs";
|
|
10
|
+
import { applyPlan, planInstall } from "./install.mjs";
|
|
11
|
+
|
|
12
|
+
const USAGE = `Usage: npx create-pathfinder [options]
|
|
13
|
+
|
|
14
|
+
Installs the Pathfinder workflow kit into the current Git repository.
|
|
15
|
+
|
|
16
|
+
Copies: ${COPY_LIST.join(", ")}
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--dry-run Report what would be written; change nothing.
|
|
20
|
+
--force Overwrite files that already exist. Off by default.
|
|
21
|
+
-h, --help Show this message.
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export function run(argv, { cwd, out, err }) {
|
|
25
|
+
const options = parseArguments(argv);
|
|
26
|
+
|
|
27
|
+
if (options.error) {
|
|
28
|
+
err(`create-pathfinder: ${options.error}\n\n${USAGE}`);
|
|
29
|
+
return 2;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (options.help) {
|
|
33
|
+
out(USAGE);
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Refused rather than allowed with a warning: this tool writes several
|
|
38
|
+
// hundred files, and without version control the user has no way to inspect
|
|
39
|
+
// or undo what it did.
|
|
40
|
+
const gitRoot = findGitRoot(cwd);
|
|
41
|
+
if (gitRoot === null) {
|
|
42
|
+
err(
|
|
43
|
+
`create-pathfinder: ${cwd} is not inside a Git repository.\n\n` +
|
|
44
|
+
"The kit is installed into version control so you can review the\n" +
|
|
45
|
+
"files it adds and undo them if you change your mind. Run `git init`\n" +
|
|
46
|
+
"here first, or cd into an existing repository, then run this again.\n",
|
|
47
|
+
);
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const kitRoot = findKitRoot();
|
|
52
|
+
if (kitRoot === null) {
|
|
53
|
+
err(
|
|
54
|
+
"create-pathfinder: this package is missing the kit files it should copy.\n" +
|
|
55
|
+
"That is a packaging bug, not something you did. Please report it at\n" +
|
|
56
|
+
"https://github.com/rikilamadrid/pathfinder/issues\n",
|
|
57
|
+
);
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (kitRoot === gitRoot) {
|
|
62
|
+
err(
|
|
63
|
+
"create-pathfinder: this is the Pathfinder kit repository itself.\n" +
|
|
64
|
+
"There is nothing to install here. Run it in the project you want the\n" +
|
|
65
|
+
"kit copied into.\n",
|
|
66
|
+
);
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const plan = planInstall(kitRoot, cwd, { force: options.force });
|
|
71
|
+
const result = applyPlan(plan, { dryRun: options.dryRun });
|
|
72
|
+
|
|
73
|
+
report({ result, plan, cwd, gitRoot, options, out, err });
|
|
74
|
+
return result.errors.length > 0 ? 1 : 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parseArguments(argv) {
|
|
78
|
+
const options = { dryRun: false, force: false, help: false, error: null };
|
|
79
|
+
|
|
80
|
+
for (const argument of argv) {
|
|
81
|
+
switch (argument) {
|
|
82
|
+
case "--dry-run":
|
|
83
|
+
options.dryRun = true;
|
|
84
|
+
break;
|
|
85
|
+
case "--force":
|
|
86
|
+
options.force = true;
|
|
87
|
+
break;
|
|
88
|
+
case "-h":
|
|
89
|
+
case "--help":
|
|
90
|
+
options.help = true;
|
|
91
|
+
break;
|
|
92
|
+
default:
|
|
93
|
+
options.error = `unknown option \`${argument}\``;
|
|
94
|
+
return options;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return options;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Say what happened, in full.
|
|
103
|
+
*
|
|
104
|
+
* Skipped files are listed individually, not counted. The whole promise of the
|
|
105
|
+
* default mode is that it left your work alone, and a bare "42 skipped" does
|
|
106
|
+
* not let anyone check that claim.
|
|
107
|
+
*/
|
|
108
|
+
function report({ result, plan, cwd, gitRoot, options, out, err }) {
|
|
109
|
+
const lines = [];
|
|
110
|
+
const verb = options.dryRun ? "Would install" : "Installed";
|
|
111
|
+
|
|
112
|
+
lines.push(`${verb} the Pathfinder kit into ${cwd}`);
|
|
113
|
+
if (gitRoot !== cwd) {
|
|
114
|
+
lines.push(`Note: the repository root is ${gitRoot}, not this directory.`);
|
|
115
|
+
}
|
|
116
|
+
lines.push("");
|
|
117
|
+
|
|
118
|
+
const written = options.dryRun ? plan.filter((i) => i.status === "write").length : result.written;
|
|
119
|
+
lines.push(` ${written} file${plural(written)} ${options.dryRun ? "to write" : "written"}`);
|
|
120
|
+
|
|
121
|
+
if (result.overwritten > 0) {
|
|
122
|
+
lines.push(` ${result.overwritten} file${plural(result.overwritten)} overwritten (--force)`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const skipped = plan.filter((item) => item.status === "skip");
|
|
126
|
+
if (skipped.length > 0) {
|
|
127
|
+
lines.push(` ${skipped.length} file${plural(skipped.length)} left untouched because they already exist:`);
|
|
128
|
+
for (const item of skipped) lines.push(` ${item.relativePath}`);
|
|
129
|
+
lines.push("");
|
|
130
|
+
lines.push(" Nothing above was modified. Re-run with --force to replace them.");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (written === 0 && skipped.length === plan.length) {
|
|
134
|
+
lines.push("");
|
|
135
|
+
lines.push("The kit is already installed here.");
|
|
136
|
+
} else {
|
|
137
|
+
lines.push("");
|
|
138
|
+
lines.push("Next step — give your agent this prompt:");
|
|
139
|
+
lines.push("");
|
|
140
|
+
lines.push(" Use skills/kickstart-pathfinder/SKILL.md. Help me initialize this");
|
|
141
|
+
lines.push(" project. Do not install packages or write product code yet.");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
out(lines.join("\n") + "\n");
|
|
145
|
+
|
|
146
|
+
if (result.errors.length > 0) {
|
|
147
|
+
const failures = result.errors
|
|
148
|
+
.map((error) => ` ${error.relativePath}: ${error.message}`)
|
|
149
|
+
.join("\n");
|
|
150
|
+
err(`\ncreate-pathfinder: ${result.errors.length} file${plural(result.errors.length)} could not be written:\n${failures}\n`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function plural(count) {
|
|
155
|
+
return count === 1 ? "" : "s";
|
|
156
|
+
}
|
package/src/install.mjs
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The copier.
|
|
3
|
+
*
|
|
4
|
+
* Split in two on purpose. `planInstall` decides what would happen and touches
|
|
5
|
+
* nothing; `applyPlan` carries a plan out. That split is what makes --dry-run
|
|
6
|
+
* honest — it runs the identical planning code the real install runs, rather
|
|
7
|
+
* than a parallel description of it that can drift.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs";
|
|
11
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
12
|
+
|
|
13
|
+
import { COPY_LIST, isExcluded } from "./kit.mjs";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Decide the fate of every file in the kit. Reads only; writes nothing.
|
|
17
|
+
*
|
|
18
|
+
* Existing files are detected one file at a time rather than one top-level
|
|
19
|
+
* entry at a time. A project that already has `context/` gets the files it is
|
|
20
|
+
* missing and keeps the ones it wrote, instead of the whole directory being
|
|
21
|
+
* declared present and skipped.
|
|
22
|
+
*
|
|
23
|
+
* @returns {{relativePath: string, source: string, destination: string,
|
|
24
|
+
* status: "write" | "skip" | "overwrite"}[]}
|
|
25
|
+
*/
|
|
26
|
+
export function planInstall(kitRoot, targetRoot, { force = false } = {}) {
|
|
27
|
+
const plan = [];
|
|
28
|
+
|
|
29
|
+
for (const entry of COPY_LIST) {
|
|
30
|
+
for (const source of walkFiles(join(kitRoot, entry))) {
|
|
31
|
+
const relativePath = relative(kitRoot, source).split(sep).join("/");
|
|
32
|
+
const destination = join(targetRoot, relativePath);
|
|
33
|
+
const exists = existsSync(destination);
|
|
34
|
+
|
|
35
|
+
plan.push({
|
|
36
|
+
relativePath,
|
|
37
|
+
source,
|
|
38
|
+
destination,
|
|
39
|
+
status: !exists ? "write" : force ? "overwrite" : "skip",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return plan.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Carry out a plan.
|
|
49
|
+
*
|
|
50
|
+
* Failures are collected rather than thrown, so one unwritable path reports
|
|
51
|
+
* itself alongside everything that did succeed instead of aborting the run
|
|
52
|
+
* halfway with no summary. Entries marked `skip` are never opened.
|
|
53
|
+
*
|
|
54
|
+
* @returns {{written: number, skipped: number, overwritten: number,
|
|
55
|
+
* errors: {relativePath: string, message: string}[]}}
|
|
56
|
+
*/
|
|
57
|
+
export function applyPlan(plan, { dryRun = false } = {}) {
|
|
58
|
+
const result = { written: 0, skipped: 0, overwritten: 0, errors: [] };
|
|
59
|
+
|
|
60
|
+
for (const item of plan) {
|
|
61
|
+
if (item.status === "skip") {
|
|
62
|
+
result.skipped += 1;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!dryRun) {
|
|
67
|
+
try {
|
|
68
|
+
mkdirSync(dirname(item.destination), { recursive: true });
|
|
69
|
+
copyFileSync(item.source, item.destination);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
result.errors.push({
|
|
72
|
+
relativePath: item.relativePath,
|
|
73
|
+
message: error.message,
|
|
74
|
+
});
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (item.status === "overwrite") result.overwritten += 1;
|
|
80
|
+
else result.written += 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Every file under `path`, recursively, minus junk. A file yields itself. */
|
|
87
|
+
function* walkFiles(path) {
|
|
88
|
+
const stats = statSync(path);
|
|
89
|
+
|
|
90
|
+
if (!stats.isDirectory()) {
|
|
91
|
+
yield path;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const child of readdirSync(path).sort()) {
|
|
96
|
+
if (isExcluded(child)) continue;
|
|
97
|
+
yield* walkFiles(join(path, child));
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/kit.mjs
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the kit is, and where to find it.
|
|
3
|
+
*
|
|
4
|
+
* The copy list is the single fact this whole tool exists to act on. It is
|
|
5
|
+
* duplicated in exactly two other places — the README quickstart and
|
|
6
|
+
* NOT_A_FRAMEWORK.md — and nowhere else. Nothing here embeds a copy of the
|
|
7
|
+
* kit's content: the real directories are read at install time.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import { dirname, join, resolve } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Everything a destination project receives, and nothing else.
|
|
18
|
+
*
|
|
19
|
+
* Read from copy-list.json rather than declared here, so the list is data with
|
|
20
|
+
* one home instead of a value restated in every file that needs it. The
|
|
21
|
+
* validator reads the same file and checks the README and npm's `files`
|
|
22
|
+
* against it; nothing has to parse this source code to learn the list.
|
|
23
|
+
*
|
|
24
|
+
* Deliberately absent from it: CHANGELOG.md and README.md (the kit's own
|
|
25
|
+
* history and front page, not the project's), .github/, assets/, site/,
|
|
26
|
+
* packages/, and .features/. A destination project that wants a changelog
|
|
27
|
+
* starts from templates/CHANGELOG.template.md instead.
|
|
28
|
+
*
|
|
29
|
+
* Read with readFileSync rather than a JSON import, which still requires an
|
|
30
|
+
* import attribute on the Node 18 this package supports.
|
|
31
|
+
*/
|
|
32
|
+
export const COPY_LIST = Object.freeze(
|
|
33
|
+
JSON.parse(readFileSync(join(HERE, "..", "copy-list.json"), "utf8")).entries,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Junk that is never part of the kit, matched by basename at any depth.
|
|
38
|
+
*
|
|
39
|
+
* These are git-ignored in the kit repository, so they exist in a maintainer's
|
|
40
|
+
* working tree but not in version control — and npm strips them from the
|
|
41
|
+
* published tarball on its own. Without this list the installer would copy
|
|
42
|
+
* `skills/.DS_Store` when run from a checkout and not when run from npm, which
|
|
43
|
+
* is the worst kind of difference: invisible, and only on someone else's
|
|
44
|
+
* machine.
|
|
45
|
+
*/
|
|
46
|
+
const EXCLUDED = new Set([".DS_Store", "Thumbs.db", "__MACOSX", ".git"]);
|
|
47
|
+
|
|
48
|
+
export function isExcluded(basename) {
|
|
49
|
+
return EXCLUDED.has(basename) || basename.startsWith("._");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const PACKAGE_ROOT = resolve(HERE, "..");
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Locate the kit directories this tool should copy from.
|
|
56
|
+
*
|
|
57
|
+
* Two layouts are possible and both are checked, nearest first:
|
|
58
|
+
*
|
|
59
|
+
* 1. Published package — the kit ships inside the tarball alongside this
|
|
60
|
+
* code. How it gets there is chunk 2's decision; this resolver only
|
|
61
|
+
* requires that it ends up at the package root.
|
|
62
|
+
* 2. Development — running from a checkout of the kit itself, where the
|
|
63
|
+
* package sits at packages/create-pathfinder/ and the kit is two levels up.
|
|
64
|
+
*
|
|
65
|
+
* Returns null when neither candidate holds a complete kit, which is a bug in
|
|
66
|
+
* the package rather than a user error, and is reported as such.
|
|
67
|
+
*/
|
|
68
|
+
export function findKitRoot() {
|
|
69
|
+
const candidates = [PACKAGE_ROOT, resolve(PACKAGE_ROOT, "..", "..")];
|
|
70
|
+
return candidates.find(isKitRoot) ?? null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isKitRoot(directory) {
|
|
74
|
+
return COPY_LIST.every((entry) => existsSync(join(directory, entry)));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Find the Git repository containing `startDirectory`, or null.
|
|
79
|
+
*
|
|
80
|
+
* Walks the filesystem rather than shelling out to `git`, so the tool works
|
|
81
|
+
* without the git binary on PATH and cannot be confused by its exit codes.
|
|
82
|
+
* `.git` is tested with existsSync rather than as a directory because linked
|
|
83
|
+
* worktrees and submodules use a `.git` *file* pointing elsewhere.
|
|
84
|
+
*/
|
|
85
|
+
export function findGitRoot(startDirectory) {
|
|
86
|
+
let current = resolve(startDirectory);
|
|
87
|
+
|
|
88
|
+
for (;;) {
|
|
89
|
+
if (existsSync(join(current, ".git"))) return current;
|
|
90
|
+
const parent = dirname(current);
|
|
91
|
+
if (parent === current) return null;
|
|
92
|
+
current = parent;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# [Feature Name]
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Not Started
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Describe the smallest coherent outcome, where it fits, and why it matters.
|
|
10
|
+
|
|
11
|
+
## Problem
|
|
12
|
+
|
|
13
|
+
- What is missing, risky, confusing, inaccessible, unreliable, or inefficient?
|
|
14
|
+
- Who or what is affected?
|
|
15
|
+
- Why does this matter now?
|
|
16
|
+
|
|
17
|
+
## Goal
|
|
18
|
+
|
|
19
|
+
State one user-visible or system-verifiable outcome.
|
|
20
|
+
|
|
21
|
+
## Dependencies
|
|
22
|
+
|
|
23
|
+
- Earlier feature, system, decision, prototype, or `None`.
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- Requirement 1
|
|
28
|
+
- Requirement 2
|
|
29
|
+
- Requirement 3
|
|
30
|
+
- Include relevant failure, permission, loading, empty, retry, responsive, accessibility, operational, or compatibility states only when applicable.
|
|
31
|
+
|
|
32
|
+
## Out of Scope
|
|
33
|
+
|
|
34
|
+
- Explicit exclusion
|
|
35
|
+
- Later feature, if known
|
|
36
|
+
- Unrelated refactors, dependencies, or polish
|
|
37
|
+
|
|
38
|
+
## Experience or Operational Notes — When Applicable
|
|
39
|
+
|
|
40
|
+
- User interaction, system behavior, responsive behavior, accessibility, observability, performance, security, or operational expectations.
|
|
41
|
+
|
|
42
|
+
## Technical Notes
|
|
43
|
+
|
|
44
|
+
Likely areas:
|
|
45
|
+
|
|
46
|
+
- `[specific path or bounded area]`
|
|
47
|
+
|
|
48
|
+
Implementation constraints:
|
|
49
|
+
|
|
50
|
+
- Follow project context and approved prototype direction.
|
|
51
|
+
- Do not silently resolve open architecture decisions.
|
|
52
|
+
- Keep prototype and production code boundaries explicit.
|
|
53
|
+
|
|
54
|
+
## Context Boundary
|
|
55
|
+
|
|
56
|
+
Read:
|
|
57
|
+
|
|
58
|
+
- `[specific context and code]`
|
|
59
|
+
|
|
60
|
+
Avoid loading:
|
|
61
|
+
|
|
62
|
+
- `[unrelated systems, old specs, generated output]`
|
|
63
|
+
|
|
64
|
+
Split this feature further if its required context is not focused enough for reliable implementation and verification.
|
|
65
|
+
|
|
66
|
+
## Delivery Chunks
|
|
67
|
+
|
|
68
|
+
1. `[stable, verifiable increment]`
|
|
69
|
+
2. `[stable, verifiable increment]`
|
|
70
|
+
3. `[optional stable increment]`
|
|
71
|
+
|
|
72
|
+
Each chunk should leave the project stable.
|
|
73
|
+
|
|
74
|
+
## Acceptance Criteria
|
|
75
|
+
|
|
76
|
+
- The defined outcome works end to end within scope.
|
|
77
|
+
- Relevant failure and edge behavior is handled.
|
|
78
|
+
- Applicable quality requirements are met.
|
|
79
|
+
- Required automated/manual checks pass.
|
|
80
|
+
- The work can be reviewed independently.
|
|
81
|
+
|
|
82
|
+
## Verification
|
|
83
|
+
|
|
84
|
+
- Manual or operational checks:
|
|
85
|
+
- Automated checks:
|
|
86
|
+
- Edge cases:
|
|
87
|
+
- Quality checks:
|
|
88
|
+
|
|
89
|
+
## Learning Targets
|
|
90
|
+
|
|
91
|
+
- Concepts worth explaining after completion:
|
|
92
|
+
- Diagram or demonstration opportunity:
|
|
93
|
+
- Quiz ideas:
|
|
94
|
+
|
|
95
|
+
## Suggested Delivery Metadata
|
|
96
|
+
|
|
97
|
+
- Git action: `[follow project workflow]`
|
|
98
|
+
- Suggested branch, when applicable: `[project naming convention]`
|
|
99
|
+
- Suggested commit, when applicable: `[project convention]`
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Lesson — [Feature Name]
|
|
2
|
+
|
|
3
|
+
- Date:
|
|
4
|
+
- Branch:
|
|
5
|
+
- Feature spec:
|
|
6
|
+
- Commit or diff range:
|
|
7
|
+
- Difficulty:
|
|
8
|
+
- Estimated review time:
|
|
9
|
+
|
|
10
|
+
## 1. Feature in One Minute
|
|
11
|
+
|
|
12
|
+
What changed, why it exists, and the user or system value it creates.
|
|
13
|
+
|
|
14
|
+
## 2. Mental Model
|
|
15
|
+
|
|
16
|
+
Explain the feature through one clear model. Include a small Mermaid diagram when useful.
|
|
17
|
+
|
|
18
|
+
## 3. Execution and Data Flow
|
|
19
|
+
|
|
20
|
+
Trace one representative interaction from input to visible result.
|
|
21
|
+
|
|
22
|
+
## 4. Important Files
|
|
23
|
+
|
|
24
|
+
| File | Responsibility | Why it matters |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
|
|
27
|
+
## 5. Key Decisions and Tradeoffs
|
|
28
|
+
|
|
29
|
+
For each meaningful decision:
|
|
30
|
+
|
|
31
|
+
- Decision
|
|
32
|
+
- Evidence in the repository
|
|
33
|
+
- Benefit
|
|
34
|
+
- Cost
|
|
35
|
+
- Credible alternative
|
|
36
|
+
- When the alternative would be better
|
|
37
|
+
|
|
38
|
+
## 6. Concepts Worth Retaining
|
|
39
|
+
|
|
40
|
+
Limit this to the most transferable concepts.
|
|
41
|
+
|
|
42
|
+
## 7. Quality Lens
|
|
43
|
+
|
|
44
|
+
### Testing
|
|
45
|
+
|
|
46
|
+
### Accessibility
|
|
47
|
+
|
|
48
|
+
### Performance
|
|
49
|
+
|
|
50
|
+
### Maintainability
|
|
51
|
+
|
|
52
|
+
### Error and edge states
|
|
53
|
+
|
|
54
|
+
## 8. Production-Scale Gap
|
|
55
|
+
|
|
56
|
+
What would need to change for higher traffic, larger datasets, multiple teams, stricter security, or real-time collaboration?
|
|
57
|
+
|
|
58
|
+
## 9. Interview Preparation
|
|
59
|
+
|
|
60
|
+
- Likely question:
|
|
61
|
+
- Strong answer outline:
|
|
62
|
+
- Follow-up question:
|
|
63
|
+
- Vocabulary to use carefully:
|
|
64
|
+
|
|
65
|
+
## 10. Check Your Understanding
|
|
66
|
+
|
|
67
|
+
Three short retrieval questions. Do not include answers here.
|
|
68
|
+
|
|
69
|
+
## 11. Suggested Next Action
|
|
70
|
+
|
|
71
|
+
Choose one: quiz, challenge, architecture lesson, spaced review, or no further work.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## Progress Update — [Date]
|
|
2
|
+
|
|
3
|
+
### Lesson
|
|
4
|
+
|
|
5
|
+
- Feature:
|
|
6
|
+
- Lesson file:
|
|
7
|
+
- Evidence reviewed:
|
|
8
|
+
|
|
9
|
+
### Concepts
|
|
10
|
+
|
|
11
|
+
| Concept | Previous | New | Evidence |
|
|
12
|
+
| --- | --- | --- | --- |
|
|
13
|
+
|
|
14
|
+
### Misconceptions or Gaps
|
|
15
|
+
|
|
16
|
+
- Gap:
|
|
17
|
+
- Correction:
|
|
18
|
+
- Recommended reinforcement:
|
|
19
|
+
|
|
20
|
+
### Next Review
|
|
21
|
+
|
|
22
|
+
- Topic:
|
|
23
|
+
- Suggested timing or trigger:
|