Skip to content
162 changes: 162 additions & 0 deletions ipfs_datasets_py/mcp_server/MASTER_IMPROVEMENT_PLAN_2026_v37.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Master Improvement Plan 2026 — v37: Session 81 (v36 Next Steps)

**Created:** 2026-02-23 (Session 81)
**Branch:** `copilot/refactor-ipfs-datasets-mcp-server`
**Reference:** https://github.com/endomorphosis/Mcp-Plus-Plus
**Supersedes:** [MASTER_IMPROVEMENT_PLAN_2026_v36.md](MASTER_IMPROVEMENT_PLAN_2026_v36.md)

---

## Overview

Session 81 implements all five "Next Steps" from the v36 plan:

| # | Feature | Status |
|---|---------|--------|
| 1 | `MergeResult.__iter__` — yields `(field, value)` pairs | ✅ COMPLETE |
| 2 | `IPFSReloadResult.iter_failed()` — generator of `(name, error)` pairs | ✅ COMPLETE |
| 3 | `PubSubBus.subscriber_ids(topic)` — sorted SID list for a topic | ✅ COMPLETE |
| 4 | `ComplianceChecker.backup_summary(path)` — full summary dict | ✅ COMPLETE |
| 5 | Session 81 E2E test (`test_mcplusplus_v36_session81.py`, 42 tests) | ✅ COMPLETE |

**1,444+ total spec tests pass (sessions 50–81, 0 new failures).**

---

## Item 1 — `MergeResult.__iter__` ✅

**File:** `ipfs_datasets_py/mcp_server/ucan_delegation.py`

```python
def __iter__(self):
yield ("added_count", self.added_count)
yield ("conflict_count", self.conflict_count)
yield ("revocations_copied", self.revocations_copied)
```

Yields three `(field, value)` pairs in stable order. Enables idiomatic
`dict(result)` packing:

```python
d = dict(result)
# {"added_count": 3, "conflict_count": 1, "revocations_copied": 0}
```

---

## Item 2 — `IPFSReloadResult.iter_failed()` ✅

**File:** `ipfs_datasets_py/mcp_server/nl_ucan_policy.py`

```python
def iter_failed(self):
errors = self.pin_errors or {}
for name, cid in self.pin_results.items():
if cid is None:
yield (name, errors.get(name, "unknown error"))
```

Generator that yields `(name, error)` tuples for every failed pin.
Falls back to `"unknown error"` when no `pin_errors` entry exists.
Yields nothing when all pins succeeded (`total_failed == 0`).

---

## Item 3 — `PubSubBus.subscriber_ids(topic)` ✅

**File:** `ipfs_datasets_py/mcp_server/mcp_p2p_transport.py`

```python
def subscriber_ids(self, topic):
key = topic.value if hasattr(topic, "value") else str(topic)
return sorted(
sid for sid, (k, _h) in self._sid_map.items() if k == key
)
```

Returns a sorted list of all SIDs registered to *topic*. Enables
targeted `unsubscribe_by_id` without iterating `_sid_map` manually:

```python
for sid in bus.subscriber_ids("receipts"):
bus.unsubscribe_by_id(sid)
```

---

## Item 4 — `ComplianceChecker.backup_summary(path)` ✅

**File:** `ipfs_datasets_py/mcp_server/compliance_checker.py`

Returns a single-call summary dict:

```python
{
"count": 2,
"newest": "/data/rules.enc.bak",
"oldest": "/data/rules.enc.bak.1",
"newest_age": 12.3,
"oldest_age": 45.6,
}
```

When no backups exist all path/age fields are `None` and `count` is `0`.
Internally calls `list_bak_files` + `os.path.getmtime`; catches `OSError`
on each mtime call individually.

---

## Item 5 — Session 81 E2E Test ✅

**File:** `tests/mcp/unit/test_mcplusplus_v36_session81.py`

42 tests across 5 sections:

| Section | Tests |
|---------|-------|
| `TestMergeResultIter` | 10 |
| `TestIPFSReloadResultIterFailed` | 10 |
| `TestPubSubBusSubscriberIds` | 10 |
| `TestComplianceCheckerBackupSummary` | 8 |
| `TestE2ESession81` | 4 |

All 42 tests pass with 0 failures.

---

## Cumulative MCP++ Status

