← The Store

CRSS Exam-Prep Pipeline

Corpus checks, snapshots, edit digests and SME queues for an exam bank.

PipelinesLivePythonNode.jsSupabase
0builds

What it is

A deterministic content pipeline for the CRSS recovery-support question bank: gated corpus checks, bank snapshots, edit digests and an SME review queue, with every build recorded as a pipeline run.

Take it with you

The real, committed source behind this system — copy it or download the file. Plus a portable spec of everything on this page.

corpus-checks-lib.mjsjavascript186 lines

The corpus-checks library — the gate of pure checks the exam bank must pass before any snapshot or digest is produced.

// ---------------------------------------------------------------------------
// Pure-logic corpus-level QA checks (Layer 5, deterministic subset).
//
// Each check is a function that takes an array of normalized items and a
// config object, and returns a "check result" of the shape expected by
// the crss_corpus_checks table:
//
//   {
//     check_code: 'answer_distribution',
//     layer: 'L5',
//     status: 'pass' | 'warn' | 'fail',
//     observed: {...},
//     expected: {...},
//     affected_items: [id, ...],   // optional
//     notes: '...'                 // optional
//   }
//
// No I/O, no env access — makes the logic trivial to unit-test.
// ---------------------------------------------------------------------------

export const ANSWER_LETTERS = ['A', 'B', 'C', 'D'];

// Layer 5a: answer-letter distribution ~25% each.
export function answerDistributionCheck(items, opts = {}) {
  const min = opts.minPct ?? 0.2;
  const max = opts.maxPct ?? 0.3;

  const counts = { A: 0, B: 0, C: 0, D: 0 };
  for (const it of items) {
    if (counts[it.correct] !== undefined) counts[it.correct] += 1;
  }
  const total = items.length;
  const pcts = {};
  for (const l of ANSWER_LETTERS) pcts[l] = total ? counts[l] / total : 0;

  const outOfBand = ANSWER_LETTERS.filter((l) => pcts[l] < min || pcts[l] > max);
  const status = total === 0 ? 'fail' : outOfBand.length === 0 ? 'pass' : 'warn';

  return {
    check_code: 'answer_distribution',
    layer: 'L5',
    status,
    observed: { counts, pcts, total },
    expected: { min, max, target: 0.25 },
    affected_items: [],
    notes: outOfBand.length
      ? `Letters outside ${(min * 100).toFixed(0)}%–${(max * 100).toFixed(0)}% band: ${outOfBand.join(', ')}`
      : null,
  };
}

// Layer 5b: blueprint weights — domain proportions vs. target.
// `targets` is an object like {domain: 0.20, ...} summing to ~1.
export function blueprintWeightsCheck(items, { targets, toleranceBp = 0.03 } = {}) {
  const total = items.length;
  const counts = {};
  for (const it of items) counts[it.domain] = (counts[it.domain] ?? 0) + 1;

  const observed = {};
  const drift = {};
  const breaches = [];
  for (const domain of Object.keys(targets ?? {})) {
    const pct = total ? (counts[domain] ?? 0) / total : 0;
    observed[domain] = { count: counts[domain] ?? 0, pct };
    const d = pct - targets[domain];
    drift[domain] = d;
    if (Math.abs(d) > toleranceBp) breaches.push(domain);
  }
  for (const domain of Object.keys(counts)) {
    if (targets && !(domain in targets)) {
      observed[domain] = { count: counts[domain], pct: counts[domain] / total, untargeted: true };
    }
  }

  const status = total === 0
    ? 'fail'
    : breaches.length === 0
      ? 'pass'
      : breaches.length <= 2
        ? 'warn'
        : 'fail';

  return {
    check_code: 'blueprint_weights',
    layer: 'L5',
    status,
    observed: { counts: observed, drift, total },
    expected: { targets, tolerance_bp: toleranceBp },
    affected_items: [],
    notes: breaches.length ? `Drift > ${(toleranceBp * 100).toFixed(1)}pp in: ${breaches.join(', ')}` : null,
  };
}

