LearnNewsExamplesServices
Frontmatter
id13272
titleNeural Link mutation undo — Sub-B2: remove_component reverse-capture (position-preserving)
stateClosed
labels
enhancement
assigneesneo-opus-vega
createdAtJun 15, 2026, 1:19 AM
updatedAtJun 15, 2026, 1:40 AM
githubUrlhttps://github.com/neomjs/neo/issues/13272
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 15, 2026, 1:40 AM

Neural Link mutation undo — Sub-B2: remove_component reverse-capture (position-preserving)

neo-opus-vega
neo-opus-vega commented on Jun 15, 2026, 1:19 AM

Context

Sub-B2 of #13221 (NL mutation undo — Slice-1) — the second half of create/remove capture (Sub-B1 = create, #13261/#13264 merged). gpt cross-family-vetted the Sub-B design (guardrail 2: position-preservation). Completes the create/remove set so the merged undo tool (#13259) reverts a removal cleanly.

Seam + Design (gpt-vetted)

remove_component forwards server-side as call_method component.destroy(true); the app-side InstanceService.callMethod captures the reverse — but before the destroy (the config/parent/index must be snapshotted before the component is gone):

  • Server-stamped marker: ComponentService.removeComponent stamps undoKind: 'remove_component' on its call_method dispatch (NOT in CallMethodRequest's schema — the generic call_method forwards only {id, method, args}, stripping any injected marker).
  • App-side pre-destroy capture: on marker + canonical destroy(true) + writer identity + non-replay, snapshot parentId + the child's index (parent.indexOf) + a JSON-safe config (toJSON); reverse = call_method insert(index, config) on the parent — position-preserving (guardrail 2: add() appends → tree-order drift, so re-create via insert(index, …), not append).
  • toJSON bound: a documented JSON-safe serializable-config snapshot — no full-live-state-fidelity claim (TransactionService's data-not-code + payload caps bound it).

Acceptance Criteria

  • remove_component server-stamps undoKind (not in the public CallMethodRequest schema).
  • App-side callMethod captures remove's reverse (= insert(index, config) on the parent) before the destroy, when marked + canonical (destroy(true)) + agent-attributed; fail-closed on mismatch; suppressed on undoReplay.
  • Round-trip: a marked remove → undo → the child is re-inserted at its original index with its config; transaction consumed.
  • Negative (guardrail 1): a generic call_method destroy (no marker) is NOT captured.
  • ComponentService.spec server-side dispatch test asserts the remove_component marker — and is run (the Sub-B1 missed-server-spec lesson).

Out of Scope

  • A live-bridge / two-agent e2e (the in-process round-trip is the proof here).

Related

Parent: #13221 (Slice-1). Sub-B1: #13261/#13264 (merged). Undo tool: #13259. remove_component: #9847. Pillar-2: #13012. Slice-tracking: #9848.

Authored by @neo-opus-vega (Claude Opus 4.8, Claude Code). Origin Session ID: 4cc428e3-cf36-4324-8646-1b96cb23fa4a.