Adding a node type¶
A Metis node is data plus a handler. The catalogue entry declares what a node is (config schema, output schema, palette); the handler says what it does at run time. Inputs and outputs are deliberately generic - a node receives the run's state and gives one payload - only its configuration is typed.
1. Declare it in the catalogue¶
Add an entry to packages/metis-catalogue/src/nodeTypes.v1.json:
{
"type": "mynode",
"category": "transform",
"execution": "inline",
"versions": ["1.0.0"],
"status": "v1",
"tier": "open",
"configSchema": {
"type": "object",
"required": ["message"],
"properties": {
"message": { "type": "string", "title": "Message", "description": "Shown in the inspector as field help." }
}
},
"outputSchema": { "type": "object", "properties": { "result": { "type": "string" } } },
"handler_status": "ready",
"palette": { "label": "My Node", "icon": "bolt", "colour": "sky-500", "description": "One honest sentence about what it does." }
}
categoryis one oftrigger | logic | transform | integration.outputSchemapowers the variable picker: downstream steps offer{{node-<id>.data.<field>}}chips from it.- Add search keywords in
packages/metis-catalogue/src/loader.ts(BASE_NODE_META) so the picker finds it by synonym.
2. Implement the handler¶
Two kinds:
- Handler nodes (most nodes: API calls, email, database) - register a
NodeHandlerinpackages/metis-nodes/src/register.ts. The handler gets the node's already-substituted config ({{...}}references resolved) and returns{ status, message, nodeData }- usestateEnvelope()for the success shape. Policy (retries/backoff/timeout) is applied for you. - Inline control nodes (branching, joining, iteration) - these run inside
the engine's dispatch activity. See
packages/metis-engine/src/nodes/for the pattern (switch.ts,merge.ts,flow-inline.ts). Branch-shaped nodes returnselectedTargetIds/orphanedTargetIdsso losing branches are orphaned.
3. Canvas + inspector¶
- The inspector renders from
configSchemaautomatically (text, select, number, JSON fields;descriptionbecomes field help). Only build a bespoke widget when a schema form genuinely cannot express the config - seex-helix-widgetandpackages/metis-editor/src/builder/inspector/. - If the node branches, give its handles the ids the engine routes by in
packages/metis-editor/src/builder/node-visual.ts(outputPorts). A mismatched handle id means an edge the user draws points at a branch that never fires.
4. Tests (the definition of done)¶
- A unit test for any pure logic.
- A walk test through the real engine on
@temporalio/testing'sTestWorkflowEnvironment- copy the harness inpackages/metis-engine/src/__tests__/flow-nodes-walk.spec.ts. - An editor e2e that adds the node from the picker and round-trips its config
(
packages/metis-editor/e2e/). npm run typecheck && npm run lint && npm test && npm run gates && npm run e2eall green. The coverage test (node-coverage.spec.ts) fails the build if a catalogue type has no execution path - update its inline list for engine nodes.