Skip to content

Phase 3 — Data-integrity fixes (rides 0.19.0)#317

Merged
rennf93 merged 99 commits into
fix/scan-state-machine-gatewayfrom
fix/scan-data-integrity
Jul 7, 2026
Merged

Phase 3 — Data-integrity fixes (rides 0.19.0)#317
rennf93 merged 99 commits into
fix/scan-state-machine-gatewayfrom
fix/scan-data-integrity

Conversation

@rennf93

@rennf93 rennf93 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Phase 3 — Data-integrity fixes (rides 0.19.0)

Third phase of the 2026-07-06 full-codebase-scan fix program. Ten verified-real data-integrity / RAG / notification findings, plus an e2e smoke module, all under the existing ## [0.19.0] CHANGELOG section. Stacks on Phase 2 (fix/scan-state-machine-gateway, PR #316) — base is fix/scan-state-machine-gateway; merge #316 first.

Fixes

  • C3 (3985b009) — delete_entry unindexes the journal entry from the JOURNALS RAG corpus. Previously a deleted journal entry stayed searchable forever (the index row was never removed). Adds OptimalService.unindex_journal_entry (best-effort, mirrors the existing unindex_playbook pattern) and calls it from JournalService.delete_entry.
  • M25 (98ce3cf0) — learning_id hashes the full content (not just entry_id), so two distinct learnings that share an entry no longer collide and silently overwrite one another in the LEARNINGS index.
  • H13 (ec2cb031) — config load rejects a non-internal local_llm_base_url. Closes the fire-and-forget RAG indexing hot path calling out to an arbitrary external host; the validator runs once at Settings load, dev mode unchanged.
  • M28 (8ec382eb) — knowledge-share learning broadcasts bulk-insert the per-agent notifications instead of an N+1 loop. Closes the O(agents) insert storm on every learning broadcast.
  • M27 (a8855c36) — mark_read/mark_all_read stamp only the rows that were unread at call time (a snapshot ids set, not a re-query). Closes the read-race where a concurrent new notification landed between the unread-query and the update and got retro-acknowledged.
  • H12 (9ba49cfe) — notification dedup uses an exact to_agents predicate + a purpose discriminator + an ack DEL-aware comparison. Previously real notifications were dropped three ways: a broadcast shadowing a targeted one, a same-type-different-purpose pair, and an ack-vs-delete state mismatch.
  • M23 (7e866e8f) — playbooks.indexed_ok / indexed_at columns (migration 064) + a startup reconcile that re-indexes approved-but-unindexed playbooks. Closes the drift where an approved playbook's index write failed and the playbook stayed invisible to retrieval forever. unindex_playbook flips indexed_ok on archive.
  • M24 (809caf3b) — RAG indexing failures persist to a dead-letter (rag_index_failures, migration 065) instead of being swallowed; a janitor (reclaim_due) replays them with bounded backoff, and /health reports failed_index_count. Best-effort throughout — a failure never blocks the caller's commit.
  • M24 / C1 review fix (12da8ec9) — the dead-letter replay (_reindex_journal_entry) mirrors the original journal._schedule_rag_index path: a private entry is never indexed into the shared JOURNALS corpus, and a private learning is still recorded into LEARNINGS as non-shareable. The original replay always called index_journal_entry and skipped record_learning for private learnings — leaking private content on replay and dropping the legitimate non-shared learning. Three regression tests cover the private-learning / shared-learning / private-general matrix.
  • L23 (7ae4c8c7) — the institutional-memory briefing exposes a status sentinel distinguishing below-floor / empty / error / disabled, so a caller can tell "nothing relevant" apart from "RAG is off / broken" instead of both reading as an empty list.
  • L26 (36a017cc) — sweep_expired_notifications re-escalates a stale unacked ack-required notification (re-notify + bump) instead of silently dropping it, so a missed formal notification doesn't vanish.
  • e2e smoke (c6d931d6) — tests/e2e_smoke/test_data_integrity.py, one scenario per cross-layer finding (C3 / H12 / M27 / M23 / M24 / L26), scripted-agent over the real API + ephemeral Postgres + fake GitHub.

Verification

  • uv run ruff format + ruff check — clean.
  • uv run mypy roboco/ — clean.
  • uv run pytest tests/unit/services/test_health.py — 11 passed (8 existing + 3 new C1 regression tests).
  • Migrations 064 + 065 upgrade + downgrade cleanly on the local :55432 test Postgres.
  • make e2e-smoke — every Phase 3 scenario passes in isolation. The full-suite run remains order-dependent (session-scoped e2e_stack shares the be-dev-1 workspace across scenarios in one file) — pre-existing harness limitation, documented out of scope, not a product regression.
  • Whole-branch opus review: Fix then push → the one Critical (C1, the _reindex_journal_entry is_private leak) is fixed in 12da8ec9; two Minor (a misleading L26 docstring, M24 replay not re-notifying) are acceptable with comments. Three implementer-flagged concerns (M28 Redis guard drop, H12 _DbHolder rebind, L23 similar_memory return-type change) all adjudicated ACCEPTABLE by opus.

