Files
gbrain/test
Garry TanandClaude Opus 4.7 0fdcc54dde cycle: propose_takes phase + take_proposals queue write path (T3)
LLM-based take extraction from markdown prose. Walks pages updated since
last cycle, sends each page's body to a tuned extractor, writes the
extracted gradeable claims to the take_proposals queue. User accepts /
rejects via `gbrain takes propose --review` (lands in Lane C).

Cycle wiring:
  lint → backlinks → sync → synthesize → extract → extract_facts →
    resolve_symbol_edges → patterns → recompute_emotional_weight →
    consolidate → propose_takes (NEW) → grade_takes (NEW; T4) →
    calibration_profile (NEW; T6) → embed → orphans → purge

CyclePhase enum extended with 3 new entries; ALL_PHASES + NEEDS_LOCK_PHASES
updated. All three new phases acquire the cycle lock (writes to
take_proposals / take_grade_cache / calibration_profiles).

Idempotency contract:
  The (source_id, page_slug, content_hash, prompt_version) composite unique
  index on take_proposals means an unchanged page never re-spends LLM
  tokens. Bumping PROPOSE_TAKES_PROMPT_VERSION cleanly invalidates the
  cache so a tuned prompt re-runs proposals on every page. Mirrors the
  v0.23 dream_verdicts pattern.

F2 fence dedup:
  The phase reads the page's existing `<!-- gbrain:takes:begin -->` fence
  (when present) and passes the canonical take rows to the extractor as
  "things you have already captured." Prevents duplicate proposals when
  prose is appended to a page that already has takes. Records the fence
  rows the LLM was told to dedupe against on the take_proposals row for
  audit (dedup_against_fence_rows JSONB).

Auto-resolve posture:
  propose_takes only WRITES proposals to the queue. Nothing in this phase
  mutates the canonical takes table. Operator opt-in via the queue review
  CLI (Lane C) is the only path from queue to canonical fence (D17).

Prompt tuning status (v0.36.0.0 ship state):
  The default extractor prompt is annotated `v0.36.0.0-stub`. The real
  tuned prompt arrives via T19 synthetic corpus build (50 anonymized
  pages, 3-model parallel extraction, user reviews disagreement set,
  F1 ≥ 0.85 on training corpus + F1 ≥ 0.8 on ground-truth holdout).
  Until T19 lands, propose_takes runs but produces best-effort candidates
  the user reviews manually.

Architecture:
  ProposeTakesPhase extends BaseCyclePhase (T2). Inherits source-scope
  threading via scope(), budget metering via this.checkBudget(), error
  envelope wrapping. budgetUsdKey: cycle.propose_takes.budget_usd
  (default $5/cycle). Budget exhaustion mid-page returns status='warn'
  with details.budget_exhausted=true — clean partial-completion semantics.

  Test seam: opts.extractor injection so the phase can run hermetically
  without touching the gateway. defaultExtractor (production path) calls
  gateway.chat with the EXTRACT_TAKES_PROMPT and parses the JSON array
  output via parseExtractorOutput.

  parseExtractorOutput defends against common LLM output sins: markdown
  code fence wrapping, leading prose, single-object instead of array,
  unknown kind values, weight out of [0,1], rows missing claim_text or
  exceeding 500 chars.

Tests: 25 cases in test/propose-takes.test.ts cover the 4 pure helpers
(parseExtractorOutput, contentHash, hasCompleteFence,
extractExistingTakesForDedup) + 7 phase integration scenarios (happy path,
cache hit, fence dedup, extractor failure, empty pages, skipPagesWithFence,
proposal_run_id stability).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 16:03:42 -07:00
..