Skip to content

fix: rebuild_profiles hermeticity + bridge_finder ValueError on real session data (#174)#180

Merged
cdeust merged 1 commit into
mainfrom
fix/rebuild-profiles-hermetic-174
Jul 24, 2026
Merged

fix: rebuild_profiles hermeticity + bridge_finder ValueError on real session data (#174)#180
cdeust merged 1 commit into
mainfrom
fix/rebuild-profiles-hermetic-174

Conversation

@cdeust

@cdeust cdeust commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Closes #174

The defect, two layers

1. Real bug (production data path). scanner.discover_all_memories() returns a list[dict] of memory records, but bridge_finder.find_bridges fed that list straight into dict.update. A non-empty list of 9-key records raises:

ValueError: dictionary update sequence element #0 has length 9; 2 is required

Empty homes skip the if memories: branch, so CI (empty $HOME) stayed green while every developer machine with real ~/.claude memory files failed. This is a type-contract mismatch reached by legitimate production data — classified as a real bug, not flakiness.

2. Test isolation (hidden shared state). Three test_rebuild_profiles cases called handler({"force": True}) with no mocking, reading the developer's live ~/.claude session history. Results depended on whose machine ran them.

Minimized failing input

A single scanner-shaped record (the 9 keys file, path, project, name, description, type, body, modifiedAt, createdAt) wrapped in a list:

find_bridges(profiles, None, [{"file": "note.md", "path": "...", "project": "proj-a",
    "name": "note", "description": "", "type": "note", "body": "",
    "modifiedAt": "...", "createdAt": "..."}])   # -> ValueError pre-fix

Root-cause classification

bridge_finder should handle that shape — it is exactly what the production producer (discover_all_memories) emits. Fixed there, not by rejecting the data.

What changed

  • core/bridge_finder.py: new _index_memories() normalizes the scanner list into an id-keyed map (keyed by path/file/name); the brain_index dict shape is passed through unchanged. Signature widened to dict | list | None.
  • core/profile_assembler.py: annotations corrected to the honest dict | list[dict] | None through the call chain.
  • tests_py/core/test_bridge_finder.py: TestFindBridgesScannerListShape pins the minimized 9-key record and the list+brain_index merge.
  • tests_py/handlers/test_rebuild_profiles.py: hermetic_claude_home fixture redirects every ~/.claude path constant at a synthetic tmp_path seeded with real sessions + memory files; the three previously-live-home tests now drive the full pipeline against fixtures and consume the list-of-records shape that triggered the ValueError.

Gates (on this machine, live ~/.claude present)

🤖 Generated with Claude Code

…les tests (#174)

Two-layer fix for #174.

Real bug (production data path): discover_all_memories() returns a
list[dict] of memory records, but find_bridges fed it straight into
dict.update. A non-empty list of 9-key records raised "ValueError:
dictionary update sequence element #0 has length 9; 2 is required".
Empty homes skipped the branch, so CI stayed green while every real
~/.claude failed. Root cause: type-contract mismatch, not flakiness.
Fix: _index_memories() normalizes the scanner list into an id-keyed
map (keyed by path/file/name), leaving the brain_index dict shape
untouched. Annotations through profile_assembler updated to the honest
dict | list[dict] | None.

Test isolation (hidden shared state): three test_rebuild_profiles
cases called handler({"force": True}) unmocked, reading the developer's
live ~/.claude session data. Replaced with a hermetic_claude_home
fixture that redirects every ~/.claude path constant at a synthetic
tmp_path seeded with real sessions + memory files, so the full
scan -> group -> assemble -> bridge pipeline runs against fixtures and
consumes the exact list-of-records shape that triggered the ValueError.

Regression pinned at both levels: TestFindBridgesScannerListShape
encodes the minimized 9-key record; the handler tests assert the real
pipeline scans the fixture memories without error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cdeust
cdeust merged commit a557d1c into main Jul 24, 2026
14 checks passed
@cdeust
cdeust deleted the fix/rebuild-profiles-hermetic-174 branch July 24, 2026 22:53
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.

test: test_rebuild_profiles depends on live ~/.claude data — 3 failures reproduce on main

1 participant