LearnNewsExamplesServices
Frontmatter
id13134
titleIn-heap WriteGuard: stateful held-lock authority for multi-writer NL enforcement (#13056 leaf)
stateClosed
labels
enhancementaiarchitecture
assigneesneo-opus-ada
createdAtJun 13, 2026, 11:12 PM
updatedAtJun 21, 2026, 2:17 AM
githubUrlhttps://github.com/neomjs/neo/issues/13134
authorneo-opus-ada
commentsCount0
parentIssue13056
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 14, 2026, 12:36 AM

In-heap WriteGuard: stateful held-lock authority for multi-writer NL enforcement (#13056 leaf)

Closed v13.1.0/archive-v13-1-0-chunk-2 enhancementaiarchitecture
neo-opus-ada
neo-opus-ada commented on Jun 13, 2026, 11:12 PM

Context

Leaf of #13056 (Extended-NL coordination), building the in-heap authoritative layer the epic names ("in-heap (App-Worker) enforcement as the authoritative layer — the heap is the shared truth; Bridge-level checks are advisory"). It is the consumer of #13125's LockRegistry (the stateless conflict math): WriteGuard holds the live lock state and exposes the write-lifecycle API the in-app Neural Link client calls before applying a write-class operation.

Release classification: boardless — harness coordination line, post-v13.

Depends on: #13126 (LockRegistry must be on dev). The implementing PR stacks on the #13126 branch and retargets to dev once it merges.

The Problem

LockRegistry decides conflict over a caller-held table but holds no state. Enforcement needs a stateful authority that owns the live lock table for an App-Worker heap, so a write-class op acquires-and-holds a subtree lock and a different agent's overlapping write is denied while it is held (guardrail #1: Topological Locking before any multi-writer slice).

Scope (this leaf — the stateful authority, pure)

Neo.ai.WriteGuard — instance state locks (the held table), over LockRegistry:

  • requestWrite({agentId, sessionId, subtreePath})LockRegistry.acquire(this.locks, lock); on grant, holds the lock (updates this.locks) and returns {granted:true}; on conflict, returns {granted:false, conflict} and leaves the table unchanged. (Re-entrant per LockRegistry.)
  • releaseWrite({agentId, sessionId, subtreePath})LockRegistry.release.
  • releaseAgent({agentId, sessionId})LockRegistry.releaseAll — the disconnect / worker-restart sweep hook.
  • heldLocks() → a copy of the held table (introspection).

subtreePath is passed in (the in-heap client derives the absolute root→node path from the live component before calling) — so WriteGuard is pure over its inputs and unit-testable via event sequences, with no live-heap dependency.

Lock-lifetime decision (decide-and-documented; converging in review)

Held-until-release + disconnect-sweep (the convergence A2A's Option B): a write acquires a lock that is held until releaseWrite (explicit) or releaseAgent (disconnect). This is the epic's intent and the minimum that prevents cross-writer corruption. Per-op (acquire→write→release) was rejected as too weak; a lease/TTL (Option C) is an additive follow-up (it extends held-locks with inactivity expiry) — built only if silent-holder deadlock proves real. @neo-opus-vega / @neo-opus-grace: converge on the PR.

Out of Scope (named follow-up slices)

  • The Client write-path wiring (intercept write-class ops, derive subtreePath from the live component tree, call WriteGuard, surface the denial) — the integration / whitebox-e2e slice.
  • Bridge-side advisory mirroring; the NL-mutation→MESSAGE observability glue.
  • Lease/TTL (Option C); the canonical-session-id binding (#12984, opaque here).

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback Evidence
WriteGuard.requestWrite this leaf (new) acquire-and-hold via LockRegistry; cross-writer overlap denied while held malformed lock denied (LockRegistry fail-closed) unit: grant/deny/re-entrant event sequences
WriteGuard.releaseWrite / releaseAgent this leaf (new) release one / sweep a writer; idempotent non-held release is a no-op unit: release + disconnect-sweep sequences
Lock lifetime #13056 epic + convergence A2A held-until-release + disconnect-sweep per-op rejected; lease = additive follow-up unit: held lock blocks a 2nd writer until released

Decision Record impact

aligned-with ADR 0020 (guardrail #1); consumes #13125. No ADR amendment.

Acceptance Criteria

  • WriteGuard holds a lock table and exposes requestWrite / releaseWrite / releaseAgent / heldLocks, delegating conflict to LockRegistry.
  • Held-lock semantics unit-tested via event sequences: A holds subtree X → B's overlapping write denied → A releases → B granted; disconnect-sweep releases A's locks.
  • WriteGuard is unit-tested with subtreePath passed in (no live-heap / Bridge import — the connect-on-init-singleton trap is avoided).
  • Evidence altitude-honest: L1 authority-logic; the live two-session Client-wired enforcement is the named e2e residual.

Related

Parent: #13056. Refs #13012 (guardrail #1), #13125 / PR #13126 (LockRegistry), #13130 (sibling auto-targeting slice).

Origin Session ID: 4c598c8f-d8a7-4288-9420-e825a45d310e Retrieval Hint: "in-heap WriteGuard held lock authority requestWrite releaseWrite releaseAgent disconnect sweep multi-writer enforcement LockRegistry"