All files / scripts/roll-forward-pirs roll-forward.ts

100% Statements 12/12
100% Branches 14/14
100% Functions 3/3
100% Lines 11/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67                                                                30x 30x   30x 56x 27x   29x 29x 29x             30x   30x           30x                    
/**
 * @module roll-forward-pirs/roll-forward
 * @description Pure transformation that turns yesterday's `pir-status.json`
 * into today's: open PIRs degrade and accumulate genealogy, non-open PIRs
 * are carried forward unchanged.
 *
 * @author Hack23 AB
 * @license Apache-2.0
 */
 
import path from 'node:path';
 
import { degrade } from './confidence.js';
import { REPO_ROOT } from './constants.js';
import type { CycleType, PirEntry, PirStatusFile } from './types.js';
 
/**
 * Roll-forward semantics:
 * - Open PIRs carry forward to the new file with confidence degraded one
 *   step and `inherits_from` extended by the parent `pir_id` so genealogy
 *   inheritance is fully preserved.
 * - Non-open PIRs (answered, superseded, deferred, cancelled) are carried
 *   forward UNCHANGED — including any pre-existing `inherits_from` chain —
 *   so the historical lineage is never lost.
 */
export function rollForward(
  source: PirStatusFile,
  sourcePath: string,
  targetDate: string,
  targetCycle: CycleType,
  options: { now?: () => Date; repoRoot?: string } = {},
): PirStatusFile {
  const now = options.now ?? (() => new Date());
  const repoRoot = options.repoRoot ?? REPO_ROOT;
 
  const pirs: PirEntry[] = source.pirs.map((p) => {
    if (p.status !== 'open') {
      return { ...p };
    }
    const { answer_summary: _dropped, ...rest } = p;
    void _dropped;
    return {
      ...rest,
      confidence: degrade(p.confidence),
      inherits_from: [...(p.inherits_from ?? []), p.pir_id],
    };
  });
 
  const relativeToRepo = path.relative(repoRoot, sourcePath);
  const relativeSourcePath =
    relativeToRepo &&
    !relativeToRepo.startsWith('..') &&
    !path.isAbsolute(relativeToRepo)
      ? relativeToRepo.split(path.sep).join('/')
      : sourcePath.split(path.sep).join('/');
 
  return {
    schema_version: '1.0',
    cycle: targetCycle,
    date: targetDate,
    subfolder: targetCycle,
    generated_at: now().toISOString(),
    inherited_from: relativeSourcePath,
    pirs,
  };
}