import type { ImplementationFact } from "../adapters/types.js"; import type { ModelGraph } from "../loader/model-graph.js"; import { type ReadRepoFile } from "./delegation.js"; /** * C2 T1 reconciliation (Proposal 006 Phase 3): which extracted implementation * facts (pg-boss queues, setInterval pollers) are NOT registered by any model * node. This is the "unregistered route/queue/poller 拦截" check — B1 facts * reconciled against the model registry. * * ADVISORY (T1), not a T0 gate: it reports, it does not fail the build. The * enforcement-tier promotion (report → block) is C3, gated on a measured * false-positive rate on real target-repo PRs. */ /** * A reconciliation partitions every fact into exactly ONE of three disjoint * buckets — `registered + dormantSuppressed + unregistered === total`. * (Delegation-registered facts, below, are a SUBSET of `registered` — they do * not add a fourth bucket, they just widen how a fact earns its way into the * one it already had.) */ export interface Reconciliation { /** * facts whose file is covered by an ACTIVE model node (a real, modeled * loop/junction), OR whose line falls inside a one-hop delegation target * resolved from an active loop's anchor (see `delegationHits`). */ registered: ImplementationFact[]; /** * facts whose file is covered ONLY by a dormant Loop (an N-series * "baseline-only" registration) — NOT by any active node. A distinct bucket, * not a subset of `registered`: a dormant registration quiets the advisory * signal without being real modeling, so it is surfaced separately (001 §12 * 不掩盖缺口 — the quieting must be visible, not hidden inside `registered`). */ dormantSuppressed: ImplementationFact[]; /** facts whose file no model node anchors — the advisory findings. */ unregistered: ImplementationFact[]; /** repo-relative files the model registers (active + dormant anchors), sorted. */ coveredFiles: string[]; /** * Queue names declared in some active loop's `consumes_queues` that matched NO * extracted fact — surfaced so a declaration can't silently fail to register * (and so the model stays honest as the code moves under it). */ unmatchedConsumedQueues: UnmatchedConsumedQueue[]; /** * One-hop delegation targets resolved from an ACTIVE loop's anchors while * reconciling (#23 PR3), and how many facts each one pulled into * `registered`. Always `[]` when `readFile` was not passed to * `reconcileFacts` — delegation is opt-in, see that function's doc. * * WHY THIS EXISTS, SEPARATELY FROM THE BUCKET COUNTS: a delegation hit and * a check that plain didn't run both leave `unregistered` unchanged from * the no-delegation baseline in the case that matters least (nothing to * find either way) — but they must NOT look the same in the case that * matters: a hop that resolved and found nothing vs. a hop that never got * attempted are different failure modes to debug. This field is the * "the tracer ran" tripwire the CLI prints, mirroring `checkLoopMechanism`'s * `severity: "info"` Violation for the identical reason. */ delegationHits: DelegationHit[]; } /** * One `Loop.anchors` entry that resolved to a one-hop delegation target * (`delegation.ts`), and how many facts that target's OWN symbol span pulled * into `registered`. `registeredFactCount` can be 0 — the hop resolved (the * wrapper really does hand off) but nothing in this batch of facts happened * to land in the target's span; that is still worth showing, it is not the * same thing as "delegation didn't run". */ export interface DelegationHit { /** the active loop whose anchor delegated. */ loopId: string; /** the anchor (`file#symbol`) that delegated. */ anchor: string; /** where it resolved to, as `file#symbol`. */ target: string; /** facts inside the target's symbol span that this hop moved into `registered`. */ registeredFactCount: number; } /** * A declared-but-unmatched queue, carrying the loops that declared it. * * The bare name alone reads as an accusation against the model, and that * reading is wrong about half the time: the name may be perfectly correct in * code the extractor never reached (unscanned file, unresolved cross-file * constant). Naming the declaring loops lets a reader jump straight to their * anchors and settle which of the two it is, instead of being steered toward * "fix the model" by default. */ export interface UnmatchedConsumedQueue { queue: string; /** ids of the active loops whose `consumes_queues` declared it, sorted. */ declaredBy: string[]; } /** File part of an anchor `FILE#SYMBOL` (or the whole string when there is no `#`). */ export declare function anchorFile(anchor: string): string; /** * The set of repo-relative files the model registers, via `Loop.anchors`, * `Flow.anchors` and `Junction` evidence anchors (active + dormant). Non-code * evidence anchors (spec/issue files) are folded in too — harmless, they simply * never match a code fact's `filePath`. */ export declare function coveredFiles(graph: ModelGraph): Set; /** * Reads a repo-relative file (required to follow delegation at all — without * it `reconcileFacts` cannot parse anything, so delegation is simply skipped), * and whether to actually follow it. `followDelegation` defaults to `true` but * only takes effect when `readFile` is also given; it exists mainly so the CLI * can offer `--no-follow-delegation` for a direct "with vs. without" comparison * without the caller having to omit `readFile` (which would also disable * `checkLoopMechanism`'s independent delegation following if the two ever * shared a call site). */ export interface ReconcileDelegationOptions { readFile?: ReadRepoFile; followDelegation?: boolean; } /** * Reconcile extracted facts against the model's registered anchors. * * FILE-LEVEL granularity is the BASE mechanism — a fact whose file carries ANY * loop/junction anchor counts as covered. That stays true and stays the ONLY * granularity for anchors themselves: the fact `name` is a queue string / * interval marker, a different namespace from the class/function symbols * anchors use, so symbol-level matching would need the extractor to also * capture each fact's enclosing symbol — future work, and if that granularity * is ever added for anchors this reconciliation must be revisited. File-level * deliberately UNDER-flags (a partially-modeled file hides an extra * unregistered loop it also contains) rather than over-flags: an advisory T1 * signal that cries wolf gets ignored, so the bias is toward silence on * ambiguity (001 §12 "不掩盖缺口" is about not hiding KNOWN gaps, not about * manufacturing noisy ones). * * DELEGATION IS THE ONE EXCEPTION, AND IT IS SYMBOL-LEVEL ON PURPOSE (#23 * PR3). When `readFile` is passed, a fact that lands inside a one-hop * delegation target's OWN symbol span also registers — but crediting the * whole target FILE, the way a direct anchor does, would be wrong here: an * anchor is a human's deliberate statement (file-level slack is a considered * trade-off, immediately above); a delegation target is the TOOL's inference, * and a service file typically holds several methods. Granting file-level * credit for an inferred hop would rebuild the exact false-green this whole * change removes, one hop further out (see delegation.ts's `symbolLineSpan` * doc, and `mechanism.ts`, which drew this same line first). Opt-in, and * doubly so: omit `readFile` and behaviour is byte-identical to before this * option existed (every pre-existing caller does exactly that); pass it but * set `followDelegation: false` and it is skipped anyway (the CLI's * `--no-follow-delegation`). * * N-series background loops (ttl-renewal, vault-refresh, ...) are suppressed by * registering them as minimal (dormant, owner-null) Loop nodes carrying an * anchor — that is where "baseline-only" N-loops live (001 §9), NOT in the debt * list (debt = dead-state-machines, a structurally different thing). */ export declare function reconcileFacts(facts: ImplementationFact[], graph: ModelGraph, nameMatchableSignalKinds?: readonly string[], delegationOptions?: ReconcileDelegationOptions): Reconciliation; //# sourceMappingURL=unregistered.d.ts.map