Files
deepseek-harness/docs/subsystems/scope.md
T
Tianyi Cui a7a5be1703 docs(notes): archive low-future-value Agent Notes
Run the dsh-archive-agent-notes audit over every active Agent Note on
current master, judging each record by whether its rationale still guides
work rather than by size or age.

- Archive 453 implemented bilingual triplets (417,882 English words):
  completed UI chrome, narrow adapters, closed bug fixes, implementation
  walkthroughs whose package READMEs, docs pages, generators, or successor
  notes now carry the useful behavior, and 51 records fully superseded by
  a later active note. Keep 201 implemented notes whose ownership rules,
  negative guarantees, durable or wire semantics, security rules,
  reintroduction conditions, or still-tempting rejected alternatives
  remain useful.
- Reject 7 proposals whose premise is gone or whose work shipped in
  amended form under other records; delete 2 rejected notes that no
  longer prevent a plausible mistake.
- Retarget every remaining inbound link to the archived path, and repair
  active prose that named an archived record as the owner of a live fact:
  parenthetical citations drop, ownership sentences redirect to the
  README, docs page, or active note that states the fact, and history
  citations say so. Chinese files link the English archived path because
  the pairing gate treats the frozen tree as outside the bilingual corpus.
- Seal 1,359 new frozen artifacts; existing seals are unchanged and
  outbound links from archived notes are neither inspected nor repaired.
- Regenerate docs/config-catalog.md after the hook-bridge comment edits
  shifted two source line numbers.
2026-09-05 14:37:32 +08:00

3.7 KiB

Scoped Registration

English | 中文

The scope package supplies the identity, carrier, and scoped-layer vocabulary that makes one registration context mean both per-agent visibility and shared lifetime ownership. It is a library primitive rather than a Cordis service; the agent-scope runtime-design Agent Note owns the lifecycle rationale, and the package README owns the callable API and filtering semantics.

Sources: packages/core/scope/src/index.ts and packages/core/scope/src/store.ts.

Identity and dispatch carrier

ScopeKey is an opaque object identity. The shipped loop uses the live Agent object as its own key, but the primitive never inspects the object.

/** An opaque, identity-compared scope key. */
type ScopeKey = object

Scoped<T> is the compile-time brand on the opaque routing receiver returned by scopeTarget(base, key). Scope-filtered event declarations require this carrier as their this type, while the real event subject remains an explicit argument.

/**
 * A routing-only event receiver built by {@link scopeTarget}. The type
 * parameter records the subject type for dispatch checking; the carrier does
 * not expose the subject's properties. Event payloads carry the real subject.
 */
type Scoped<T extends object> = object & { readonly [ScopedBrand]: T }

Owned registration context

Scope pairs the tagged registration context with two teardown paths. rawDispose preserves the exact Cordis disposer identity needed by an ordered composite effect; dispose() is the public shared quiescence boundary for direct and racing callers.

/** A minted registration scope and its quiescent disposal boundaries. */
interface Scope {
  /** Context through which scope-owned registrations are made. */
  ctx: Context
  /** Exact Cordis disposer, used when nesting this scope in an ordered composite effect. */
  rawDispose: () => Promise<void> | void
  /** Dispose every scope-owned registration; racing calls await the same completion. */
  dispose(): Promise<void>
}

Scoped registry layer

ScopeLayer represents one registry's complete contribution at the global or exact-scope level. A concrete layer may aggregate multiple named and anonymous tables; whole-layer emptiness lets ScopedLayers reclaim scoped state without discarding a sibling table.

/** One scope's aggregate contribution to a registry. */
interface ScopeLayer {
  /** Whether every table in this layer is empty. */
  isEmpty(): boolean
}

ScopedLayers<L> owns the eager global layer and lazily created exact-scope layers. Reads do not create layers: peek(undefined) means no overlay, while merge() materializes insertion-ordered global named entries followed by scoped shadows. Registrations use one context for both visibility and Cordis effect ownership, collect one synchronous undo before optional notification, return Cordis's exact disposer, and reclaim a scoped layer only when its complete ScopeLayer is empty.

NamedEntries<V> supplies insertion-ordered lookup and live iteration with caller-owned duplicate errors. AnonymousEntries<V> gives every append a unique identity so equal values remain independent. Iteration stays live within one nonempty table generation; draining the table detaches existing iterators from later insertions. Both return idempotent exact-entry undos; the shared EntryValues implementation interface is not public.