Skip to content

feat(tunnels): cross-wing entity tunnels derived from hallways#1565

Merged
igorls merged 1 commit into
developfrom
feat/entity-tunnels-from-hallways
May 21, 2026
Merged

feat(tunnels): cross-wing entity tunnels derived from hallways#1565
igorls merged 1 commit into
developfrom
feat/entity-tunnels-from-hallways

Conversation

@milla-jovovich

Copy link
Copy Markdown
Collaborator

Adds the architectural counterpart to compute_topic_tunnels that materializes cross-wing tunnels from the within-wing hallway records introduced in PR #1558. When an entity (person, project, concept, interest) has hallways in two wings, an entity tunnel bridges them — anchored on the entity. This completes the v4 sequence: Wing → Drawer-entities → Hallway → Tunnel.

Topic tunnels are NOT replaced. Both systems coexist for one release cycle so existing palaces don't lose tunnels between mines. Deprecation of topic tunnels is a separate follow-up PR after entity tunnels prove out in real use.

What this commit does

  1. Adds entity_tunnels_for_wing(wing, hallways, label_prefix) to mempalace/palace_graph.py. Pure function: groups hallway records by entity-and-wing, finds entities present in wing AND ≥1 other wing, and emits one create_tunnel call per (entity, other_wing) pair. Uses kind="entity" and synthetic endpoint room entity:<name> so the new tunnels are distinguishable from explicit/topic tunnels at read time but interchangeable with them via the standard list_tunnels / follow_tunnels API.

  2. Adds _compute_entity_tunnels_for_wing(wing) wrapper to mempalace/miner.py. Loads hallway records via hallways.list_hallways() and calls the algorithm. Module-level so tests can patch it as mempalace.miner._compute_entity_tunnels_for_wing.

  3. Wires the wrapper into _mine_impl immediately after the existing hallway-compute block. Same try/except fault-tolerance pattern as the topic-tunnel and hallway blocks — entity-tunnel computation is a derived analytic and must never fail a mine.

Tests (RED-first)

Nine algorithm tests in tests/test_palace_graph_tunnels.py (new TestEntityTunnels class):

test_entity_tunnels_creates_cross_wing_tunnel_for_shared_entity
test_entity_tunnels_skips_entities_in_only_one_wing
test_entity_tunnels_counts_entity_in_either_pair_position
test_entity_tunnels_three_wings_pairwise_from_focus_wing
test_entity_tunnels_idempotent_on_rerun
test_entity_tunnels_retrievable_via_list_tunnels
test_entity_tunnels_empty_hallways_is_noop
test_entity_tunnels_unknown_wing_is_noop
test_entity_tunnel_room_does_not_collide_with_literal_room

Two integration tests in tests/test_miner.py:

test_mine_computes_entity_tunnels_for_wing_post_mine
test_mine_entity_tunnel_failure_does_not_crash_mine

All 11 RED before this commit (AttributeError on the missing names). All 11 GREEN after.

Out of scope (deferred to follow-up PRs)

  • format_miner.py and convo_miner.py integration: separate PRs per the scope discipline used for feat(miner): integrate compute_hallways_for_wing into post-mine flow #1560.
  • Deprecating _compute_topic_tunnels_for_wing: separate PR after entity tunnels prove out in real use.
  • Surfacing kind="entity" in MCP / search-result UI: not yet required by any reader; behaviorally interchangeable with the other tunnel kinds today.

Stacking

This PR stacks on PR #1558 (which introduces the hallway primitive and its miner integration). Base branch is
feat/hallways-within-wing-connectors. When #1558 merges to develop, GitHub auto-updates this PR's base to develop and the diff reduces to just the entity-tunnel additions.

Verification

