Skip to content

feat(nodes)!: retire 24 nodes the sandbox and Code node already cover - #5348

Merged
georgi merged 2 commits into
mainfrom
claude/node-retirement-opportunities-v1wz5k
Aug 29, 2026
Merged

feat(nodes)!: retire 24 nodes the sandbox and Code node already cover#5348
georgi merged 2 commits into
mainfrom
claude/node-retirement-opportunities-v1wz5k

Conversation

@georgi

@georgi georgi commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

What changed

Retires 24 node classes across five clusters whose capability already ships somewhere else, and adds the four snippets that stand in for two of them. The registry goes 483 → 459 nodes, net −20,237 lines. lib.svg's 12 element builders go because an svg_element is a plain object and the 12 svg-* snippets already shipped; Document and SVGToImage stay, since the guest has no sharp. lib.nlp (7) goes with its 2,766 lines of vendored stemmers, naive Bayes, TF-IDF, phonetics and sentiment lexicon. lib.mail (3) goes because search_email / add_label_to_email / archive_email were already a second implementation against the same imapflow client. lib.secret.GetSecret goes because getSecret(name) does the same read bound by the run's declared secretScope, while the node read any secret and fell back to process.env[name]. lib.grid.CombineImageGrid goes because image.grid is the same sharp call host-side — SliceImageGrid stays, and its placement metadata is what makes reassembly exact.

Two of these reverse a decision recorded in docs/CODE_NODE_COVERAGE.md, and both reversals turn on the same thing: that audit asked whether a guest script could do the work, when the replacement was a host capability. It never weighed packages/agents/src/capabilities/email.ts or image.grid, neither of which it mentions.

lib.nlp is removed with no replacement, deliberately. Stemming, TF-IDF, sentiment and phonetic matching have no in-product answer until a @nodetool-ai/sandbox-nlp pack lands. That gap is written into docs/CODE_NODE_COVERAGE.md as follow-up work rather than left implied. No shipped example used any of the removed nodes.

The three shipped SVG examples now build their element list in one Code node, where array order is paint order — two Code nodes feeding a List collect in arrival order, which is not deterministic. The gradient card also now references its gradient; the original defined one and never used it.

Verification

Run on this diff (a missing mesa-vulkan-drivers ICD was installed per AGENTS.md § WebGPU on a headless machine, and packages/model3d needed its missing workspace symlink):

  • npm run test:affected — the sandbox packs are not workspaces, so selection falls back to everything. Ran the whole pass instead: packages 112/113 tasks, web 1201 suites / 13,694 tests, electron 63 suites / 686 tests, all green.
  • npm run typecheck — web and electron clean.
  • npm run lint — exit 0. It caught an orphaned GradientLibNodeOutputs type alias left by the SVG trim, now removed.
  • npm run dev:nodetool -- harness gate5/5 selfchecks passed (the diff selects workflow-authoring, workflow-execution, dsl-native-flow, sandbox-packages, web-editor).
  • npm run validate:examples255/255 graphs validate cleanly.
  • All three rewritten SVG examples executed end to end: correct paint order, and the gradient card produces a real 24 KB PNG at 800×480 (400×240 at scale 2).

Two pre-existing failures are unrelated to this diff and reproduce identically on a clean tree:

  • @nodetool-ai/protocol#test — 2 suites fail to load on Cannot find package 'ajv/dist/2020.js'; ajv 6.15.0 is hoisted at root while packages/protocol declares ^8.20.0. All 974 of its tests pass. Confirmed by git stash + re-run.
  • mobile typecheck — mobile/node_modules is absent (mobile is intentionally not a root workspace). Nothing in mobile/ is touched by this diff.

Checks inverted to prove they can fail

The validator was inverted once by pointing a rewritten example back at a retired type:

$ npm run nodetool -- validate /tmp/inv.json
❌ Workflow has 1 error(s).
  error Unknown node type "lib.svg.Rect" (not in the registry; Python-only nodes
  are not validated statically) [lib.svg.Rect r] (unknown_node)

The snippet analyzer I used to check the four new bodies was also inverted — and the first version of it was wrong: it tested "message" in parsed when the error key is error, so it reported const x = ; as parsing fine. Corrected, with controls that discriminate:

