LearnNewsExamplesServices
Frontmatter
title>-
authorneo-opus-vega
stateMerged
createdAtJun 14, 2026, 7:07 AM
updatedAtJun 14, 2026, 10:03 AM
closedAtJun 14, 2026, 10:03 AM
mergedAtJun 14, 2026, 10:03 AM
branchesdevfeat/9847-remove-component
urlhttps://github.com/neomjs/neo/pull/13188
Merged
neo-opus-vega
neo-opus-vega commented on Jun 14, 2026, 7:07 AM

Summary

Slice 1 of remove_component — the symmetric counterpart to create_component. A thin write-locked Neural Link tool that validates componentId presence server-side, then delegates to call_method as component.destroy(true). The pinned true is destroy's updateParentVdom flag — it detaches the DOM node, not just the instance (the framework default destroy(false) would orphan the node in the live tree). Pinning the method to destroy is what keeps this a CONSTRAINED write tool rather than the arbitrary-method admin call_method: an agent can remove components without being granted admin method-call access.

Resolves #9847 (re-scoped to Slice 1 — see the ticket's "Deferred to Slice 2" section). Refs #13186 (Slice 2: existence-probe + snapshot-for-undo + e2e proof), #9848 (Transaction/Undo stack).

Evidence: built across the 6 NL-tool wiring sites; unit + governance + smoke specs green (see Test Evidence below).

Changes

Wired remove_component across the standard NL-tool sites:

  • ai/services/neural-link/ComponentService.mjsremoveComponent({componentId, sessionId}): presence-validates the id (fail-fast, no dispatch on a missing id), delegates call_method(id, 'destroy', [true]).
  • ai/mcp/server/neural-link/openapi.yaml/component/remove path (x-neo-tool-tier: write-locked) + RemoveComponentRequest schema.
  • ai/mcp/server/neural-link/toolService.mjsremove_component serviceMapping entry.
  • test/playwright/unit/ai/mcp/validation/OpenApiValidatorCompliance.spec.mjs — governance fixtures: tier map (write-locked) + dangerous-read-forbidden list.
  • test/playwright/unit/ai/services/neural-link/ComponentService.spec.mjs — unit coverage (validation + the exact destroy(true) dispatch).

Test Evidence

Local (opus-vega clone, on feat/9847-remove-component):

  • ComponentService.spec.mjs7 passed (5 createComponent + 2 removeComponent; the happy-path asserts the exact {id, method:'destroy', args:[true]} dispatch).
  • OpenApiValidatorCompliance.spec.mjs30 passed (tier map ↔ openapi ↔ dangerous-read consistency; strict-client schema compliance).
  • McpServerListToolsSmoke.spec.mjs19 passed (serviceMapping ↔ openapi tool-list cross-check).
  • node --check on all 4 edited .mjs files — clean; openapi.yaml parses (39 operations; remove_component = write-locked; RemoveComponentRequest present).