Known pre-existing issues (not introduced here)

  • e2e smoke harness shares the be-dev-1 workspace across scenarios in one file — a per-scenario fixture reset would fix it. Harness change, not a service-layer fix.

Model note

Implementer ran on sonnet; whole-branch review on opus. kimi-k2.7-code:cloud is not routable in this harness.

🤖 Generated with Claude Code

rennf93 added 12 commits July 6, 2026 18:43
JournalService.delete_entry deleted the DB row but never de-indexed the
RAG chunks, so deleted/private journal content bled forever into RAG
answers and claim-time briefings. Add OptimalService.unindex_journal_entry
mirroring unindex_playbook (vector-store delete_by_source + tracking-row
delete via get_db_context, both idempotent + best-effort), and call it
from delete_entry after the row commit inside a try/except so a de-index
failure never errors the delete.
The memory distiller emits lessons with a fixed 'Problem: …' opening
shape, so two distinct lessons whose first 100 chars match collided on
learning_id = f"lrn-{md5(content[:100])[:12]}". replace_on_reingest then
routed both to the same source URI and the second ingest's replace_chunks
DELETE wiped the first lesson's chunks — silent data loss.

Hash the full content (widening the hex slice 12→16) so distinct bodies
get distinct ids and each retains its chunks.
… time

mark_read and mark_all_read used to zero the unread counter FIRST, then run
a bulk UPDATE … WHERE read_at IS NULL that stamped every inbound unread row.
A send_chat_message committing between the counter-zero and the UPDATE
inserted a new read_at NULL row that the UPDATE then stamped as read — the
new message was silently consumed while the counter stayed 0.

Mirrors get_unread_messages (same file): SELECT the unread message IDs at
call time, UPDATE exactly those IDs, then recompute the unread counter from
the DB via the existing _reset_unread_counter helper. A message arriving
mid-call is not in the selected ID set, so the UPDATE skips it and the
recomputed counter keeps it unread.
Dead-letter replay mirrors the original journal._schedule_rag_index path:
a private entry is never indexed into the shared JOURNALS corpus, and a
private learning is still recorded into LEARNINGS as non-shareable.
Previously the replay always called index_journal_entry and skipped
record_learning for private learnings, leaking private content on replay
and dropping the legitimate non-shared learning. Three regression tests.
@github-project-automation github-project-automation Bot moved this to Backlog in RoboCo Kanban Jul 6, 2026
@github-actions github-actions Bot added documentation Docs, README, CHANGELOG, governance files area: api Touches roboco/api/ (FastAPI routes, schemas, app) area: services Touches roboco/services/ (business logic, side effects) area: db Touches roboco/db/ or roboco/models/ area: alembic Touches alembic/ (database migrations) tests Test suite changes area: gateway Touches roboco/services/gateway/ (Choreographer, verb surface) labels Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Thanks for opening your first pull request on RoboCo!

Quick checklist before review (most of these are enforced by CI, but worth a glance):

  • make quality — ruff format check, ruff check, mypy, pytest (≥80% coverage), and the rest of the gate
  • Panel changes pass pnpm lint and pnpm exec tsc --noEmit (run from panel/)
  • No # noqa / # type: ignore shortcuts; pre-existing violations in touched files are fixed
  • Added an entry under ## [Unreleased] in CHANGELOG.md
  • Signed the CLA (the bot will prompt you on this PR)
  • Signed your commits — master requires verified signatures (SSH signing setup)
  • Updated any affected docs under docs/

See CONTRIBUTING.md for the full workflow and the Code of Conduct for the community standards we follow.

Welcome aboard — a maintainer will review shortly.

rennf93 and others added 24 commits July 7, 2026 06:00
…,L27,L14,L28,H19,M13,M14,H20,M16,M45,L25,M15,L24)
…ep so live cost reflects Anthropic cache spend
[phase9] Auth hardening: bound agent tokens (iat/exp) + JWT jti revocation
[scan-fix] Phase 8: LLM providers / usage / billing
[Phase 7] schemas/conventions/MegaTask/API fixes (15 findings, scan-fix program)
Phase 6 — Panel frontend hardening (rides 0.19.0)
Phase 5 — Background-engine hardening (rides 0.19.0)
Phase 4 — Git / workflow hardening (rides 0.19.0)
@github-actions github-actions Bot added dependencies pyproject.toml / uv.lock or panel package manifests area: panel Touches panel/ (Next.js control panel) area: orchestrator Touches roboco/runtime/ (agent spawner, dispatch loops) labels Jul 7, 2026
@rennf93 rennf93 merged commit 384198b into fix/scan-state-machine-gateway Jul 7, 2026
4 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in RoboCo Kanban Jul 7, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area: alembic Touches alembic/ (database migrations) area: api Touches roboco/api/ (FastAPI routes, schemas, app) area: db Touches roboco/db/ or roboco/models/ area: gateway Touches roboco/services/gateway/ (Choreographer, verb surface) area: orchestrator Touches roboco/runtime/ (agent spawner, dispatch loops) area: panel Touches panel/ (Next.js control panel) area: services Touches roboco/services/ (business logic, side effects) dependencies pyproject.toml / uv.lock or panel package manifests documentation Docs, README, CHANGELOG, governance files tests Test suite changes

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant