Skip to content

fix(wiki): refining a draft leaves its confidence as the claims set it (#578) - #580

Merged
cdeust merged 1 commit into
mainfrom
fix/refine-keeps-claim-confidence-578
Sep 15, 2026
Merged

cdeust merged 1 commit into
mainfrom
fix/refine-keeps-claim-confidence-578

Conversation

@cdeust

@cdeust cdeust commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Summary

handler_refine (mcp_server/handlers/wiki_refine.py) wrote a fixed 0.85 into every refined draft and into its refined_llm memo. The synthesizer sets a draft's confidence to the mean of its source claims (0.4 when none carries one), and the curator approves only from MIN_CONFIDENCE_APPROVE = 0.6, so a prose rewrite put any draft past the approve threshold whatever its evidence. The handler no longer passes a confidence to update_draft, and the memo records the draft's own confidence. Rationale in ADR-1065 (wiki/adr/cortex/1065-…, mirrored in docs/adr/).

The handler is not registered as an MCP tool, so the overwrite was latent: the maintainer's PostgreSQL store holds a single refined_llm memo, from a test_runner_v1 run on 2026-04-14 (read-only query, 2026-09-16).

Closes #578

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Test plan

  • New tests_py/handlers/test_wiki_refine_confidence.py, SQLite backend through the handlers' own composition root. It synthesizes a draft, sets it halfway between the hold and approve thresholds, refines its lead, then asserts:
    • the draft's confidence is unchanged;
    • the refined_llm memo carries that confidence;
    • evaluate_draft still reports "below approve threshold" and does not approve it.
  • On the previous handler all three fail (assert 0.85 == 0.5 for the first two); on this branch all three pass.
  • pytest tests_py/handlers/ tests_py/core/ -k "wiki or draft or curat or adr or manifest": 723 passed.
  • Tests that read wiki/manifest.json or docs/adr: 207 passed.
  • ruff check, ruff format --check: clean. scripts/check_craftsmanship.py --base origin/main: OK. pyright mcp_server/ in the CI typecheck environment built from uv.lock: 0 errors.
  • Mutation note: any constant confidence written by the handler fails these tests unless it equals the 0.5 the tests use.

Audit notes

Coding-standards compliance

Breaking changes

None through the MCP surface, which does not expose the handler. A direct caller of handler_refine no longer sees the draft's confidence set to 0.85.

Reviewer checklist

  • CHANGELOG.md updated under Unreleased / Fixed.
  • Documentation updated (ADR-1065, tool schema description).
  • No secrets or PII in the diff.
  • CI passes on the latest commit.

🤖 Generated with Claude Code

#578)

handler_refine wrote a fixed 0.85 into every refined draft and its
refined_llm memo. The synthesizer sets a draft's confidence to the mean
of its source claims, and the curator approves only from 0.6, so a
prose rewrite put any draft past the approve threshold. The handler no
longer passes a confidence to update_draft, and the memo records the
draft's own confidence (ADR-1065).

The handler is not registered as an MCP tool; the maintainer's store
holds one refined_llm memo, from a 2026-04-14 test runner. The three
new tests fail on the previous handler and pass on this one.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@cdeust

cdeust commented Sep 15, 2026

Copy link
Copy Markdown
Owner Author

ZETETIC-REVIEW: APPROVE

Summary

handler_refine (mcp_server/handlers/wiki_refine.py:297-317) no longer passes a fixed confidence=0.85 to update_draft, and the refined_llm memo now records float(draft["confidence"]) (the draft's own confidence, read before the update) instead of the hardcoded 0.85. Correct fix for #578, well-scoped, well-tested, well-documented.

Stakes classification (Move 7)

Medium: core curation-gate logic (affects evaluate_draft's threshold check), not auth/billing/crypto/schema/concurrency. Diff is small (3 lines in the handler; rest is tests, ADR, changelog, manifest) and single-author. Moves 1-4 applied in full; 5-6 at the touched call site.

Evidence gathered

1. Correctness of the fix

  • update_draft(conn, draft_id, confidence=None, ...) (mcp_server/infrastructure/pg_store_wiki_drafts.py:98,122-124): confidence is only added to the SQL SET list if confidence is not None. Passing no confidence argument leaves the column untouched: verified for the shared code path used by both the SQLite and PostgreSQL stores (same function, %s/%(name)s placeholder handled elsewhere).
  • wiki.drafts.confidence is NOT NULL DEFAULT 0.5 on both backends (mcp_server/infrastructure/sqlite_schema_wiki.py:76, mcp_server/infrastructure/pg_schema.py:231) with a CHECK on the drafts table in the sqlite/pg draft-list projection (lines 29-30/187-188 apply to a different table but the drafts table itself is NOT NULL-constrained): draft["confidence"] can never be None at the float(draft["confidence"]) call site (wiki_refine.py:315), so the cast is safe. Confirmed the 0.4 synthesizer fallback: mcp_server/core/draft_synthesizer.py:277 (draft_confidence = sum(confidences) / len(confidences) if confidences else 0.4).
  • Ordering: draft is fetched via get_draft before update_draft runs (wiki_refine.py:266), so draft["confidence"] used for the memo (line 315) is the pre-update value: correct here only because update_draft no longer touches confidence at all. Minor readability nit below.

2. Test strength (reproduced myself, not assumed)

  • Ran UV_NO_SYNC=1 uv run pytest tests_py/handlers/test_wiki_refine_confidence.py -v on the PR branch: all 3 pass.
  • Swapped in the pre-fix handler (git show origin/main:mcp_server/handlers/wiki_refine.py > mcp_server/handlers/wiki_refine.py) and re-ran: all 3 fail: test_refine_keeps_the_draft_confidence and test_refine_memo_records_the_draft_confidence both assert 0.85 == 0.5 ± 5.0e-07; test_a_refined_weak_draft_still_misses_the_approve_threshold fails with AssertionError: assert 'approved' != 'approved'. Matches the PR's stated fail-before/pass-after claim exactly.
  • Restored with git checkout HEAD -- mcp_server/handlers/wiki_refine.py; git status confirmed clean, nothing left behind.
  • Tests are deterministic and independent: each uses a fresh tmp_path SQLite DB via monkeypatch.setenv + reset_shared_store()/get_memory_settings.cache_clear() in a function-scoped fixture: no shared state between tests, no network, no time-of-day dependence.
  • handler_refine has no callers anywhere in the tree outside this new test file (grep -rn "handler_refine\b"), confirming the PR's "not registered as an MCP tool" claim is not just documentation: it is literally true of the whole call graph.

3. Accuracy of factual claims

  • git log -S wiki_refine --oneline -- mcp_server/tool_registry_wiki.py 'mcp_server/tool_registry*.py' returns no commits, and grep -rn "wiki_refine_draft\|wiki_get_draft" mcp_server/tool_registry*.py returns nothing: the "never registered" claim in the ADR, PR body, and issue comment is verified, not asserted.
  • Thresholds MIN_CONFIDENCE_APPROVE = 0.6 / MIN_CONFIDENCE_HOLD = 0.4 (mcp_server/core/draft_curator.py:15-16) match every reference to them in the ADR/issue/PR body.
  • 0.4 synthesizer fallback confirmed at draft_synthesizer.py:277 as cited.
  • ADR wiki page and its docs/adr/ mirror are byte-identical in content (diffed both), and wiki/manifest.json gained the matching ADR-1065 entry.

4. Project gates (run from the worktree, UV_NO_SYNC=1)

  • uv run ruff check: "All checks passed!"
  • uv run ruff format --check: "1455 files already formatted"
  • uv run python scripts/check_craftsmanship.py --base origin/main: "Craftsmanship gate: OK"
  • uv run pytest tests_py/handlers/ tests_py/core/ -k "wiki or draft or curat or adr or manifest": 723 passed, 4471 deselected: matches the PR body's number exactly.

Move 0: ledger/seen-defect check

No completion ledger is embedded (not required by this project's PR template) and no seen-defect rationalization pattern ("unrelated," "pre-existing," "out of scope" without a filed issue) appears in the diff, PR body, or issue thread. The one open item: Path B unregistered: is explicitly deferred to a filed issue (#579), which is exactly the acceptable form under §14.3. No refusal triggered.

Layer check (Move 1)

Single file changed in mcp_server/handlers/; no new imports added or changed. No boundary crossed.

SOLID (Move 2)

No new branching, no interface change, no new type dependency. N/A: this is a value-passing removal, not a structural change.

Wiring & contract drift (Move 3)

No new public symbols. No signature change to handler_refine (still (args) -> dict). update_draft's confidence parameter was already optional (confidence: float | None = None) before this PR: its removal from this call site is exactly the change described, not a drift.

Complexity (Move 5)

No smell: a one-line removal and a one-argument value change.

Security & hygiene (Move 6)

No security-relevant surface touched. Commit message is conventional (fix(wiki): ..., references #578).

Non-blocking nit

  • mcp_server/handlers/wiki_refine.py:266,315: draft is captured before update_draft runs, and the memo's confidence is read off that stale-by-construction snapshot. It happens to be correct only because update_draft no longer writes confidence at all; a future change that re-adds a confidence write to update_draft in this call would silently desync the memo from the stored value. Consider re-fetching the draft (or reading update_draft's return value, if it carries one) after the update, or a one-line comment noting the invariant this relies on.

Verdict

APPROVE. Fix is correct on both backends, the fail-before/pass-after claim reproduces exactly, tests are deterministic and independent, all cited facts (thresholds, "never registered," 0.4 fallback) verified against the code and git history, and all four project gates are green.

@cdeust
cdeust merged commit c848d51 into main Sep 15, 2026
28 checks passed
@cdeust
cdeust deleted the fix/refine-keeps-claim-confidence-578 branch September 15, 2026 22:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] wiki_refine_draft sets every refined draft's confidence to 0.85, lifting it past the curator's approve threshold

1 participant