Skip to content

fix: Resolve #1528 - fix: enforce ALLOWED_UPDATE_FIELDS in update_memory (#770)#1529

Closed
washim0988-art wants to merge 2 commits into
moorcheh-ai:mainfrom
washim0988-art:fix/bounty-1528-1784486785
Closed

fix: Resolve #1528 - fix: enforce ALLOWED_UPDATE_FIELDS in update_memory (#770)#1529
washim0988-art wants to merge 2 commits into
moorcheh-ai:mainfrom
washim0988-art:fix/bounty-1528-1784486785

Conversation

@washim0988-art

@washim0988-art washim0988-art commented Jul 19, 2026

Copy link
Copy Markdown

Resolves #1528

Built autonomously by Cloud Agent.
Bounty payout address: Gq46qirFLJY3qptAWkAmAeDfGVAE4MYYGTcRmpKjsyR

Summary by CodeRabbit

  • Bug Fixes
    • Improved updating of existing memory records by applying an approved-field whitelist, preventing unsupported fields from being changed.
    • Preserves existing record details (such as status, ownership, provenance, and source references) while applying permitted updates.
    • Adds clear error behavior when attempting to update a memory record that does not exist.

Signed-off-by: washim0988-art <islowashin@gmail.com>
@github-actions github-actions Bot added the bounty This issue has a bounty attached to it label Jul 19, 2026
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds update_memory, which filters updates against ALLOWED_UPDATE_FIELDS, loads the existing memory, preserves existing fields, and returns a reconstructed MemoryRecord.

Changes

Memory update enforcement

Layer / File(s) Summary
Filtered memory update helper
fix_1528.py
update_memory loads the existing record, logs and drops non-whitelisted fields, overlays allowed updates, supports multiple object-to-dict formats, and returns a reconstructed MemoryRecord.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 states the core fix: enforcing ALLOWED_UPDATE_FIELDS in update_memory for issue #1528.
Linked Issues check ✅ Passed The changes filter update fields, warn on dropped protected fields, and preserve allowed updates as required by #1528.
Out of Scope Changes check ✅ Passed No clear unrelated changes beyond the requested memory update enforcement fix are evident.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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: 4

🤖 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 `@fix_1528.py`:
- Around line 6-18: Update the MemoryRecord reconstruction to preserve the
complete existing record, including required agent_id and fields such as
created_at, expires_at, and ttl_seconds. In the update flow around
updated_memory, normalize memory first and overlay only values from
allowed_updates, while retaining all other existing fields unchanged.
- Line 1: Update the constants import in fix_1528.py to reference the
package-defined ALLOWED_UPDATE_FIELDS from memanto.app.constants using the
project’s established package-qualified or relative import style, rather than
the top-level constants module.
- Line 4: Update the filtering logic around allowed_updates to identify fields
present in updates but absent from ALLOWED_UPDATE_FIELDS, and emit a warning
listing only those dropped field names before proceeding with the filtered
update. Preserve the existing whitelist behavior and never include removed field
values in the log.
- Around line 3-5: Update update_memory to use the MemoryReadService.get_memory
contract by supplying the required namespace, using the appropriate service
access, and handling a None result before dereferencing memory. Preserve the
allowed_updates filtering and return the established not-found outcome when the
memory record is absent.
🪄 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 Plus

Run ID: 1ce3f332-b123-4ea4-bf4c-9c83717111ab

📥 Commits

Reviewing files that changed from the base of the PR and between 32d83bd and 1d63cc3.

📒 Files selected for processing (1)
  • fix_1528.py

