Skip to content

feat: add governance RTBF local parity - #250

Merged
guangyu-reflexio merged 46 commits into
mainfrom
feature/governance-rtbf-parity
Jun 28, 2026
Merged

feat: add governance RTBF local parity#250
guangyu-reflexio merged 46 commits into
mainfrom
feature/governance-rtbf-parity

Conversation

@guangyu-reflexio

@guangyu-reflexio guangyu-reflexio commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add the OSS/local proof for Governance RTBF: typed governance models, stable subject/request refs, and a service-level export/erase flow.
  • Add SQLite governance storage for purge operations, audit events, delete planning, rebuild-safe agent playbook handling, and retention hooks.
  • Harden RTBF minimization and rebuild invariants from review-loop findings, including lineage cleanup, purged playbook request-id removal, idempotent erase audit handling, and scheduler config compatibility.

Changes

  • Governance API/models: add RTBF domain models, subject-ref helpers, and GovernanceService export/erase orchestration.
  • SQLite storage: add governance audit/purge tables, storage contract methods, target planning, data deletion/minimization, rebuild exclusion, and audit retention GC.
  • Lineage/GC: gate governance retention in the existing GC scheduler while preserving compatibility with configs that only expose lineage_gc.
  • Tests: add local governance e2e coverage, SQLite governance storage contract coverage, subject-ref tests, retention gate tests, and rebuild-exclusion regressions.

Test Plan

  • uv run ruff check reflexio/server/services/lineage/gc_scheduler.py reflexio/server/services/storage/sqlite_storage/_governance.py reflexio/server/services/storage/sqlite_storage/_lineage.py tests/server/services/storage/sqlite_storage/test_governance_storage.py
  • uv run pyright reflexio/server/services/lineage/gc_scheduler.py reflexio/server/services/storage/sqlite_storage/_governance.py reflexio/server/services/storage/sqlite_storage/_lineage.py tests/server/services/storage/sqlite_storage/test_governance_storage.py
  • uv run pytest --no-cov tests/server/services/storage/sqlite_storage/test_governance_storage.py -q -o 'addopts=' (207 passed)
  • uv run pytest --no-cov tests/server/services/lineage/test_gc_scheduler.py tests/server/services/lineage/test_governance_retention_gates.py -q -o 'addopts=' (22 passed)

Summary by CodeRabbit

  • New Features
    • Added governance/audit support with user export and user erasure workflows.
    • Introduced governance purge tracking with deterministic subject/actor/request identifiers.
    • Added governance audit-event retention configuration and periodic retention cleanup.
  • Bug Fixes
    • Improved config fallback so governance retention defaults apply when values are missing.
    • Excluded “archive in progress” items from default reads and search results.
    • Strengthened erasure to remove related request identifiers and linked content.
  • Tests
    • Added governance and reference-generation coverage, including end-to-end erasure/rebuild behavior and pagination/contract checks.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2fab9c82-5cf3-4502-987a-81fb6c01b6fb

📥 Commits

Reviewing files that changed from the base of the PR and between 63f031c and 7ae59be.

📒 Files selected for processing (6)
  • reflexio/server/services/governance/service.py
  • reflexio/server/services/storage/sqlite_storage/_governance.py
  • reflexio/server/services/storage/sqlite_storage/_playbook.py
  • reflexio/server/services/storage/storage_base/_governance.py
  • tests/server/services/storage/sqlite_storage/test_governance_storage.py
  • tests/server/services/storage/test_storage_contract_playbook.py
💤 Files with no reviewable changes (1)
  • reflexio/server/services/governance/service.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • reflexio/server/services/storage/sqlite_storage/_playbook.py
  • reflexio/server/services/storage/storage_base/_governance.py
  • reflexio/server/services/storage/sqlite_storage/_governance.py

📝 Walkthrough

Walkthrough

Adds governance models and config, deterministic reference helpers, storage contracts and SQLite support for purge/audit workflows, export/erase service orchestration, retention GC scheduling, and tests for the new flows and playbook exclusion.

Changes

Governance Subsystem

