Codex's formal audit of fb4d55c said GO WITH CONDITIONS. Two P2 findings
to fold in before merge:
1. auto_triage.py:417 still PUT {"content": cand["content"]} — the
suggested-project correction was unreachable even with
MemoryUpdateRequest.project in place. Changed body to
{"project": suggested} so misattribution flags actually retarget the
memory. Added a regression test that asserts the script source
contains the new PUT shape, so a future "optimization" can't silently
undo this.
2. POST /memory/{id}/supersede had no status guard — calling
supersede_memory() delegated to update_memory(status="superseded"),
which would silently flip a candidate to superseded. Mirrored the
invalidate route: get_memory(id) lookup, 404 unknown / 200
already_superseded / 409 wrong-status / 200 superseded.
Plus a P3 from the same audit: covered the "retarget to project=''
when a global active duplicate exists" case via
test_update_memory_to_empty_project_detects_global_duplicate.
Tests: 581 -> 586 (+5: 3 supersede route + 1 project-empty duplicate +
1 auto_triage caller invariant).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three bugs surfaced by the 2026-04-29 Codex review of the state-of-the-service
plan, all in the memory write/read path:
1. /admin/dashboard memory counts were derived from a confidence-sorted
get_memories(limit=500) sample. With prod at 1091 active memories the
dashboard reported 315 ("active in the top 500"), while integrity
reported the SQL aggregate 1091. Replaced the sampling block with a
new get_memory_count_summary() helper that does straight SQL aggregates
over status/type/project. Dashboard memories.{active,candidates,...}
now match integrity. Adds memories.{by_status,total} for completeness.
2. PUT /memory/{id} silently dropped project changes because
MemoryUpdateRequest had no project field and update_memory() didn't
accept one. auto_triage.py:407 detects suggested_project drift and
issues a PUT to fix it; the fix never landed. Added project to the
request schema and the service signature, with resolve_project_name
canonicalization, before/after audit snapshot, and the existing
duplicate-active check now scoped to the new project.
3. POST /memory/{id}/invalidate did _get_memories(status="active",
limit=1) and looked for the target inside that single
highest-confidence row. Any other active memory 404'd. Replaced with
a direct id lookup via the new get_memory(id) helper; status branching
stays the same (404 unknown / 200 already-invalid / 409 wrong-status /
200 invalidated).
Tests added (9):
- test_get_memory_count_summary_returns_full_table_aggregates
- test_get_memory_returns_single_row_or_none
- test_update_memory_can_change_project_with_canonicalization
- test_update_memory_project_unchanged_when_not_passed
- test_api_invalidate_finds_active_memory_outside_top_one
- test_api_invalidate_already_invalid_is_idempotent
- test_api_invalidate_candidate_returns_409
- test_api_invalidate_unknown_id_is_404
- test_admin_dashboard_active_count_matches_full_table
Test count: 572 -> 581. Full suite green locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>