{"id":18085,"plugin_id":"plugins_6a85a5758d408191bbc948af18f557a2","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:14:36.802Z","digest":"ef5fe5fbefbff1c8352a1caa0697deb69c97addd3f903a7dde41464e202278f0","against":null,"payload":{"description":"Implement or review C++ hgraph compute nodes, sink nodes, algorithms, and concrete operator-node specializations. Use when adding or changing a static node `eval`, lifecycle hooks, local or recordable state, REF usage, incremental or windowed algorithms, input-type branching, operator strategy selection or overload registration, type-erased node strategy, node naming, complexity documentation, or native/Python behavioral tests for a compute or sink node.","included_files":[{"relative_path":"agents/openai.yaml","size_in_bytes":236}],"name":"hgraph-compute-sink-node","skill_md_contents":"---\nname: hgraph-compute-sink-node\ndescription: Implement or review C++ hgraph compute nodes, sink nodes, algorithms, and concrete operator-node specializations. Use when adding or changing a static node `eval`, lifecycle hooks, local or recordable state, REF usage, incremental or windowed algorithms, input-type branching, operator strategy selection or overload registration, type-erased node strategy, node naming, complexity documentation, or native/Python behavioral tests for a compute or sink node.\n---\n\n# HGraph Compute and Sink Nodes\n\nBuild C++-first nodes whose per-tick path is small, whose lifecycle and state\nsemantics are explicit, and whose names and tests fit the operator system.\n\n## Start from the existing contract\n\n1. Read the current repository's `AGENTS.md` and the relevant operator\n   definition, implementation, registration, and tests before editing.\n2. Establish whether the task targets hgraph core or a downstream extension:\n   - In the hgraph core checkout, read the applicable parts of\n     `docs/source/user_guide/cpp/authoring_nodes.rst` and use\n     `tests/cpp/test_static_node.cpp` as the compact executable reference for\n     lifecycle hooks, `State`, and `RecordableState`.\n   - In a downstream project, use that project's extension layout and tests,\n     plus the public hgraph headers and documentation for its pinned hgraph\n     version. Do not assume the hgraph core source tree is present.\n3. Classify the work before choosing a type:\n   - Use a compute node for time-series input plus an output.\n   - Use a sink node for time-series input and a side effect with no output.\n   - Implement an operator specialization when the behavior belongs to an\n     existing abstract operator or valid implementations may vary by type or\n     algorithm.\n   - Introduce a standalone node only when the behavior is not an operator and\n     cannot be expressed clearly as a graph.\n\nKeep static node implementation structs empty. Put instance data in the\nappropriate selector or plan rather than in members.\n\nKeep the node's per-tick implementation logic in `eval`. Extract a helper only\nwhen it is a well-defined operation with its own clear contract or is genuinely\nshared by multiple callers. Do not scatter one node's implementation across\nsingle-use helper functions. Keep one-off lifecycle work in `start` and `stop`\nas described below.\n\n## Design the hot path first\n\nTreat `eval` as the hot path. Leave only work that depends on the current tick.\n\n- Prefer typed `In`, `Out`, `State`, and `RecordableState` access.\n- Avoid schema discovery, registry lookup, overload selection, RTTI, string\n  construction, parsing, policy selection, and representation selection in\n  `eval`.\n- Avoid avoidable allocation, container growth, reference counting, locking,\n  and Python conversion in `eval`. Reuse pre-sized storage when the algorithm\n  requires scratch space.\n- Resolve wiring-time scalar policies to a concrete overload or plan. Do not\n  branch on a policy string every tick.\n- Read each input view only as often as needed. Use modified/delta access when\n  full-value traversal is unnecessary.\n- Emit no output when the contract calls for no tick; do not manufacture an\n  unchanged value merely to simplify control flow.\n\nIf expensive work appears unavoidable, state why, inspect neighboring hot-path\ncode, and add focused performance evidence for a material regression risk.\n\n## Select input types through operators\n\nTreat an `if`/`else`, `switch`, schema-kind test, RTTI check, or visitor branch\nthat chooses node behaviour from an input's type as a design signal: stop and\nmake the alternatives operator overloads. Wiring already knows the concrete\ninput schemas, so select the implementation once there instead of rediscovering\nthe type during `start` or on every `eval`.\n\n- If an operator already describes the behaviour, implement and register a\n  more-specific node or graph overload.\n- If no operator exists, introduce an implementation-free operator contract,\n  then register the concrete implementations under it. Follow\n  `../hgraph-write-operators/SKILL.md`.\n- Keep each node signature as specific as its implementation. Template shared\n  code where useful, but register the concrete instances that wiring should\n  choose.\n- Use a wiring-time scalar value and overload `requires_` predicate when a\n  fixed policy selects an implementation. Do not carry that choice into the\n  hot path.\n- Use runtime switching only when the selecting value is itself time-series\n  data and the contract genuinely permits the implementation choice to change\n  while the graph runs.\n\nDo not hide semantic type selection inside an erased helper or plan. An erased\nops-table may dispatch representation mechanics at a required abstraction\nboundary; it must not replace the operator registry's wiring-time selection of\nmeaningfully different behaviours.\n\n## Use REF deliberately\n\nTreat `REF` as a semantic indirection with significant memory overhead, not as\nthe default way to connect nodes. Input bindings to non-`REF` outputs are\nlightweight C++ structures.\n\n- Use a normal binding when the consumer only needs to read an output.\n- Use `REF` when indirection prevents a value from being copied from an input\n  to an output. Selection and routing operators such as `if_then` and `route`\n  are the primary pattern: capture the selected source output and direct that\n  source instead of copying its current value.\n- Use `REF` when dynamic source identity is part of the operator contract.\n- Before adding `REF`, state which copy or dynamic binding it avoids and verify\n  that plain input binding cannot express the behavior.\n\nDo not reject `REF` merely because it is expensive. Use it whenever the\nindirection is required, and pay its overhead intentionally.\n\n## Place one-off work in lifecycle hooks\n\nUse `start` for work performed once per node lifetime, including:\n\n- initializing `State`, or seeding `RecordableState` only when it is invalid;\n  never overwrite recordable state restored for replay;\n- initializing sequences, cursors, buffers, or cached plans associated with\n  that state;\n- acquiring run-scoped resources or establishing subscriptions;\n- scheduling the initial evaluation when declarative `schedule_on_start` is\n  insufficient.\n\nUse `stop` to flush, finalize, unsubscribe, or release what `start` acquired.\nKeep teardown safe for partial lifecycle progress and follow existing scope\nguard patterns where rollback is required.\n\nRequest only the selectors each hook needs. When lifecycle-only arguments are\nrequired, use the established explicit `signature_args` pattern rather than\npolluting `eval`.\n\n## Choose state by semantics\n\n- Use no state for a pure transformation or side effect.\n- Use `State<T>` for private, ephemeral implementation state that does not\n  participate in record/replay.\n- Use `RecordableState<TSchema>` when a tick updates state that affects later\n  ticks: this is loopback or feedback state and should be observable and\n  restorable by record/replay.\n- Do not combine `State` and `RecordableState` in one static node.\n- Keep recordable state structured and typed. Update only the fields modified\n  by the current tick.\n\nDo not replace semantic loopback state with an opaque cache merely because the\ncache is easier to implement.\n\n## Implement algorithms incrementally\n\nPrefer an incremental algorithm that consumes the current tick or delta and\nupdates only the sufficient statistics needed for the result. Do not retain an\nentire history or window in private state when an online formulation exists.\nMinimal sufficient statistics are algorithmic state; they are preferable to\ncapturing the input history.\n\n- Put incremental state that affects later ticks in `RecordableState`.\n- When an exact result intrinsically requires the window contents, use `TSW`\n  as the semantic window rather than copying the window into private state.\n  Exact median is the standard example.\n- Do not maintain a private duplicate of a `TSW` merely to simplify the\n  implementation.\n- Test an incremental implementation against a simple full-recomputation\n  reference over multiple ticks, including removals and boundary conditions.\n\nDocument algorithmic cost beside the implementation and public operator:\n\n- state the worst-case or amortized time cost per tick;\n- state retained-memory cost;\n- for `TSW`, express both costs in terms of window size `W` and document the\n  supported window semantics.\n\n## Exploit concrete C++ types\n\nUse type erasure at boundaries that genuinely need to accept independently\nrealized types. Once wiring or plan construction selects a concrete strategy,\nmake the per-tick implementation typed.\n\n- Template an implementation when its scalar or time-series types vary, and\n  use those template parameters to remove runtime conversion and dispatch.\n- Use the operator registry to choose a concrete typed overload instead of\n  inspecting `TSTypeKind`, scalar metadata, or a schema in the node.\n- Select erased representation operations once from immutable wiring or plan\n  metadata, then dispatch through the installed ops table.\n- Follow the repository passive ops-table plus explicit erased-ownership\n  pattern for a reusable erased contract. Do not add a facade `std::variant`\n  or scatter strategy branches through semantic node code.\n- Preserve native C++ authoring as the primary path. Adapt Python values and\n  callables to the same node rather than implementing separate Python runtime\n  semantics.\n\n## Name operator implementations consistently\n\nName a concrete operator node from the operator plus a concise specialization\nsuffix, using lower snake case.\n\n- If the operator name ends in `_`, append the suffix directly:\n  `add_` + `float` becomes `add_float`.\n- Otherwise insert `_` as the separator:\n  `debug_print` + `tsb` becomes `debug_print_tsb`.\n- Describe the specialization by the distinguishing type, shape, policy, or\n  direction. Avoid generic suffixes such as `impl` when a precise name exists.\n- Apply the same rule to the implementation type and any explicit diagnostic\n  `name` unless a nearby registration convention requires a different label.\n\nKeep the abstract operator in its operator header, the concrete node under the\nexisting `impl` boundary, and register it through the neighboring operator\nregistration mechanism.\n\nUse the operator as the public abstraction whenever implementation selection\nmay vary by type or algorithm. Keep concrete node choices behind overload\nresolution or the operator's wiring contract.\n\nWhen an operator offers meaningful algorithmic trade-offs, expose a strongly\ntyped enum as a wiring-time scalar policy:\n\n- Give each enum value a name that describes the accepted property or risk.\n- Document accuracy, overflow, numerical stability, time, and memory trade-offs\n  that differ between values.\n- Select the concrete overload or immutable plan before execution; do not\n  switch on the enum in `eval`.\n- Use a graph-level switch only when the policy is genuinely time-varying.\n\nFor example, average may offer sum divided by count, with overflow risk, and an\nonline recurrence based on the previous average, count, and next value, with\ndifferent floating-point accuracy behavior. Test every exposed strategy.\n\n## Document standalone nodes\n\nFor a node that is not an operator, add documentation beside its public or\nimplementation declaration that states:\n\n- why a primitive node is required instead of a graph or existing operator;\n- its input activation and validity behavior;\n- its output or side-effect contract;\n- its lifecycle and state semantics;\n- any intentional hot-path cost or external-resource behavior.\n\nAdd user-facing documentation when the standalone node is public. Do not add\ndomain-specific behavior to core merely because the algorithm is generic.\n\n## Test through public wiring\n\n1. Add native C++ behavior coverage using a concrete minimal graph and\n   `eval_node`. Cover sink side effects without driving runtime internals\n   directly.\n2. Cover multiple ticks for stateful nodes, including initialization, update,\n   no-tick behavior, and teardown where relevant.\n3. Prove recordable loopback state through its public recordable-state behavior.\n4. Add equivalent Python authoring/bridge coverage for every Python-visible\n   behavior; keep the semantic implementation in C++.\n5. Test invalid input, passive/active behavior, and lifecycle failure in\n   proportion to the risk.\n6. Run focused tests while iterating, then every acceptance gate required by\n   the current repository `AGENTS.md`. Add an installed-SDK consumer check for\n   public-header changes and cross-platform validation for large runtime or\n   type-erasure changes.\n\nBefore handing off, review the final diff specifically for work that can move\nout of `eval`, unnecessary `REF`, retained history that can become incremental,\nstate that should be recordable, undocumented per-tick or window cost, policy\nbranches that should select an overload, per-tick erased dispatch that can\nbecome typed, and names that do not identify their operator specialization.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}