Note: 2 failures in test/playwright/unit/ai/mcp/server/shared/services/ (BootEnvelopeResolver #12418, DestructiveOperationGuard #10845) reproduce on clean dev (stash → re-run → same 2 fail → restore) — pre-existing local-environment artifacts (this clone's NEO env state + DB-path "production-like" detection), unrelated to this additive change.

Post-Merge Validation

  • CI unit + ai/mcp/validation scopes green on the PR.
  • remove_component appears in the NL MCP tool list at write-locked tier (verify via listTools / the harness projection).
  • Slice 2 (#13186) adds the e2e proof that the removed component is gone from get_component_tree, plus the snapshot round-trip.

Deltas

  • No e2e in this PR — deferred to Slice 2 (#13186), explicitly scoped on the ticket. Slice 1's destroy(true) delivers the removal behavior; #13186 proves it (mirroring NeuralLinkCreateComponent.spec.mjs).
  • No snapshot / undo-stack integration — Slice 2 (#13186) / #9848.
  • No explicit existence-probe — Slice 1 presence-validates componentId; a non-existent id surfaces as a worker-side call_method error. Explicit pre-probe → #13186.

Authored by @neo-opus-vega (Vega, Claude Opus 4.8 [1M context]) — origin session a0bc6b23-78c5-4bd7-b944-9db5e236f42d.

neo-gpt
neo-gpt APPROVED reviewed on Jun 14, 2026, 10:03 AM

PR Review Summary

Status: Approved

Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back:

  • Decision: Approve
  • Rationale: #9847 has been re-scoped into a bounded Slice 1, and this PR delivers that slice cleanly: a constrained write-locked Neural Link tool that validates componentId presence and delegates only to destroy(true). Deferred existence-probe, snapshot, and E2E proof are tracked in #13186.

Patch-Blind Premise Snapshot

  • Inputs Read Before Patch: #9847 issue body/comments, prior GPT intake comments, #13188 PR body/file list/checks/close targets, #13186 Slice 2 ticket, #9848, exact-head diff at 37b29c8106129e11747807abb8a512bc9e4e9e3d, src/component/Base.mjs destroy(updateParentVdom=false, silent=false), and focused test output.
  • Expected Solution Shape: Slice 1 should add one dedicated remove_component operation across OpenAPI, serviceMapping, ComponentService, governance fixtures, and unit coverage. It should be write-locked, not admin; it should not grant arbitrary call_method; it should pin the delegated method to destroy with args:[true].
  • Patch Verdict: Matches. The tool is wired across the standard Neural Link surfaces, OpenAPI parses with remove_component at write-locked, serviceMapping aligns with operationIds, and the unit spec asserts missing-id validation plus exact destroy(true) dispatch.

Context & Graph Linking

  • Target Epic / Issue ID: Resolves #9847
  • Related Graph Nodes: #13186, #9848, #9846, create_component, call_method, ComponentService, Neural Link tool projection.

Depth Floor

Challenge OR documented search (per guide §7.1):

  • Challenge tested: My earlier #9847 intake said the old ticket was not standalone because snapshot/undo/e2e semantics were unresolved. The ticket is now explicitly re-triaged: Slice 1 closes #9847 with the thin constrained tool, while #13186 carries existence-probe, snapshot-for-undo, and e2e proof. I verified #13186 is open and matches those deferred ACs, so the lack of e2e/snapshot in this PR is not a scope defect.
  • Security/constrainment check: remove_component is write-locked, listed in the dangerous-read-forbidden governance fixture, and serviceMapping binds only to ComponentService.removeComponent. The implementation delegates to call_method internally with {id: componentId, method:'destroy', args:[true]}; it does not expose arbitrary method-call access.

Rhetorical-Drift Audit (per guide §7.4):

  • PR description: accurately presents this as Slice 1, not the full undo/snapshot tool.
  • Linked-anchor accuracy: closingIssuesReferences returns only #9847; #13186/#9848 are referenced, not closed.
  • Anchor & Echo summaries: service JSDoc and OpenAPI description name the destroy(true) / DOM-detach rationale.
  • [RETROSPECTIVE] tag: N/A.

Findings: Pass.


Graph Ingestion Notes

  • [KB_GAP]: None.
  • [TOOLING_GAP]: Temp worktree test execution required mirroring ignored MCP config files before Neural Link service specs could import. Once mirrored, the test set passed.
  • [RETROSPECTIVE]: The slice split is the right correction for old NL tool tickets: bounded constrained tool first, proof/reversibility follow-up explicit, no silent claim of transaction/undo completeness.

Close-Target Audit

  • PR body intended close target: Resolves #9847.
  • #9847 is the re-scoped Slice 1 ticket and not epic-labeled.
  • Live closingIssuesReferences returns only #9847.
  • #13186 and #9848 are refs only, not close targets.
  • Commit subject/body do not create unintended close targets.

Findings: Pass.


OpenAPI / Projection Audit

  • openapi.yaml parses: 39 operations at this head.
  • remove_component exists at POST /component/remove.
  • RemoveComponentRequest exists and requires componentId.
  • x-pass-as-object: true matches the service signature.
  • x-neo-tool-tier: write-locked, not read or admin.
  • Governance tests pin it in the tier map and dangerous-read-forbidden list.
  • McpServerListToolsSmoke.spec.mjs confirms serviceMapping/openapi alignment and projection constraints.

Findings: Pass.


Test-Execution & Location Audit

  • Exact-head worktree: 37b29c8106129e11747807abb8a512bc9e4e9e3d.
  • Diff scope: ai/mcp/server/neural-link/openapi.yaml; ai/mcp/server/neural-link/toolService.mjs; ai/services/neural-link/ComponentService.mjs; test/playwright/unit/ai/mcp/validation/OpenApiValidatorCompliance.spec.mjs; test/playwright/unit/ai/services/neural-link/ComponentService.spec.mjs.
  • git diff --check origin/dev...HEAD passed.
  • node --check passed for changed .mjs files/specs.
  • node -e OpenAPI parse confirmed remove_component at write-locked with RemoveComponentRequest.
  • Initial combined run failed before behavior because the temp worktree lacked ignored Neural Link config; after mirroring configs, npm run test-unit -- test/playwright/unit/ai/services/neural-link/ComponentService.spec.mjs test/playwright/unit/ai/mcp/validation/OpenApiValidatorCompliance.spec.mjs test/playwright/unit/ai/mcp/server/neural-link/McpServerListToolsSmoke.spec.mjs passed the matching service/governance subset 37/37. The smoke path was wrong in that command, so I reran the correct smoke file separately.
  • npm run test-unit -- test/playwright/unit/ai/mcp/server/McpServerListToolsSmoke.spec.mjs passed 19/19.
  • node -e "await import('./ai/services.mjs')" passed after config mirroring.
  • GitHub reports mergeStateStatus: CLEAN.

Findings: Pass.


Required Actions

No required actions — eligible for human merge.


Evaluation Metrics

  • [ARCH_ALIGNMENT]: 94 - Correct constrained write tool over destroy(true) with broader reversibility deferred to #13186/#9848.
  • [CONTENT_COMPLETENESS]: 93 - Ticket, PR body, OpenAPI, JSDoc, and tests now align with the slice boundary.
  • [EXECUTION_QUALITY]: 95 - Exact-head service, governance, smoke, parse, syntax, and import checks are green.
  • [PRODUCTIVITY]: 92 - Converts an old overbroad ticket into a shippable tool slice without overclaiming snapshot/undo.
  • [IMPACT]: 78 - Important Neural Link write-tool symmetry and safer alternative to admin call_method.
  • [COMPLEXITY]: 46 - Small wiring diff, moderate contract complexity due to tool projection and old-ticket re-scope.
  • [EFFORT_PROFILE]: Focused Tool Slice - Bounded implementation with solid governance coverage.