Files
Hankan/eslint.config.js
MechaCat02 e72b77d6c2 chore: take in the 16 Sep bundle — lib, curriculum v5, prompt, gate audit
The artifact was reworked after real incidents: a week of lost data, a
student taught out of order, and spelling diagnoses the model invented.
This takes the new export in verbatim; the port catches up in the
commits that follow.

Copied byte-identical from the bundle:
  lib/        lexicon.js and sync.js are new; gate.js gains enforcement,
              hangul.js letter-level marking, srs.js recall evidence,
              conjugation.js deconjugate(); blocks.js now takes the last
              block, closes gloss at "=", and parses recall, ::result and
              ::confirmed
  data/       curriculum.json v5 — six 다지기 phase reviews; the 371
              roadmap words are unchanged and no band moves
  prompt/     English-only rule, recall, LETTER-LEVEL CHECK, marking
  audit-gate.mjs, run-checks.sh, fixtures/  — the word gate measured
              against 54 real tutor messages

CI runs run-checks.sh in place of validate.mjs alone, and `npm run check`
gains the audit. Baselines: validate PASS 0/0; audit 7 of 41 and 2 of 13.

types/lib/ declares the new API, and test/lib/ pins it: letterCheck on
the prompt's own 짧다/빫다 case, deconjugation, the roadmap-first order
that keeps 마셔 out of Phase 1, sync's three gates, and recall evidence —
including the two ways lib's evidence is looser than PORT.md, pinned as
they are so the call site that tightens them is visibly needed.

TaskHost gains a plain recall renderer so the tree typechecks against the
wider Task union.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 19:49:52 +02:00

79 lines
2.4 KiB
JavaScript

import js from "@eslint/js";
import tseslint from "typescript-eslint";
import reactHooks from "eslint-plugin-react-hooks";
/* The clock guard.
The artifact's sync bug was a fresh device stamping its own empty defaults
as newer than the server's real data. db/writes.ts owns the distinction
between a seed write (updated_at stays 0) and a user edit (stamped), so it
is the only file under src/db/ allowed to read the clock. Anywhere else,
reaching for Date.now() means someone is about to stamp a row without
deciding which kind of write it is. */
const CLOCK_BAN = [
{
selector: "CallExpression[callee.object.name='Date'][callee.property.name='now']",
message:
"Only db/writes.ts may read the clock. Use a seedX() helper (leaves updated_at 0) " +
"or an editX() helper (stamps it) — see the timestamp rule in db/writes.ts.",
},
{
selector: "NewExpression[callee.name='Date']",
message:
"Only db/writes.ts may read the clock. Use a seedX() or editX() helper from db/writes.ts.",
},
];
export default tseslint.config(
// lib/, data/, fixtures/, validate.mjs and audit-gate.mjs are copied in
// from the export bundle unchanged and must stay byte-identical — they are
// not ours to restyle.
{
ignores: [
"**/dist/**",
"**/node_modules/**",
"export/**",
"vendor/**",
"app/android/**",
"lib/**",
"data/**",
"fixtures/**",
"validate.mjs",
"audit-gate.mjs",
],
},
js.configs.recommended,
...tseslint.configs.recommended,
// Build scripts and the shared modules are plain Node ESM.
{
files: ["tools/**/*.mjs", "shared/**/*.mjs", "*.config.js"],
languageOptions: {
globals: {
console: "readonly",
process: "readonly",
URL: "readonly",
fetch: "readonly",
Buffer: "readonly",
},
},
},
/* The rules of hooks, and exhaustive deps in particular. A stale closure
in an effect is what let the tutor seed its opening turn three times. */
{
files: ["app/src/**/*.{ts,tsx}"],
plugins: { "react-hooks": reactHooks },
rules: {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
},
},
{
files: ["app/src/db/**/*.ts"],
rules: { "no-restricted-syntax": ["error", ...CLOCK_BAN] },
},
{
files: ["app/src/db/writes.ts"],
rules: { "no-restricted-syntax": "off" },
},
);