Skip to content

feat(memory): expand working-memory event semantics#567

Merged
jamiepine merged 4 commits intomainfrom
codex/land-memory-observation-p2
Apr 20, 2026
Merged

feat(memory): expand working-memory event semantics#567
jamiepine merged 4 commits intomainfrom
codex/land-memory-observation-p2

Conversation

@vsumner
Copy link
Copy Markdown
Collaborator

@vsumner vsumner commented Apr 15, 2026

Summary

  • Adds richer working-memory event semantics for deadlines, blockers, constraints, and terminal outcomes.
  • Classifies branch and worker summaries into typed conversational events when they use explicit prefixes.
  • Emits outcome/blocker events from task delegation, task completion, and duplicate worker suppression.
  • Aligns memory persistence prompts and tool schema with the expanded event taxonomy.
  • Hardens working-memory event decoding and makes event query ordering deterministic.
  • Updates working-memory design docs to match the runtime behavior.

Testing

  • cargo test -p spacebot task_update -- --test-threads=1 --nocapture
  • just preflight
  • just gate-pr

Notes

Note

AI Summary: This PR expands the working-memory event system to capture structured conversational semantics. The core addition is a new WorkingMemoryEventType enum with 17 event types covering outcomes, blockers, deadlines, constraints, and errors. Events are auto-classified from branch/worker summaries when they use explicit prefixes (e.g., "Outcome: ..."), enabling the memory system to track not just facts but the state and progress of work. The task_update tool now validates worker ownership before global task lookups, closing a security/authorization gap. Key files: src/memory/working.rs (event types + store), src/tools/task_update.rs (worker assignment validation), and updated memory docs.

Written by Tembo for commit 0af6765. This will update automatically on new commits.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 15, 2026

Walkthrough

Extends working-memory event taxonomy with new event types, updates prompts/docs and tool JSON schema, adds conversational prefix classification/formatting in the agent channel, makes SQL event ordering deterministic, changes tool emissions and task-store update flows, and updates tests across memory, tools, agent, and tasks.

Changes

Cohort / File(s) Summary
Documentation & Prompts
docs/design-docs/working-memory-implementation-plan.md, docs/design-docs/working-memory.md, prompts/en/memory_persistence.md.j2
Expanded documented and template-allowed events.event_type values (added deadline_set, blocked_on, constraint, outcome, plus previously added conversational types); updated persistence payload guidance and emission descriptions.
Working Memory Core
src/memory/working.rs
Added DeadlineSet, BlockedOn, Constraint, Outcome variants; added string/parse/format mappings; made SQL event queries deterministic by tie-breaking with id; changed row_to_event to return Result on unknown types and propagated decode errors; updated tests.
Agent Channel — Classification & Formatting
src/agent/channel.rs
Added helpers to parse/normalize optional <prefix>: <content> event summaries, map prefixes to event types, truncate UTF‑8 summaries, and format emission lines; refactored working-memory emission paths for branch/worker events; added unit tests for prefix handling and cancellation detection.
Memory Persistence Tool Schema
src/tools/memory_persistence_complete.rs
Expanded tool JSON schema and doc comments to accept the new event types in events[].event_type (added deadline_set, blocked_on, constraint, outcome).
Tool Event Emissions
src/tools/send_agent_message.rs, src/tools/spawn_worker.rs, src/tools/task_create.rs, src/tools/task_update.rs
Shifted many tool emissions to richer event types: send_agent_messageOutcome; spawn_worker emits BlockedOn for duplicate tasks; task_create may emit Outcome when created Done; task_update now uses status-transition-aware update paths and emits Outcome on transitions to Done (else TaskUpdate); added tests.
Task Store API & Tests
src/tasks/store.rs, src/tasks.rs
Introduced TaskUpdateResult and WorkerTaskUpdateResult types; added update_with_status_transition and update_worker_task using immediate transactions and in-transaction re-reads; exposed test helpers and re-exported new types; added tests.
Tests & Integration
src/tools/..., src/tasks/..., src/memory/...
Added and adjusted unit/integration tests to cover new event types, classification behavior, status-transition semantics, worker-scope errors, and unknown-event-type decode errors.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jamiepine
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.66% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(memory): expand working-memory event semantics' accurately captures the main change—extending the working-memory event system with new event types and semantics.
Description check ✅ Passed The description clearly explains the key objectives and scope of the PR, covering the new event types, auto-classification from prefixes, task validation improvements, and testing instructions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 codex/land-memory-observation-p2

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 and usage tips.

@vsumner vsumner requested a review from jamiepine April 15, 2026 21:47
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/memory/working.rs (1)

887-896: ⚠️ Potential issue | 🟠 Major

The activity-map topic-hint query no longer matches all branch results.