| Component | Module | Sessions |
|-----------|--------|---------|
| UCAN Delegation | `ucan_delegation.py` | 53, 56–81 |
| P2P Transport | `mcp_p2p_transport.py` | 54, 55, 56, 64–81 |
| Compliance | `compliance_checker.py` | 53, 60–81 |
| NL→UCAN Policy Gate | `nl_ucan_policy.py` | 51, 52, 56, 57, 62–81 |
| MergeResult: full API (repr+str+bool+len+iter+from/to_dict+comparisons) | `ucan_delegation.py` | 71–81 |
| IPFSReloadResult: full API (repr+str+bool+len+iter_failed+from/to_dict+summarize) | `nl_ucan_policy.py` | 71–81 |
| PubSubBus: subscribe ID+count+topics+clear+snapshot+resubscribe+subscriber_ids | `mcp_p2p_transport.py` | 71–81 |
| ComplianceChecker: bak lifecycle (rotate+list+purge+age+newest+oldest+summary) | `compliance_checker.py` | 71–81 |

**1,444+ spec tests pass (sessions 50–81).**

---

## Next Steps (Session 82+)

1. **`MergeResult.keys()`** — return a list of field names mirroring `dict.keys()`
for further `dict`-protocol compatibility (`["added_count", "conflict_count",
"revocations_copied"]`).

2. **`IPFSReloadResult.iter_succeeded()`** — generator yielding `(name, cid)`
pairs for all successful pins (complement of `iter_failed()`).

3. **`PubSubBus.topic_sid_map()`** — return `{topic: [sid, ...]}` mapping,
the SID-based analogue of `topic_handler_map()`.

4. **`ComplianceChecker.backup_names(path)`** — return only the *file names*
(not full paths) of existing backup files; useful for display and logging
without exposing absolute paths.

5. **Session 82 full E2E** — verify `keys()` round-trip, `iter_succeeded`
filtering, `topic_sid_map` consistency with `subscriber_ids`, and
`backup_names` in a purge cycle.
163 changes: 163 additions & 0 deletions ipfs_datasets_py/mcp_server/MASTER_IMPROVEMENT_PLAN_2026_v38.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# Master Improvement Plan 2026 — v38: Session 82 (v37 Next Steps)

**Created:** 2026-02-23 (Session 82)
**Branch:** `copilot/refactor-ipfs-datasets-mcp-server`
**Reference:** https://github.com/endomorphosis/Mcp-Plus-Plus
**Supersedes:** [MASTER_IMPROVEMENT_PLAN_2026_v37.md](MASTER_IMPROVEMENT_PLAN_2026_v37.md)

---

## Overview

Session 82 implements all five "Next Steps" from the v37 plan:

| # | Feature | Status |
|---|---------|--------|
| 1 | `MergeResult.keys()` + `__getitem__` — full dict-protocol compatibility | ✅ COMPLETE |
| 2 | `IPFSReloadResult.iter_succeeded()` — generator of `(name, cid)` pairs | ✅ COMPLETE |
| 3 | `PubSubBus.topic_sid_map()` — `{topic: sorted_sid_list}` mapping | ✅ COMPLETE |
| 4 | `ComplianceChecker.backup_names(path)` — basenames of backup files | ✅ COMPLETE |
| 5 | Session 82 E2E test (`test_mcplusplus_v37_session82.py`, 42 tests) | ✅ COMPLETE |
| 6 | Fixed duplicate code block in `backup_summary` (dead code after `return`) | ✅ FIXED |

**1,486+ total spec tests pass (sessions 50–82, 0 new failures).**

---

## Item 1 — `MergeResult.keys()` + `MergeResult.__getitem__` ✅

**File:** `ipfs_datasets_py/mcp_server/ucan_delegation.py`

```python
def keys(self) -> list:
return ["added_count", "conflict_count", "revocations_copied"]

def __getitem__(self, key: str):
if key == "added_count": return self.added_count
if key == "conflict_count": return self.conflict_count
if key == "revocations_copied": return self.revocations_copied
raise KeyError(key)
```

`keys()` alone triggers Python's mapping protocol in `dict()`, requiring
`__getitem__` as well. Together they enable the full dict protocol:

```python
d = dict(result)
# {"added_count": 3, "conflict_count": 1, "revocations_copied": 0}
result["added_count"] # 3
```

**Bug fix:** Adding `keys()` without `__getitem__` broke the existing
`dict(result)` usage that relied on `__iter__` pairs (Python switches to
the mapping protocol when `keys()` is present). Both methods are needed.

