feat: add explicit playbook provenance contracts#302
Conversation
📝 WalkthroughWalkthroughThis PR expands the ChangesCitationKind Expansion and Propagation
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant ReflectionServiceRun as ReflectionService.run
participant CollectCitations as _collect_citations
participant KeyHelper as _reflection_citation_key
participant FilterByHorizon as _filter_citations_by_horizon
ReflectionServiceRun->>CollectCitations: gather assistant citations from window
CollectCitations->>KeyHelper: normalize (kind, real_id)
KeyHelper-->>CollectCitations: ("playbook"|"profile", real_id)
CollectCitations-->>ReflectionServiceRun: deduplicated citations
ReflectionServiceRun->>FilterByHorizon: apply horizon eligibility
FilterByHorizon->>KeyHelper: build citation_by_key via setdefault
FilterByHorizon-->>ReflectionServiceRun: eligible citations (earliest per key)
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
reflexio/server/services/reflection/service.py (1)
170-174: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRedundant None-filtering on
horizon_by_key.
eligibleis already built from_filter_citations_by_horizon, which only includes citations with a non-None_reflection_citation_key. Theif (key := ...) is not Noneguard here is therefore dead code in practice, though harmless.🤖 Prompt for 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. In `@reflexio/server/services/reflection/service.py` around lines 170 - 174, The horizon_by_key comprehension in reflection.service._filter_citations_by_horizon is redundantly filtering out None keys even though eligible already contains only citations with a valid _reflection_citation_key. Remove the unnecessary if (key := ...) is not None guard and keep the key assignment in the dict comprehension so the existing eligible set is mapped directly by _reflection_citation_key.reflexio/models/api_schema/domain/entities.py (1)
410-467: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueComplex but internally consistent backfill validator.
Traced all branches of
backfill_legacy_target_fields: legacy-only (agent_playbook_idset), explicittarget_kind="user_playbook", explicittarget_kind="agent_playbook"with/withoutagent_playbook_id, and mismatch detection all resolve correctly and match the test expectations intest_storage_contract_retrieval_logs.py. No functional issue found, though the six-branch validator is dense — consider extracting the "agent_playbook" reconciliation branch into a helper for readability if this grows further.🤖 Prompt for 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. In `@reflexio/models/api_schema/domain/entities.py` around lines 410 - 467, The backfill logic in backfill_legacy_target_fields is correct but the agent_playbook reconciliation path is too dense. Refactor the explicit agent_playbook branch into a small helper on PlaybookRetrievalLogItem (or a nearby private function) and keep the model_validator focused on dispatching between legacy, user_playbook, and agent_playbook cases while preserving the same validation behavior and error messages.reflexio/server/services/storage/sqlite_storage/_extras.py (1)
28-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider importing the shared
CitationKindinstead of duplicating it locally.
_CitationKind/_SUPPORTED_CITATION_KINDSre-declare the same literal values asreflexio.models.api_schema.domain.CitationKind(used elsewhere in this same PR). This duplication is a drift risk — this PR itself required manually updating two locations to adduser_playbook/agent_playbook; a future expansion could easily miss this file and silently drop valid citations from the stats/filter queries.♻️ Suggested consolidation
-type _CitationKind = Literal["playbook", "profile", "user_playbook", "agent_playbook"] - -_SUPPORTED_CITATION_KINDS: tuple[_CitationKind, ...] = ( - "playbook", - "profile", - "user_playbook", - "agent_playbook", -) +from reflexio.models.api_schema.domain import CitationKind + +_SUPPORTED_CITATION_KINDS: tuple[CitationKind, ...] = ( + "playbook", + "profile", + "user_playbook", + "agent_playbook", +)If there's a deliberate reason to keep the storage layer decoupled from
reflexio.models.api_schema.domain, please confirm and disregard.🤖 Prompt for 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. In `@reflexio/server/services/storage/sqlite_storage/_extras.py` around lines 28 - 35, The storage layer is duplicating the citation kind literals instead of reusing the shared domain type, which can drift from `reflexio.models.api_schema.domain.CitationKind`. Update `_extras.py` to import and use `CitationKind` directly where `_CitationKind` and `_SUPPORTED_CITATION_KINDS` are defined, and ensure the tuple of supported kinds stays derived from the shared source used by the rest of the PR. Keep the relevant storage helpers aligned so future additions only need to be made in one place.
🤖 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.
Nitpick comments:
In `@reflexio/models/api_schema/domain/entities.py`:
- Around line 410-467: The backfill logic in backfill_legacy_target_fields is
correct but the agent_playbook reconciliation path is too dense. Refactor the
explicit agent_playbook branch into a small helper on PlaybookRetrievalLogItem
(or a nearby private function) and keep the model_validator focused on
dispatching between legacy, user_playbook, and agent_playbook cases while
preserving the same validation behavior and error messages.
In `@reflexio/server/services/reflection/service.py`:
- Around line 170-174: The horizon_by_key comprehension in
reflection.service._filter_citations_by_horizon is redundantly filtering out
None keys even though eligible already contains only citations with a valid
_reflection_citation_key. Remove the unnecessary if (key := ...) is not None
guard and keep the key assignment in the dict comprehension so the existing
eligible set is mapped directly by _reflection_citation_key.
In `@reflexio/server/services/storage/sqlite_storage/_extras.py`:
- Around line 28-35: The storage layer is duplicating the citation kind literals
instead of reusing the shared domain type, which can drift from
`reflexio.models.api_schema.domain.CitationKind`. Update `_extras.py` to import
and use `CitationKind` directly where `_CitationKind` and
`_SUPPORTED_CITATION_KINDS` are defined, and ensure the tuple of supported kinds
stays derived from the shared source used by the rest of the PR. Keep the
relevant storage helpers aligned so future additions only need to be made in one
place.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: d0859915-0f41-4096-a2a7-8eaa64c878a8
📒 Files selected for processing (14)
reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.pyreflexio/integrations/openclaw/plugin/tests/test_state.pyreflexio/models/api_schema/domain/entities.pyreflexio/models/api_schema/eval_overview_schema.pyreflexio/models/api_schema/internal_schema.pyreflexio/models/api_schema/retriever_schema.pyreflexio/server/services/evaluation_overview/components/rule_attribution.pyreflexio/server/services/reflection/service.pyreflexio/server/services/storage/sqlite_storage/_extras.pyreflexio/server/services/storage/storage_base/_retrieval_log.pytests/server/services/reflection/test_post_horizon_filter.pytests/server/services/reflection/test_reflection_service.pytests/server/services/storage/test_sqlite_storage_bc_extras.pytests/server/services/storage/test_storage_contract_extras.py
Summary
Changes
Test Plan
uv run pytest --no-cov tests/server/services/reflection/test_post_horizon_filter.py tests/server/services/reflection/test_reflection_service.py tests/server/services/storage/test_sqlite_storage_bc_extras.py tests/server/services/storage/test_storage_contract_extras.py -qREFLEXIO_GOVERNANCE_REF_SECRET=test-governance-ref-secret.Summary by CodeRabbit
New Features
Bug Fixes
Documentation