Skip to content

audit: close the second-pass findings — replica refresh debt, migration float equality, and five sibling asymmetries - #523

Merged
t0k0sh1 merged 2 commits into
mainfrom
522-second-pass-fixes
Aug 9, 2026
Merged

audit: close the second-pass findings — replica refresh debt, migration float equality, and five sibling asymmetries#523
t0k0sh1 merged 2 commits into
mainfrom
522-second-pass-fixes

Conversation

@t0k0sh1

@t0k0sh1 t0k0sh1 commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

2周目の重点監査(#522)で見つかった7件の修正。各修正にミュータントを殺すテスト付き。

  • replica: tailerにpending_refresh(refresh債務)をポーリング横断で持たせた。tailerのhydration失敗後にクライアント読みが先にstemを完了させると、retargetは二度とstaleと報告せずreplica_refreshが永続スキップされ、メタデータ(pinned/description/revision)だけ凍結していた — 債務は次のポーリングで必ず支払われる
  • replication: replica_refreshが両WALゲージを再stat。replicaではバイトがtailerのファイルコピーで届くため、cold・unpinnedなコンテキストのtaguru_wal_bytesが無期限に過小報告だった
  • context/image: pre-v5移行のsourceless call検出をf64完全一致から一次丸め誤差境界(summation_gap_is_realとして切り出し、境界値を厳密にピン)に変更 — 加算順序の再結合ノイズがretract_sourceで消せないphantom attributionを生まなくなる
  • context/consolidation: ObjectRow.sourcesがzero-sumレコードを除外(sign_conflictsと同じ姿勢) — 自己相殺したソースがLLM判定の証拠として並ばない
  • api/consolidation: checksに兄弟リスト入力と同じoverlong()上限(dedupは連続重複しか畳まないため交互リストが素通りだった)
  • extract: --source-idの空チェックを--tag同様trim付きに
  • mcp/schema: search_passages/explain_searchsince/untilに兄弟ツール同様minimum: 0

1周目(#520)からの継続で、tombstoneフェンス契約・ship世代フェンシング・hydrator状態機械・route mapホットリロード・breakerは網羅追跡の結果、指摘ゼロでした。

Closes #522

Test plan

  • cargo fmt / cargo clippy --all-targets(警告ゼロ)
  • cargo test フルスイート 3219 件パス(失敗 0)— replica競合はフォールト注入テスト(torn-segment方式)で再現・検証
  • diff スコープ mutation gate: 初回 50 mutants(5 missed)→ 許容誤差式をヘルパーに切り出し境界値テストでピン → --iterate 再検証で 35/35 caught、missed 0

https://claude.ai/code/session_011NozdDS9JqgCpi9Z3wo4Pd

Summary by CodeRabbit

  • バグ修正

    • 検索・統合リクエストの入力上限を確実に検証し、超過時に適切なエラーを返すよう改善しました。
    • 取り消し済みの出典が証拠情報に表示される問題を修正しました。
    • 画像移行時の丸め誤差による件数の不整合を修正しました。
    • レプリカ更新の失敗後も保留中の更新を再試行するよう改善しました。
    • レプリカのWALサイズ情報が実際のファイルサイズと一致するよう修正しました。
  • 入力検証

    • 空白のみの--source-idを無効として扱うよう変更しました。
    • 検索期間に負の値を指定できないよう制限しました。

float equality, and five sibling asymmetries

Findings from the focused second-pass review (issue #522), each with a
test that kills its mutants:

- replica: the tailer now carries a pending_refresh debt across polls —
  a stem whose hydration a per-request loader completed after the
  tailer's own failed attempt never turns stale again, yet its
  in-memory meta (pinned, description, revision bookkeeping) was never
  re-read; the remembered debt pays out on the next poll instead of
  freezing the meta until an unrelated manifest change.
- replication: replica_refresh re-stats both WAL gauges — on a replica
  the bytes arrive as tailed file copies, never through the writer's
  live accounting, so a cold unpinned context understated
  taguru_wal_bytes indefinitely.
- context/image: the pre-v5 migration's sourceless-call detection
  compares the two summation orders under a first-order rounding bound
  (extracted as summation_gap_is_real, boundary-pinned) instead of
  exact f64 equality — regrouping noise no longer credits a phantom
  attribution that retract_source can never remove.
- context/consolidation: contradiction-group ObjectRow.sources drops
  zero-sum records, the same posture sign_conflicts already takes — a
  source that cancelled its own assertion no longer reads as attesting
  evidence to the judge.
- api/consolidation: `checks` passes the same overlong() ceiling every
  sibling list input enforces (dedup folds only consecutive repeats,
  so an alternating list dodged both selector guards at any length the
  body cap admits).
- extract: --source-id trims its emptiness check like --tag's.
- mcp/schema: search_passages/explain_search declare `minimum: 0` on
  since/until like every sibling tool.

Closes #522

Claude-Session: https://claude.ai/code/session_011NozdDS9JqgCpi9Z3wo4Pd
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 172999fc-1ea4-41a0-804e-b0cf1e2add98

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

統合処理、旧形式属性移行、入力検証、WAL統計、レプリカ更新の7件の監査指摘を修正しました。各変更に回帰テストを追加しました。

Changes

監査指摘の修正

Layer / File(s) Summary
統合処理の入力と証拠の検証
src/api/consolidation.rs, src/context/consolidation.rs, tests/http_api/consolidation.rs
checks の重複排除前に入力数を検証します。正味ゼロの帰属ソースを証拠から除外します。各動作をテストします。
旧形式属性移行の丸め誤差処理
src/context/image.rs
属性チェーンの絶対値合計を追跡します。丸め誤差を超える差分だけを未出典寄与として扱います。境界値と移行結果をテストします。
入力値とMCPスキーマの検証
src/extract/args.rs, src/extract/tests.rs, src/mcp/schema.rs
空白だけの --source-id を拒否します。search_passagesexplain_searchsinceuntilminimum: 0 を追加します。
WALサイズ統計の更新
src/registry/replication.rs
replica_refresh がWALとpassages WALを再統計し、各バイトカウンターを実ファイルサイズへ更新します。
保留中のレプリカ更新負債
src/replica.rs
失敗したコンテキスト更新のstemを pending_refresh に保持します。後続ポーリングで再試行し、成功時に削除します。失敗後にメタデータ更新が完了する経路をテストします。

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • t0k0sh1/taguru#468: 統合APIと矛盾グループの関連ロジックを変更しています。
  • t0k0sh1/taguru#513: --source-id の検証処理を変更しています。
  • t0k0sh1/taguru#521: API入力コレクションに overlong() による上限検証を追加しています。
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルは、レプリカ更新負債、浮動小数点移行判定、および関連する5件の修正を明確に示しています。
Linked Issues check ✅ Passed Issue [#522] の7件の要件を、実装変更と回帰テストで対応しています。
Out of Scope Changes check ✅ Passed 変更はIssue [#522] の7件の監査指摘と対応テストに限定されており、明らかな範囲外変更はありません。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 522-second-pass-fixes

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/context/image.rs`:
- Around line 757-759: Update summation_gap_is_real and its migration callers to
conservatively account for cancellation among records from the same source,
rather than relying only on final LegacyAttributionRecord.weight-derived
magnitude. Avoid classifying an unexplainable gap as a sourceless call when the
legacy image lacks source-level addition history, and add a regression test
covering A:+1e16, B:+1.0, A:-1e16 followed by retracting A and B without leaving
a phantom count.

In `@src/registry/replication.rs`:
- Around line 114-119: Update the WAL size assignments for inner.wal_bytes and
inner.passages_wal_bytes to distinguish metadata outcomes: set the gauge to zero
only for NotFound, use the file length on success, and preserve the existing
gauge value for all other I/O or permission errors. Avoid unwrap_or(0) for both
wal_path and passages_wal_path.

In `@src/replica.rs`:
- Around line 319-329: Move the `report.stale` insertion into
`self.pending_refresh` in `retarget()` so it occurs before the fallible
`self.hydrator.hydrate_shared().await?` call, preserving the refresh debt when
hydration fails. Keep the existing worklist construction based on
`pending_refresh`, and add a regression test covering the `hydrate_shared()`
failure path and confirming `replica_refresh` is eventually performed.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e9c98faa-5f95-4b41-a512-b27a9c156dcc

📥 Commits

Reviewing files that changed from the base of the PR and between c55f7d8 and d3e56b1.

📒 Files selected for processing (9)
  • src/api/consolidation.rs
  • src/context/consolidation.rs
  • src/context/image.rs
  • src/extract/args.rs
  • src/extract/tests.rs
  • src/mcp/schema.rs
  • src/registry/replication.rs
  • src/replica.rs
  • tests/http_api/consolidation.rs

Comment thread src/context/image.rs
Comment thread src/registry/replication.rs Outdated
Comment thread src/replica.rs
zero WAL gauges only on NotFound

CodeRabbit on #523:

- replica: report.stale joins pending_refresh BEFORE hydrate_shared
  can fail the poll — a debt recorded only after the shared pass was
  lost with the error, reopening the same reader-hydrates-first
  freeze one step earlier. Regression test tears the published
  meta.json object so the shared pass itself aborts the poll.
- replication: the WAL gauge re-stat zeroes only on NotFound (no WAL
  shipped yet); any other stat failure keeps the last-known value
  instead of walking a live gauge to nothing on a transient error.

Claude-Session: https://claude.ai/code/session_011NozdDS9JqgCpi9Z3wo4Pd
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.

second-pass audit: 7 findings (replica meta freeze, migration float equality, ほか)

1 participant