---

## Item 2 — `IPFSReloadResult.iter_succeeded()` ✅

**File:** `ipfs_datasets_py/mcp_server/nl_ucan_policy.py`

```python
def iter_succeeded(self):
for name, cid in self.pin_results.items():
if cid is not None:
yield (name, cid)
```

Complement of `iter_failed()`. Yields `(name, cid)` pairs for every
policy whose pin succeeded. Together the two generators partition
`pin_results` exactly: `set(iter_succeeded names) ∪ set(iter_failed names) == all names`.

---

## Item 3 — `PubSubBus.topic_sid_map()` ✅

**File:** `ipfs_datasets_py/mcp_server/mcp_p2p_transport.py`

```python
def topic_sid_map(self) -> Dict[str, List[int]]:
result = {}
for sid, (k, _h) in self._sid_map.items():
result.setdefault(k, [])
result[k].append(sid)
return {k: sorted(v) for k, v in result.items() if v}
```

SID-based analogue of `topic_handler_map()`. Returns a fresh dict; modifying
it does not affect the live registry. Consistent with `subscriber_ids(topic)`:
`topic_sid_map()[topic] == subscriber_ids(topic)` for every topic present.

---

## Item 4 — `ComplianceChecker.backup_names(path)` ✅

**File:** `ipfs_datasets_py/mcp_server/compliance_checker.py`

```python
@staticmethod
def backup_names(path: str) -> List[str]:
import os as _os
return [_os.path.basename(p) for p in ComplianceChecker.list_bak_files(path)]
```

Returns only the *file names* (no directory component), safe for display and
logging without exposing absolute paths. Count matches `backup_count(path)`.

---

## Item 5 — Session 82 E2E Test ✅

**File:** `tests/mcp/unit/test_mcplusplus_v37_session82.py`

42 tests across 5 sections:

| Section | Tests |
|---------|-------|
| `TestMergeResultKeys` | 10 |
| `TestIPFSReloadResultIterSucceeded` | 10 |
| `TestPubSubBusTopicSidMap` | 10 |
| `TestComplianceCheckerBackupNames` | 8 |
| `TestE2ESession82` | 4 |

All 42 tests pass with 0 failures.

---

## Cumulative MCP++ Status

| Component | Module | Sessions |
|-----------|--------|---------|
| UCAN Delegation | `ucan_delegation.py` | 53, 56–82 |
| P2P Transport | `mcp_p2p_transport.py` | 54, 55, 56, 64–82 |
| Compliance | `compliance_checker.py` | 53, 60–82 |
| NL→UCAN Policy Gate | `nl_ucan_policy.py` | 51, 52, 56, 57, 62–82 |
| MergeResult: full API (repr+str+bool+len+iter+keys+getitem+from/to_dict+comparisons) | `ucan_delegation.py` | 71–82 |
| IPFSReloadResult: full API (repr+str+bool+len+iter_failed+iter_succeeded+from/to_dict+summarize) | `nl_ucan_policy.py` | 71–82 |
| PubSubBus: full API (subscribe ID+count+topics+clear+snapshot+resubscribe+subscriber_ids+topic_sid_map) | `mcp_p2p_transport.py` | 71–82 |
| ComplianceChecker: bak lifecycle (rotate+list+purge+age+newest+oldest+summary+names) | `compliance_checker.py` | 71–82 |

**1,486+ spec tests pass (sessions 50–82).**

---

## Next Steps (Session 83+)

1. **`MergeResult.values()`** — return a list of field values in the same order
as `keys()`, completing the `dict`-protocol triad (`keys/values/items`).

2. **`IPFSReloadResult.iter_all()`** — generator yielding `(name, cid_or_none)`
pairs for *all* entries regardless of success/failure; useful for unified
reporting.

3. **`PubSubBus.total_subscriptions()`** — return the total number of SIDs
currently active (i.e., `len(_sid_map)`); complement to `handler_count()`
but counts registrations not unique handlers.

4. **`ComplianceChecker.newest_backup_name(path)`** — return only the file
name (basename) of the newest backup, or `None`; complement to
`backup_names()`.

5. **Session 83 full E2E** — verify `values()` consistency with `keys()` and
`__iter__`, `iter_all` coverage, `total_subscriptions` vs. `handler_count`,
and `newest_backup_name` in a rotate+verify cycle.
Loading
Loading