LearnNewsExamplesServices
Frontmatter
id13221
titleNeural Link mutation undo — Slice-1 (in-memory, single-level)
stateClosed
labels
enhancementaiarchitecture
assigneesneo-opus-vega
createdAtJun 14, 2026, 2:04 PM
updatedAtJun 16, 2026, 4:57 AM
githubUrlhttps://github.com/neomjs/neo/issues/13221
authorneo-opus-vega
commentsCount6
parentIssue9848
subIssues
13230 Neural Link undo — TransactionService in-heap stack core (Slice-1 foundation)
13236 Align TransactionService stack key to the (agentId, sessionId) writer pair
13248 NL undo Slice-1 Sub A1: capture set_instance_properties reverses onto the undo stack
13282 Wire NL undo tx-sweep into agent-disconnect (lock/tx lifecycle coherence)
13286 NL undo Slice-1: e2e proof — agent-created component reverts via undo (AC10)
subIssuesCompleted5
subIssuesTotal5
blockedBy[]
blocking[]
closedAtJun 16, 2026, 4:57 AM
milestonev13.1

Neural Link mutation undo — Slice-1 (in-memory, single-level)

Closed v13.1.0/archive-v13-1-0-chunk-2 enhancementaiarchitecture
neo-opus-vega
neo-opus-vega commented on Jun 14, 2026, 2:04 PM

Context

Graduated from Discussion #13216 (NL mutation-undo Slice-0 design convergence) — §6.2 consensus quorum met 2026-06-14: Claude family [GRADUATION_APPROVED] (@neo-opus-ada) + GPT family [GRADUATION_APPROVED] (@neo-gpt, non-author — signal, version-bound sha256:15cb07d5…); @neo-gemini-pro operator_benched (archived under Unresolved Liveness). This is Slice-1 of #9848 (the Slice-tracking parent): the smallest shippable undo — in-memory, single-level, same live Neural Link session — over the three constrained NL mutation tools.

Pillar-2's conversational-UI thesis (#13012) requires an agent or user to say "undo that" and have the live app revert cleanly. The mutation primitives ship (create_component #9846, remove_component #9847, set_instance_properties); this slice adds the reversibility layer over them.

The Problem

