Skip to content

feat(contexts): add advisory memory context with logged recall effect#32

Open
sorcerai wants to merge 1 commit into
shepherd-agents:mainfrom
sorcerai:feat/advisory-memory-context
Open

feat(contexts): add advisory memory context with logged recall effect#32
sorcerai wants to merge 1 commit into
shepherd-agents:mainfrom
sorcerai:feat/advisory-memory-context

Conversation

@sorcerai

@sorcerai sorcerai commented Jul 7, 2026

Copy link
Copy Markdown

Summary

Adds an advisory memory context — a new reference context that surfaces cross-run memory into a task's system prompt and records what it surfaced as a logged effect in the trace.

Shepherd's concepts docs note that cross-run memory is absent today (workspaces.md: "configuration, not history"; runs.md: manual compare only). This fills that gap with a context that follows the existing KVStoreContext reference pattern.

What's included

  • MemoryContext — recalls advisory hints into the prompt and emits a MemoryRecalled effect. Advisory-only: apply_effect is a no-op, so memory never enters state(t) = fold(apply_effect, ...) and never justifies a release.
  • MemoryRecalled effect — the audit record (query, backend, hint titles + digests). Registered via the shepherd.effects entry-point contributor; round-trips through the composed registry.
  • MemoryBackend Protocol + InMemoryBackend — pluggable backend SPI with a deterministic, zero-dependency default. Concrete backends for an external substrate live out-of-tree.
  • observations_from_effects — derive bugfix/decision observations from TaskFailed / settlement (select/discard) for out-of-band capture.

Design

  • Logged, not injected invisibly. MemoryRecalled records exactly what reached the prompt — the recall is in the trace, not a hidden prompt mutation.
  • Advisory-only by construction. apply_effect returns self; memory shapes the prompt but never the effect-replay fold or a release.
  • Pure configure(). Recall happens eagerly in create(); configure() only reads the already-recalled hints.
  • No kernel change. Lives entirely in v1 contexts; no shepherd2 / shepherd.kernel.abi.v0 touch.

Test coverage

27 tests (unit + end-to-end with the deterministic MockProvider): recall surfaces hints into the provider binding's system_prompt_additions, MemoryRecalled lands in the trace with correct provenance, the recall round-trips through the composed registry, advisory-only guarantees (no-op apply_effect, invisible body, only read/lifecycle effects on the binding), backend protocol conformance, max_hints truncation, and the write-path extraction (TaskFailed → bugfix, settlement → decision/anti-pattern).

ruff and strict mypy clean for the package; no regressions in the contexts suite (275 passed).

Known limitations

  • MemoryRecalled is emitted at extract time (after execution). A run that fails before extract emits only TaskFailed, not the recall record — ContextConfigured still records that a memory binding was active. Emitting the recall before execution would require a framework hook to publish effects at configure/prepare time.
  • Hints reach the prompt via ProviderBinding.system_prompt_additions, the same channel every context uses. Any device/container execution mode that does not propagate system_prompt_additions (a framework-wide concern, not specific to memory) will omit the hints there; the in-process path is unaffected.

Follows CONTRIBUTING.md (Conventional Commits title; make baseline + quickstart green on macOS).

@dcx

dcx commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Thank you for your contribution! I'll review this and get back later today.

@dcx dcx self-requested a review July 7, 2026 14:52
@dcx dcx assigned dcx and IvanLim and unassigned dcx and IvanLim Jul 7, 2026
@dcx

dcx commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

I've done a preliminary review. This is clever and useful, and I'm interested in taking this on.

We have some changes currently cooking that partially intersect with this, both on the arch and capability side. I'll need to do some careful thinking about how to make these work well together.

I'll do my best to have a more cohesive path forward by tomorrow!

Thanks so much for the contribution – and for your patience. The concrete is still setting on this framework + project + infrastructure in many places, and so things will take longer for a little while!

MemoryContext surfaces cross-run advisory memory into a task's system prompt
and records what it surfaced as a MemoryRecalled effect in the trace, so what
influenced a run is auditable (never an invisible prompt mutation). Memory is
advisory-only: apply_effect is a no-op, so it never enters the
state(t)=fold(apply_effect, ...) derivation and never justifies a release.

- MemoryBackend Protocol + InMemoryBackend (deterministic, zero-dep default);
  concrete backends for an external substrate live out-of-tree.
- MemoryRecalled effect, registered via the shepherd.effects entry-point
  contributor; round-trips through the composed registry.
- observations_from_effects: derive bugfix/decision observations from
  TaskFailed / settlement (select/discard) for out-of-band capture.

Follows the KVStoreContext reference pattern; entry points registered under
shepherd.contexts + shepherd.effects.
@sorcerai sorcerai force-pushed the feat/advisory-memory-context branch from c4a7f81 to ba24951 Compare July 9, 2026 11:55
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.

3 participants