zettelkasten.synapse.grounding_router¶
zettelkasten.synapse.grounding_router ¶
The grounding + answer-gate layer of enforced epistemic honesty.
This is the layer that turns the deterministic status engine
(:mod:zettelkasten.synapse.epistemics) into an actual ANSWER-vs-ABSTAIN
verdict. The LLM reasons freely and returns a cited reasoning-DAG
(:class:~zettelkasten.synapse.memdsl.schema.ReasoningDAG); this router owns the
verdict, so fabrication is structurally impossible and abstention is automatic
when grounding or coverage breaks. It has four responsibilities:
-
An extensible verifier registry (:class:
VerifierRegistry) keyed by a verifier-type string, each mapping a store-native grounding check (quote/expr-recompute/equation/figure/decision_tree) onto ONE normalized :class:VerifierResult(ok/failed/unverifiable+ an optional status contribution). A new verifier-type (SQL-exec, temporal-freshness) is a new registry entry — the normalized contract never has to be re-cut. -
A citation-anchored prose↔DAG traceability gate (:func:
check_traceability): every non-trivial prose assertion must be licensed by a SINGLE cited verified claim under a FULL-CONTENT MATCH — the prose sentence's content-word sequence must EQUAL that ONE claim's whole content-word sequence (order preserved; the droppable-word set is EMPTY, so this is a VERBATIM content match modulo case/punctuation/whitespace), its numbers placed in that claim's own clause. This gate is EXTRACTIVE and sound with NO word classification at all; its ATOMIC-CLAIM PRECONDITION is that a claim is a single-fact assertion. A COMPOUND claim is DETERMINISTICALLY atomized into its sub-propositions (reusing the same clause boundaries the number gate uses), so prose that faithfully restates ONE fact of a compound claim GROUNDS by full-matching that fact's sub-clause, while a full restatement still matches the whole. Paraphrastic restatement (a non-verbatim rewrite of a fact) still OVER-ABSTAINS by design (delegated to the LLM as INFERRED, never GROUNDED). Licensing never pools across claims, so an ORPHAN assertion (uncited, novel, a non-matching partial restatement, or a re-contextualized number) is a coverage break, never allowed. An additive, in-memory :class:~zettelkasten.synapse.memdsl.schema.ProseCitationbinding supplies the per-assertion→claim link; absent it (the production path today, where the wire@answercarries no prose binding) the gate falls back to the strictest whole-prose-stream check. -
Coverage-gated abstention — when grounding or coverage breaks, the router emits a FIRST-CLASS :class:
~zettelkasten.synapse.memdsl.schema.Abstention(the contract type, reused rather than reinvented) carrying reason tokens from :data:~zettelkasten.synapse.memdsl.schema.ABSTAIN_REASONS(missing-hop/stale/false-premise/low-coverage). Coverage is read from the deterministic :mod:zettelkasten.coverageprimitives'blocking_count/_coverage_matrixshapes (accepted as an injected report so the router stays pure and store-free). -
A closed-book-vs-open-book confabulation hook (:data:
ConfabulationDetector) — a real, injected, CALLED seam that flags when an answer could be produced without the retrieved substrate (a confabulation signal). The v1 default is deliberately simple; a fuller closed-book probe slots in without a signature change.
Design invariants, mirrored from the epistemics layer:
- Grounding is structural, never the LLM's say-so. The router NEVER trusts an
agent-asserted status: it consumes :func:
~zettelkasten.synapse.epistemics.check_honestyverdicts and floors any agent upgrade to the deterministically-derived status. - Claim decomposition is contract-side. The reasoning-DAG is the decomposition — there is no post-hoc claim extractor. Imperfect atomicity is SAFE by construction: a weaker/mixed conjunct lowers the propagated status (weakest-conjunct → lower status → over-abstain), and can never fabricate.
- Pure and deterministic. Verifier callables, the confabulation detector, and the coverage report are all injected, so the router runs in tests with no LLM, no git, and no live stores. Output ordering is deterministic.
VerifierStatus ¶
Bases: str, Enum
The normalized outcome every wrapped verifier maps its native return into.
OK— the verifier confirms the claim's grounding (the tether/quote/ recomputation/clause holds). Contributes a VERIFIED status.FAILED— the verifier DEFINITIVELY refutes the grounding (a fabricated quote absent from the source, a data value that did not reproduce, invalid LaTeX, an excluded decision-tree clause). This is a FALSE PREMISE.UNVERIFIABLE— the verifier cannot decide here (source text unavailable, derivation not registered in this environment, an unresolved clause, a missing figure snapshot). Treated CONSERVATIVELY — it never grounds, but it is not a definitive refutation either.
Source code in zettelkasten/synapse/grounding_router.py
VerifierResult
dataclass
¶
One verifier's outcome, normalized across every verifier-type.
status is the tri-state verdict; detail a short human note (usually the
native verifier's method/reason); verdict is the OPTIONAL status
contribution the verifier proposes for the substrate node it grounds — a
:class:~zettelkasten.synapse.epistemics.StatusVerdict the router feeds into
the epistemics propagation as the cited node's per-node status. When None
the router derives a conservative default from status (see
:func:verdict_for_status), so every verifier need not spell out a verdict.
method is the OPTIONAL machine-readable match method the native verifier
reported (e.g. verify_quote's "exact" | "fuzzy" | "annotation"). It is
purely ADDITIVE (default None) and never changes what verifies or grounds;
it exists so the router can decide licensing separately from grounding — an
"exact" needle or an "annotation" highlight is provably a substring of
the SOURCE and so may confer an extractive restatement license (see
:func:_confirmed_verifier_text). A fuzzy match still VERIFIES and GROUNDS
the node, but its accepted window may diverge from the supplied body, so it
confers NO licensing text.
confirmed_source_text is the OPTIONAL text the verifier actually PROVED is
present in the SOURCE — the matched span, NOT any caller-supplied body. For a
quote it is the reader-note-stripped, normalized needle that verify_quote
confirmed was a substring of the source (for an exact match), or the real
Zotero highlight span the body overlaps (for an annotation match); None
for fuzzy/absent. It is purely ADDITIVE (default None) and is the ONLY text
the restatement floor licenses off a verifier —
:func:_confirmed_verifier_text returns it (never the caller's payload body),
which structurally excludes reader-note tails and any other never-matched
caller text.
Source code in zettelkasten/synapse/grounding_router.py
VerifierRegistry
dataclass
¶
An extensible dispatch table mapping verifier-type → :class:Verifier.
New verifier-types (SQL-exec, temporal-freshness, …) are added with
:meth:register WITHOUT changing the normalized :class:VerifierResult
contract — that is the whole point of keying on a type string. Dispatch is
total and safe: an UNKNOWN verifier-type resolves to a conservative
UNVERIFIABLE result rather than raising, so a mis-typed grounding can
never crash a reasoning turn (it simply fails to ground).
Source code in zettelkasten/synapse/grounding_router.py
register ¶
get ¶
Return the verifier for verifier_type, or None if unregistered.
types ¶
verify ¶
verify(verifier_type: str, payload: Mapping[str, Any] | None) -> VerifierResult
Dispatch payload to the verifier for verifier_type.
An unregistered type yields a conservative UNVERIFIABLE result (the
grounding simply does not resolve) so dispatch never raises.
Source code in zettelkasten/synapse/grounding_router.py
GroundingSpec
dataclass
¶
How one cited substrate ref is grounded: a verifier-type + its payload.
The router runs registry.verify(verifier_type, payload) and feeds the
resulting per-node verdict into the epistemics propagation as the cited node's
status. payload is opaque to the router — its keys are the native
verifier's arguments (see the default wrappers in :func:default_registry).
Source code in zettelkasten/synapse/grounding_router.py
TraceabilityResult
dataclass
¶
The verdict of the citation-anchored prose↔DAG traceability gate.
ok is True when every non-trivial prose assertion is licensed by a
single cited verified claim (no orphan). orphans lists the offending
assertions — unlicensed clauses/spans, uncited:<word> tokens outside every
citation span, and number:<tok> fabricated / re-contextualized numbers —
deterministically ordered for a caller to surface.
Source code in zettelkasten/synapse/grounding_router.py
ConfabulationSignal
dataclass
¶
Whether an answer could be produced WITHOUT the retrieved substrate.
closed_book True means the answer is not anchored in any verified retrieved
ground — a confabulation risk (the model could have produced it from parametric
memory alone). detail is a short human note. Advisory in v1: reported
always, and it only tips a NON-verified conclusion further toward abstention;
it never overrides an otherwise-verified, grounded answer.
Source code in zettelkasten/synapse/grounding_router.py
GroundingContext
dataclass
¶
The bundle the confabulation detector reasons over.
Carries the DAG, the prose answer, the merged per-node verdicts, the re-derived per-claim verdicts, the verifier results, and the resolved conclusion id — everything a closed-book probe needs, so a richer detector can replace the v1 default without a signature change.
Source code in zettelkasten/synapse/grounding_router.py
RouterDecision
dataclass
¶
The first-class ANSWER-vs-ABSTAIN verdict the router owns.
verdict is :data:ANSWER or :data:ABSTAIN. On ANSWER, status is the
conclusion's deterministically-derived WIRE status and abstention is
None; on ABSTAIN, reasons carries the ordered reason tokens (a subset
of :data:ABSTAIN_REASONS) and abstention is the reused contract-type
:class:~zettelkasten.synapse.memdsl.schema.Abstention. The remaining fields
expose the full derivation (honesty report, per-node and per-claim
verdicts, verifier results, traceability, confabulation) for inspection.
outdated is the PRIMARY, inseparable committed-drift disclosure: it is
True when the conclusion's verdict is outdated (grounded as of a
superseded pin — the file has since changed). It is carried on the decision
itself so a consumer of an ANSWER can never present an outdated-grounded
conclusion as fresh-grounded; it is NOT an abstain reason (an outdated
conclusion still ANSWERS).
Source code in zettelkasten/synapse/grounding_router.py
verdict_for_status ¶
verdict_for_status(status: VerifierStatus) -> StatusVerdict
The conservative default :class:StatusVerdict for a normalized status.
OK → grounded (a plain verified tier; a verifier that means
measured — e.g. data recomputation — attaches its own richer verdict);
FAILED → inferred (the ground did not hold); UNVERIFIABLE →
inferred + unresolved taint (cannot confirm, never clean).
Source code in zettelkasten/synapse/grounding_router.py
normalize_quote_result ¶
normalize_quote_result(res: Mapping[str, Any]) -> VerifierResult
Normalize :func:zettelkasten.grounding.verify_quote's return.
Native shape: {verified, method, score, page, near_miss}. A verified quote
is grounded; a quote that is simply not present in the source
(method == "none") is a FALSE PREMISE (a fabricated/mis-remembered quote);
an unavailable source (method == "unavailable") is UNVERIFIABLE (cannot
check here, never a refutation).
The native method ("exact" | "fuzzy" | "annotation" | ...) is carried
verbatim onto :attr:VerifierResult.method — an ADDITIVE surface that leaves
what verifies/grounds unchanged but lets the restatement floor gate licensing
on a source-derived match (see :func:_confirmed_verifier_text). The native
confirmed_source_text (the source-matched span, populated for an exact
needle or an annotation highlight, None for fuzzy) is likewise carried
onto :attr:VerifierResult.confirmed_source_text — the only text the floor may
license off this verifier, never the caller body.
Source code in zettelkasten/synapse/grounding_router.py
normalize_data_result ¶
normalize_data_result(res: Mapping[str, Any]) -> VerifierResult
Normalize :func:zettelkasten.data_grounding.verify_data_grounding's return.
Native shape carries a severity tier: ok (clean reproduce) →
measured; hard (a violation angelo could re-run and it disagreed) →
FALSE PREMISE; soft (cannot re-run here) → UNVERIFIABLE (retains its
prior stamp, never a refutation) — exactly the split
:func:zettelkasten.coverage._data_claim_grounded treats as blocking vs not.
Source code in zettelkasten/synapse/grounding_router.py
normalize_equation_result ¶
normalize_equation_result(res: Mapping[str, Any]) -> VerifierResult
Normalize :func:zettelkasten.grounding.verify_equation's return.
Native shape: {valid, method, reason, page, locator}. Render-valid LaTeX
is grounded; structurally-broken LaTeX (unbalanced braces/environments,
empty, mojibake) is a FALSE PREMISE.
Source code in zettelkasten/synapse/grounding_router.py
normalize_figure_result ¶
normalize_figure_result(res: Mapping[str, Any]) -> VerifierResult
Normalize :func:zettelkasten.grounding.verify_figure's return.
Native shape: {grounded, method, reason, page, locator}. A resolvable
snapshot is grounded; a missing/unresolvable snapshot is UNVERIFIABLE
(per verify_figure's ADVISORY contract — a figure has no source text to
reproduce verbatim, so a snapshot miss lowers grounding but is NEVER a
refutation).
Source code in zettelkasten/synapse/grounding_router.py
normalize_decision_walk ¶
normalize_decision_walk(walk_result: Mapping[str, Any], target: str) -> VerifierResult
Normalize a :func:zettelkasten.decision_tree.walk bundle for one node.
REUSES the walk's per-node applies / excluded / unresolved
classification (never re-evaluating a predicate), mirroring
:func:zettelkasten.synapse.epistemics.resolver_from_walk. target may be a
namespaced graph::id uid or a bare id. An applies clause is
grounded; an excluded clause is a FALSE PREMISE (the clause
definitively does not hold); an unresolved or unreached node is
UNVERIFIABLE.
Source code in zettelkasten/synapse/grounding_router.py
default_registry ¶
default_registry() -> VerifierRegistry
Build the v1 registry wiring the five shipped verifier-types.
Each wrapper lazily imports its target so importing this module stays cheap and store-free; the payload keys are the native verifier's own arguments:
quote→ :func:zettelkasten.grounding.verify_quote(body,fulltext,annotations, optionalfuzzy_floor);expr-recompute→ :func:zettelkasten.data_grounding.verify_data_grounding(note,graph_dir);equation→ :func:zettelkasten.grounding.verify_equation(body, optionalpage/fulltext/context);figure→ :func:zettelkasten.grounding.verify_figure(snapshot, optionalpage/fulltext/caption);decision_tree→ :func:zettelkasten.decision_tree.walk(a pre-computedwalkbundle + atargetnode id, orgraph/params/ injectedget_graph/load_projectto compute one).
Source code in zettelkasten/synapse/grounding_router.py
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | |
dag_vocabulary ¶
dag_vocabulary(dag: ReasoningDAG) -> set[str]
dag_numbers ¶
dag_numbers(dag: ReasoningDAG) -> set[str]
check_traceability ¶
check_traceability(prose: str, dag: ReasoningDAG, *, floor: float = DEFAULT_TRACE_FLOOR, extra_vocab: Sequence[str] = (), claim_verdicts: Mapping[str, StatusVerdict] | None = None, conclusion: str | None = None) -> TraceabilityResult
Gate the prose answer by POSITIVE per-assertion citation-anchoring.
Every non-trivial prose assertion must be licensed by a SINGLE cited verified
claim under a FULL-CONTENT MATCH: its content-word sequence must EQUAL that ONE
claim's whole content-word sequence — or the whole sequence of one of that claim's
ATOMIC SUB-PROPOSITIONS (:func:_claim_source_clauses) — order preserved; the
droppable-word set is EMPTY, so this is a VERBATIM content match modulo
case/punctuation/whitespace (no leading/trailing/interior content-word drop); its
content tokens all lie in that ONE claim's closed vocabulary (novelty is BINARY —
any novel content word disqualifies the assertion, there is no untraced-word
budget), and every number it carries must sit in that claim's OWN clause beside the
words that accompanied it there. The gate is EXTRACTIVE and its ATOMIC-CLAIM
PRECONDITION is that a claim is a single-fact assertion or a CONJUNCTION of them: a
COMPOUND claim is deterministically atomized, so a faithful restatement of ONE of
its facts GROUNDS while a connective-drop / conjunct-reorder / non-verbatim
paraphrase over-abstains (delegated to the LLM as INFERRED). Licensing never
pools across claims, which is what structurally ends the N-word whack-a-mole
the old coverage heuristic could not escape.
The set of claims that may license prose is chosen exactly as before: with
claim_verdicts (the answer-gate path) it is
:func:_verified_closure_claims — claims whose RE-DERIVED wire status is
VERIFIED and that lie in the conclusion's transitive premise closure, so an
inferred / assumption / tainted claim's text and numbers can never license
prose; without verdicts (a bare structural check) it is every claim.
extra_vocab (e.g. cited node titles) augments every candidate claim's
vocabulary.
Two modes share the per-claim licensing core (WHOLE-RUN SINGLE-CLAIM FULL-CONTENT MATCH):
- CITATION mode (
dag.prose_citationspresent) — the prose is partitioned into SENTENCES on strong terminators (a comma is not a break) and each SENTENCE's whole content run must FULL-MATCH a SINGLE claim cited by one of that sentence's spans (so a sentence cannot span two claims); any prose token outside every span is an UNCITED orphan (:func:_cited_orphans). Spans index intoproseas passed. - FALLBACK mode (no binding — the production path today, where the wire
@answercarries no prose binding and callers passprose='') — the WHOLE reconstructed prose content-word stream must FULL-MATCH SOME single verified in-closure claim (:func:_fallback_orphans); a genuine multi-claim un-cited synthesis intentionally OVER-ABSTAINS.
floor is retained for signature stability only (the gate is a binary
per-assertion novelty + full-content-match check, not a ratio). Truly EMPTY prose
(whitespace only) trivially passes — there is no assertion to ground; but
propositionally-EMPTY prose (non-empty text with zero content words and zero
numbers — e.g. a punctuation-only sentence) is an orphan, never a vacuous GROUND.
When no claim FULL-MATCHes prose, every non-trivial assertion is an orphan.
Source code in zettelkasten/synapse/grounding_router.py
1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 | |
default_confabulation_detector ¶
default_confabulation_detector(ctx: GroundingContext) -> ConfabulationSignal
The v1 closed-book detector: is the answer anchored in retrieved ground?
Flags closed_book when NEITHER any substrate node cited in the conclusion's
premise closure resolves to a VERIFIED wire status, NOR any verifier confirmed
a grounding OK. In that case nothing retrieved actually grounds the answer, so
it could have been produced closed-book — a confabulation signal. This is a
genuine, CALLED interface; a fuller closed-book/open-book contrast probe (run
the model without the substrate and compare) slots in here later.
Source code in zettelkasten/synapse/grounding_router.py
route ¶
route(dag: ReasoningDAG, *, prose: str = '', node_bodies: Mapping[str, str] | None = None, node_status: Mapping[str, StatusVerdict] | None = None, groundings: Mapping[str, GroundingSpec] | None = None, registry: VerifierRegistry | None = None, asserted: Mapping[str, EpistemicStatus] | None = None, edges: Sequence[Edge] = (), coverage_report: Mapping[str, Any] | None = None, confabulation: ConfabulationDetector | None = None, trace_floor: float = DEFAULT_TRACE_FLOOR, derivation_available: Callable[[Any], bool] | None = None, validate: bool = True) -> RouterDecision
Route a cited reasoning-DAG to a first-class ANSWER or ABSTAIN verdict.
The router NEVER trusts the LLM's say-so: it derives every claim's status deterministically from the substrate and the DAG's structure, and only answers when that derivation grounds the conclusion and coverage holds.
Pipeline (deterministic, no LLM / IO of its own):
- Ground each cited ref. For every ref in
groundings, dispatch its :class:GroundingSpecto theregistry(default :func:default_registry) and take the resulting per-node :class:StatusVerdict. An explicitnode_status(e.g. tether verdicts from :func:~zettelkasten.synapse.epistemics.apply_node_statuses) OVERRIDES a verifier verdict for the same ref — EXCEPT it can never UPGRADE a ref whose verifier returnedUNVERIFIABLEorFAILED(those stay capped at the conservative verifier verdict, so an unchecked/refuted ground cannot be laundered into a grounded answer by an injected status). - Re-derive + honesty. Run :func:
~zettelkasten.synapse.epistemics.check_honestyover the merged node statuses, which validates the DAG, re-derives every claim's status (weakest-conjunct propagation → imperfect atomicity over-abstains, never fabricates), and REJECTS any agent-asserted upgrade inasserted. Then ALWAYS (a missingnode_bodiesis treated as an empty mapping — the public contract is FAIL-CLOSED, not fail-open), floor any RESTATEMENT claim whose text is not EXTRACTIVELY LICENSED by an authoritative source of a cited ref (:func:_floor_drifted_restatements) — a drifted restatement of a grounded node can never remain grounded (closes F4). The floor is FAIL-CLOSED and CONFIRMED-TEXT LICENSED: a restatement stays grounded only when its text is licensed by a NON-EMPTY cited node body OR by the verifier-CONFIRMED text of a ref whose verifier returned OK (a partial quote whose confirmed text licenses the claim). A verifier OK alone is NOT a blanket exemption — it validated the payload, not the claim text — so a fabricated restatement citing a verifier-OK node whose confirmed text does not license it is floored. The floor is RESTATEMENT-only; deduction/inference TEXT is not checked here (grounded-by-design). The honesty report is then rebound to the floored verdicts (FIX B) so an asserted upgrade above a floored status is a violation. - Traceability. Gate the
proseanswer against the DAG (:func:check_traceability); an orphan assertion is a coverage break. - Confabulation. Call the
confabulationdetector (default :func:default_confabulation_detector) — a closed-book signal on a non-verified conclusion tips toward abstention. - Decide. Collect reason tokens (
missing-hopfor a cited ref with no resolved verdict,stalewhen the conclusion rests on a drifted tether,false-premisewhen a verifier definitively refuted a ground,low-coveragefor a traceability/coverage break, a contested/unresolved conclusion, or an otherwise-unverified conclusion). ANSWER (status=verified) iff there are no reasons, honesty holds, traceability holds, and the conclusion's wire status is verified. A genuinely INFERRED conclusion that PROVABLY RESTS ON ≥1 verified premise CLAIM in its transitive premise closure (followingClaim.premises), is honest, traceable, coverage-clean, and has no structural break (missing-hop / stale / false-premise / contested / unresolved) is instead returned as a HEDGED ANSWER (verdict=ANSWER, status=inferred) rather than alow-coverageabstain — an honestly-labeled extrapolation off a verified anchor, never a fabrication (see the guards at the decision site). A conclusion that merely cites a grounded node without deriving from a verified premise claim, or rests on a bare/floored restatement, ABSTAINS. Otherwise ABSTAIN with the ordered reasons and a first-class :class:Abstention.
node_bodies maps a cited ref (substrate token or envelope ref) to the RAW,
un-elided node body used by the restatement floor; None (the default) is treated
IDENTICALLY to an empty mapping {} — the floor still runs FAIL-CLOSED, so a
RESTATEMENT claim with no licensing body floors to INFERRED rather than grounding off
node status alone. A DAG with no RESTATEMENT claims is unaffected (nothing to floor).
Returns a :class:RouterDecision.
Source code in zettelkasten/synapse/grounding_router.py
2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 | |