import type { ModelGraph } from "../loader/model-graph.js"; import type { AxisStatus, Conformance, ConformanceVerdict } from "../query/conformance.js"; /** * `overview` — an interactive, self-contained HTML map of the WHOLE system built * for developer comprehension (not drift-spotting like `graph`, not one-flow like * `view`). Every loop is clickable and expands to plain-language detail: what it * does, which package owns it, where the code is, which queues it consumes, its * GWT test scenarios with the bound test files, and its implementation status * (met / partial / gap) spelled out in words — all from the model, resolved * against the real checkout. * * Three organizing sections, in the order a reader needs them (016 T7 — the page * must let someone who does not read code answer: what machinery runs by itself, * how is it wired together, and where is it unguarded): * * 1. the OUTSTANDING LEDGER — debt entries plus every non-`met` node. First, * because these are the rows that need a human decision; the report card is * the proof, not the point. * 2. the flows (end-to-end journeys); * 3. the loops NO flow references. That is the literal and only criterion for * this bucket, and the section says exactly that (016 D7): a loop lands here * because nothing traverses/guards it, which may mean it really is * standalone background machinery OR simply that no flow has picked it up * yet. The section used to ASSERT the former ("the always-on machinery — * renewals, sweeps, caches, pollers") — on a real 30-loop model 21 loops * landed here including the main REPL loop, so the assertion was plainly * false and the wording is now the criterion itself. * * The flow/no-flow split exists because a flow-only view silently drops more * than half the control loops. * * Same posture as the other views: a PROJECTION regenerated into the gitignored * `.loopgraph/ws/` side-channel, never committed (the model is the artifact). * Output is ONE self-contained HTML string — inline CSS + JS + JSON, zero external * hosts — so it opens over `file://` and survives an Artifact CSP. */ export interface RefStatus { ref: string; /** repo-relative file part of the anchor, or null for a table-style anchor. */ file: string | null; /** true = file exists, false = missing, null = not resolved (no repo root). */ ok: boolean | null; } export interface OverviewScenario { id: string; missing?: boolean; given?: string; when?: string; then?: string; level?: string; tests?: RefStatus[]; } export interface OverviewJunctionRef { id: string; title: string; risk: string; between: string[]; /** * Conformance verdict for this junction. Junctions ARE graded (they count * toward the met/partial/gap headline), so leaving it off meant the report * card could show a `gap` that no element on the page accounted for — the * same defect the flow cards had. `null` only if the junction is somehow not * in the conformance result. */ verdict: ConformanceVerdict | null; } export interface OverviewLoop { id: string; title: string; boundary: string; owner: string | null; section: string | null; parent: string | null; embedded: boolean; dormant: boolean; notes: string | null; anchors: RefStatus[]; queues: string[]; verdict: ConformanceVerdict | "dormant" | "unknown"; code: AxisStatus | null; test: AxisStatus | null; gaps: { kind: string; detail: string; }[]; scenarios: OverviewScenario[]; junctions: OverviewJunctionRef[]; } export interface OverviewFlow { id: string; title: string; summary: string | null; steps: string[]; guards: string[]; junctions: OverviewJunctionRef[]; /** * Conformance verdict when this flow is GRADED (it carries its own anchors — * F1/F2a). `null` for a composition-only flow, which is deliberately not * graded (its constituent loops are) and must not be dressed up as a verdict. * Without these the report card could show "1 met / 1 partial" while no card * on the page explained either number — the zero-loop repo case. */ verdict: ConformanceVerdict | null; code: AxisStatus | null; test: AxisStatus | null; gaps: { kind: string; detail: string; }[]; /** * The flow's OWN code anchors (F1). In a flow-shaped repo these are the only * substance the card has — `steps` is empty because there are no loops to * traverse — so without them a zero-loop repo renders empty flow cards. * * NOT either/or with `steps`: an `anchored` flow may also compose (see * `flowShape` in schema/model.ts), so both render. */ anchors: RefStatus[]; /** * The flow's OWN GWT scenarios (F2b) — the same mechanism `Loop.scenarios` * uses, resolved through the same builder so both anchor forms * (`path#symbol` and the `{file,text}` test-title anchor, 016 T6) reach the * page. Absent from this payload until the flow drawer existed: the flow card * had nowhere to put them, so a flow could carry a fully bound scenario and * the page would show nothing but a title — the "how is it wired" question * answered halfway. 13 of pi-full's 19 flows carry their own scenarios. */ scenarios: OverviewScenario[]; } /** * One row of the outstanding ledger (016 T7). Two row kinds share the block * because they answer the same question ("where is this system not holding up") * and a reader should not have to visit two places for it: * * - `debt`: a baseline debt entry — something that LOOKS like behavior but was * disqualified (dead state machine, static flag, deferred build). It has no * verdict because it is not graded; `reality` is the finding. * - `node`: a graded loop/flow/junction whose verdict is `gap` or `partial`. * * `met` nodes are deliberately absent: this block is the punch list, and the * stats row above already carries the totals. */ export interface OverviewFinding { row: "debt" | "node"; id: string; /** debt: `subject`. node: the node title. */ title: string; /** node only: which model kind, so a reader can tell a junction row from a loop row. */ kind?: "loop" | "flow" | "junction"; /** node only. Debt rows are ungraded by design and must not be dressed up with one. */ verdict?: "gap" | "partial"; /** node only: `missing` on either axis drives the ✗ badges. */ code?: AxisStatus; test?: AxisStatus; /** node only: the concrete gap list, same vocabulary as the drawer. */ gaps?: { kind: string; detail: string; }[]; /** debt only: category / what is actually true / who owns it / when it may leave. */ category?: string; reality?: string; claim?: string | null; owner?: string | null; removalCondition?: string | null; } export interface OverviewModel { summary: { loops: number; inFlow: number; background: number; /** * Baseline debt entries. Counted here because nothing else on the page * would otherwise account for them — they are not graded, so the * met/partial/gap headline is silent about them. */ debts: number; /** * Distinct repo files the model anchors into — the numerator of the * coverage declaration (016 T5). Identical to `backtest`'s * `anchoredFileCount` because both call `coveredFiles`. Stated on the page * so a PARTIAL map cannot be read as the whole system. It measures how * thoroughly the code was searched, NOT business completeness; see the * page's own wording, which must keep saying so. */ anchoredFiles: number; /** * Dormant loops — counted, never graded, and shown as an explicit stat so the * exclusion is visible rather than silent (same discipline conformance.ts * applies when it reports "N dormant loop(s) excluded"). NOT a partition slot: * a dormant loop wired into a flow is counted here AND in `inFlow` (it still * renders as a chip there), so these fields must not be summed to the total. */ dormant: number; flows: number; /** * Composition-only flows kept OUT of grading (their loops are already * graded). Surfaced for the same reason `dormant` is: an exclusion the * headline depends on must be visible, never silent. */ flowsExcluded: number; junctions: number; met: number; partial: number; gap: number; }; flows: OverviewFlow[]; background: { owner: string; ids: string[]; }[]; loops: Record; /** Outstanding ledger, already ordered debt → gap → partial. */ findings: OverviewFinding[]; } export interface OverviewMeta { title: string; repoResolved: boolean; /** Base URL to prefix a repo-relative file path with, e.g. `https://github.com/o/r/blob//`. Absent → no code links. */ blobBase?: string | null; /** Link to the repo root at the pinned ref, shown in the header. */ repoHref?: string | null; /** Human label for the repo + ref, e.g. `owner/repo @ 1a2b3c4`. */ repoLabel?: string | null; /** When this page was generated (ISO), shown as provenance in the footer. */ generatedAt?: string | null; /** * Fingerprint of the model bytes this page was rendered from. `repoLabel` says * which CODE state was graded; this says which MODEL state produced the page — * the two move independently, so a saved copy needs both to be interpretable. */ modelHash?: string | null; /** * Commit-side half of the coverage declaration (016 T5): of the last * `total` commits that touched a `.ts`/`.tsx` file, `hit` of them touched a * file this model anchors. Same computation `loopgraph backtest` reports * (the CLI layer literally calls it), surfaced here because the coverage * number belongs next to the map it qualifies, not in a separate command. * Absent (null) whenever no repo root was given or git could not be walked — * a best-effort extra, never a reason for the page to fail. */ commitTouch?: { hit: number; total: number; } | null; } /** * Turn a git remote URL + ref into the base URL that makes each anchor a * clickable code link (so a human or agent jumps straight to the file). Pure and * host-aware: github/GHE use `/blob//`, gitlab uses `/-/blob//`. Returns * null for a remote it can't parse (links then simply stay off). The ref is * pinned (usually the HEAD sha) so links point at the exact state that was graded. */ export declare function repoLinks(remoteUrl: string, ref: string): { blobBase: string; repoHref: string; repoLabel: string; } | null; /** * Build the overview render model from the loaded graph + a conformance result. * `existingFiles` is the set of repo-relative paths that exist under the checkout * (from the CLI layer); when undefined, file existence is unresolved and every * `ok` is null (structural mode, matching conformance's own repoResolved=false). */ export declare function computeOverviewModel(graph: ModelGraph, conformance: Conformance, existingFiles?: Set): OverviewModel; /** * Render the whole self-contained interactive page. The model + meta are embedded * as one inline JSON island; ALL DOM (chips, detail panel) is built by the inline * script from that data, so free-text (titles, scenario prose) is escaped by the * DOM, never hand-concatenated into markup. The script uses only string * concatenation (no template literals / `${}`), so this whole file drops in with a * single interpolation — the data — and no escaping surprises. */ export declare function renderOverviewHtml(model: OverviewModel, meta: OverviewMeta): string; //# sourceMappingURL=overview-html.d.ts.map