Skip to content

Feature request / design discussion: task-scoped memory prefetch by ID for subagent delegation #449

Description

@G0-0000

Feature request / design discussion: task-scoped memory prefetch by ID for subagent delegation

Motivation: users are forced to maintain private patches

I use magic-context for multi-agent orchestration. When a parent agent delegates a task, it often already knows exactly which memories the child needs. There is currently no task-scoped prefetch-by-ID capability, so I maintain several local patches to simulate it. Every upstream upgrade requires rebasing and re-adapting those patches. This is costly and fragile.

I am not opening this issue to define the cross-harness contract, and I am not pushing a specific syntax. I want to describe the use case and its user cost. The design shape is for maintainers to decide.

How I use it now, and where it hurts

My current options are roughly:

  • the parent pastes memory bodies into the delegation message;
  • the child is asked to call ctx_memory get itself;
  • a local patch injects by ID.

None are good:

  • Pasting bodies: duplicates bytes, drifts from the memory store, and makes delegation output tokens and latency very high.
  • Child calls ctx_memory get: adds tool-round-trip latency, and the child may not know it needs to.
  • Local patches: every upgrade requires rebasing; costly and conflict-prone.

This is especially visible with defaultContext=fresh children: they share the project memory store but have no parent conversation, so there is no stable channel for IDs the parent already knows.

What I want

A parent can request memories by ID at delegation time, and the child receives the full content before its first token:

  • delegation messages carry only the goal and IDs, not pasted bodies;
  • the child gets the original memory-store content, not a parent paraphrase;
  • no embedding, retrieval ranking, or tool round-trip;
  • unaffected by the automatic injection budget — the IDs the parent names may be exactly what the budget excluded;
  • users no longer need to maintain private patches.

For typical SOP / config payloads, this can reduce parent output by hundreds to over a thousand tokens per delegation, with corresponding output-latency savings. Exact numbers depend on memory size and task shape.

What I am not trying to decide

These are yours to define, not mine:

  • the cross-harness contract across OpenCode / Pi/OMP / Rust lane;
  • whether IDs travel through a structured orchestration API, task-text markers, or both;
  • the exact syntax;
  • whether a new persisted namespace or schema migration is needed;
  • which layer owns frozen delivery, capacity checks, and policy consistency.

My earlier prototype only validated the Pi path. It is not a claim for a single-harness design. If the final design only needs part of it, I can adapt to whatever shape you define.

Existing prototype (material only, not an implementation request)

On the Pi path I validated the core mechanics: primary-key reads, no embedding, opaque Unavailable for invisible IDs, snapshot freezing, replay, and explicit capacity refusal. Tests covered parsing boundaries, invisible IDs, replay, and transient-fault retry.

But this is only material. Whether to use it, how much, and in which layer, is up to you.

One question I would like to discuss

Is this use case worth including in the cross-harness design discussion?

  • If yes: would you prefer IDs to travel through a structured orchestration API, or task-text markers? I can provide material or implement part of it in whatever direction you define.
  • If no: please tell me, so I at least know I need to keep maintaining patches.