pytest tests/test_palace_graph_tunnels.py::TestEntityTunnels
→ 9 passed (RED before, GREEN after)
pytest tests/test_miner.py::test_mine_computes_entity_tunnels_for_wing_post_mine
tests/test_miner.py::test_mine_entity_tunnel_failure_does_not_crash_mine
→ 2 passed (RED before, GREEN after)
pytest tests/test_palace_graph_tunnels.py
→ 39 passed (no regressions)
pytest tests/test_miner.py
→ 49 passed (no regressions)
pytest -q (full mempalace suite)
→ 1949 passed, 1 skipped, 0 regressions
ruff check mempalace/palace_graph.py mempalace/miner.py tests/
→ All checks passed!
ruff format --check ...
→ 4 files already formatted (pinned 0.15.9)

Adds the architectural counterpart to ``compute_topic_tunnels`` that
materializes cross-wing tunnels from the within-wing hallway records
introduced in PR #1558. When an entity (person, project, concept,
interest) has hallways in two wings, an entity tunnel bridges them —
anchored on the entity. This completes the v4 sequence: Wing →
Drawer-entities → Hallway → Tunnel.

Topic tunnels are NOT replaced. Both systems coexist for one release
cycle so existing palaces don't lose tunnels between mines. Deprecation
of topic tunnels is a separate follow-up PR after entity tunnels prove
out in real use.

## What this commit does

1. Adds ``entity_tunnels_for_wing(wing, hallways, label_prefix)`` to
   ``mempalace/palace_graph.py``. Pure function: groups hallway records
   by entity-and-wing, finds entities present in ``wing`` AND ≥1 other
   wing, and emits one ``create_tunnel`` call per (entity, other_wing)
   pair. Uses ``kind="entity"`` and synthetic endpoint room
   ``entity:<name>`` so the new tunnels are distinguishable from
   explicit/topic tunnels at read time but interchangeable with them
   via the standard ``list_tunnels`` / ``follow_tunnels`` API.

2. Adds ``_compute_entity_tunnels_for_wing(wing)`` wrapper to
   ``mempalace/miner.py``. Loads hallway records via
   ``hallways.list_hallways()`` and calls the algorithm. Module-level
   so tests can patch it as ``mempalace.miner._compute_entity_tunnels_for_wing``.

3. Wires the wrapper into ``_mine_impl`` immediately after the existing
   hallway-compute block. Same try/except fault-tolerance pattern as
   the topic-tunnel and hallway blocks — entity-tunnel computation is
   a derived analytic and must never fail a mine.

## Tests (RED-first)

Nine algorithm tests in ``tests/test_palace_graph_tunnels.py``
(new ``TestEntityTunnels`` class):

  test_entity_tunnels_creates_cross_wing_tunnel_for_shared_entity
  test_entity_tunnels_skips_entities_in_only_one_wing
  test_entity_tunnels_counts_entity_in_either_pair_position
  test_entity_tunnels_three_wings_pairwise_from_focus_wing
  test_entity_tunnels_idempotent_on_rerun
  test_entity_tunnels_retrievable_via_list_tunnels
  test_entity_tunnels_empty_hallways_is_noop
  test_entity_tunnels_unknown_wing_is_noop
  test_entity_tunnel_room_does_not_collide_with_literal_room

Two integration tests in ``tests/test_miner.py``:

  test_mine_computes_entity_tunnels_for_wing_post_mine
  test_mine_entity_tunnel_failure_does_not_crash_mine

All 11 RED before this commit (AttributeError on the missing names).
All 11 GREEN after.

## Out of scope (deferred to follow-up PRs)

- ``format_miner.py`` and ``convo_miner.py`` integration: separate PRs
  per the scope discipline used for #1560.
- Deprecating ``_compute_topic_tunnels_for_wing``: separate PR after
  entity tunnels prove out in real use.
- Surfacing ``kind="entity"`` in MCP / search-result UI: not yet
  required by any reader; behaviorally interchangeable with the other
  tunnel kinds today.

## Stacking

This PR stacks on PR #1558 (which introduces the hallway primitive and
its miner integration). Base branch is
``feat/hallways-within-wing-connectors``. When #1558 merges to develop,
GitHub auto-updates this PR's base to ``develop`` and the diff reduces
to just the entity-tunnel additions.

