Skip to content

Import summarizeSkillEvidence and trainingEntryTask instead of re-copying them into meatspacePostStats #5709

Description

@atomantic

Problemserver/services/meatspacePostStats.js already imports three helpers from server/services/meatspacePost.js, and its own header says "Shared session and legacy-task helpers intentionally stay there" — yet two of those helpers, trainingEntryTask and summarizeSkillEvidence, are pasted into it character for character instead of imported. They are the normalizers that turn a training-log entry into a scored task and roll drill evidence into the accuracy/completion means the POST stats surface. A change to how a legacy training entry maps to accuracy (the exact area the #2094 n-back rescoring touched) now has to be made twice, and if it isn't, the stats endpoint and the session view report different accuracy for the same drill.

Evidenceserver/services/meatspacePostStats.js:7 already reaches into the sibling:

import { getPostSessions, deriveTaskAccuracy, deriveTaskCompletion } from './meatspacePost.js';

and then re-declares, at :13 and :40, bodies that are byte-identical to server/services/meatspacePost.js:949 and :990 (verified by extracting both function bodies and diffing — the only difference is the export keyword, which neither copy carries). The shared body:

function summarizeSkillEvidence(sessions, training) {
  const accuracyLists = {}; const completionLists = {}; const counts = {};
  const add = (task) => {
    if (!task?.module || !task?.type) return;
    const key = `${task.module}:${task.type}`;
    counts[key] = (counts[key] || 0) + 1;
    const accuracy = deriveTaskAccuracy(task);
    if (accuracy != null) (accuracyLists[key] ||= []).push(accuracy);
    ...

Consumers: meatspacePost.js:985 and :1173 use trainingEntryTask; meatspacePostStats.js:54 and :93 use the copies.

Plan

  1. In server/services/meatspacePost.js, add export to function trainingEntryTask (:949) and function summarizeSkillEvidence (:990).
  2. In server/services/meatspacePostStats.js, delete lines 13-38 (trainingEntryTask) and 40-65 (summarizeSkillEvidence) and add both names to the existing import on line 7.
  3. Leave skillEvidenceSessions (meatspacePost.js:975) private — it has one caller.
  4. The circular edge already exists and is safe: meatspacePost.js:58 imports getPostStats from meatspacePostStats.js while meatspacePostStats.js:7 imports back the other way, and ESM live bindings resolve it. Adding two more names to the existing back-import changes nothing about the cycle — but run cd server && npm test for the meatspace suites specifically to confirm no TDZ error at module init.

Tests — extend server/services/meatspacePostStats.test.js (no such file today, so create it) with one boundary case: a training-log entry whose accuracy is absent but whose questionCount/correctCount are present appears in getPostStats()'s evidenceByDrillAccuracy with the derived ratio. That uniquely catches the copies drifting on the derive rule, which is the regression this change prevents.

Acceptance criteria

  • grep -c "function trainingEntryTask\|function summarizeSkillEvidence" server/services/meatspacePostStats.js returns 0.
  • getPostStats() returns identical output before and after the change for a fixture with both session and training evidence.
  • cd server && npm test passes.

Out of scope — the client mirror of nBackBalancedAccuracy in client/src/components/meatspace/post/constants.js; any change to the scoring rules themselves.


Filed by a /do:better --scan-only --issues audit (2026-09-01). Category: dry · Severity: low · Files: server/services/meatspacePostStats.js:13, server/services/meatspacePostStats.js:40, server/services/meatspacePost.js:949, server/services/meatspacePost.js:990

All labels already exist in the repo; do NOT create labels. Never add planner:* labels.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions