All files / src planning-payload.ts

62.19% Statements 51/82
76.47% Branches 13/17
66.66% Functions 10/15
62.19% Lines 51/82

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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 831x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x             1x 1x 1x                                       1x 1x 1x             1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x  
import * as path from "node:path"
 
import {
  loadPlanningPayloadFromSqliteDetailed,
  sqliteDbExists,
} from "./load-from-sqlite.js"
import { sortByIdAsc } from "./domain/sort-by-id.js"
import { allSelectedVersionIds } from "./domain/version-filter.js"
import type { EpicSummary } from "./load-epics.js"
import type { SprintSummary } from "./load-sprints.js"
import type { VersionSummary } from "./load-versions.js"
import type { UserStory } from "./domain/types.js"
 
export type PlanningPayload = {
  versions: VersionSummary[]
  epics: EpicSummary[]
  sprints: SprintSummary[]
  stories: UserStory[]
}
 
export type PlanningLoadResult =
  | { ok: true; payload: PlanningPayload }
  | { ok: false; error: string }
 
function sqliteRequiredMessage(packageRoot: string): string {
  return (
    `Meridian: .meridian/meridian.db not found under ${packageRoot}.\n` +
    "Run: python3 .agent/scripts/meridian_delivery.py bootstrap"
  )
}
 
/** v10+: delivery data comes only from SQLite — no docs/us fallback. */
export function loadPlanningPayloadDetailed(
  docsRoot: string,
  packageRoot?: string,
): PlanningLoadResult {
  const pkg = packageRoot ?? path.dirname(docsRoot)
  if (!sqliteDbExists(pkg)) {
    return { ok: false, error: sqliteRequiredMessage(pkg) }
  }
  const fromDb = loadPlanningPayloadFromSqliteDetailed(pkg)
  if (fromDb.error || !fromDb.payload) {
    return {
      ok: false,
      error: fromDb.error ?? "Meridian: SQLite planning export failed.",
    }
  }
  if (fromDb.payload.stories.length === 0) {
    return { ok: false, error: "Meridian: meridian.db has zero user stories." }
  }
  return { ok: true, payload: fromDb.payload }
}
 
/** Throws if SQLite load fails — use loadPlanningPayloadDetailed when UI needs error text. */
export function loadPlanningPayload(docsRoot: string, packageRoot?: string): PlanningPayload {
  const result = loadPlanningPayloadDetailed(docsRoot, packageRoot)
  if (!result.ok) {
    throw new Error(result.error)
  }
  return result.payload
}
 
/** Lists sorted by id (v0, v4-S2, EPIC-05); version chips default to all selected. */
export function planningPayloadForListViews(
  payload: PlanningPayload,
): PlanningPayload & { defaultVersions: string[] } {
  const versions = sortByIdAsc(payload.versions)
  return {
    ...payload,
    versions,
    sprints: sortByIdAsc(payload.sprints),
    epics: sortByIdAsc(payload.epics),
    defaultVersions: allSelectedVersionIds(versions.map((v) => v.id)),
  }
}
 
export function planningPayloadForVersionsView(payload: PlanningPayload): PlanningPayload {
  return {
    ...payload,
    versions: sortByIdAsc(payload.versions),
  }
}