// Layer 5c: module coverage. Study guide has 48 modules; any item whose
// subcategory falls outside that set is an orphan. Any module with fewer
// than `minPerModule` items is under-covered.
export function moduleCoverageCheck(items, { allowedModules, minPerModule = 3 } = {}) {
  const counts = new Map();
  for (const it of items) {
    const m = it.subcategory ?? '(none)';
    counts.set(m, (counts.get(m) ?? 0) + 1);
  }

  const allowed = allowedModules instanceof Set
    ? allowedModules
    : Array.isArray(allowedModules)
      ? new Set(allowedModules)
      : null;

  const orphans = [];
  const underCovered = [];
  for (const [module, n] of counts.entries()) {
    if (allowed && !allowed.has(module)) orphans.push(module);
    else if (n < minPerModule) underCovered.push({ module, count: n });
  }

  const orphanItems = allowed
    ? items.filter((it) => !allowed.has(it.subcategory ?? '(none)')).map((it) => it.id)
    : [];

  const status = orphans.length > 0
    ? 'fail'
    : underCovered.length > 0
      ? 'warn'
      : 'pass';

  return {
    check_code: 'module_coverage',
    layer: 'L5',
    status,
    observed: {
      unique_modules: counts.size,
      per_module: Object.fromEntries(counts),
      orphan_modules: orphans,
      under_covered: underCovered,
    },
    expected: {
      allowed_modules: allowed ? [...allowed] : null,
      min_per_module: minPerModule,
    },
    affected_items: orphanItems,
    notes: orphans.length
      ? `Orphan modules (not in allowed list): ${orphans.join(', ')}`
      : underCovered.length
        ? `Modules below ${minPerModule} items: ${underCovered.map((x) => x.module).join(', ')}`
        : null,
  };
}

// Layer 5d: cog-level distribution. Warn on extreme skew.
const COG_LEVELS = ['recall', 'understanding', 'application', 'analysis'];
export function cogLevelDistributionCheck(items, { minPctAnalysis = 0.05 } = {}) {
  const counts = Object.fromEntries(COG_LEVELS.map((l) => [l, 0]));
  let untagged = 0;
  for (const it of items) {
    if (COG_LEVELS.includes(it.cog_level)) counts[it.cog_level] += 1;
    else untagged += 1;
  }
  const total = items.length;
  const pcts = Object.fromEntries(
    COG_LEVELS.map((l) => [l, total ? counts[l] / total : 0])
  );
  const analysisLow = pcts.analysis < minPctAnalysis;
  const status = total === 0 ? 'fail' : analysisLow ? 'warn' : 'pass';

  return {
    check_code: 'cog_level_distribution',
    layer: 'L5',
    status,
    observed: { counts, pcts, untagged, total },
    expected: { min_pct_analysis: minPctAnalysis },
    affected_items: [],
    notes: analysisLow
      ? `analysis items are ${(pcts.analysis * 100).toFixed(1)}% of bank (floor ${(minPctAnalysis * 100).toFixed(0)}%)`
      : null,
  };
}

export function runAllChecks(items, config = {}) {
  return [
    answerDistributionCheck(items, config.answer_distribution),
    blueprintWeightsCheck(items, config.blueprint_weights),
    moduleCoverageCheck(items, config.module_coverage),
    cogLevelDistributionCheck(items, config.cog_level_distribution),
  ];
}

Where it lives

  • scripts/crss/
  • crss-exam-prep/
  • src/app/crss/

See it in action

CRSS

FAQ

What guards the CRSS bank?

A set of corpus checks must pass before any snapshot or digest is produced — the gate that keeps the bank consistent.

Related systems

Enrichment OrchestratorFive-agent pipeline that writes SEO metadata for every asset.HF Open-Model Research CrewA CrewAI crew that scouts, benchmarks and writes up open Hub encoders.Content SeedersThe bridge that pushes committed content into the live database.

Explore the full catalog →

Want a system like this built for you?Work with me →