core/core
Shay 1573064349
refactor(contemplation): converge to shared discovery-sink plumbing (#58)
Connects ADR-0080's read-only contemplation loop to the existing
teaching-pipeline plumbing without forcing a type collapse.  The
SPECULATIVE-only invariant from #55 is preserved verbatim; what
changes is *where the findings flow*.

What was wrong with the prior shape
-----------------------------------
PR #55 shipped a parallel core/contemplation/ package whose findings
were written as one JSON blob per CLI invocation, with no consumer.
The SPECULATIVE-only invariant protected a write path that didn't
exist.  My closed PR #56 (second miner) would have entrenched the
duplication.

What this PR changes
--------------------
1. Schema (core/contemplation/schema.py)
   - Adds a BOUNDARY note documenting why EvidencePointer (teaching)
     and ContemplationEvidenceRef (core) intentionally stay separate:
     EvidencePointer.source is constrained to {corpus, pack,
     vault_coherent} — pointers into reviewed in-process memory the
     runtime trusts.  ContemplationEvidenceRef points to external
     report files that have NOT been reviewed.  Converging them would
     either widen the runtime-grounding enum (losing the "reviewed
     memory only" guarantee) or force benchmark reports to masquerade
     as vault_coherent.  Both are worse than keeping them separate.
   - Adds format_contemplation_finding_jsonl(finding) — the canonical
     JSONL formatter mirroring teaching.discovery.format_candidate_jsonl.

2. Runner (core/contemplation/runner.py)
   - Both runners gain an optional sink: DiscoveryCandidateSink | None
     parameter.  When supplied, each finding is emitted as one
     canonical JSONL line via the SHARED protocol — same protocol
     that backs DiscoveryBufferSink and DiscoveryMonthlyFileSink.
   - Sink path is additive: the ContemplationRun blob is byte-identical
     whether or not a sink is supplied (pinned by test).
   - No sink supplied → existing in-memory behavior preserved exactly.

3. CLI (core/contemplation/__main__.py)
   - Adds --lane {frontier_compare, contradiction_detection} flag.
     Default unchanged.
   - Adds --sink-root <path> flag.  When set, instantiates a
     DiscoveryMonthlyFileSink and findings land at
     <root>/<YYYY>/<YYYY-MM>.jsonl — the SAME layout discovery
     candidates use, so operators can grep one stream.

4. Miner (core/contemplation/miners/contradiction_detection.py)
   - Restored from closed PR #56 under the unified pipeline.
   - Failure-mode split preserved (missed_contradiction /
     false_contradiction_flag) with asymmetric repair actions.

What this PR does NOT do
------------------------
- Does NOT unify ContemplationFinding with DiscoveryCandidate.
  DiscoveryCandidate.trigger is Literal[would_have_grounded,
  successful_comparison, hedge_acknowledged, oov_resolved_via_decomp]
  — all turn-loop flavored.  None describe "I parsed a benchmark
  report."  Forcing a 5th trigger that no turn-loop extractor
  produces would pollute the turn-loop type for the schema's sake.
- Does NOT extend teaching/gaps.py.  Gap aggregates DiscoveryCandidate
  cells by (subject, intent) — domain nouns.  ContemplationFinding
  subjects are namespaced ("contradiction_detection/CON-PUB-002").
  Different operator views.  A sibling aggregator can come later
  when an operator actually asks for it.

Why this is the right unification point
---------------------------------------
The honest convergence is at the *sink* (so all SPECULATIVE evidence
lives in one rooted append-only stream), not the *aggregator* (which
appropriately produces typed views per evidence family).  The boundary
doctrine from #55 is preserved; it now connects to existing plumbing
instead of writing JSON to disk with no consumer.

Tests (tests/test_contemplation_pipeline_convergence.py, 10 cases)
------------------------------------------------------------------
- DiscoveryBufferSink satisfies DiscoveryCandidateSink (shared protocol)
- frontier runner emits findings to shared sink
- contradiction runner emits findings to shared sink
- sink is optional — no-op when absent
- emission is canonical JSONL (sorted keys, no newline, deterministic)
- DiscoveryMonthlyFileSink persists findings at <root>/<YYYY>/<YYYY-MM>.jsonl
- sink emission does not alter the ContemplationRun blob (additive)
- contradiction miner predicate split + repair-action asymmetry
- config_hash differs between lanes (replay can distinguish)
- BOUNDARY doc is present in schema.py (regression guard)
- ContemplationEvidenceRef field invariants
- format_contemplation_finding_jsonl is deterministic + canonical

All 18 tests pass (5 original ADR-0080 + 13 new convergence).

Live evidence
-------------
$ uv run python -m core.contemplation \
    evals/contradiction_detection/results/v1_public_*.json \
    --lane contradiction_detection \
    --sink-root /tmp/sink_demo

  /tmp/sink_demo/2026/2026-05.jsonl  ← same layout as discovery candidates

  predicate=missed_contradiction         subject=contradiction_detection/CON-PUB-002
  predicate=missed_contradiction         subject=contradiction_detection/CON-PUB-004
  predicate=false_contradiction_flag     subject=contradiction_detection/CON-PUB-005
  predicate=false_contradiction_flag     subject=contradiction_detection/CON-PUB-006
2026-05-20 12:32:53 -07:00
..
cognition
contemplation refactor(contemplation): converge to shared discovery-sink plumbing (#58) 2026-05-20 12:32:53 -07:00
physics
__init__.py
_safe_display.py
cli.py
config.py