Layer / File(s) Summary
Governance domain models and config
reflexio/models/api_schema/domain/governance.py, reflexio/models/api_schema/domain/__init__.py, reflexio/models/config_schema.py
Defines governance audit/purge result models, re-exports them from the domain package, and adds GovernanceRetentionConfig plus governance_retention wiring in Config.
Subject reference generation
reflexio/server/services/governance/subject_refs.py, tests/server/services/governance/test_subject_refs.py
Adds HMAC-based subject_ref, actor_ref, and request_ref helpers, plus stable_id, with tests for determinism and prefix handling.
Governance storage contract
reflexio/server/services/storage/storage_base/_governance.py, reflexio/server/services/storage/storage_base/__init__.py
Declares the governance storage mixin contract and composes it into BaseStorage.
SQLite governance schema and purge lifecycle
reflexio/server/services/storage/sqlite_storage/_governance.py
Defines governance tables, validation, purge orchestration, audit persistence, rebuild handling, failure/completion paths, and governance retention GC.
SQLite wiring and playbook exclusion
reflexio/server/services/storage/sqlite_storage/__init__.py, reflexio/server/services/storage/sqlite_storage/_base.py, reflexio/server/services/storage/sqlite_storage/_lineage.py, reflexio/server/services/storage/sqlite_storage/_playbook.py
Wires governance tables into migration and storage composition, clears request_id on purge, and excludes ARCHIVE_IN_PROGRESS from default playbook reads/search while adding user-playbook pagination offset support.
GovernanceService export and erase
reflexio/server/services/governance/service.py
Implements user export and erasure orchestration, purge bookkeeping, rebuild field reconstruction, and audit emission.
Retention GC scheduler
reflexio/server/services/lineage/gc_scheduler.py
Runs governance retention GC alongside tombstone GC and widens scheduler startup conditions.
Governance and retention tests
tests/server/services/governance/test_governance_local_e2e.py, tests/server/services/lineage/test_governance_retention_gates.py, tests/server/services/storage/test_agent_playbook_rebuild_exclusion.py
Adds end-to-end governance export/erase coverage, retention gate tests, subject ref tests, and archived playbook exclusion regression coverage.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant GovernanceService
  participant SQLiteStorage

  Caller->>GovernanceService: export_user(user_id, request_id)
  GovernanceService->>SQLiteStorage: load sessions, requests, playbooks
  GovernanceService->>SQLiteStorage: append_audit_event(EXPORT)
  GovernanceService-->>Caller: UserExportResult

  Caller->>GovernanceService: erase_user(user_id, request_id)
  GovernanceService->>SQLiteStorage: begin_purge_operation(...)
  GovernanceService->>SQLiteStorage: prepare_governance_erase_targets(...)
  GovernanceService->>SQLiteStorage: hide_governance_agent_playbooks_for_rebuild(...)
  GovernanceService->>SQLiteStorage: apply_governance_user_data_delete(...)
  GovernanceService->>SQLiteStorage: apply_governance_agent_playbook_rebuild(...)
  GovernanceService->>SQLiteStorage: complete_purge_operation_with_audit(...)
  GovernanceService-->>Caller: UserEraseResult
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • ReflexioAI/reflexio#188: Both PRs modify LineageGCScheduler startup and _gc_tick routing; this one adds governance retention GC alongside lineage tombstone GC.
  • ReflexioAI/reflexio#199: Both PRs change SQLite migration startup logic in reflexio/server/services/storage/sqlite_storage/_base.py.
  • ReflexioAI/reflexio#219: Both PRs adjust the user-playbook purge SQL in reflexio/server/services/storage/sqlite_storage/_lineage.py.

Poem

🐇 I hop through audits, neat and bright,
Purges and exports in balanced flight.
Secret-bound refs go whisker-true,
Old playbooks fade from search view.
The retention moon keeps records light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: adding local parity for governance RTBF and related storage/service support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 feature/governance-rtbf-parity

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.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (1)
tests/server/services/lineage/test_governance_retention_gates.py (1)

22-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a config fixture variant without governance_retention.

_gc_tick and maybe_start_lineage_gc both have an explicit getattr(..., "governance_retention", GovernanceRetentionConfig()) fallback, but _make_ctx always supplies that attribute. That means this file never locks in the legacy-config path the PR summary calls out, so a future regression there would still pass these tests.

Suggested change
 def _make_ctx(
     *,
     lineage_gc_enabled: bool,
     purge_expired_profiles_enabled: bool = False,
     row_count_retention_enabled: bool = False,
     audit_events_retention_enabled: bool = False,
+    include_governance_retention: bool = True,
 ):
     storage = MagicMock()
     storage.gc_expired_tombstones.return_value = 0
     storage.gc_governance_retention.return_value = 0