Activity

  1. G0-0000 commented on Sep 15, 2026

    @G0-0000
    Author

    Update: I've rewritten this issue based on the feedback above. The core request is now stated as a user pain point, and all design decisions are explicitly left to maintainers. I am not requesting approval for any specific syntax, default configuration, or single-harness implementation. The previous prototype is kept only as reference material.

  2. changed the title [-]Feature Request: task-requested memory injection via ⟦mc-mem: …⟧ markers (Pi v1)[/-] [+]Feature request / design discussion: task-scoped memory prefetch by ID for subagent delegation[/+] on Sep 15, 2026
  3. G0-0000 commented on Sep 16, 2026

    @G0-0000
    Author

    Cross-harness design for task-scoped memory prefetch by ID

    Thanks for the detailed review and for opening this discussion. I agree the current PR is shaped incorrectly — it's single-harness, and it introduced agent-facing syntax and a default-on config surface without a prior design approval step. I'll keep the implementation frozen until we agree on the cross-harness contract here.

    Below is a proposal for the orchestrator-to-child contract, addressing the questions you raised.

    1. Orchestrator-to-child contract

    I propose moving IDs out of task text and into a structured delegation field. The orchestrator passes memory prefetch instructions as part of the subagent invocation payload, not as a marker inside the natural-language task.

    Proposed shape:

    {
      "task": "Fix the race condition in the scheduler",
      "memory": {
        "prefetch_ids": ["11", "55"],
        "mode": "frozen"
      }
    }

    Or, more preferably, passing a snapshot reference:

    {
      "task": "Fix the race condition in the scheduler",
      "memory": {
        "snapshot_ref": "snap_abc123"
      }
    }

    In both cases, the task text stays clean. The child's memory adapter reads the structured field and injects the corresponding snapshot through the existing ctx_memory get path.

    The key principle: IDs travel through a structured orchestration API, not through task-text markers. The child harness never needs to parse the task string for memory instructions.

    2. Reuse of ctx_memory get and m[0]; no separate memory policy

    The prefetch mechanism should be a thin batch optimization over the existing ctx_memory get primitive, not a parallel memory policy.

    • If prefetch_ids is provided, the child resolves each ID through ctx_memory get and injects the results.
    • If snapshot_ref is provided, the child resolves the frozen snapshot and injects it directly.
    • The existing m[0] semantics (auto-recall of the most relevant memory) remain untouched; prefetch is additive and explicit, not a replacement.

    This keeps one memory resolution path and avoids divergent behavior between prefetch and normal recall.

    3. Enforcement of memory-off, project-config trust, workspace visibility, and Rust-facade ownership

    These should be enforced at the point of snapshot creation, not at the point of injection. That is, the orchestrator (or the Rust facade) decides what IDs are eligible for prefetch based on:

    • memory-off: if memory is disabled for the project or session, no prefetch IDs are emitted; the structured field is absent.
    • project-config trust: only memories from trusted project configs are eligible for prefetch; untrusted sources are filtered before the ID list is assembled.
    • workspace visibility: IDs are resolved against the workspace's visibility scope; cross-workspace IDs are rejected at snapshot creation time.
    • Rust-facade ownership: the Rust facade owns snapshot creation and ID validation; the child harness only consumes the validated result. This keeps policy enforcement in one place.

    4. Frozen delivery across restart and compaction windows

    The owner of frozen delivery should be the snapshot creation layer (Rust facade or orchestrator-side memory service), not the child harness.

    • When a snapshot_ref is created, it is persisted with a defined TTL and a frozen state.
    • Across restart: the child resolves snapshot_ref from the persisted store; the snapshot bytes are unchanged.
    • Across compaction: the snapshot is pinned and exempt from compaction eviction; the orchestrator re-validates the ref before handing it to the child.
    • Outgoing capacity check: before emitting a snapshot or ID list, the orchestrator checks the serialized size of the memory payload against the child's context budget. If it exceeds the budget, the prefetch is either trimmed (by priority or recency) or split into a paginated fetch.

    5. Persisted namespace and schema restart

    I do not think a new persisted namespace is required if snapshot_ref points to the existing memory store.

    The frozen snapshot can be represented as a normal memory entry with a frozen flag and a stable ID. This reuses the existing schema and avoids a coordinated schema restart. If the current schema cannot express a frozen pin, the minimal change would be an additive column or a side table, which is a much smaller blast radius than a namespace change.

    If we can avoid a new namespace, we should. If we cannot, the cost of a coordinated schema restart should be justified against the simpler additive change.

    6. Open questions for the maintainers

    • Harness support: Which harnesses currently support structured delegation metadata? I am least familiar with OpenCode and the Rust lane. If the contract cannot be uniform today, where should the adapter layer live?
    • Snapshot vs. ID list: Do you prefer passing snapshot_ref (frozen at creation) or prefetch_ids (resolved by the child)? The former gives stronger frozen-delivery guarantees; the latter is simpler and closer to the current ctx_memory get path.
    • Fallback: Should we keep the text-marker parser as a deprecated fallback for harnesses that cannot pass structured metadata, or drop it entirely once the contract is agreed?
    • Orchestrator ownership: Should the orchestrator emit the structured memory field, or should the Rust facade intercept the delegation call and inject it? This affects where capacity checks and trust filtering live.

    Next steps

    I'll hold the implementation until we converge on the contract. Once the shape is agreed, I can implement across OpenCode, Pi/OMP, and the Rust lane as a single coherent change, with tests for each harness and for the frozen-delivery path.

    Thank you for the detailed questions — they made the right shape much clearer.

  4. magic-alfonso commented on Sep 16, 2026

    @magic-alfonso

    Thanks for taking the decline the right way and for the proposal — it is the shape of thinking we wanted, and the questions it raises are the real ones.

    Two facts first, because they decide what can be built today. There is no structured delegation carrier on OpenCode: on both 1.18.x and 2.0.x the task/subagent tool passes only the prompt text to the child; the child session row has a metadata column that the spawn path never populates, and on 2.0.x a plugin cannot create a parented child itself. So a memory.prefetch_ids field on the invocation is not something a plugin can honour on OpenCode without an upstream change, which would make it a Pi/OMP-only feature in practice. What every harness does carry reliably is the child's parent session id, and that is the join key any cross-harness delivery has to hang off.

    On the direction: you are right that there should be a way for an orchestrator to hand memories to the agents it spawns, and we agree it belongs in the structured layer rather than in task prose. We are designing it, but under a bigger umbrella than this plugin — it has to line up with how the harness integration we are building delegates work, what a child receives at spawn, and how frozen delivery survives compaction and restart on every lane at once. That design needs to be done carefully and as one piece, so we are not going to converge on a contract in this issue and then have it superseded a few weeks later.

    Concretely: we will keep this issue open as the tracking point, post the shape here once it is settled enough to build against, and would be glad to have you implement or review it at that stage. Until then there is no version of this that would merge, so please hold the implementation as you offered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions