fix(tools): stage course cards from M5, audit noun words without flagging identifiers, Node<24.2 main guard, rename-proof expansion-check, safe check-links

- cardDirs: course/cards only from Module 5 (spec 6.2a); fixture modules 04/05 and tests
- coupling-audit: noun words match as UPPERCASE, PascalCase or a whole string literal;
  lowercase identifiers like key/flag pass; card names keep every variant
- isMain() argv guard replaces import.meta.main in coupling-audit and verify-references;
  engines.node >=22
- expansion-check: git diff --no-renames so moving engine code into content fails
- start-module and other tools: clear error when .course.json is missing
- check-links: malformed URLs reported as invalid URL instead of throwing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-29 19:06:56 +01:00
1 parent 0700cac492
commit ad0701395d
26 files changed
+213 -47

No files matched your search

+6 -5
View File
@@ -2,13 +2,13 @@
// Copies module NN's tests for the learner's track into <workspace>/tests/.
import { cpSync, existsSync, mkdirSync, readFileSync } from "node:fs";
import { basename, join } from "node:path";
import { cardDirs, moduleDirs, courseRoot, testFiles, fail } from "./lib.mjs";
import { cardDirs, moduleDirs, courseRoot, testFiles, fail, readCourseConfig } from "./lib.mjs";
const [num, ws] = process.argv.slice(2);
if (!/^\d\d$/.test(num || "") || !ws) fail("usage: start-module.mjs <NN> <workspaceDir>");
if (num === "00") fail("Module 00 is not startable: M0 uses the naive-engine/ project, not a workspace.");
const cfg = JSON.parse(readFileSync(join(ws, ".course.json"), "utf8"));
const cfg = readCourseConfig(ws);
const track = cfg.track;
if (track !== "rust" && track !== "ts") fail(`bad track in .course.json: ${track}`);
@@ -32,9 +32,10 @@ mkdirSync(join(ws, "tests"), { recursive: true });
for (const f of files) cpSync(f, join(ws, "tests", basename(f)));
console.log(`Copied ${files.length} test file(s) for module ${num} into ${join(ws, "tests")}`);
// The course's card data (read by your content from Module 5): course/cards, plus the cards
// of each module up to this one that has its own (Module 10's SpellGrid cards). Refreshed on
// every start; card files you add yourself are kept.
// The course's card data (read by your content from Module 5): course/cards from Module 5 on
// (Modules 1-4 keep their cards in your own code), plus the cards of each module up to this
// one that has its own (Module 10's SpellGrid cards). Refreshed on every start; card files you
// add yourself are kept.
const cardSources = cardDirs(num);
for (const cards of cardSources) cpSync(cards, join(ws, "cards"), { recursive: true });
if (cardSources.length) console.log(`Copied the course card data into ${join(ws, "cards")}`);