-    config = SimpleNamespace(
-        lineage_gc=LineageGCConfig(enabled=lineage_gc_enabled),
-        governance_retention=SimpleNamespace(
-            purge_expired_profiles_enabled=purge_expired_profiles_enabled,
-            row_count_retention_enabled=row_count_retention_enabled,
-            audit_events_retention_enabled=audit_events_retention_enabled,
-        ),
-    )
+    config = SimpleNamespace(
+        lineage_gc=LineageGCConfig(enabled=lineage_gc_enabled),
+    )
+    if include_governance_retention:
+        config.governance_retention = SimpleNamespace(
+            purge_expired_profiles_enabled=purge_expired_profiles_enabled,
+            row_count_retention_enabled=row_count_retention_enabled,
+            audit_events_retention_enabled=audit_events_retention_enabled,
+        )
     return SimpleNamespace(
         org_id="org_1",
         storage=storage,
         configurator=SimpleNamespace(get_config=MagicMock(return_value=config)),
     )

Then add one _gc_tick case and one maybe_start_lineage_gc case with include_governance_retention=False.

🤖 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 `@tests/server/services/lineage/test_governance_retention_gates.py` around
lines 22 - 44, Add a _make_ctx fixture variant that can omit the
governance_retention attribute so the legacy fallback path is actually tested.
Update _make_ctx to accept an include_governance_retention flag and, when false,
build the config without governance_retention; then add one _gc_tick case and
one maybe_start_lineage_gc case using that variant to cover the getattr(...,
"governance_retention", GovernanceRetentionConfig()) fallback in both code
paths.
🤖 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 `@reflexio/models/api_schema/domain/governance.py`:
- Around line 48-60: AuditEvent currently allows request_ref to be omitted even
though SQLite governance storage rejects it later. Update the AuditEvent model
in governance.py so request_ref is required (non-optional) and keep the schema
aligned with the persistence contract. Make sure any constructors or validators
that rely on AuditEvent use the required request_ref field consistently.

In `@reflexio/models/config_schema.py`:
- Around line 711-716: The governance retention config exposes flags that are
not actually used by gc_governance_retention(), so enabling
purge_expired_profiles_enabled or row_count_retention_enabled only starts
scheduling without any deletion behavior. Update the retention flow in
config_schema/GovernanceRetentionConfig and the related gating logic (including
_is_governance_retention_enabled and gc_governance_retention) so each exposed
flag has a real implementation path, or remove/defer the unused flags until they
are wired through.

In `@reflexio/server/services/governance/service.py`:
- Around line 51-57: The RTBF export/erase flow in the governance service
currently relies on get_user_playbooks with a hard-coded limit of 1_000_000,
which can silently miss records for large accounts. Update the logic in the
service methods that build the export payload and prepare erase coverage to use
pagination or streaming over get_user_playbooks, or else enforce and clearly
document a storage-level maximum before depending on that bound. Ensure the
affected paths around the user_playbooks collection and the erase preparation
logic both handle all playbooks without truncation.
- Around line 80-129: The erase workflow in the governance service currently
lets exceptions from target preparation, hiding, delete, rebuild, or completion
escape without marking the purge failed. Wrap the post-`begin_purge_operation`
flow in the service method that handles user erasure, and on any exception call
`fail_purge_operation` with the current `purge_id` before re-raising so the
purge state is not left active. Keep the existing completed-purge fast path
unchanged, and make sure the failure handling covers the calls to
`prepare_governance_erase_targets`,
`hide_governance_agent_playbooks_for_rebuild`,
`apply_governance_user_data_delete`, `_rebuild_agent_playbooks`, and
`complete_purge_operation_with_audit`.
- Around line 146-163: The pagination check in the session export loop is
counting only appended requests, so skipped rows with request is None can make
the loop stop early. Update the paging logic in the governance service method
that iterates over grouped_sessions so it bases page exhaustion on the total
rows returned from storage, not just the number of exported requests; keep
skipping None requests when building requests and sessions_by_id, but use a
separate returned-row counter (or the raw grouped rows) to decide when to break
and when to advance offset.

In `@reflexio/server/services/lineage/gc_scheduler.py`:
- Around line 34-41: The scheduler startup gate in
_is_governance_retention_enabled is too broad because it enables governance GC
for flags that the SQLite storage path does not actually handle. Update this
helper (and any related scheduler checks in gc_scheduler) to only return true
for the implemented audit-events retention path, or implement the missing
purge_expired_profiles and row_count retention branches in the storage layer
before keeping those flags wired here. Make sure the scheduler only starts and
calls governance GC through the flags that the storage implementation in this
codebase truly honors.

In `@reflexio/server/services/storage/sqlite_storage/_governance.py`:
- Around line 1868-1881: The empty-windows rebuild path in the agent playbook
deletion branch is hard-deleting rows without creating the matching lineage
record. Update the branch that deletes from agent_playbooks to also emit an
op=hard_delete lineage event, mirroring the behavior in delete_agent_playbook
and delete_agent_playbooks_by_ids, unless this RTBF rebuild path is explicitly
meant to be exempt; use the same agent-playbook deletion flow symbols to locate
and align the implementation.

