Skip to content

feat: select open-world tuning evidence - #444

Open
guangyu-reflexio wants to merge 13 commits into
codex/offline-tuner-open-world-phase1-restoredfrom
codex/offline-tuner-open-world-phase2-evidence-selection
Open

feat: select open-world tuning evidence#444
guangyu-reflexio wants to merge 13 commits into
codex/offline-tuner-open-world-phase1-restoredfrom
codex/offline-tuner-open-world-phase2-evidence-selection

Conversation

@guangyu-reflexio

@guangyu-reflexio guangyu-reflexio commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add the shared open-world evidence identity and storage contracts needed to select offline-tuner evidence deterministically.
  • Preserve finalized session outcomes and bound maintenance and retention behavior without introducing customer replay infrastructure.
  • Keep Phase 2 stacked on the restored Phase 1 evidence foundation for focused review.

Changes

  • Add open-world optimization and session-outcome identity models.
  • Add storage contracts for deterministic evidence reconstruction and playbook optimization history.
  • Harden queued metering lifecycle startup, retained outcomes, request-source compatibility, trajectory digest streaming, and fixed exposure retention.
  • Align lifecycle tests with queued metering while preserving the Phase 1 exposure-before-metering invariant.

Test Plan

  • nice -n 10 uv run python -m reflexio_ext.scripts.phase2_evidence_gate from the enterprise checkout: 693 passed, 14 expected storage-specific skips.
  • Focused conflict-sensitive route and capability tests: 29 passed.
  • Ruff and Pyright passed for the restacked conflict-sensitive shared tests.

Summary by CodeRabbit

  • New Features

    • Added stricter validation for new session outcome sources while preserving legacy values in historical records.
    • Improved handling of large session trajectories with consistent, memory-efficient outcome finalization.
    • Added synchronous timeout coverage for playbook aggregation requests.
    • Applied fixed retention limits to playbook exposure events.
  • Bug Fixes

    • Prevented duplicate lineage cleanup callbacks.
    • Improved recovery when metering services fail during startup.
    • Protected finalized session outcomes from generic retention cleanup.
  • Documentation

    • Clarified source-value behavior for new inputs and historical responses.

@coderabbitai

coderabbitai Bot commented Aug 11, 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

Run ID: 1538bc7d-24e3-44cd-9242-5b582ea8819d

📥 Commits

Reviewing files that changed from the base of the PR and between 855271b and bb9f6ae.

📒 Files selected for processing (2)
  • tests/server/services/storage/sqlite_storage/test_session_outcome_migration.py
  • tests/server/services/storage/test_storage_contract_session_outcomes.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/server/services/storage/test_storage_contract_session_outcomes.py
  • tests/server/services/storage/sqlite_storage/test_session_outcome_migration.py

📝 Walkthrough

Walkthrough

The changes separate strict source validation from persisted source preservation, add streaming session trajectory digests, bound SQLite migration reads, and update session outcome finalization. They also harden worker startup, timeout routing, sweep registration, and retention configuration.

Changes

Session outcome contracts and persistence

Layer / File(s) Summary
Source contract and persisted models
reflexio/models/api_schema/..., reflexio/server/services/storage/sqlite_storage/_requests.py, tests/models/..., tests/server/services/storage/test_storage_contract_requests.py, AI_AGENT_INTEGRATION.md
New source inputs use strict validation. Persisted models preserve legacy source strings. Storage rejects invalid new request sources.
Canonical trajectory digesting
reflexio/server/services/storage/session_outcome_identity.py, tests/models/test_session_outcome_identity.py
The identity module adds shared outcome constants, canonical request normalization, and a streaming digest accumulator with ordering and error-state validation.
SQLite finalization and bounded reads
reflexio/server/services/storage/sqlite_storage/_base.py, reflexio/server/services/storage/sqlite_storage/_session_outcomes.py, tests/server/services/storage/test_storage_contract_session_outcomes.py
Session outcome writes use canonical snapshots and subject barriers. Trajectory rows use ordered joins and bounded fetches. Tests cover digest completeness, retries, conflicts, streaming behavior, nullable sources, and retention protection.
Bounded legacy outcome migration
reflexio/server/services/storage/sqlite_storage/_base.py, tests/server/services/storage/sqlite_storage/test_session_outcome_migration.py
Migration uses keyset-paginated batches, computes missing digests incrementally, and preserves legacy identity and source values.
Fixed retention limits
reflexio/server/services/storage/retention.py, reflexio/server/services/storage/retention_mixin.py
Retention targets can use fixed row limits. The user playbook exposure target uses the default fixed limit, and age filtering documentation states the strict cutoff condition.