Comment thread fix_1528.py Outdated
Comment thread fix_1528.py Outdated
Comment on lines +3 to +5
def update_memory(memory_id, updates):
allowed_updates = {key: value for key, value in updates.items() if key in ALLOWED_UPDATE_FIELDS}
memory = get_memory(memory_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Align with the get_memory contract and handle missing records.

MemoryReadService.get_memory requires (memory_id, namespace) and returns a dictionary or None. This helper has no namespace, calls an undefined one-argument get_memory, and dereferences the result without a not-found check.

🤖 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 `@fix_1528.py` around lines 3 - 5, Update update_memory to use the
MemoryReadService.get_memory contract by supplying the required namespace, using
the appropriate service access, and handling a None result before dereferencing
memory. Preserve the allowed_updates filtering and return the established
not-found outcome when the memory record is absent.

Comment thread fix_1528.py Outdated
Comment thread fix_1528.py Outdated
Refactor update_memory function to include namespace handling, logging for dropped fields, and reconstruct the MemoryRecord from existing data.

@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: 3

🧹 Nitpick comments (1)
fix_1528.py (1)

17-18: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use a module-level logger.

Since it is recommended to remove the global logging.basicConfig() at the module level, consider using a module-specific logger here instead of the root logger.

♻️ Proposed refactor
     if dropped_fields:
-        logging.warning(f"Update dropped protected fields: {', '.join(dropped_fields)}")
+        logging.getLogger(__name__).warning(f"Update dropped protected fields: {', '.join(dropped_fields)}")
🤖 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 `@fix_1528.py` around lines 17 - 18, Replace the root logger call in the
dropped-fields warning block with a module-level logger, defining or reusing the
module-specific logger consistently in fix_1528.py. Keep the existing warning
message and conditional behavior unchanged.
🤖 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 `@fix_1528.py`:
- Around line 1-5: Add the missing MemoryRecord import and resolve get_memory
through its properly scoped MemoryReadService API before update_memory uses
them. Update update_memory or its callers to provide the service instance as
needed, while preserving the existing memory update behavior and avoiding an
undefined global get_memory reference.
- Around line 25-30: Update the memory conversion block to handle the dictionary
returned by get_memory directly, copying its contents into existing_data before
the Pydantic and standard-object fallbacks. Avoid calling vars(memory) when
memory is a dictionary, while preserving the existing model_dump and dict
compatibility paths.
- Line 4: Remove the module-level logging.basicConfig() call from fix_1528.py,
leaving logging configuration to the application's entry point while preserving
the module's other logging behavior.

---

Nitpick comments:
In `@fix_1528.py`:
- Around line 17-18: Replace the root logger call in the dropped-fields warning
block with a module-level logger, defining or reusing the module-specific logger
consistently in fix_1528.py. Keep the existing warning message and conditional
behavior unchanged.
🪄 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 Plus

Run ID: f6ce7e48-b635-4359-92e0-e2eb495e8042

📥 Commits

Reviewing files that changed from the base of the PR and between 1d63cc3 and 1c06a61.

📒 Files selected for processing (1)
  • fix_1528.py

Comment thread fix_1528.py
Comment on lines +1 to +5
import logging
from memanto.app.constants import ALLOWED_UPDATE_FIELDS

logging.basicConfig(level=logging.INFO)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Add missing imports for MemoryRecord and get_memory.

MemoryRecord and get_memory are used in the update_memory function but are not imported in this file. This will cause a NameError at runtime. Note that get_memory appears to be a method of MemoryReadService according to the provided context, so you may need to either instantiate the service, pass the service as an argument, or import a properly scoped wrapper function.

🤖 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 `@fix_1528.py` around lines 1 - 5, Add the missing MemoryRecord import and
resolve get_memory through its properly scoped MemoryReadService API before
update_memory uses them. Update update_memory or its callers to provide the
service instance as needed, while preserving the existing memory update behavior
and avoiding an undefined global get_memory reference.

Comment thread fix_1528.py
import logging
from memanto.app.constants import ALLOWED_UPDATE_FIELDS

logging.basicConfig(level=logging.INFO)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove module-level logging.basicConfig().

Calling logging.basicConfig() at the module level is an anti-pattern as it forces a global logging configuration on the entire application when this module is imported. The application's entry point should be responsible for configuring logging.

♻️ Proposed fix
-logging.basicConfig(level=logging.INFO)
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logging.basicConfig(level=logging.INFO)
🤖 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 `@fix_1528.py` at line 4, Remove the module-level logging.basicConfig() call
from fix_1528.py, leaving logging configuration to the application's entry point
while preserving the module's other logging behavior.

Comment thread fix_1528.py
Comment on lines +25 to +30
if hasattr(memory, 'model_dump'): # Pydantic v2
existing_data = memory.model_dump()
elif hasattr(memory, 'dict'): # Pydantic v1
existing_data = memory.dict()
else: # Standard object
existing_data = vars(memory).copy()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Handle dict return type from get_memory directly to avoid TypeError.

According to the get_memory contract, the returned memory object is a dictionary, not a Pydantic model or an object with a __dict__ attribute.
Calling vars(memory) on a dictionary will raise a TypeError: vars() argument must have __dict__ attribute.

Since memory is already a dictionary, you can safely simplify this block by handling dictionaries directly.

🐛 Proposed fix
-    if hasattr(memory, 'model_dump'):  # Pydantic v2
-        existing_data = memory.model_dump()
-    elif hasattr(memory, 'dict'):      # Pydantic v1
-        existing_data = memory.dict()
-    else:                               # Standard object
-        existing_data = vars(memory).copy()
+    if isinstance(memory, dict):
+        existing_data = memory.copy()
+    elif hasattr(memory, 'model_dump'):  # Pydantic v2
+        existing_data = memory.model_dump()
+    elif hasattr(memory, 'dict'):        # Pydantic v1
+        existing_data = memory.dict()
+    else:                                 # Standard object
+        existing_data = vars(memory).copy()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if hasattr(memory, 'model_dump'): # Pydantic v2
existing_data = memory.model_dump()
elif hasattr(memory, 'dict'): # Pydantic v1
existing_data = memory.dict()
else: # Standard object
existing_data = vars(memory).copy()
if isinstance(memory, dict):
existing_data = memory.copy()
elif hasattr(memory, 'model_dump'): # Pydantic v2
existing_data = memory.model_dump()
elif hasattr(memory, 'dict'): # Pydantic v1
existing_data = memory.dict()
else: # Standard object
existing_data = vars(memory).copy()
🤖 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 `@fix_1528.py` around lines 25 - 30, Update the memory conversion block to
handle the dictionary returned by get_memory directly, copying its contents into
existing_data before the Pydantic and standard-object fallbacks. Avoid calling
vars(memory) when memory is a dictionary, while preserving the existing
model_dump and dict compatibility paths.

washim0988-art added a commit to washim0988-art/memanto that referenced this pull request Jul 19, 2026
Signed-off-by: washim0988-art <islowashin@gmail.com>
@Xenogents

Copy link
Copy Markdown
Collaborator

Thanks for your contributions! We're closing this PR as this is a duplicate.

@Xenogents Xenogents closed this Jul 21, 2026
@washim0988-art
washim0988-art deleted the fix/bounty-1528-1784486785 branch July 21, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bounty This issue has a bounty attached to it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants