personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

think/activities: add ledger edit payload support

+61 -9
+37
tests/test_surfaces_ledger.py
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2026 sol pbc 3 + 4 + import pytest 5 + 6 + 7 + def test_append_edit_payload_validation(): 8 + from think.activities import append_edit 9 + 10 + merged = append_edit( 11 + {"id": "coding_090000_300"}, 12 + actor="cli:update", 13 + fields=["details"], 14 + note="updated", 15 + payload={"foo": "bar"}, 16 + ) 17 + assert merged["edits"][-1]["foo"] == "bar" 18 + 19 + with pytest.raises( 20 + ValueError, match="payload cannot overwrite canonical edit fields" 21 + ): 22 + append_edit( 23 + {"id": "coding_090000_300"}, 24 + actor="cli:update", 25 + fields=["details"], 26 + note="updated", 27 + payload={"timestamp": "x"}, 28 + ) 29 + 30 + with pytest.raises(TypeError, match="payload must be dict\\[str, Any\\]"): 31 + append_edit( 32 + {"id": "coding_090000_300"}, 33 + actor="cli:update", 34 + fields=["details"], 35 + note="updated", 36 + payload="not a dict", 37 + )
+24 -9
think/activities.py
··· 803 803 804 804 805 805 def append_edit( 806 - record: dict[str, Any], *, actor: str, fields: list[str], note: str | None 806 + record: dict[str, Any], 807 + *, 808 + actor: str, 809 + fields: list[str], 810 + note: str | None, 811 + payload: dict[str, Any] 812 + | None = None, # Additive: Ledger close writes a `ledger_close` sub-dict alongside the audit edit; keep spread so edit readers see a flat entry. 807 813 ) -> dict[str, Any]: 808 814 """Append an edit entry to an activity record and return the record.""" 809 815 normalized = _normalize_activity_record(record) 810 816 edits = [dict(edit) for edit in normalized.get("edits", [])] 811 - edits.append( 812 - { 813 - "timestamp": datetime.now(UTC).isoformat().replace("+00:00", "Z"), 814 - "actor": actor, 815 - "fields": list(fields), 816 - "note": note, 817 - } 818 - ) 817 + edit_entry: dict[str, Any] = { 818 + "timestamp": datetime.now(UTC).isoformat().replace("+00:00", "Z"), 819 + "actor": actor, 820 + "fields": list(fields), 821 + "note": note, 822 + } 823 + if payload is not None: 824 + if not isinstance(payload, dict): 825 + raise TypeError("payload must be dict[str, Any] when provided") 826 + collision_keys = sorted(set(payload) & set(edit_entry)) 827 + if collision_keys: 828 + raise ValueError( 829 + "payload cannot overwrite canonical edit fields: " 830 + + ", ".join(collision_keys) 831 + ) 832 + edit_entry = {**edit_entry, **payload} 833 + edits.append(edit_entry) 819 834 normalized["edits"] = edits 820 835 return normalized 821 836