Runtime safeguards

Layer / File(s) Summary
Transactional metering-worker startup
reflexio/server/services/search_metering_worker.py, tests/server/services/test_search_metering_worker.py, tests/server/test_create_app_capabilities.py
Partial thread-start failures stop started threads, clear worker state, propagate the error, and allow a later retry.
Playbook aggregation timeout
reflexio/server/middleware.py, tests/server/test_api_security_middleware.py
/api/run_playbook_aggregation now receives the synchronous request timeout.
Sweep callback deduplication
reflexio/server/services/lineage/gc_scheduler.py
Global, always-global, and per-organization sweep registrations ignore duplicate callbacks.

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

Sequence Diagram(s)

sequenceDiagram
  participant StorageClient
  participant SQLiteStorage
  participant CanonicalTrajectoryDigestAccumulator
  StorageClient->>SQLiteStorage: finalize session outcome
  SQLiteStorage->>SQLiteStorage: read ordered request and interaction rows
  SQLiteStorage->>CanonicalTrajectoryDigestAccumulator: stream rows in bounded batches
  CanonicalTrajectoryDigestAccumulator-->>SQLiteStorage: return canonical digest
  SQLiteStorage-->>StorageClient: return finalized outcome
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.48% 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 clearly identifies the primary objective: selecting evidence for open-world tuning through new identity and storage contracts.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/offline-tuner-open-world-phase2-evidence-selection

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

@guangyu-reflexio

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@guangyu-reflexio
guangyu-reflexio force-pushed the codex/offline-tuner-open-world-phase2-evidence-selection branch from 36a642c to 4a9b4ef Compare August 11, 2026 07:18
@guangyu-reflexio

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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

🧹 Nitpick comments (3)
reflexio/server/services/storage/sqlite_storage/_session_outcomes.py (1)

93-99: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Align source normalization with the writer.

Line 95 computes str(first["source"]). The writer computes str(first["source"] or "") at Line 244. If requests.source is ever NULL, the reader produces the string "None" and the writer produces "". The equality check at Line 248 then fails and record_session_outcome returns context_changed=True for every attempt on that session.

The current requests schema declares source TEXT NOT NULL DEFAULT '', so NULL is not reachable today. Applying the same normalization in both places removes the asymmetry.