## Verification

  pytest tests/test_palace_graph_tunnels.py::TestEntityTunnels
    → 9 passed (RED before, GREEN after)
  pytest tests/test_miner.py::test_mine_computes_entity_tunnels_for_wing_post_mine
        tests/test_miner.py::test_mine_entity_tunnel_failure_does_not_crash_mine
    → 2 passed (RED before, GREEN after)
  pytest tests/test_palace_graph_tunnels.py
    → 39 passed (no regressions)
  pytest tests/test_miner.py
    → 49 passed (no regressions)
  pytest -q (full mempalace suite)
    → 1949 passed, 1 skipped, 0 regressions
  ruff check mempalace/palace_graph.py mempalace/miner.py tests/
    → All checks passed!
  ruff format --check ...
    → 4 files already formatted (pinned 0.15.9)
@milla-jovovich milla-jovovich requested a review from igorls as a code owner May 20, 2026 21:07

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces cross-wing entity tunnels, which link different wings when the same entity is identified in their hallway records. The implementation includes the core logic in palace_graph.py, integration into the mining process in miner.py, and comprehensive test coverage. Feedback focuses on performance and maintainability improvements, specifically suggesting that tunnel creation be batched to avoid excessive I/O operations, the entity mapping logic be optimized to reduce memory usage, and hardcoded synthetic room prefixes be replaced with constants.