---

Nitpick comments:
In `@tests/server/services/lineage/test_governance_retention_gates.py`:
- Around line 22-44: Add a _make_ctx fixture variant that can omit the
governance_retention attribute so the legacy fallback path is actually tested.
Update _make_ctx to accept an include_governance_retention flag and, when false,
build the config without governance_retention; then add one _gc_tick case and
one maybe_start_lineage_gc case using that variant to cover the getattr(...,
"governance_retention", GovernanceRetentionConfig()) fallback in both code
paths.
🪄 Autofix (Beta)

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: c8c03a57-fdb1-427e-acd8-2dbeec7f9ac1

📥 Commits

Reviewing files that changed from the base of the PR and between 8a199e9 and 2cdf332.

📒 Files selected for processing (18)
  • reflexio/models/api_schema/domain/__init__.py
  • reflexio/models/api_schema/domain/governance.py
  • reflexio/models/config_schema.py
  • reflexio/server/services/governance/service.py
  • reflexio/server/services/governance/subject_refs.py
  • reflexio/server/services/lineage/gc_scheduler.py
  • reflexio/server/services/storage/sqlite_storage/__init__.py
  • reflexio/server/services/storage/sqlite_storage/_base.py
  • reflexio/server/services/storage/sqlite_storage/_governance.py
  • reflexio/server/services/storage/sqlite_storage/_lineage.py
  • reflexio/server/services/storage/sqlite_storage/_playbook.py
  • reflexio/server/services/storage/storage_base/__init__.py
  • reflexio/server/services/storage/storage_base/_governance.py
  • tests/server/services/governance/test_governance_local_e2e.py
  • tests/server/services/governance/test_subject_refs.py
  • tests/server/services/lineage/test_governance_retention_gates.py
  • tests/server/services/storage/sqlite_storage/test_governance_storage.py
  • tests/server/services/storage/test_agent_playbook_rebuild_exclusion.py

Comment thread reflexio/models/api_schema/domain/governance.py
Comment thread reflexio/models/config_schema.py
Comment thread reflexio/server/services/governance/service.py
Comment thread reflexio/server/services/governance/service.py Outdated
Comment thread reflexio/server/services/governance/service.py
Comment thread reflexio/server/services/lineage/gc_scheduler.py Outdated
Comment thread reflexio/server/services/storage/sqlite_storage/_governance.py
@guangyu-reflexio

guangyu-reflexio commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review-body nitpick as well: the governance retention tests now cover legacy configs without governance_retention, and the scheduler/startup paths fall back safely to the default audit-retention config. Pushed in OSS commit 63f031c.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@reflexio/server/services/storage/sqlite_storage/_playbook.py`:
- Around line 321-322: The pagination in the playbook query uses only created_at
DESC, which can produce unstable ordering when multiple rows share the same
timestamp. Update the query-building logic in _playbook.py so the ORDER BY
clause includes a deterministic tie-breaker in addition to created_at, using the
existing query path that appends LIMIT and OFFSET. This should be done in the
storage query used by the playbook export/erase flow so the results stay stable
across pages.
🪄 Autofix (Beta)

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: de5399dc-d6d7-4464-90da-489f928c9a14

📥 Commits

Reviewing files that changed from the base of the PR and between 2cdf332 and 63f031c.

📒 Files selected for processing (10)
  • reflexio/models/api_schema/domain/governance.py
  • reflexio/models/config_schema.py
  • reflexio/server/services/governance/service.py
  • reflexio/server/services/lineage/gc_scheduler.py
  • reflexio/server/services/storage/sqlite_storage/_governance.py
  • reflexio/server/services/storage/sqlite_storage/_playbook.py
  • reflexio/server/services/storage/storage_base/_playbook.py
  • tests/server/services/governance/test_governance_local_e2e.py
  • tests/server/services/lineage/test_governance_retention_gates.py
  • tests/server/services/storage/sqlite_storage/test_governance_storage.py
💤 Files with no reviewable changes (1)
  • reflexio/models/config_schema.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/server/services/lineage/test_governance_retention_gates.py
  • reflexio/models/api_schema/domain/governance.py
  • reflexio/server/services/governance/service.py
  • reflexio/server/services/lineage/gc_scheduler.py
  • reflexio/server/services/storage/sqlite_storage/_governance.py

Comment thread reflexio/server/services/storage/sqlite_storage/_playbook.py Outdated
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.

1 participant