♻️ Proposed change
             return SessionOutcomeContext(
                 user_id=str(first["user_id"]),
-                source=str(first["source"]),
+                source=str(first["source"] or ""),
                 first_request_at=_iso_to_epoch(first["created_at"]),
🤖 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/_session_outcomes.py` around
lines 93 - 99, Update the source assignment in SessionOutcomeContext
construction to normalize NULL values the same way as record_session_outcome:
convert first["source"] or an empty string to str. Preserve the existing source
value behavior for non-NULL inputs and align it with the writer’s normalization.
tests/server/services/storage/test_storage_contract_session_outcomes.py (1)

419-420: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The fetch-size assertion covers only the conflict call.

Line 419 rebinds guarded_connection to a new _NoFetchAllConnection. That instance owns a fresh fetch_sizes list. The assertions at Lines 437-438 therefore inspect only the third record_session_outcome call. The sizes recorded during the first and retry calls are discarded.

The no-fetchall guard still applies to all three calls, so the main invariant holds. If you want the batch-size assertion to cover every call, keep both wrappers and assert over the combined list.

♻️ Proposed change
-    guarded_connection = _NoFetchAllConnection(raw_connection)
-    cast(Any, sqlite_storage).conn = guarded_connection
+    conflict_connection = _NoFetchAllConnection(raw_connection)
+    cast(Any, sqlite_storage).conn = conflict_connection
     try:
         conflict = storage.record_session_outcome(
             outcome,
             created_at=503,
             expected_context=storage.get_session_outcome_context(session_id),
         )
     finally:
         cast(Any, sqlite_storage).conn = raw_connection
-    assert guarded_connection.fetch_sizes
-    assert len(set(guarded_connection.fetch_sizes)) == 1
+    all_fetch_sizes = guarded_connection.fetch_sizes + conflict_connection.fetch_sizes
+    assert all_fetch_sizes
+    assert len(set(all_fetch_sizes)) == 1

Also applies to: 437-438

🤖 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/storage/test_storage_contract_session_outcomes.py`
around lines 419 - 420, Update the test setup around _NoFetchAllConnection and
the fetch_sizes assertions so size records from the initial, retry, and conflict
record_session_outcome calls are preserved and checked together. Keep the
no-fetchall guard active for every call, but avoid replacing the earlier
wrapper’s fetch_sizes collection with a fresh list; aggregate both wrappers’
recorded sizes before the assertions.
tests/server/services/storage/sqlite_storage/test_session_outcome_migration.py (1)

395-402: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the expected trajectory-query count from both chunk sizes

_prefetch_canonical_session_trajectory_digests uses RETENTION_DELETE_CHUNK (500), while migration batches use size 256. The 501 test rows therefore produce two queries today. If RETENTION_DELETE_CHUNK is lowered below 256, the assertion fails even though the migration remains correct. Derive the expected count from both constants, or assert the required lower bound.

🤖 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/storage/sqlite_storage/test_session_outcome_migration.py`
around lines 395 - 402, Update the trajectory_input_queries assertion in the
migration test to derive the expected query count from both
RETENTION_DELETE_CHUNK and the migration batch size, or assert only the required
lower bound. Keep validating that every matching query includes LEFT JOIN
interactions while avoiding a hard-coded count tied to current chunk sizes.

Source: Linters/SAST tools

🤖 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 `@tests/models/test_session_outcome_identity.py`:
- Around line 881-884: Update the match pattern in the pytest.raises call to use
a raw string literal, preserving the existing “canonical trajectory digest
accumulator is invalid$” regex unchanged.

---

Nitpick comments:
In `@reflexio/server/services/storage/sqlite_storage/_session_outcomes.py`:
- Around line 93-99: Update the source assignment in SessionOutcomeContext
construction to normalize NULL values the same way as record_session_outcome:
convert first["source"] or an empty string to str. Preserve the existing source
value behavior for non-NULL inputs and align it with the writer’s normalization.

In
`@tests/server/services/storage/sqlite_storage/test_session_outcome_migration.py`:
- Around line 395-402: Update the trajectory_input_queries assertion in the
migration test to derive the expected query count from both
RETENTION_DELETE_CHUNK and the migration batch size, or assert only the required
lower bound. Keep validating that every matching query includes LEFT JOIN
interactions while avoiding a hard-coded count tied to current chunk sizes.

In `@tests/server/services/storage/test_storage_contract_session_outcomes.py`:
- Around line 419-420: Update the test setup around _NoFetchAllConnection and
the fetch_sizes assertions so size records from the initial, retry, and conflict
record_session_outcome calls are preserved and checked together. Keep the
no-fetchall guard active for every call, but avoid replacing the earlier
wrapper’s fetch_sizes collection with a fresh list; aggregate both wrappers’
recorded sizes before the assertions.
🪄 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

Run ID: ad85775a-5934-4a8c-b1d0-e2cd46714909

📥 Commits

Reviewing files that changed from the base of the PR and between 5c15470 and 3df6bfb.

📒 Files selected for processing (19)
  • AI_AGENT_INTEGRATION.md
  • reflexio/models/api_schema/domain/entities.py
  • reflexio/models/api_schema/validators.py
  • reflexio/server/middleware.py
  • reflexio/server/services/lineage/gc_scheduler.py
  • reflexio/server/services/search_metering_worker.py
  • reflexio/server/services/storage/retention.py
  • reflexio/server/services/storage/retention_mixin.py
  • reflexio/server/services/storage/session_outcome_identity.py
  • reflexio/server/services/storage/sqlite_storage/_base.py
  • reflexio/server/services/storage/sqlite_storage/_requests.py
  • reflexio/server/services/storage/sqlite_storage/_session_outcomes.py
  • tests/models/test_session_outcome_identity.py
  • tests/server/services/storage/sqlite_storage/test_session_outcome_migration.py
  • tests/server/services/storage/test_storage_contract_requests.py
  • tests/server/services/storage/test_storage_contract_session_outcomes.py
  • tests/server/services/test_search_metering_worker.py
  • tests/server/test_api_security_middleware.py
  • tests/server/test_create_app_capabilities.py

Comment thread tests/models/test_session_outcome_identity.py
@guangyu-reflexio

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

🧹 Nitpick comments (2)
tests/server/services/storage/sqlite_storage/test_session_outcome_migration.py (1)

113-127: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

__getattr__ does not cover implicit dunder lookups.

Python looks up special methods on the type, not the instance, so __getattr__ will not forward __iter__ or __next__ to the wrapped cursor. If the migration code ever iterates the cursor directly (for row in cursor), the wrapper raises TypeError instead of streaming, and the failure will look unrelated to the guard. Adding an explicit __iter__ that raises the same AssertionError as fetchall keeps the intent of the double clear.

♻️ Optional hardening of the cursor double
     def fetchall(self) -> Any:
         raise AssertionError("trajectory migration must not call fetchall")
 
+    def __iter__(self) -> Any:
+        raise AssertionError("trajectory migration must not iterate the cursor")
+
     def fetchmany(self, size: int) -> Any:
🤖 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/storage/sqlite_storage/test_session_outcome_migration.py`
around lines 113 - 127, Update _NoTrajectoryFetchAllCursor with an explicit
__iter__ method that raises the same AssertionError as fetchall, ensuring direct
cursor iteration fails with the intended guard instead of relying on
__getattr__.
tests/server/services/storage/test_storage_contract_session_outcomes.py (1)

680-702: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The mock relies on a fixed execute call order.

connection.execute.side_effect supplies exactly three cursors in a fixed order. Any added or reordered query inside get_session_outcome_context raises StopIteration instead of a clear failure. Consider keying the cursors by statement text so the test fails with a readable message and survives benign query additions.

🤖 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/storage/test_storage_contract_session_outcomes.py`
around lines 680 - 702, Update
test_sqlite_context_normalizes_nullable_request_source to replace the positional
connection.execute.side_effect list with a statement-aware dispatcher that
returns each mocked cursor based on the SQL statement, raising a clear assertion
for unexpected queries. Preserve the existing cursor results and assertions
while making the test resilient to query additions or reordering in
get_session_outcome_context.
🤖 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
`@tests/server/services/storage/sqlite_storage/test_session_outcome_migration.py`:
- Around line 113-127: Update _NoTrajectoryFetchAllCursor with an explicit
__iter__ method that raises the same AssertionError as fetchall, ensuring direct
cursor iteration fails with the intended guard instead of relying on
__getattr__.

In `@tests/server/services/storage/test_storage_contract_session_outcomes.py`:
- Around line 680-702: Update
test_sqlite_context_normalizes_nullable_request_source to replace the positional
connection.execute.side_effect list with a statement-aware dispatcher that
returns each mocked cursor based on the SQL statement, raising a clear assertion
for unexpected queries. Preserve the existing cursor results and assertions
while making the test resilient to query additions or reordering in
get_session_outcome_context.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1980a3fb-3235-4955-8bb8-1a37222bfcd4

📥 Commits

Reviewing files that changed from the base of the PR and between 3df6bfb and 855271b.

📒 Files selected for processing (4)
  • reflexio/server/services/storage/sqlite_storage/_session_outcomes.py
  • tests/models/test_session_outcome_identity.py
  • tests/server/services/storage/sqlite_storage/test_session_outcome_migration.py
  • tests/server/services/storage/test_storage_contract_session_outcomes.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • reflexio/server/services/storage/sqlite_storage/_session_outcomes.py

@guangyu-reflexio

Copy link
Copy Markdown
Contributor Author

Addressed both current-head CodeRabbit nitpicks in bb9f6ae: the cursor double now rejects implicit iteration explicitly, and the nullable-source test dispatches mocked cursors by SQL statement. Focused verification: 27 passed; Ruff, format, and Pyright clean. @coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

@guangyu-reflexio Acknowledged. I will review the changes in bb9f6ae3.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@guangyu-reflexio
guangyu-reflexio marked this pull request as ready for review August 11, 2026 22:18
@guangyu-reflexio

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@guangyu-reflexio

Copy link
Copy Markdown
Contributor Author

The two review-body nitpicks are already addressed in current head bb9f6ae38: _NoTrajectoryFetchAllCursor.__iter__ now fails explicitly so the migration remains bound to chunked fetchmany(), and the nullable-source test now uses a SQL-aware dispatcher with a clear assertion for unexpected statements. Focused verification passed (2 passed), and there are no unresolved review threads.

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