feat: agent state compaction#3265
Conversation
|
Caution Review failedPull request was closed or merged during review Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds cutoff-based agent input-log capture and LLM summarization compaction, integrates summarizer/compactor into TaskAgentWorkflow with content-addressed system-prompt persistence, migrates AgentDatabase to schema v11 with a partial unique index for agent_links, introduces checklist archival and a runtime compaction flag/UI/i18n, and updates tests and docs. ChangesAgent compaction, DB v11 migration, archival, workflow, and UI
Sequence Diagram(s)sequenceDiagram
participant Workflow
participant Compactor
participant LLM as CloudInferenceAPI
participant DB
Workflow->>DB: read `enable_agent_compaction` flag
Workflow->>Compactor: maybeCompact(agentId, budget, retainTokens, inline decisions)
Compactor->>LLM: summarize(chunked tail, model, provider)
LLM-->>Compactor: streamed summary text
Compactor-->>Workflow: checkpoint (cutoff, coveredSources) + visible tail
Workflow->>DB: upsert system prompt payload (content-digest)
Workflow-->>DB: persist per-wake system message + user message/context
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Code Review
This pull request introduces 'Agent memory compaction' to summarize older task-log entries into a rolling memory, keeping the wake context compact. It updates task agents to conditionally update reports only on material changes, allows proposing checklist item archivals, and displays exact system prompts in the conversation view. The review identified two key issues: a compile-time syntax error in agent_log_compactor.dart due to invalid Dart syntax (?loaded.event.contentDigest), and a schema mismatch caused by the missing idx_agent_links_active_to_type index definition in the agent_database.drift file.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/features/ai/functions/lotti_checklist_update_handler.dart (1)
141-146:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate validation hints to mention
isArchivedas a valid update field.The handler now accepts
isArchived, but these two guidance strings still only mentionisChecked/title, which can mislead retry behavior.Suggested patch
- 'Invalid or missing "items". Provide a JSON array of update objects: ' - '{"items": [{"id": "...", "isChecked": true}]}', + 'Invalid or missing "items". Provide a JSON array of update objects: ' + '{"items": [{"id": "...", "isChecked": true}, {"id": "...", "isArchived": true}]}', @@ - 'Item at index $i is not an object. Each item must be an object ' - 'with id and at least one of isChecked or title.', + 'Item at index $i is not an object. Each item must be an object ' + 'with id and at least one of isChecked, title, or isArchived.',Also applies to: 168-173
🤖 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 `@lib/features/ai/functions/lotti_checklist_update_handler.dart` around lines 141 - 146, Update the user-facing validation/help strings in lotti_checklist_update_handler.dart to include `isArchived` as a valid update field; specifically change the message passed to _createErrorResult (the "Provide a JSON array..." hint) and the other similar guidance string later in the same handler so they list {"id": "...", "isChecked": true, "isArchived": true} (and mention title if present) — search for the current strings that mention only "isChecked" / "title" in the handler and add `isArchived` to both messages.
🤖 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 `@docs/implementation_plans/2026-06-02_executor_idempotency_fencing_plan.md`:
- Line 128: The phrase "synced lease state + fence-rejection-on-reconnect,
degrades to this design" uses nonstandard/awkward wording; change the defer
rationale to clearly phrase that the system "needs lease state to be synced" (or
equivalent like "requires the lease state to be synced") and keep the rest of
the clause intact so it reads e.g. "needs lease state to be synced +
fence-rejection-on-reconnect, degrades to this design." Update the sentence
containing "synced lease state + fence-rejection-on-reconnect, degrades to this
design" accordingly to improve clarity.
In `@lib/features/agents/projection/compaction_summary.dart`:
- Around line 155-159: The body text normalization currently uses rawBody.trim()
which preserves embedded newlines; update the normalization in the compaction
logic (references: rawBody, body, editedTag and the similar block around lines
166-168) to collapse all newline and excessive whitespace into single spaces and
then trim so each entry is a single line (e.g., replace sequences of
newline/whitespace with a single space, then .trim()) to maintain the compacted
tail's line-oriented contract.
In `@lib/features/agents/projection/decision_events.dart`:
- Around line 27-33: The summary and reason may still contain embedded
newlines/tabs because only trim() is used; normalize internal whitespace for
entry.humanSummary and entry.reason so they become single-line strings (e.g.,
collapse all runs of whitespace to a single space) before using them to build
summary, trimmedReason and reasonSuffix in decision_events.dart; update the
variables summary and trimmedReason to perform this normalization (split/join or
regex replace) so the returned ledger line always stays on one line.
In `@lib/features/agents/README.md`:
- Around line 442-465: The README currently contradicts itself about whether the
read-path and summary-checkpoint flow are active behind the
enable_agent_compaction flag; reconcile the two sections so there's a single
source of truth by either (A) marking the earlier block as "Planned
Improvements" and noting that AgentLogCompactor.maybeCompact,
AgentLogCompactor.assembleContext, AgentLogLlmSummarizer and
buildTaskStateMarkdown are not yet wired into the live wake read-path, or (B)
updating the later "Planned Improvements" lines to state that these features are
already live when enable_agent_compaction is true (with
TaskAgentWorkflow.compactionEnabled remaining a test override); pick one
approach and make both sections consistent about the runtime behavior and flag
semantics.
In `@lib/features/agents/tools/agent_tool_registry.dart`:
- Around line 228-235: The item schema currently requires "id" but allows `{
"id": "..." }`; update the JSON schema for the checklist item (the schema object
inside the parameters for the checklist-update tool) to require "id" and also
add an anyOf constraint so at least one of isChecked, title, or isArchived is
present (e.g. keep required: ["id"] and add anyOf:
[{required:["isChecked"]},{required:["title"]},{required:["isArchived"]}]).
Apply the same change to the other identical item schema occurrence mentioned in
the comment so the validator rejects no-op items.
In `@lib/features/agents/workflow/task_agent_workflow.dart`:
- Around line 360-363: The DB read for enableAgentCompactionFlag can throw and
currently will abort wake execution; change the compactionOn initialization to
catch failures from journalDb.getConfigFlag and default to false so compaction
remains optional: when computing compactionOn (which currently uses
compactionEnabled ?? await journalDb.getConfigFlag(enableAgentCompactionFlag)),
wrap the await call in a try/catch (or use a safe helper) to return false on any
error, then continue constructing compactor as before (referencing compactionOn,
compactionEnabled, journalDb.getConfigFlag, enableAgentCompactionFlag, and
compactor).
In `@test/features/agents/workflow/task_state_markdown_test.dart`:
- Around line 62-65: Replace the brittle .singleWhere lookup in the test with a
safer .firstWhere (or .where(...).first) and include the closing parenthesis in
the match so you match the full "(id: ...)" token; specifically change the
expression on text.split('\n') that currently uses .singleWhere((l) =>
l.contains('(id: ${item.id}')) to e.g. .firstWhere((l) => l.contains('(id:
${item.id})'), orElse: () => throw StateError('no matching line for id
${item.id}')) and keep the existing expect assertion using item.completed.
---
Outside diff comments:
In `@lib/features/ai/functions/lotti_checklist_update_handler.dart`:
- Around line 141-146: Update the user-facing validation/help strings in
lotti_checklist_update_handler.dart to include `isArchived` as a valid update
field; specifically change the message passed to _createErrorResult (the
"Provide a JSON array..." hint) and the other similar guidance string later in
the same handler so they list {"id": "...", "isChecked": true, "isArchived":
true} (and mention title if present) — search for the current strings that
mention only "isChecked" / "title" in the handler and add `isArchived` to both
messages.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 14544369-788b-4585-9f3d-3e4b9d03f7a1
📒 Files selected for processing (62)
CHANGELOG.mddocs/implementation_plans/2026-06-02_executor_idempotency_fencing_plan.mdflatpak/com.matthiasn.lotti.metainfo.xmllib/database/journal_db/config_flags.dartlib/features/agents/README.mdlib/features/agents/database/agent_database.dartlib/features/agents/database/agent_database.driftlib/features/agents/database/agent_database.g.dartlib/features/agents/database/agent_repository.dartlib/features/agents/projection/compaction_summary.dartlib/features/agents/projection/decision_events.dartlib/features/agents/projection/input_events.dartlib/features/agents/projection/side_effect_key.dartlib/features/agents/service/agent_log_llm_summarizer.dartlib/features/agents/state/agent_providers.g.dartlib/features/agents/state/agent_query_providers.dartlib/features/agents/state/agent_workflow_providers.dartlib/features/agents/state/agent_workflow_providers.g.dartlib/features/agents/sync/agent_log_compactor.dartlib/features/agents/tools/agent_tool_registry.dartlib/features/agents/ui/agent_activity_log.dartlib/features/agents/workflow/change_set_builder.dartlib/features/agents/workflow/task_agent_strategy.dartlib/features/agents/workflow/task_agent_workflow.dartlib/features/agents/workflow/task_source_renderer.dartlib/features/agents/workflow/task_state_markdown.dartlib/features/ai/functions/lotti_checklist_update_handler.dartlib/features/ai/repository/ai_input_repository.dartlib/features/settings/ui/pages/flags_page.dartlib/l10n/app_cs.arblib/l10n/app_de.arblib/l10n/app_en.arblib/l10n/app_es.arblib/l10n/app_fr.arblib/l10n/app_localizations.dartlib/l10n/app_localizations_cs.dartlib/l10n/app_localizations_de.dartlib/l10n/app_localizations_en.dartlib/l10n/app_localizations_es.dartlib/l10n/app_localizations_fr.dartlib/l10n/app_localizations_ro.dartlib/l10n/app_ro.arblib/utils/consts.darttest/features/agents/database/agent_database_test.darttest/features/agents/projection/compaction_summary_test.darttest/features/agents/projection/decision_events_test.darttest/features/agents/projection/input_events_test.darttest/features/agents/projection/side_effect_key_test.darttest/features/agents/service/agent_log_llm_summarizer_test.darttest/features/agents/state/agent_providers_test.darttest/features/agents/sync/agent_log_compactor_test.darttest/features/agents/ui/agent_activity_log_test.darttest/features/agents/workflow/change_set_builder_test.darttest/features/agents/workflow/task_agent_strategy_test.darttest/features/agents/workflow/task_agent_workflow_test.darttest/features/agents/workflow/task_agent_workflow_test_helpers.darttest/features/agents/workflow/task_source_renderer_test.darttest/features/agents/workflow/task_state_markdown_test.darttest/features/ai/functions/lotti_checklist_update_handler_test.darttest/features/ai/repository/ai_input_repository_test.darttest/helpers/fallbacks.darttest/mocks/mocks.dart
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3265 +/- ##
========================================
Coverage 99.11% 99.11%
========================================
Files 1196 1200 +4
Lines 96845 97165 +320
========================================
+ Hits 95987 96306 +319
- Misses 858 859 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
bb9c102 to
4f1f959
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
lib/features/agents/projection/compaction_summary.dart (1)
185-189:⚠️ Potential issue | 🟠 Major | ⚡ Quick winNormalize body text to keep the promised single-line format.
rawBody.trim()preserves embedded newlines, so one captured entry can spill into multiple lines and break the compacted tail's line-oriented contract.💡 Suggested fix
- final body = rawBody.trim(); + final body = rawBody.replaceAll(RegExp(r'\s+'), ' ').trim();🤖 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 `@lib/features/agents/projection/compaction_summary.dart` around lines 185 - 189, The code builds rawBody/body which can contain embedded newlines and thus violate the single-line compacted tail; update the normalization so rawBody collapses all whitespace/newlines into a single space before trimming (e.g. replace runs of whitespace like \s+ with a single space on rawBody) so that body = normalizedRawBody.trim() yields a single-line string; apply this change around the rawBody/body construction (referencing rawBody, body, and editedTag) so entries never contain embedded newlines.
🤖 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.
Duplicate comments:
In `@lib/features/agents/projection/compaction_summary.dart`:
- Around line 185-189: The code builds rawBody/body which can contain embedded
newlines and thus violate the single-line compacted tail; update the
normalization so rawBody collapses all whitespace/newlines into a single space
before trimming (e.g. replace runs of whitespace like \s+ with a single space on
rawBody) so that body = normalizedRawBody.trim() yields a single-line string;
apply this change around the rawBody/body construction (referencing rawBody,
body, and editedTag) so entries never contain embedded newlines.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 724841ef-be15-40f4-a27f-6eef86b03409
📒 Files selected for processing (11)
docs/implementation_plans/2026-06-02_executor_idempotency_fencing_plan.mdlib/features/agents/README.mdlib/features/agents/projection/compaction_summary.dartlib/features/agents/projection/decision_events.dartlib/features/agents/projection/join_plan.dartlib/features/agents/sync/agent_log_compactor.dartlib/features/agents/workflow/task_agent_workflow.darttest/database/database_test.darttest/features/agents/projection/compaction_summary_test.darttest/features/agents/projection/join_plan_test.darttest/features/agents/sync/agent_log_compactor_test.dart
✅ Files skipped from review due to trivial changes (4)
- lib/features/agents/projection/join_plan.dart
- test/features/agents/projection/join_plan_test.dart
- docs/implementation_plans/2026-06-02_executor_idempotency_fencing_plan.md
- lib/features/agents/README.md
🚧 Files skipped from review as they are similar to previous changes (3)
- lib/features/agents/projection/decision_events.dart
- lib/features/agents/sync/agent_log_compactor.dart
- lib/features/agents/workflow/task_agent_workflow.dart
4f1f959 to
20499ac
Compare
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces agent memory compaction for task agents, allowing older task-log entries to be summarized into a rolling memory while keeping recent entries verbatim. It also adds support for archiving duplicate checklist items, makes report updates conditional on material changes, and improves the agent conversation view. Database schema version 11 is introduced to allow multiple capture links to share a single payload digest. The review feedback suggests passing compactedTaskLog directly to _buildUserMessage to avoid mutable class-level fields, optimizing the invalidated check in selectActiveSummary via direct map lookups, and adding a null-assertion operator to loaded.event.inlineContent to prevent static analysis warnings.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
test/features/agents/projection/compaction_summary_test.dart (1)
127-145: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy liftExtend the oracle to validate the completeness check.
The property test's oracle (lines 128-132) only validates retraction invalidation, but the production
selectActiveSummaryalso performs a completeness check: every non-suppressed event at or before the cutoff must be covered bycoveredSources(context snippet 1, lines 105-110).Currently the test passes
log: _log(retractions: retractions)with noevents:parameter, soeventsdefaults to[]and the completeness check always passes. This means the oracle doesn't exercise that validation branch.To achieve full property coverage, generate sample events alongside the retractions and extend the oracle's
isValidpredicate to mirror the production completeness logic:final incomplete = log.events.any((event) { if (event.position.isAfter(s.cutoff!)) return false; final retractedAt = latestRetraction[event.contentEntryId]; final suppressed = retractedAt != null && retractedAt.isAfter(event.position); if (suppressed) return false; return !s.coveredSources.containsKey(event.contentEntryId); }); return !incomplete && /* existing retraction check */;(The example tests at lines 236-292 already cover completeness scenarios, so this is a property-test-specific improvement.)
🤖 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 `@test/features/agents/projection/compaction_summary_test.dart` around lines 127 - 145, The oracle's isValid(SummaryCheckpoint s) only checks retractions but ignores the completeness check performed by selectActiveSummary; update isValid to also validate that every non-suppressed event at or before s.cutoff is covered by s.coveredSources by inspecting log.events and latestRetraction: for each event where event.position <= s.cutoff, skip if suppressed (latestRetraction[event.contentEntryId] exists and isAfter(event.position)), otherwise require s.coveredSources contains event.contentEntryId; only return true when both the retraction check and this completeness check pass so the property test exercises the same logic as selectActiveSummary.test/features/agents/workflow/task_agent_workflow_test.dart (2)
819-831:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winReturn the requested digest as the cached payload id.
getEntity(digest)should hand back an entity whoseidis that same digest. Returning'sha256-v1:existing'here weakens the content-addressed contract and would still let a badcontentEntryIdimplementation pass.Suggested fix
when( () => mockAgentRepository.getEntity( any(that: startsWith('sha256-v1:')), ), - ).thenAnswer( - (_) async => AgentDomainEntity.agentMessagePayload( - id: 'sha256-v1:existing', + ).thenAnswer((invocation) async { + final digest = invocation.positionalArguments.single as String; + return AgentDomainEntity.agentMessagePayload( + id: digest, agentId: 'shared-input-content', createdAt: DateTime(2024), vectorClock: null, content: const {'role': 'system', 'text': 'cached'}, - ), - ); + ); + });🤖 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 `@test/features/agents/workflow/task_agent_workflow_test.dart` around lines 819 - 831, The test stub for mockAgentRepository.getEntity returns a hardcoded id ('sha256-v1:existing') instead of echoing the requested digest; update the when(...).thenAnswer(...) to capture the incoming digest argument and use it as the returned entity's id so the mocked content-addressing contract holds. Specifically, change the thenAnswer closure for mockAgentRepository.getEntity to read the digest from the invocation (e.g., invocation.positionalArguments.first or invocation.positionalArguments[0]) and pass that value as the id to AgentDomainEntity.agentMessagePayload (while keeping the other fields the same).
2784-2801:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winThe “null id” linked-task case is no longer actually covered.
parseLinkedTasks()rewrites a missingidto'linked-task', so the “Null ID Task” row becomes a valid lookup candidate instead of malformed input. That means this test only proves the empty-string case, not the missing-id branch it describes.Also applies to: 4428-4458
🤖 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 `@test/features/agents/workflow/task_agent_workflow_test.dart` around lines 2784 - 2801, parseLinkedTasks currently replaces a missing id with the string 'linked-task', which hides the "null id" test case; change the id assignment to preserve null by using id = row['id'] as String? (no default) and update the title fallback to (row['title'] as String?) ?? id ?? 'linked-task' so empty-string and missing-id cases are handled differently; apply the same change to the other parseLinkedTasks occurrences that construct AiLinkedTaskContext to ensure tests for null-id remain valid (refer to parseLinkedTasks and AiLinkedTaskContext usages).
🧹 Nitpick comments (2)
lib/features/agents/README.md (1)
319-323: ⚡ Quick winConsider clarifying "only capture runs" phrasing.
Line 322's phrase "with the flag off, only capture runs and the wake reads the journal as before" could be misread. The intent appears to be that capture always runs (building the event log in shadow), but when the flag is off, the wake ignores the captured events and reads the journal directly—so "only" scopes to "capture is the only new-system component active."
A reader might instead parse it as "nothing happens except capture," which obscures that the full legacy wake path still executes. Consider rephrasing for clarity, e.g., "with the flag off, capture still runs (building the event log in shadow) but the wake reads the journal as before—byte-identical behavior."
🤖 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 `@lib/features/agents/README.md` around lines 319 - 323, Reword the ambiguous sentence that describes behavior when enable_agent_compaction is false: update the sentence around "with the flag off, only capture runs and the wake reads the journal as before" to explicitly state that capture still runs in shadow (building the event log) while the legacy wake path continues to read the journal unchanged; reference the flag name enable_agent_compaction and ADR 0017/ADR 0020 if helpful and produce a clarified line such as "with the flag off, capture still runs (building the event log in shadow), but the wake reads the journal as before — byte-identical behavior."test/features/agents/workflow/task_agent_workflow_test.dart (1)
761-763: ⚡ Quick winAssert the expected persisted entities instead of the total write count.
This already had to move once when prompt persistence added more writes. Another benign persistence change will break the test again without regressing the behavior under test.
🤖 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 `@test/features/agents/workflow/task_agent_workflow_test.dart` around lines 761 - 763, Replace the brittle numeric assertion on mockSyncService.upsertEntity with assertions that the expected persisted entities (e.g., the system prompt entity, the user message entity, and the state update entity) were upserted: capture the arguments passed to mockSyncService.upsertEntity (using the mock's capture mechanism or an ArgumentCaptor equivalent), then assert the captured list contains entities with the expected identifiers/types/contents (inspect properties such as role/messageId/payload) rather than asserting .called(5); focus assertions on upsertEntity and the specific entity properties to make the test resilient to unrelated persistence changes.
🤖 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.
Outside diff comments:
In `@test/features/agents/projection/compaction_summary_test.dart`:
- Around line 127-145: The oracle's isValid(SummaryCheckpoint s) only checks
retractions but ignores the completeness check performed by selectActiveSummary;
update isValid to also validate that every non-suppressed event at or before
s.cutoff is covered by s.coveredSources by inspecting log.events and
latestRetraction: for each event where event.position <= s.cutoff, skip if
suppressed (latestRetraction[event.contentEntryId] exists and
isAfter(event.position)), otherwise require s.coveredSources contains
event.contentEntryId; only return true when both the retraction check and this
completeness check pass so the property test exercises the same logic as
selectActiveSummary.
In `@test/features/agents/workflow/task_agent_workflow_test.dart`:
- Around line 819-831: The test stub for mockAgentRepository.getEntity returns a
hardcoded id ('sha256-v1:existing') instead of echoing the requested digest;
update the when(...).thenAnswer(...) to capture the incoming digest argument and
use it as the returned entity's id so the mocked content-addressing contract
holds. Specifically, change the thenAnswer closure for
mockAgentRepository.getEntity to read the digest from the invocation (e.g.,
invocation.positionalArguments.first or invocation.positionalArguments[0]) and
pass that value as the id to AgentDomainEntity.agentMessagePayload (while
keeping the other fields the same).
- Around line 2784-2801: parseLinkedTasks currently replaces a missing id with
the string 'linked-task', which hides the "null id" test case; change the id
assignment to preserve null by using id = row['id'] as String? (no default) and
update the title fallback to (row['title'] as String?) ?? id ?? 'linked-task' so
empty-string and missing-id cases are handled differently; apply the same change
to the other parseLinkedTasks occurrences that construct AiLinkedTaskContext to
ensure tests for null-id remain valid (refer to parseLinkedTasks and
AiLinkedTaskContext usages).
---
Nitpick comments:
In `@lib/features/agents/README.md`:
- Around line 319-323: Reword the ambiguous sentence that describes behavior
when enable_agent_compaction is false: update the sentence around "with the flag
off, only capture runs and the wake reads the journal as before" to explicitly
state that capture still runs in shadow (building the event log) while the
legacy wake path continues to read the journal unchanged; reference the flag
name enable_agent_compaction and ADR 0017/ADR 0020 if helpful and produce a
clarified line such as "with the flag off, capture still runs (building the
event log in shadow), but the wake reads the journal as before — byte-identical
behavior."
In `@test/features/agents/workflow/task_agent_workflow_test.dart`:
- Around line 761-763: Replace the brittle numeric assertion on
mockSyncService.upsertEntity with assertions that the expected persisted
entities (e.g., the system prompt entity, the user message entity, and the state
update entity) were upserted: capture the arguments passed to
mockSyncService.upsertEntity (using the mock's capture mechanism or an
ArgumentCaptor equivalent), then assert the captured list contains entities with
the expected identifiers/types/contents (inspect properties such as
role/messageId/payload) rather than asserting .called(5); focus assertions on
upsertEntity and the specific entity properties to make the test resilient to
unrelated persistence changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 71de289d-d577-44a4-87fc-5a6774a9a35e
📒 Files selected for processing (13)
docs/implementation_plans/2026-06-02_executor_idempotency_fencing_plan.mdlib/features/agents/README.mdlib/features/agents/projection/compaction_summary.dartlib/features/agents/projection/decision_events.dartlib/features/agents/projection/join_plan.dartlib/features/agents/sync/agent_log_compactor.dartlib/features/agents/workflow/task_agent_workflow.darttest/database/database_test.darttest/features/agents/projection/compaction_summary_test.darttest/features/agents/projection/join_plan_test.darttest/features/agents/sync/agent_log_compactor_test.darttest/features/agents/workflow/task_agent_workflow_test.darttest/features/agents/workflow/task_state_markdown_test.dart
✅ Files skipped from review due to trivial changes (3)
- lib/features/agents/projection/join_plan.dart
- test/features/agents/projection/join_plan_test.dart
- docs/implementation_plans/2026-06-02_executor_idempotency_fencing_plan.md
🚧 Files skipped from review as they are similar to previous changes (7)
- test/database/database_test.dart
- lib/features/agents/projection/decision_events.dart
- lib/features/agents/projection/compaction_summary.dart
- test/features/agents/workflow/task_state_markdown_test.dart
- test/features/agents/sync/agent_log_compactor_test.dart
- lib/features/agents/sync/agent_log_compactor.dart
- lib/features/agents/workflow/task_agent_workflow.dart
Summary by CodeRabbit
New Features
Improvements