Skip to content

refactor: one sidecar transport, one graph-export default - #123

Open
endrix wants to merge 2 commits into
refactor/toolkit-helpersfrom
refactor/sidecar-transport
Open

endrix wants to merge 2 commits into
refactor/toolkit-helpersfrom
refactor/sidecar-transport

Conversation

@endrix

@endrix endrix commented Nov 8, 2026 •

Copy link
Copy Markdown
Owner

Phase 4b of the declutter plan, second half. Stacked on #122 (it uses the source-files.ts helpers that PR introduces); the base moves to main once #122 lands.

Six transports, one

Each of these spawned the sidecar, wrote { file, op, args } on stdin and parsed stdout with its own wording, its own deadline and its own idea of an empty answer:

caller before now
SidecarInvoker.invokeSidecar (every diagram edit) own copy, 60 s default invokeSidecar({ command, file, op, args })
invokeSidecarOp (chat edit backend, registry tools, capabilities, openability's sibling) own copy with PYTHONPATH from the package root thin wrapper, pythonPackageRoot: true
exportWorkflowGraph (chat exportGraph) own copy, returned the graph or undefined deleted; exportGraph sends the export op through invokeSidecarOp and reads diagnostic.graph
tryLoadGraphFromSidecar (model source) own copy with the graph-load deadline and the timeout hint invokeSidecar({ ..., timeoutMs, pythonPackageRoot: true }), then the same graph handling
probeDiagramOpenability own copy, 20 s, read the last stdout line invokeSidecar({ ..., timeoutMs: 20 s }); ok → true, status: 'error' → false, else unknown
sidecar.send command own copy invokeSidecar(...) → { ok, message }

sidecar-transport.ts never throws; it describes a child that could not start, timed out or exited non-zero with describeChildFailure plus the op, carries stderr, code (from diagnostic.code) and timedOut, and reads the last non-empty stdout line, so a sidecar that logs above its answer is still understood (the openability probe did this; the other five would have failed on the log line). The diagram's own edits still run without the package-root cwd/PYTHONPATH, as before; the requests that import the file (export, chat edits) keep it.

One graph-export default

All four sidecars answer exportGraph (wfpy and calpy alias it to their older names; hdl and ndf accept all three). The model source used to pick exportNetworkGraph or exportWorkflowGraph by acquisition strategy and ignore the product's exportOp (sv says exportModuleGraph and only worked through hdl-sidecar's aliases); registry tools and the chat defaulted to exportWorkflowGraph. Now: SidecarRuntimeConfig.exportOp is filled from the profile input, and every sender uses exportOp ?? DEFAULT_EXPORT_OP ('exportGraph').

What the review made of it

  • A non-zero exit lost the sidecar's stderr from the message. The message is now what describeChildFailure says (command and stderr) plus the op, for every failure kind; failure: 'start' | 'timeout' | 'exit' | 'rejected' | 'invalid-json' says which, and the model source prefixes the command only where the message does not already carry it.
  • A pretty-printed answer was refused by last-line parsing. The whole output is parsed first, the last line second.
  • The probe took an empty answer for yes. ok with a response is yes; status: 'error' is no; nothing is unknown, as before. The dead payload object and the catch around a call that never throws are gone.
  • The per-caller pythonPackageRoot flag kept two environments for one sidecar. Every request now runs from the file's package root with it on PYTHONPATH, the diagram's own edits included; stated in the changelog.
  • DEFAULT_EXPORT_OP lives in sidecar-transport.ts (re-exported by sidecar-graph-export.ts), and the profile's literal uses it.

Tests

sidecar-transport.test.ts mocks the child process and pins the request line, last-line parsing, the empty answer, a rejected request with its code, exit/timeout/spawn failures with stderr, the deadline, and that nothing throws. chat-edit-backend.test.ts asserts the export through invokeSidecarOp; two characterization tests now expect exportGraph. Typecheck 5/5, neutrality 5/5, toolkit 336 with the known local wizard timeouts (green alone, green in CI).

https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv

Phase 4b, second half. Six places each spawned the sidecar, wrote the
request and parsed the answer with their own wording and deadlines: the
invoker behind every diagram edit, the chat's invokeSidecarOp and
exportWorkflowGraph, the model source's graph export, the openability
probe and the raw sidecar.send command. sidecar-transport.ts is the one;
the answer is the last line the sidecar prints, so one that logs above
it is still read. exportWorkflowGraph is gone: the export is an op like
the others.

The graph-export op has one default, exportGraph, which every sidecar
answers to. The model source used to pick exportNetworkGraph or
exportWorkflowGraph by acquisition strategy and ignore the product's
exportOp; it now sends the product's, carried by SidecarRuntimeConfig.

Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
A process failure is reported as the child-process module describes it,
command and stderr included, with the op; the model source prefixes the
command only on a refused or garbled answer, where it is not already
named. The answer is read whole when it is one JSON document and by its
last line when the sidecar logs above it. The openability probe takes
an empty answer for no answer again. Every request runs from the file's
Python package root with it on PYTHONPATH, the diagram's own edits too,
so one sidecar sees one environment. DEFAULT_EXPORT_OP lives beside the
transport and the profile uses it. Merges the helper branch's review
fixes.

Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
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.

1 participant