generated from coulomb/repo-seed
feat(memory) + chore(workplan): complete T02 for CYA-WP-0003 — port extensions for activation, kinds, and retrospection records (backward compatible)
This commit is contained in:
@@ -23,6 +23,11 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
# Standard memory kinds used by cya (especially after CYA-WP-0003)
|
||||||
|
KIND_PREFERENCE = "preference"
|
||||||
|
KIND_RETROSPECTION = "retrospection"
|
||||||
|
KIND_INTERACTION_GOAL = "interaction_goal"
|
||||||
|
|
||||||
|
|
||||||
def _warn_not_connected(feature: str) -> None:
|
def _warn_not_connected(feature: str) -> None:
|
||||||
"""Loud, visible marker that phase-memory is not yet wired (fallback path)."""
|
"""Loud, visible marker that phase-memory is not yet wired (fallback path)."""
|
||||||
@@ -67,10 +72,10 @@ def _save(scope: str, items: list[dict[str, Any]]) -> None:
|
|||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Explicit ports (the four capabilities from the workplan)
|
# Explicit ports (the four capabilities from the workplan)
|
||||||
# Refined in T01 per phase-memory architecture + interop + lifecycle.
|
# Refined in T01 (0003) per phase-memory architecture + interop + lifecycle.
|
||||||
# Real (non-no-op) implementation added in T02: actual persist + recall across
|
# Real (non-no-op) implementation added in T02 (0002): actual persist + recall.
|
||||||
# invocations, with provenance for explainability. Still graceful.
|
# Extended in T02 (0003) for contextual activation and retrospection support.
|
||||||
# See MemoryVision.md "cya ↔ phase-memory Integration Contract".
|
# See docs/cya-memory-activation-and-retrospection-concept.md and MemoryVision.md.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -81,12 +86,15 @@ def remember_preference(
|
|||||||
*,
|
*,
|
||||||
profile: str | None = None,
|
profile: str | None = None,
|
||||||
ttl: str | None = None,
|
ttl: str | None = None,
|
||||||
|
kind: str = KIND_PREFERENCE,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Remember a user preference or workflow pattern (preference kind).
|
"""Remember a user preference, workflow pattern, retrospection outcome, or goal.
|
||||||
|
|
||||||
Real T02: persists to user-controlled json (scoped).
|
`kind` defaults to "preference" for backward compatibility.
|
||||||
Future: delegates to phase-memory profile execution / graph store + planner.
|
Special kinds (e.g. "retrospection", "interaction_goal") are supported for
|
||||||
Dry-run plans and policy checks will come from phase-memory lifecycle.
|
higher-order memory from retrospection sessions (see CYA-WP-0003).
|
||||||
|
|
||||||
|
Real T02 + 0003: persists to user-controlled json (scoped).
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
items = _load(scope)
|
items = _load(scope)
|
||||||
@@ -96,7 +104,7 @@ def remember_preference(
|
|||||||
"ts": time.time(),
|
"ts": time.time(),
|
||||||
"scope": scope,
|
"scope": scope,
|
||||||
"profile": profile,
|
"profile": profile,
|
||||||
"kind": "preference",
|
"kind": kind,
|
||||||
}
|
}
|
||||||
# avoid exact dups for same key in small stores
|
# avoid exact dups for same key in small stores
|
||||||
items = [i for i in items if i.get("key") != key]
|
items = [i for i in items if i.get("key") != key]
|
||||||
@@ -115,30 +123,52 @@ def recall_preferences(
|
|||||||
kinds: list[str] | None = None,
|
kinds: list[str] | None = None,
|
||||||
profile: str | None = None,
|
profile: str | None = None,
|
||||||
limit: int = 50,
|
limit: int = 50,
|
||||||
|
activation_context: dict | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Recall relevant history / preferences for cwd + task (preference + context).
|
"""Recall relevant memory for the current context.
|
||||||
|
|
||||||
Real T02: loads from user-controlled scoped json.
|
Supports:
|
||||||
Returns structured payload with items, provenance, phase for explain.
|
- `kinds`: filter or boost specific kinds (e.g. ["interaction_goal", "retrospection"])
|
||||||
Future: real phase-memory retrieval + planner + dry_run_plan.
|
- `activation_context`: hints for smarter activation (e.g. {"cwd": "...", "project": "..."})
|
||||||
|
- Backward compatible with all previous call sites.
|
||||||
|
|
||||||
|
Real T02 + 0003 extensions for contextual activation and retrospection support.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
items = _load(scope)
|
items = _load(scope)
|
||||||
|
|
||||||
|
# Basic kind filtering (existing behavior + 0003 enhancement)
|
||||||
if kinds:
|
if kinds:
|
||||||
items = [i for i in items if i.get("kind") in kinds or not i.get("kind")]
|
items = [i for i in items if i.get("kind") in kinds or not i.get("kind")]
|
||||||
items = items[-limit:] # most recent
|
|
||||||
|
# Simple activation boost: prefer items whose scope or profile matches context
|
||||||
|
if activation_context:
|
||||||
|
ctx_cwd = activation_context.get("cwd") or activation_context.get("scope")
|
||||||
|
ctx_profile = activation_context.get("profile")
|
||||||
|
boosted = []
|
||||||
|
normal = []
|
||||||
|
for item in items:
|
||||||
|
if (ctx_cwd and item.get("scope") == ctx_cwd) or (ctx_profile and item.get("profile") == ctx_profile):
|
||||||
|
boosted.append(item)
|
||||||
|
else:
|
||||||
|
normal.append(item)
|
||||||
|
items = boosted + normal
|
||||||
|
|
||||||
|
items = items[-limit:] # most recent first (after boosting)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"items": items,
|
"items": items,
|
||||||
"provenance": [
|
"provenance": [
|
||||||
{
|
{
|
||||||
"source": "cya-local-memory-json (T02; phase models ready)",
|
"source": "cya-local-memory-json (T02+0003; activation + retrospection support)",
|
||||||
"scope": scope,
|
"scope": scope,
|
||||||
"count": len(items),
|
"count": len(items),
|
||||||
|
"activation_context": activation_context,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"phase": "fluid",
|
"phase": "fluid",
|
||||||
"profile": profile,
|
"profile": profile,
|
||||||
"note": "real persist across invocations; full phase-memory graph/planner in later T02 slice",
|
"note": "real persist + contextual activation; full phase-memory in later slices",
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_warn_not_connected(
|
_warn_not_connected(
|
||||||
@@ -164,23 +194,32 @@ def forget(scope: str = "cwd", keys: list[str] | None = None, *, profile: str |
|
|||||||
_warn_not_connected(f"forget(scope={scope}, keys={keys}, profile={profile}) err={e}")
|
_warn_not_connected(f"forget(scope={scope}, keys={keys}, profile={profile}) err={e}")
|
||||||
|
|
||||||
|
|
||||||
def export_memory(scope: str = "cwd", *, profile: str | None = None) -> dict[str, Any]:
|
def export_memory(scope: str = "cwd", *, profile: str | None = None, kinds: list[str] | None = None) -> dict[str, Any]:
|
||||||
"""Inspect / export current memory for this project or user.
|
"""Inspect / export current memory for this project or user.
|
||||||
|
|
||||||
Real T02: returns the raw items + meta for full transparency (used by explain).
|
Supports optional `kinds` filter (e.g. to export only retrospection records).
|
||||||
Includes phase, provenance summary, policy notes placeholder.
|
Real T02 + 0003: improved transparency for different memory kinds.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
items = _load(scope)
|
items = _load(scope)
|
||||||
|
if kinds:
|
||||||
|
items = [i for i in items if i.get("kind") in kinds]
|
||||||
|
|
||||||
|
by_kind = {}
|
||||||
|
for item in items:
|
||||||
|
k = item.get("kind", "unknown")
|
||||||
|
by_kind.setdefault(k, []).append(item)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"status": "real (T02 local json; phase ready)",
|
"status": "real (T02+0003 local json; activation + retrospection ready)",
|
||||||
"scope": scope,
|
"scope": scope,
|
||||||
"profile": profile,
|
"profile": profile,
|
||||||
"count": len(items),
|
"count": len(items),
|
||||||
"items": items,
|
"items": items,
|
||||||
|
"by_kind": {k: len(v) for k, v in by_kind.items()},
|
||||||
"phase": "fluid",
|
"phase": "fluid",
|
||||||
"provenance_summary": f"{len(items)} preference records from ~/.config/cya/memory/",
|
"provenance_summary": f"{len(items)} records from ~/.config/cya/memory/ (kinds: {list(by_kind.keys())})",
|
||||||
"note": "Replace this module with real phase-memory ports/graph (see MemoryVision contract). User can edit the json directly.",
|
"note": "User-controlled. Replace with real phase-memory when available.",
|
||||||
"phases": ["ephemeral", "fluid", "stabilized", "rigid"],
|
"phases": ["ephemeral", "fluid", "stabilized", "rigid"],
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -191,10 +230,30 @@ def export_memory(scope: str = "cwd", *, profile: str | None = None) -> dict[str
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def remember_retrospection_outcome(
|
||||||
|
key: str,
|
||||||
|
value: Any,
|
||||||
|
scope: str = "cwd",
|
||||||
|
*,
|
||||||
|
profile: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Convenience helper to record outcomes from a retrospection session.
|
||||||
|
|
||||||
|
Stores with kind="retrospection" or "interaction_goal" (caller decides via key/value).
|
||||||
|
This is a thin wrapper around remember_preference for clarity in T04+.
|
||||||
|
"""
|
||||||
|
kind = KIND_RETROSPECTION if "retrospection" in str(key).lower() else KIND_INTERACTION_GOAL
|
||||||
|
remember_preference(key, value, scope=scope, profile=profile, kind=kind)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"remember_preference",
|
"remember_preference",
|
||||||
"recall_preferences",
|
"recall_preferences",
|
||||||
"forget",
|
"forget",
|
||||||
"export_memory",
|
"export_memory",
|
||||||
|
"remember_retrospection_outcome",
|
||||||
|
"KIND_PREFERENCE",
|
||||||
|
"KIND_RETROSPECTION",
|
||||||
|
"KIND_INTERACTION_GOAL",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -73,20 +73,26 @@ completed: "2026-05-27"
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: CYA-WP-0003-T02
|
id: CYA-WP-0003-T02
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "6f50bdf4-6252-4b93-9697-407ef432cd90"
|
state_hub_task_id: "6f50bdf4-6252-4b93-9697-407ef432cd90"
|
||||||
|
started: "2026-05-27 ralph continuation (after T01 review)"
|
||||||
|
completed: "2026-05-27"
|
||||||
```
|
```
|
||||||
|
|
||||||
- Evolve the four ports (or add minimal new ones) to better support:
|
**Done** — implemented in `src/cya/memory/__init__.py`.
|
||||||
- Richer scoping and activation hints (e.g., directory + project identity + optional profile).
|
|
||||||
- Storing and retrieving "retrospection outcomes" and "interaction goals" as first-class memory.
|
|
||||||
- Define lightweight structures for retrospection records and activated context packages.
|
|
||||||
- Maintain full backward compatibility and user inspectability.
|
|
||||||
|
|
||||||
**Acceptance criteria**:
|
- Added `kind` parameter to `remember_preference` (defaults to "preference" for full backward compat).
|
||||||
- Ports and data model support the concepts from T01 without breaking existing usage.
|
- Added `KIND_*` constants and `remember_retrospection_outcome()` convenience helper.
|
||||||
- All new memory remains fully user-visible and editable.
|
- Enhanced `recall_preferences` with better `kinds` filtering + new `activation_context` parameter for smarter directory/project-aware activation.
|
||||||
|
- Improved `export_memory` with optional `kinds` filter and `by_kind` summary.
|
||||||
|
- All changes maintain 100% backward compatibility with existing call sites.
|
||||||
|
- Verified manually (roundtrips for preferences, retrospection records, kind filtering, and activation hints all work).
|
||||||
|
|
||||||
|
**Acceptance criteria met**:
|
||||||
|
- Ports and data model now support the T01 concepts.
|
||||||
|
- Everything remains fully user-visible/editable (still plain JSON under `~/.config/cya/memory/`).
|
||||||
|
- No breaking changes.
|
||||||
|
|
||||||
### T03 — Implement directory- and project-bound memory activation
|
### T03 — Implement directory- and project-bound memory activation
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user