control:syntax-error: parses=false (expected false) Unexpected token (1:10)
control:valid:        parses=true  (expected true) imports=["@nodetool-ai/sandbox-yaml"] returns=1
PASS gmail-search     imports=["@nodetool-ai/sandbox-nodetool/google"] returns=1 fallsThrough=false
PASS gmail-add-label  imports=["@nodetool-ai/sandbox-nodetool/google"] returns=1 fallsThrough=false
PASS gmail-archive    imports=["@nodetool-ai/sandbox-nodetool/google"] returns=1 fallsThrough=false
PASS image-grid       imports=[]                                       returns=1 fallsThrough=false

The docs/nodes/catalog.json edit was checked semantically rather than by diff size — git presented 160 insertions that were pure alignment noise: 24 types removed, 0 added, 0 surviving entries modified, no metadata drift.

Agent capabilities

No capability is added, and no capability's declared contract changes. The four new entries are editor snippets in web/src/config/codeSnippets.ts, which are virtual nodes, not capabilities — they call gmail_search / gmail_modify_labels, which already exist with unchanged schemas.

New checks

No check, rule, or audit is added. Two existing ones were repointed off retired nodes rather than weakened:

  • packages/image-nodes/tests/lib-grid.test.ts keeps the assertion that slice placements tile the canvas exactly with no gaps or overlaps — the property that makes reassembly possible — and drops only the two tests that needed CombineImageGrid.
  • packages/base-nodes/tests/e2e/lib-grid.test.ts now asserts CombineImageGrid is absent from the registry, so the retirement itself is pinned.
  • The SVG suite keeps its XML-escaping coverage by passing a raw element with &, < and " through Document, exercising the same escape helpers the deleted builders used.

Four synthetic test fixtures that used lib.grid.CombineImageGrid as an arbitrary list-input node were repointed to lib.svg.Document, which still exists and has the same list fan-in shape.


Generated by Claude Code

claude added 2 commits August 29, 2026 11:04
Remove five clusters whose capability ships elsewhere, and add the snippets
that stand in for two of them.

- lib.svg element builders (12): an svg_element is a plain object, so a Code
  node builds the whole list in one place where array order is paint order.
  Document and SVGToImage stay — the guest has no sharp.
- lib.nlp (7): 501 lines of node class over 2,766 of vendored stemmers, Bayes,
  TF-IDF, phonetics and a sentiment lexicon. No shipped example used it.
- lib.mail (3): search_email / add_label_to_email / archive_email were already
  a second implementation against the same imapflow client, and Gmail reaches a
  Code node through @nodetool-ai/sandbox-nodetool/google with the OAuth token
  held host-side.
- lib.secret.GetSecret: getSecret(name) does the same read, bound by the run's
  declared secretScope. The node read any secret and fell back to
  process.env[name], which is a way around a guarantee the sandbox enforces.
- lib.grid.CombineImageGrid: image.grid is the same sharp call, host-side.
  SliceImageGrid stays; its placement metadata is what makes reassembly exact.

Two of these reverse a decision in CODE_NODE_COVERAGE.md. Both reversals turn
on the same thing: that audit asked whether a guest script could do the work,
when the replacement was a host capability.

New snippets: gmail-search, gmail-add-label, gmail-archive (category Email) and
image-grid (category Image).

lib.nlp is removed with no replacement. Stemming, TF-IDF, sentiment and
phonetic matching have no in-product answer until a sandbox-nlp pack lands;
the gap is written down in docs/CODE_NODE_COVERAGE.md rather than implied.

The three shipped SVG examples now build their elements in one Code node. The
gradient card also references its gradient, which the original defined and
never used.
lib-nlp.ts was the only importer of `compromise` (entity extraction), and
lib-mail.ts the only importer of `imapflow` in integration-nodes. Nothing in
the repo imports either now — `imapflow` stays declared in packages/agents,
which still uses it for the email capabilities.

Lockfile drops compromise and its transitive tree (efrt, grad-school,
suffix-thumb); no package is added.
@georgi
georgi enabled auto-merge August 29, 2026 11:45
@georgi
georgi merged commit ec8a13c into main Aug 29, 2026
23 checks passed
@georgi
georgi deleted the claude/node-retirement-opportunities-v1wz5k branch August 29, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants