# Extension Points

## Stability

Supported Extension API Level: Intermediate

## Goal

Add new behavior without breaking runtime assumptions.

## Supported extensions

* Flow nodes (graph-level behavior).
* Instructions and Conditions (runtime execution).
* Custom types and enum buckets.
* Editor drawers and summaries.

## Context shortcuts (FlowContext)

Use built-in, lazily cached context values and add your own when needed.

Built-in keys:

* `FlowContextKeys.MainCamera`
* `FlowContextKeys.MousePosition` (screen position)
* `FlowContextKeys.MouseRay`
* `FlowContextKeys.MouseRay2D`

Access patterns:

* Payload mode: `ContextMainCamera`, `ContextMousePosition`, `ContextMouseRay`, `ContextMouseRay2D`.
* Context payload shortcuts: `Self`, `SelfTransform`, `FCP`, `Parent`, `ParentTransform`, `FCPInParent`, `Target`, `Invoker`, `TargetProcess`, `InvokerProcess`.
  * `Parent` / `ParentTransform`: nearest ancestor that has a `GameObjectGuid` component.
  * `FCPInParent`: nearest ancestor FlowCoreProcess, starting from `self.parent`.
* API: `context.TryGetMainCamera(...)`, `context.TryGetMousePosition(...)`, or `context.TryGetContextValue(FlowContextKeys.*)`.

Register a custom provider:

```csharp
public static class MyContextProviders
{
    public static readonly FlowContextKey<Ray> MouseRayWorld = new FlowContextKey<Ray>("MouseRayWorld");

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Register()
    {
        FlowContextProviderRegistry.Register(MouseRayWorld, TryBuildMouseRayWorld);
    }

    private static bool TryBuildMouseRayWorld(FlowContext context, out Ray value)
    {
        if (!context.TryGetMouseRay(out var ray))
        {
            value = default;
            return false;
        }

        value = ray;
        return true;
    }
}
```

Settings:

* Flow Core Settings → Context Shortcuts controls whether these values are available at runtime.
* For visual authoring decisions around context, payload, TempPayload, Blackboard, and target resolver paths, see [Value Sources and Target Resolution](/flow-core-docs/documentation/runtime-guide/value-sources-and-target-resolution.md).

Performance notes:

* Values are cached per frame only after first access.
* Custom providers cache boxed values for structs (one box per frame on first access).

Failure modes:

* `MainCamera` returns null if no tagged Main Camera exists.
* Mouse values return false when no mouse is present or outside Play Mode.

## Minimal extension checklist

1. Define the runtime type (instruction/condition/node).
2. Register it via the codegen pipeline.
3. Provide editor UI if designers must edit it.
4. Validate blackboard key types and container expectations.

## Step-by-step: adding a new instruction (summary)

1. Create the instruction class in runtime assembly.
2. Add the generator attribute/config (per codegen rules).
3. Run codegen to update registries.
4. Add a drawer/summary for editor usage.
5. Verify the instruction appears in the node menu.

Example: minimal instruction (runtime).

```csharp
using System;
using System.Threading.Tasks;
using UnityEngine;
using TwoCatsCode.FlowCore;

[FlowInstruction("Log Message", "Example", "Log a string value.")]
public sealed class InstructionLogMessage : IInstruction
{
    [Serializable]
    public sealed class Data : InstructionData
    {
        public FlowValueGetStringData Message = new FlowValueGetStringData();

        public override InstructionData Clone()
        {
            return new Data
            {
                Message = Message?.Clone() as FlowValueGetStringData ?? new FlowValueGetStringData()
            };
        }

        public override string Summary(FlowContext context)
        {
            return $"Message: {Message?.Summary(context)}";
        }
    }

    private readonly Data _data;

    public InstructionLogMessage(Data data)
    {
        _data = data ?? new Data();
        _data.Message ??= new FlowValueGetStringData();
    }

    public ValueTask<InstructionResult> ExecuteAsync(FlowContext context)
    {
        if (FlowValueResolveHelper.TryResolveValue(_data.Message, context, out var value, out _))
            Debug.Log(value);

        return new ValueTask<InstructionResult>(InstructionResult.Continue);
    }

    public string Summary(FlowContext context) => string.Empty;
}
```

Example: minimal condition (runtime).