This still hard-codes event_type = 'branch_completed', but src/agent/channel.rs, Lines 3291-3303 now stores prefixed branch conclusions as Outcome, BlockedOn, Constraint, or DeadlineSet. Any channel whose latest branch result uses one of those prefixes will lose its topic hint entirely until this query distinguishes branch-originated events from worker events and includes the new branch terminal types.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/memory/working.rs` around lines 887 - 896, The query in working.rs that
builds the recent-branch-per-channel window (the variable named query) currently
filters only event_type = 'branch_completed' and therefore misses branch
terminal events now stored with prefixes (Outcome, BlockedOn, Constraint,
DeadlineSet); update the WHERE clause to either (1) match all branch-originated
terminal events by including those event_type values (e.g., IN
('branch_completed','Outcome','BlockedOn','Constraint','DeadlineSet')) or
(better) use the column that denotes the event origin/source (if present) to
restrict to branch-originated events while allowing any terminal event_type;
adjust the query construction in the same function/variable to include those
additional event types or the origin check so the ROW_NUMBER window returns the
true latest branch result per channel.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/agent/channel.rs`:
- Around line 3291-3303: The code is recording cancelled branches as
BranchCompleted because classify_conversational_event_summary is called before
detecting a cancellation; change the logic in the block that handles
BranchResult (where you call classify_conversational_event_summary,
format_conversational_event_summary, and self.deps.working_memory.emit) to first
inspect the BranchResult conclusion (emitted by cancel_branch_with_reason, which
prefixes with "Branch cancelled:") and, if it starts with that prefix, map the
event to a cancellation/error type (e.g., use
WorkingMemoryEventType::BranchCancelled or an appropriate error variant) and
format the summary without giving it a "completed" conversational prefix; then
call self.deps.working_memory.emit with that corrected event_type and formatted
summary (still using format_conversational_event_summary when appropriate) so
cancellations are not recorded as completed.

In `@src/tools/task_update.rs`:
- Around line 158-203: The current code reads previous_status via
self.task_store.get_by_number / get_by_worker_id before calling update(), which
allows races that produce duplicate terminal Outcome events; instead move the
old/new status comparison into the same transactional update path that writes
the task (e.g., change the TaskStore::update implementation to SELECT ... FOR
UPDATE or perform the update in a single DB transaction and return both previous
and new status), then use that returned (previous_status, new_status) to decide
emitting Outcome; update both the Branch and Worker branches (previous_status
usage and the worker validation around TaskUpdateScope::Branch /
TaskUpdateScope::Worker and the args checks) so the status comparison is
performed atomically inside the update routine rather than by a separate
get_by_number / get_by_worker_id call (also apply same fix for the code around
lines cited 250-267).

---

Outside diff comments:
In `@src/memory/working.rs`:
- Around line 887-896: The query in working.rs that builds the
recent-branch-per-channel window (the variable named query) currently filters
only event_type = 'branch_completed' and therefore misses branch terminal events
now stored with prefixes (Outcome, BlockedOn, Constraint, DeadlineSet); update
the WHERE clause to either (1) match all branch-originated terminal events by
including those event_type values (e.g., IN
('branch_completed','Outcome','BlockedOn','Constraint','DeadlineSet')) or
(better) use the column that denotes the event origin/source (if present) to
restrict to branch-originated events while allowing any terminal event_type;
adjust the query construction in the same function/variable to include those
additional event types or the origin check so the ROW_NUMBER window returns the
true latest branch result per channel.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 80f44010-bf77-4ca1-a305-c349aa4679cc

📥 Commits

Reviewing files that changed from the base of the PR and between df4ac10 and 0af6765.

📒 Files selected for processing (11)
  • docs/design-docs/working-memory-implementation-plan.md
  • docs/design-docs/working-memory.md
  • prompts/en/memory_persistence.md.j2
  • src/agent/channel.rs
  • src/memory/working.rs
  • src/tasks/store.rs
  • src/tools/memory_persistence_complete.rs
  • src/tools/send_agent_message.rs
  • src/tools/spawn_worker.rs
  • src/tools/task_create.rs
  • src/tools/task_update.rs

Comment thread src/agent/channel.rs Outdated
Comment thread src/tools/task_update.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/agent/channel.rs (1)

196-207: ⚠️ Potential issue | 🟡 Minor

Handle Branch cancelled. as a cancellation too.

cancel_branch_with_reason() still emits Branch cancelled. when the reason is empty (Lines 531-536), but these checks only recognize Branch cancelled:. In that path branch_success stays true and working memory falls back to BranchCompleted, so empty-reason cancels are still recorded as successful completions. Normalize both shapes before deriving success/event type.