Comment thread mempalace/palace_graph.py
Comment on lines +847 to +854
tunnel = create_tunnel(
source_wing=own_wing_display,
source_room=room,
target_wing=other_display,
target_room=room,
label=f"{label_prefix}: {entity}",
kind="entity",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Calling create_tunnel inside a nested loop is a significant performance bottleneck. Each call to create_tunnel performs a full read-mutate-write cycle on the tunnels.json file, including acquiring a file lock and performing an fsync.

As the number of shared entities and wings grows, this will cause the mining process to slow down quadratically. It would be much more efficient to load the tunnels once, perform all updates in memory, and then save the updated list in a single operation.

Comment thread mempalace/palace_graph.py
Comment on lines +814 to +828
entity_wings: dict = {}
for h in hallways:
if not isinstance(h, dict):
continue
h_wing = h.get("wing")
if not isinstance(h_wing, str) or not h_wing.strip():
continue
h_wing_norm = normalize_wing_name(h_wing.strip())
for ent_key in ("entity_a", "entity_b"):
ent = h.get(ent_key)
if not isinstance(ent, str) or not ent.strip():
continue
# setdefault preserves the first-seen display form so the
# tunnel endpoint matches the wing name the caller used.
entity_wings.setdefault(ent, {}).setdefault(h_wing_norm, h_wing)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This loop builds a global mapping of all entities to all wings across the entire palace, even though the function only needs to create tunnels for entities present in the current wing. In a palace with many wings and hallways, this builds a large, redundant data structure in memory every time a single wing is mined.

Consider a two-pass approach: first identify entities present in the target wing, then only process hallway records for those specific entities.

Comment thread mempalace/palace_graph.py
other_wings_norm = sorted(w for w in wings_for_entity if w != wing_norm)
for other_norm in other_wings_norm:
other_display = wings_for_entity[other_norm]
room = f"entity:{entity}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The synthetic room prefix "entity:" is hardcoded here. To improve maintainability and ensure consistency with the topic tunnel implementation (which uses TOPIC_ROOM_PREFIX and topic_room()), consider defining an ENTITY_ROOM_PREFIX constant and a helper function.

@igorls igorls changed the base branch from feat/hallways-within-wing-connectors to develop May 21, 2026 02:47
@igorls igorls merged commit ed80f80 into develop May 21, 2026
mvalentsev pushed a commit to mvalentsev/mempalace that referenced this pull request May 21, 2026
…#1555

PR MemPalace#1555 (format coverage + virtual line numbering) merged with twelve
inline polish comments from Copilot + gemini-code-assist that weren't
load-bearing enough to block the original ship but are real cleanups.
This PR addresses them.

Twelve items in scope; one item (drawer ID delimiter — Copilot MemPalace#13) is
deferred to its own dedicated PR because it's a breaking schema change
that requires migration design beyond the scope of a polish PR.

## Behavioral fixes (5 items, RED-tested first)

1. **FileNotFoundError vs broken symlink (Copilot MemPalace#8).** ``extract_text``
   previously mapped every ``FileNotFoundError`` from ``stat()`` to
   ``SKIP_BROKEN_SYMLINK``. That's misleading for the common case of a
   regular file deleted between scan and extract. Now distinguishes:
   ``SKIP_BROKEN_SYMLINK`` only when ``p.is_symlink()`` is true;
   ``SKIP_UNREADABLE`` otherwise.

2. **``file_already_mined`` extract_mode scoping (Copilot MemPalace#11, MemPalace#12).**
   Both call sites in ``mine_formats`` and ``_file_chunks_locked`` now
   pass ``extract_mode="format"``. Previously the format miner could
   falsely treat drawers from project / convo miner on the same source
   file as "already mined" (and vice versa). Scopes idempotency to the
   correct drawer subset.

3. **Sentinel skip for transient missing-dep statuses (Copilot MemPalace#14).**
   New ``_TRANSIENT_MISSING_DEP_STATUSES`` set + ``_register_skip_sentinel_if_appropriate``
   helper. Skip variants like ``SKIP_NO_MARKITDOWN`` /
   ``SKIP_NO_STRIPRTF`` / ``SKIP_MISSING_FORMAT_DEPS`` /
   ``SKIP_NETWORK_TIMEOUT`` no longer write the "already-mined" sentinel.
   Otherwise installing the missing extra later wouldn't trigger a re-mine.

4. **Outer ``except Exception`` in ``mine_formats`` (Gemini MemPalace#5).** The
   outer try around the loop previously caught only ``KeyboardInterrupt``,
   leaving any setup-time error (e.g., ``scan_formats`` raising) to
   propagate as a bare traceback. Now catches ``Exception`` defensively,
   logs it, prints a partial-progress summary, and lets the ``finally``
   PID-cleanup run. Mirrors miner.py's belt-and-suspenders pattern.

5. **Thread user's ``chunk_size`` / ``chunk_overlap`` / ``min_chunk_size``
   through to ``chunk_text`` (Gemini MemPalace#3).** ``MempalaceConfig`` was loaded
   only to validate readability; users who tuned their config saw no
   effect in format-mode mining. Now properly threaded.

## Trivial cleanups (5 items)

6. **Path expanduser in ``extract_text`` (Copilot MemPalace#7).** ``Path(path)`` →
   ``Path(path).expanduser()`` so CLI inputs like ``~/docs/file.pdf``
   resolve correctly.

7. **Path expanduser+resolve in ``scan_formats`` (Copilot MemPalace#9).** Same
   fix; ``~/docs`` and relative paths now work consistently.

8. **Use resolved ``format_path`` in ``mine_formats`` (Copilot MemPalace#10).**
   ``scan_formats(format_dir)`` → ``scan_formats(format_path)`` so the
   already-resolved path is used.

9. **``render_with_line_numbers`` type annotation (Copilot MemPalace#15).**
   ``text: "str | None"`` reflects the documented + tested ``None``
   handling.

10. **Test + docs claims (Copilot MemPalace#16, MemPalace#17, MemPalace#18).** Stale framings
    removed:
    - ``docs/format-coverage.md`` — 14 fringe cases + "see the file for
      the current test inventory" (no more frozen test count).
    - ``tests/test_line_numbers.py`` — drops "proposed for mempalace
      3.3.6" + "run from the proposal directory" references.
    - ``tests/test_format_miner.py`` — drops "MarkItDown is mocked
      throughout" (live integration tests exist) + proposal-directory
      framing.

## Module-level hoists (enables clean test patching)

- ``MempalaceConfig`` (from ``.config``) hoisted from lazy local import
  to module-level so tests can patch ``mempalace.format_miner.MempalaceConfig``.
- ``chunk_text`` (from ``.miner``) hoisted similarly.

Both follow the pattern PR MemPalace#1565 used for ``compute_hallways_for_wing``.

## Complexity refactor

Extracted ``_print_mine_summary`` from ``mine_formats`` so the orchestrator
stays under the project's ``max-complexity = 25`` ceiling (per
``pyproject.toml [tool.ruff.lint.mccabe]``). Behavior unchanged; pure
extraction.

## Out of scope (intentionally deferred)

- **Drawer ID delimiter collision (Copilot MemPalace#13)** — ``f"{source_file}{chunk_index}"``
  can theoretically collide (``"/path/a1" + "23"`` == ``"/path/a" + "123"``).
  Fixing this is a breaking schema change to drawer IDs and requires a
  migration plan; will land as its own PR after design.

- The four bot comments that were ALREADY addressed by amendment MemPalace#3
  before the PR MemPalace#1555 merge (``_SKIP_DIRS`` dedup, ``scan_formats``
  symlink skip, ``source_mtime`` tracking, hall+entities metadata) —
  no action needed; verified during audit.

## Tests (RED-first)

Six new RED-first tests in ``tests/test_format_miner.py``:

  test_extract_text_nonexistent_regular_file_returns_unreadable_not_broken_symlink
  test_mine_formats_passes_extract_mode_format_to_file_already_mined
  test_mine_formats_does_not_write_sentinel_for_skip_no_markitdown
  test_mine_formats_does_not_write_sentinel_for_skip_missing_format_deps
  test_mine_formats_catches_unexpected_exception_and_prints_summary
  test_mine_formats_threads_chunk_size_from_user_config

All six RED before this commit (failures correctly identified the bugs
they're targeting), all six GREEN after.

One existing test (``test_mine_formats_continues_after_per_file_error``)
updated to patch the new module-level binding
``mempalace.format_miner.chunk_text`` instead of the old
``mempalace.miner.chunk_text`` source location, and to accept the
``**kwargs`` the call now passes through. Behavior unchanged.

## Verification

  pytest -q (full mempalace suite)
    → 2065 passed, 3 skipped, 0 regressions
  ruff check mempalace/format_miner.py mempalace/searcher.py tests/
    → All checks passed!
  ruff format --check ...
    → 4 files already formatted (pinned 0.15.9)
  mine_formats complexity
    → ≤ 25 (under the project ceiling)
Ev3lynx727 pushed a commit to Ev3lynx727/mempalace that referenced this pull request May 22, 2026
…unnels

Adds the L7 living-connection dynamics layer for the hallway primitive
(PR MemPalace#1558) and the tunnel primitive (PR MemPalace#1565). Connections now carry
strength, stability, last_activated, and access_count fields; pure-math
helpers in a new ``mempalace.dynamics`` module update these via Hebbian
potentiation (Hebb 1949) on co-access and Ebbinghaus exponential decay
(Ebbinghaus 1885) on time-since-use, with the Cepeda spacing effect
(Cepeda et al. 2006) growing stability when reinforcement is distributed
rather than massed.

Existing palaces continue to work unchanged — the new fields populate
via safe defaults (strength=1.0, stability=1.0, last_activated=created_at,
access_count=0) lazily on any potentiate/decay call. No migration step
required. No schema break. Old records work; new records ship with full
L7 state from creation.

## What this commit does

1. New module ``mempalace/dynamics.py`` — pure math, no I/O, no chromadb.
   Three public helpers:

     - ``initialize_dynamics_fields(connection, *, now=None)``
       Backfill helper. Populates missing strength / stability /
       last_activated / access_count fields. Existing fields are NOT
       overwritten. Safe for pre-L7 records.

     - ``potentiate(connection, *, increment=POTENTIATION_INCREMENT, now=None)``
       Hebbian strengthening on a co-access event. Increments strength
       (capped at MAX_STRENGTH), updates last_activated, increments
       access_count. Grows stability by STABILITY_INCREMENT ONLY when
       the gap since the prior activation is at least SPACED_INTERVAL_HOURS
       — implements the Cepeda spacing effect (rapid bursts don't
       build durability; distributed practice does).

     - ``apply_decay(connection, *, now=None)``
       Ebbinghaus exponential decay. New strength = old * exp(-days_since /
       stability), floored at STRENGTH_FLOOR (0.05) so connections never
       reach zero — only become dim. Idempotent at the same instant.
       Higher stability = slower decay.

2. Integration in ``mempalace/hallways.py:compute_hallways_for_wing`` —
   before persisting the new hallway list for a wing, looks up the
   existing wing's hallways and builds a (entity_a, entity_b) → dynamics
   lookup. Each new record gets the preserved dynamics if the entity
   pair existed before, OR default dynamics via
   ``initialize_dynamics_fields`` if it's a brand-new pair. Without
   this preservation, every mine would wipe accumulated connection
   weights — defeating the whole L7 layer.

3. Integration in ``mempalace/palace_graph.py:create_tunnel`` — the
   existing dedup-on-canonical-id path now also preserves the four
   dynamics fields from the existing record onto the recreated tunnel
   (in addition to the already-preserved ``created_at``). Brand-new
   tunnels and legacy records (created before L7) get default dynamics
   via ``initialize_dynamics_fields``.

## Tests (RED-first)

Twenty-five new tests in ``tests/test_dynamics.py`` covering the pure
math: field initialization defaults, potentiation strength increment +
cap, last_activated update, access_count increment, spaced vs. rapid
reinforcement (Cepeda spacing effect), decay rate (exp(-days/stability)),
floor clamping, idempotency at same instant, higher-stability slower-decay
behavior, no-time-passed no-op, backfill on missing fields, mutate-and-
return-same-dict chaining. Plus integration scenarios: potentiation
restoring decayed strength, repeated spaced reinforcement growing
stability monotonically, burst reinforcement not growing stability.

Six new integration tests:

  test_new_hallway_record_carries_all_dynamics_fields
  test_recompute_preserves_accumulated_strength
  test_recompute_initializes_dynamics_for_brand_new_pairs
  test_new_tunnel_carries_all_dynamics_fields
  test_recreate_tunnel_preserves_accumulated_dynamics
  test_recreate_tunnel_initializes_dynamics_for_legacy_records

All RED-first (KeyError: 'strength' before the integration commits).
All GREEN after.

## Backward compatibility

Pre-existing palaces work unchanged:

- Old hallway records with no dynamics fields are still readable; the
  next ``compute_hallways_for_wing`` recompute populates the missing
  fields via ``initialize_dynamics_fields``.
- Old tunnel records similarly; the next ``create_tunnel`` recreate (or
  any future call passing the record through ``initialize_dynamics_fields``)
  populates the missing fields.
- A ``test_recreate_tunnel_initializes_dynamics_for_legacy_records`` test
  pins this contract explicitly.

No forced migration. No schema break. Old records work; new behavior
kicks in on first touch.

## What this does NOT do (intentionally scoped out)

- Spreading activation at search time — that's a search-layer change;
  bigger PR; separate work.
- Lazy decay-on-read in ``list_hallways`` / ``list_tunnels`` — the math
  is here; wiring it into the read path is a follow-up.
- Config exposure of the tunable constants — STRENGTH_FLOOR, MAX_STRENGTH,
  POTENTIATION_INCREMENT etc. are hardcoded module-level for v1. If
  empirical palace data shows the defaults need tuning, expose via
  MempalaceConfig in a follow-up.
- Decay-aware ranking in search — the strength field is now there; the
  search ranker doesn't yet use it. Follow-up.

## Verification

  pytest tests/test_dynamics.py
    → 25 passed (RED before this commit; GREEN after)
  pytest tests/test_hallways.py::TestHallwayDynamicsIntegration
    → 3 passed (RED before; GREEN after)
  pytest tests/test_palace_graph_tunnels.py::TestTunnelDynamicsIntegration
    → 3 passed (RED before; GREEN after)
  pytest -q (full mempalace suite)
    → 2111 passed, 3 skipped, 0 regressions
  ruff check mempalace/dynamics.py mempalace/hallways.py mempalace/palace_graph.py tests/
    → All checks passed!
  ruff format --check ...
    → 6 files already formatted (pinned 0.15.9)

## Research grounding

  - Hebb, D. O. (1949). The Organization of Behavior. Wiley.
    → "Neurons that fire together, wire together" → potentiate()
  - Ebbinghaus, H. (1885). Über das Gedächtnis.
    → Exponential forgetting curve → apply_decay()
  - Cepeda, N. J., Pashler, H., Vul, E., Wixted, J. T., & Rohrer, D.
    (2006). Distributed practice in verbal recall tasks: A review and
    quantitative synthesis. Psychological Bulletin, 132(3), 354-380.
    → Spacing effect → stability growth on spaced reinforcement
@igorls igorls mentioned this pull request May 24, 2026
3 tasks
arnoldwender pushed a commit to arnoldwender/mempalace that referenced this pull request May 24, 2026
Bumps version 3.3.5 → 3.3.6 across pyproject.toml, version.py, plugin
manifests (.claude-plugin/plugin.json, .claude-plugin/marketplace.json,
.codex-plugin/plugin.json), README badge, and uv.lock. Flips CHANGELOG.md
from ``[Unreleased]`` to ``[3.3.6] — 2026-05-24`` and backfills the
major user-facing entries that landed without changelog entries during
the cycle:

Features:
- MemPalace#1555 office-document mining via --mode extract + virtual line numbers
- MemPalace#1584 surgical closet pointers with date+line locators (Tier 6a)
- MemPalace#1558 + MemPalace#1560 within-wing hallways (entity co-occurrence graph)
- MemPalace#1565 cross-wing tunnels auto-promoted from hallways
- MemPalace#1578 Hebbian potentiation + Ebbinghaus decay on hallways/tunnels
- MemPalace#1236 API-tool transcripts auto-route to wing_api
- MemPalace#711 hooks.auto_save toggle for silent-mode sessions
- MemPalace#1605 COCA content-word filter for entity detection
- MemPalace#1557 case-insensitive entity matching at mine time
- MemPalace#1483 multilingual embeddings (embeddinggemma-300m) by default

Bug Fixes (selected, user-visible):
- MemPalace#1540 silent data loss in three unchunked upsert sites
- MemPalace#1538 paragraph chunker oversized chunks
- MemPalace#1554 per-file chunk cap too low for transcripts
- MemPalace#1562 Windows hook subprocess/ChromaDB deadlock
- MemPalace#1529 create_tunnel corrupted hyphenated wing names
- MemPalace#1424 save-hook truncated hyphenated project folders
- MemPalace#1383 KG cache duplicated graphs for symlinked/cased paths
- MemPalace#1466 silent symlink skip now logged
- MemPalace#1441 macOS stock-bash 3.2 hook compatibility
- MemPalace#1500 / MemPalace#1513 structured JSON-RPC errors on bad MCP input
- MemPalace#1523 VACUUM + FTS5 rebuild after repair
- MemPalace#1548 FTS5 validation at end of mine
- plus MemPalace#1216, MemPalace#1408, MemPalace#1438, MemPalace#1439, MemPalace#1445, MemPalace#1452, MemPalace#1459, MemPalace#1461, MemPalace#1466,
  MemPalace#1470, MemPalace#1477, MemPalace#1485, MemPalace#1500, MemPalace#1513, MemPalace#1528, MemPalace#1532, MemPalace#1543, MemPalace#1546, MemPalace#1585

Performance:
- MemPalace#1474 convo miner pre-fetches mined-set
- MemPalace#1487 rebuild_index progress callback
- MemPalace#1530 MCP cold-start diagnostics + opt-in warmup

Lint passes (ruff 0.15.14); mempalace-mcp entry point alignment
verified per RELEASING.md.
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