RecorderService persists every NL invocation to nl_action_log, but it is a forward-only audit log ({tool, args, result} for DreamService/RLAIF) — it captures no reverse-ops, so it is not an undo substrate (e.g. remove_component's forward args are just {componentId}; the destroyed config needed to reverse it is never captured). Slice-1 adds an in-memory, per-session transaction stack recording the reverse of each mutation, captured after the multi-writer enforcement grant.

The Architectural Reality

  • Mutation seam: create_component/remove_component are constrained ai/services/neural-link/ComponentService.mjs tools delegating app-side to call_method (container.add(config) / component.destroy(true)); set_instance_properties is the constrained instance mutation. App-side mutation lands at src/ai/client/InstanceService.mjs.
  • Enforcement seam (#13167): resolveWriteLockWriteGuard.requestWrite (heap-side, (agentId, sessionId)-keyed subtree lock). Reverse-capture MUST follow the grant. deriveSubtreePath(componentId, parentOf) derives the absolute path at request time — re-used at undo time.
  • Capability-security: the App Worker emits only JSON DOM-ops; reverse-ops are data-not-code by construction (each reverse = a re-dispatched validated NL tool).

The Fix

A new in-memory TransactionService (per-session undo stack) + reverse-capture hooks in the InstanceService write-path (post-enforcement-grant) + an undo NL tool (6-site wiring). Reverses: create⁻¹ = remove(boundNewId), set⁻¹ = set(oldValues), remove⁻¹ = create(originatingConfig). Undo re-enters resolveWriteLock/WriteGuard.requestWrite enforced as the current caller (provenance ≠ enforcement identity), re-deriving the subtreePath on the current tree.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
undo NL tool (new, write-locked) D#13216 OQ4-B + #13167 Pops the last committed tx for the requester; re-dispatches the reverse as a validated tool, enforced as the current caller No tx / denied enforcement → no-op + recoverable error openapi.yaml + tool JSDoc unit + e2e
TransactionService (new, src/ai/TransactionService.mjs, in-memory) D#13216 OQ2-C Per-session stack keyed on the live {agentId, sessionId} writer pair (the same identity WriteGuard keys locks on; txId = per-transaction handle within the stack, not part of the key); states open→committed→undone / aborted/timedOut Disconnect/releaseAgent/timeout → abort + reconcile locks class JSDoc unit
Reverse-record schema D#13216 OQ1+OQ5 + @neo-opus-ada Stores WHO (originWriter, provenance) + WHAT (inverse-op) + targetSubtreePath (audit only) + sequenceId + bounded label; enforcement re-derives path at undo Unresolvable target → fail-closed undo-denial class JSDoc unit (incl. negative: moved/destroyed/unauthorized)
InstanceService capture hook D#13216 OQ1-B Post-enforcement-grant capture for the 3 whitelisted tools only; denied write → no reverse record Non-whitelisted → not captured inline JSDoc unit

Decision Record

Not needed (D#13216 graduation classification) — valid only while Slice-1 stays same-session, in-memory, no nl_action_log schema mutation, no Memory Core persistence, no privileged rollback capability. Any of those four flips it to Required in the relevant later slice. Decision Record impact: none — no ADR amended/superseded; aligned-with the #13167 enforcement contract + ADR 0020 (Agent Harness).

Discussion Criteria Mapping (#13216)

  • OQ1 (reverse-capture hook) → AC: per-method at InstanceService, post-grant.
  • OQ2 (stack location) → AC: in-memory per-session; nl_action_log stays forward-audit.
  • OQ3 (destructive-op reverse) → AC: replay create-config; pre-existing undo-of-remove deferred.
  • OQ4 (security boundary) → AC: reverse via re-dispatched validated tools.
  • OQ5 (× WriteGuard #13167) → AC: Option D — provenance ≠ enforcement-identity; re-enter enforcement as caller.

Acceptance Criteria

  • undo NL tool (write-locked tier) reverts the last committed transaction for the requester over the 3 whitelisted tools (create_component, remove_component, set_instance_properties); generic admin call_method is NOT undoable.
  • Reverse-capture happens only after the forward write is enforcement-granted (resolveWriteLockWriteGuard.requestWrite succeeds); a denied write creates no reverse record.
  • The reverse mutation is dispatched as a validated NL tool, enforced as the current requester (re-enters resolveWriteLock/WriteGuard.requestWrite); originWriter is stored as provenance/authorization only and is never impersonated.
  • Enforcement re-derives the subtreePath at undo via deriveSubtreePath on the current tree (NOT the stored capture-time path). Negative coverage (per @neo-gpt residual): moved target enforces on the new path; destroyed target → null → fail-closed undo-denial (unresolvable-target); unauthorized (different-writer-held overlap) → denied.
  • create⁻¹ binds the created component id from the successful add result (or an explicit tested read-back contract) before remove(newId). (per @neo-gpt residual — explicit test contract.)
  • Reverse-record schema: {sequenceId, originWriter, inverse-op descriptor, forward descriptor, targetSubtreePath (audit), bounded label/source}.
  • Transaction states open → committed → undone + aborted/timedOut; disconnect/releaseAgent/timeout sweeps the requester's open tx + reconciles held locks (lifecycles can't diverge); denied/aborted/timed-out tx are not undoable + leave no active reverse records.
  • Explicit caps: stack depth, max ops per transaction, max serialized reverse-payload size.
  • Undo-of-remove for a component with no captured create-config (pre-existing/non-agent-created) is denied + deferred (documented limitation).
  • 6-site NL-tool wiring complete (openapi path+tier+schema · toolService serviceMapping · the service method · OpenApiValidatorCompliance fixtures · unit spec autoConnect=false · e2e).
  • e2e proof: agent-created component → undo → reverts cleanly in the live tree.

Out of Scope (later #9848 children)

  • Persistence (Memory-Core / nl_action_log schema mutation) — Slice-3.
  • redo + named/atomic transaction batching — Slice-2.
  • systemRollback (privileged cross-writer rescue) — separate capability with its own security review.
  • Generic admin call_method undo (only the 3 whitelisted tools).
  • Undo-of-remove for non-agent-created components.

Avoided Traps

  • Storing the subtreePath for enforcement — rejected (per @neo-opus-ada): a moved/destroyed target between capture + undo mis-computes lock overlap; enforcement re-derives at undo (fail-closed), mirroring resolveWriteLock's request-time derivation.
  • Undo bypassing WriteGuard (enforcement-exempt) — rejected (OQ5-B): re-opens the ACRFence rollback-injection surface OQ4 closes.
  • nl_action_log as the active undo authority — rejected (STEP_BACK #7): it stays forward-audit; the in-memory stack is the active authority.
  • Pre-image capture of a live config (OQ3-A) for remove⁻¹ — rejected: the create_component module-footgun (class refs/functions don't cross the wire); replay the originating create-config instead.

Related

  • Graduated from Discussion #13216 (the design convergence, full divergence trail + Signal Ledger).
  • Parent: #9848 (Slice-tracking parent — the NL Transaction/Undo Stack).
  • Epic: #13012 (Agent Harness, Pillar-2). Enforcement contract: #13167. Doc: #13218 (learn/agentos/MultiWriterEnforcement.md).
  • Builds on: create_component (#9846), remove_component (#9847), set_instance_properties.

Release classification: post-release (Agent Harness Pillar-2 feature; not v13-release-blocking) — boardless.

Origin Session ID: 4cc428e3-cf36-4324-8646-1b96cb23fa4a

Handoff Retrieval Hints: query_summaries("Neural Link mutation undo Slice-1 transaction stack 13216 graduation") · ask_knowledge_base("NL undo WriteGuard reverse-capture", type='ticket') · the D#13216 convergence trail + Signal Ledger.

Authored by @neo-opus-vega (Claude Opus 4.8, Claude Code).

tobiu referenced in commit 8086fd2 - "fix(ai): align TransactionService stack key to the (agentId, sessionId) writer pair (#13236) (#13238) on Jun 14, 2026, 6:41 PM
tobiu referenced in commit e02a25b - "feat(ai): capture set_instance_properties reverses onto the per-session undo stack (#13248) (#13249) on Jun 14, 2026, 8:54 PM
tobiu referenced in commit 1035769 - "feat(ai): the undo Neural Link tool — peek, re-dispatch reverse, consume (#13257) (#13259) on Jun 14, 2026, 10:02 PM
tobiu referenced in commit d03161f - "feat(ai): capture create_component's reverse for undo — server-stamped marker (#13261) (#13264) on Jun 15, 2026, 1:10 AM
tobiu referenced in commit fbec07f - "feat(ai): capture remove_component's reverse for undo — position-preserving insert (#13272) (#13274) on Jun 15, 2026, 1:40 AM
tobiu referenced in commit 29b45ac - "fix(ai): wire tx-sweep into agent-disconnect (lock/tx coherence) (#13282) (#13284) on Jun 15, 2026, 2:50 AM