Also applies to: 3302-3319

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/agent/channel.rs` around lines 196 - 207,
branch_working_memory_event_summary currently only recognizes
BRANCH_CANCELLED_PREFIX with a colon, so "Branch cancelled." (emitted by
cancel_branch_with_reason when reason is empty) is misclassified as success;
modify branch_working_memory_event_summary to normalize both "Branch cancelled:"
and "Branch cancelled." shapes before extracting the reason (e.g., check
strip_prefix for both BRANCH_CANCELLED_PREFIX variants or strip a trailing ':'
or '.' after matching), then trim and truncate the resulting reason and produce
the Error/summary branch as before; ensure the same normalization logic is
applied wherever branch cancellation is detected (references:
branch_working_memory_event_summary and cancel_branch_with_reason).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/tasks/store.rs`:
- Around line 431-454: The current lookup in src/tasks/store.rs uses a
worker-only query sorted by updated_at which can return stale rows when
worker_id is non-unique; instead first query the tasks table for the exact
(worker_id, task_number) row and handle success (use task_from_row and proceed),
and only if that exact row is missing do a second query for any row with
worker_id to decide between NotAssigned and WrongTask; update the logic around
the sqlx::query call, the bind usage for worker_id and task_number, and the
return paths that produce WorkerTaskUpdateResult::{NotAssigned, WrongTask} to
rely on the exact-match result rather than the updated_at-sorted worker-only
result.

---

Duplicate comments:
In `@src/agent/channel.rs`:
- Around line 196-207: branch_working_memory_event_summary currently only
recognizes BRANCH_CANCELLED_PREFIX with a colon, so "Branch cancelled." (emitted
by cancel_branch_with_reason when reason is empty) is misclassified as success;
modify branch_working_memory_event_summary to normalize both "Branch cancelled:"
and "Branch cancelled." shapes before extracting the reason (e.g., check
strip_prefix for both BRANCH_CANCELLED_PREFIX variants or strip a trailing ':'
or '.' after matching), then trim and truncate the resulting reason and produce
the Error/summary branch as before; ensure the same normalization logic is
applied wherever branch cancellation is detected (references:
branch_working_memory_event_summary and cancel_branch_with_reason).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0c6cf52d-9b6e-4906-8711-0a609cd80036

📥 Commits

Reviewing files that changed from the base of the PR and between 0af6765 and 99ea296.

📒 Files selected for processing (5)
  • src/agent/channel.rs
  • src/memory/working.rs
  • src/tasks.rs
  • src/tasks/store.rs
  • src/tools/task_update.rs
✅ Files skipped from review due to trivial changes (2)
  • src/tasks.rs
  • src/memory/working.rs

Comment thread src/tasks/store.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/tasks/store.rs (1)

807-855: Avoid hand-maintaining a second copy of the task schema in tests.

setup_test_store() now embeds the tasks and task_number_seq DDL inline. The next migration-only constraint or index change can leave these tests green against a database shape production never uses. Prefer bootstrapping the in-memory database through the real task migration path instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tasks/store.rs` around lines 807 - 855, Replace the hand-maintained
inline DDL in setup_test_store() with the project's canonical migration runner
so the in-memory test DB matches production schema; locate setup_test_store()
and remove the sqlx::query(...) blocks that create the tasks and task_number_seq
tables and the seed insert, then invoke the existing migration path (e.g. call
the project's migration function or sqlx::migrate!()/MIGRATOR runner used
elsewhere) against the SqlitePool so migrations create the tasks table,
task_number_seq, and seed values instead of embedding them here.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/tasks/store.rs`:
- Around line 807-855: Replace the hand-maintained inline DDL in
setup_test_store() with the project's canonical migration runner so the
in-memory test DB matches production schema; locate setup_test_store() and
remove the sqlx::query(...) blocks that create the tasks and task_number_seq
tables and the seed insert, then invoke the existing migration path (e.g. call
the project's migration function or sqlx::migrate!()/MIGRATOR runner used
elsewhere) against the SqlitePool so migrations create the tasks table,
task_number_seq, and seed values instead of embedding them here.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b19599a7-37d5-407d-9409-77d94fbf4b7a

📥 Commits

Reviewing files that changed from the base of the PR and between 99ea296 and 99fe556.

📒 Files selected for processing (2)
  • src/agent/channel.rs
  • src/tasks/store.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/agent/channel.rs

Copy link
Copy Markdown
Member

@jamiepine jamiepine left a comment

Choose a reason for hiding this comment

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

LGTM. Taxonomy extension is clean, atomic status transitions look right (BEGIN IMMEDIATE + SELECT/UPDATE in same tx), branch cancellation handling covers both shapes, outside-diff topic-hint query disambiguates branch vs worker via summary prefix. Test coverage is solid.

@jamiepine jamiepine enabled auto-merge April 20, 2026 00:54
@jamiepine jamiepine merged commit 0c55f54 into main Apr 20, 2026
4 checks passed
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.

2 participants