```csharp
using System;
using System.Threading.Tasks;
using TwoCatsCode.FlowCore;

[FlowCondition("Float > Threshold", "Example", "Check a float against a threshold.")]
public sealed class ConditionFloatAbove : ICondition
{
    [Serializable]
    public sealed class Data : ConditionData
    {
        public FlowValueGetFloatData Value = new FlowValueGetFloatData();
        public float Threshold = 0.5f;

        public override ConditionData Clone()
        {
            return new Data
            {
                Value = Value?.Clone() as FlowValueGetFloatData ?? new FlowValueGetFloatData(),
                Threshold = Threshold
            };
        }

        public override string Summary(FlowContext context)
        {
            return $"Value: {Value?.Summary(context)} > {Threshold}";
        }
    }

    private readonly Data _data;

    public ConditionFloatAbove(Data data)
    {
        _data = data ?? new Data();
        _data.Value ??= new FlowValueGetFloatData();
    }

    public ValueTask<bool> EvaluateAsync(FlowContext context)
    {
        if (FlowValueResolveHelper.TryResolveValue(_data.Value, context, out var value, out _))
            return new ValueTask<bool>(value > _data.Threshold);

        return new ValueTask<bool>(false);
    }

    public string Summary(FlowContext context) => string.Empty;
}
```

## Constraints

* Avoid boxing/unboxing in hot paths.
* Do not rely on runtime reflection.
* Keep data access explicit via blackboard and payload.
* All runtime mutation is main-thread only.

## Control input port contract

* Control input ports must be appended at the end of `InputPorts`. Runtime locates the control block by matching a name sequence at the tail.
* The control port name sequence is part of the ABI. When adding/removing control ports, update these together.
* `FlowGraphFactory.GetControlPortNames` and `AppendControlInputPorts`.
* `FlowGraphRuntimeBuilder.ResolveControlInputCount`.
* `FlowGraphView.GraphOps` control port count/insert logic.
* `FlowGraphValidation` control port count checks.
* `FlowGraphRuntimeData.TryResolveControlInput`.
* Do not add `ForceSuccess/ForceFailure` (or any new control ports) to unsupported node kinds unless runtime parsing and validation are updated accordingly.

## Reference fallback (runtime-only)

* Reference fallback is resolved at single-value / single-element granularity, not by replacing entire list payloads.
* A single-value fallback can resolve `GameObjectReference` / `ComponentReference<T>` / `AssetReference<T>` into their canonical Direct value (`GameObject` / `Component` / asset) when needed.
* List fallback is element-wise on read paths and does not alter stored list data.
* Fallback is runtime-only and does not persist. Missing `GameObjectGuid` uses a transient cache.
* `Payload` and `TempPayload` are Direct-only carriers; there is no second-pass reference fallback for these payload channels.
* Payload resolution reads the raw payload value. There is no automatic reference → object conversion unless the specific reference getter implements it.
* Blackboard resolution for `T` accepts either `T` or `TReference`, and resolves to `T` at runtime (e.g., `GameObject`, `Component`, `Asset`).
* Stack owner checks require a valid `GameObjectGuid`. A fallback-created `GameObjectReference` with an empty GUID never matches.
* PoolManager: when `PrefabSource = Payload`, payload must be `AssetReference<GameObject>` (no fallback from `GameObject` / `GameObjectReference`).
* "Compare Reference" conditions use `FlowValueGetAnyReferenceData` and accept reference types only. Struct references (`GameObjectReference`, `ComponentReference<T>`, `AssetReference<T>`, `SceneReference`) are not compatible.
* `FlowCoreProcess` is serialized via `FlowCoreProcessSerializer` using `ComponentReference<FlowCoreProcess>` (GUID + component index). UI still shows the type as `FlowCoreProcess` for consistency.
* Do not rely on fallback in hot paths; store \*Reference types explicitly for long-lived data. Fallback is intended as a safety net, not a primary data path.
* For the reader-facing runtime model behind these choices, see [Value Sources and Target Resolution](/flow-core-docs/documentation/runtime-guide/value-sources-and-target-resolution.md).

## Do / Don’t

* Do keep nodes pure; move state into FlowContext.
* Do expose parameters as typed keys.
* Don’t allocate per-frame.
* Don’t mutate FlowGraphAsset at runtime.

## Related

* [Codegen Pipeline](/flow-core-docs/documentation/api-extension-guide/codegen-pipeline.md)
* [Generated Items](/flow-core-docs/documentation/api-extension-guide/generated-items.md)
* [Value Sources and Target Resolution](/flow-core-docs/documentation/runtime-guide/value-sources-and-target-resolution.md)
* [Flow Node Kinds](/flow-core-docs/documentation/reference/flow-node-kinds.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://flow-core.gitbook.io/flow-core-docs/documentation/api-extension-guide/extension-points.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
