/** * Compile-manifest staleness delta naming (mmnto-ai/totem#2399). * * The staleness check in `lint.ts` compares one AGGREGATE `input_hash` against * the manifest and, on mismatch, warned with a fixed string that named nothing. * This module turns that boolean "stale" verdict into a NAMED delta: which * lesson files changed / were added / were removed since the last compile, so * the consumer can tell whether the drift is theirs, rode in on a merge, or is * the mmnto-ai/totem#2113 untracked-at-compile class. * * Every export here is PURE (no `node:fs`, no `child_process`): the classify * and format logic is unit-tested without a real git repo or temp dir (the * cohort's standing "no real `git` with cwd=temp on Windows" rule — it leaves * undeletable temp dirs). `lint.ts` owns the impure gathering (the `git` * name-status diff + `git log` provenance via the shared `safeExec` helper, and * the `fs` mtime walk) and feeds the results to these helpers. */ /** * Max lesson names printed in the warning before collapsing the tail to * "…and K more". Keeps the advisory to a glanceable block and caps the number * of per-file `git log` provenance spawns at exactly this many. */ export declare const STALE_LESSON_NAME_CAP = 5; /** * Above this many lesson files, skip per-file provenance (name-only mode). * Provenance is already bounded to at most {@link STALE_LESSON_NAME_CAP} `git * log` calls, but the whole staleness check is a non-blocking advisory on the * lint hot path: on a very large lesson corpus we decline to spawn git per * named file at all rather than let the naming logic pace the run. */ export declare const PROVENANCE_LESSON_FILE_CAP = 500; export type LessonChangeKind = 'changed' | 'added' | 'removed'; export interface LessonDeltaEntry { /** * Path of the changed lesson. Repo-root-relative forward-slash form when the * delta came from `git diff --name-status`; lessons-dir-relative in the mtime * fallback. Callers render a short display name via `path.basename`, so either * basis reads correctly. */ path: string; kind: LessonChangeKind; } export interface LessonDelta { /** Classified entries, sorted by path for deterministic output. */ entries: LessonDeltaEntry[]; } /** Last-commit provenance for one named lesson. */ export interface LessonProvenance { /** Short commit sha of the last commit that touched the file. */ shortSha: string; /** Author name of that commit. */ author: string; } /** * Sentinel provenance for a lesson with no commit history — staged-but-never- * committed / untracked. This is the mmnto-ai/totem#2113 class and is worth * distinguishing from a real commit in the warning. */ export declare const UNTRACKED_PROVENANCE = "untracked"; export type ProvenanceValue = LessonProvenance | typeof UNTRACKED_PROVENANCE; export interface FormatStalenessOptions { /** Max named lessons before the "…and K more" tail (usually {@link STALE_LESSON_NAME_CAP}). */ nameCap: number; /** Map a delta `path` to the short display name shown in the warning. */ displayNameFor: (path: string) => string; /** * Per-path provenance keyed by the delta `path`. `null` disables provenance * entirely (name-only mode — git unavailable, or above * {@link PROVENANCE_LESSON_FILE_CAP}). When non-null, a path absent from the * map simply renders without a provenance suffix. */ provenance: Map | null; } /** * Parse `git diff --name-status ` output into a classified lesson delta, * keeping only `.md` files under `lessonsPrefix` (a repo-relative, forward-slash * directory prefix such as `.totem/lessons`). * * Status letters map: `A`/`C` → added, `D` → removed, `M`/`T` → changed. A * rename (`R`) reports its DESTINATION path as `changed` (a lesson whose path * moved shifts the input hash because `generateInputHash` folds the relative * path into the digest). Rename/copy lines carry `OLDNEW`, so the current * path is the third tab field; A/M/D carry `STATUSPATH`. Unknown status * letters (`U` unmerged, etc.) are ignored — best-effort naming, never a throw. */ export declare function parseLessonNameStatus(raw: string, lessonsPrefix: string): LessonDelta; export interface LessonFileStat { /** Lessons-dir-relative (or any stable) forward-slash path. */ path: string; /** File mtime in epoch-milliseconds. */ mtimeMs: number; } /** * Fallback classifier for when git is unavailable (no repo, or the manifest was * never committed so there is no anchor): a lesson file whose mtime is strictly * after the manifest's compile instant is reported as `changed`. * * mtime alone cannot recover added-vs-removed-vs-edited (a removed file has no * mtime to observe; a freshly-written file is indistinguishable from an edit), * so every hit is `changed` — the honest floor when there is no git baseline. * A non-finite `compiledAtMs` (unparseable `compiled_at`) yields an empty delta * rather than naming every file. */ export declare function classifyLessonsByMtime(files: readonly LessonFileStat[], compiledAtMs: number): LessonDelta; /** * Build the single warn-block body for a stale manifest. One block, ready to * hand to `uiLog.warn(TAG, …)`. * * - `total === 0` (nothing could be named — git unavailable and no mtime hit, * or an anchor miss): fall back to the generic advisory. The stable * `Compile manifest is stale` prefix is preserved so any consumer matching on * it keeps working, and we never assert "0 lesson(s) changed". * - otherwise: a counted first line, up to `nameCap` named lessons with their * change kind and (when derivable) `— by ` provenance or * `(untracked)`, then "…and K more" when over the cap, then the remediation. * * The remediation intentionally points public consumers at `totem lesson * compile`. This formatter stays freeze-agnostic — it always includes the * remediation; the caller (`lint.ts`) suppresses the whole advisory while a * rule-compilation freeze is active, since `totem lesson compile` is on that * freeze's do-not list (mmnto-ai/totem#2463 slice B). */ export declare function formatStalenessWarning(delta: LessonDelta, opts: FormatStalenessOptions): string; //# sourceMappingURL=lint-staleness.d.ts.map