# Build libraries and tools

> Compose and test your own libraries, declare custom data and actions, and feed recorded external model predictions into an incremental strategy.

[Canonical documentation](https://flowscript.ftw.trade/docs/extending)

The standalone compiler takes a host declaration; it has no dependency on
ftw.trade. An application chooses the reads, libraries and actions its programs
can use. These extension paths have different responsibilities:

| Extension | Declare | Execute |
|---|---|---|
| Pure language helper | `Host.libraries[name]`, then `import "name";` | Flowscript evaluates the imported `.flow` functions |
| Data or external model output | A typed entry in `Host.sources` | The application records observations and supplies them to `run.step` / `replay` |
| Application tool | A capability manifest with arguments and host authority | The application authorizes and consumes the returned decisions |
| Several providers of an interface | `Host.integrations` and their served members/markets | The application connects the declared runtime implementations |

Imports provide source functions, not JavaScript callbacks or installed tools.
A capability manifest describes an action; declaring it does not implement its
consumer, grant signing authority, or authorize network access. Unknown imports,
reads and actions are compiler refusals. Use host documentation for source and
action descriptions; comments document imported functions for editor help.

## Author and compose libraries

Supply your `.flow` source under a name in `Host.libraries`. A library may
import other libraries and declare functions or contract interfaces. It cannot
declare `uses`, metrics, triggers or action side effects; the calling program
owns its host capabilities. See [Libraries](https://flowscript.ftw.trade/docs/reference#libraries) and
[Embedding](https://flowscript.ftw.trade/docs/embedding).

Dependencies form a closure of at most 15 unique libraries, including reached
standard libraries. Shared dependencies in a diamond are loaded once. Repeating
an import in the same file, an import cycle, an unknown dependency or a duplicate
function name is refused. Function names are global and unqualified:
`import "user/risk";` exposes `userCappedEdge(...)`, not `risk.userCappedEdge(...)`.
Choose distinct prefixes for reusable helpers. On ftw.trade, take observations
as function arguments rather than hiding source reads in a helper, so the
platform can discover the caller's data subscriptions.

Test a library through a small importing program. Resolve and save the exact
dependency sources with the program and its compiled language version:

```js
import { loadFlowscript } from '@flowscript/core';
const fs = await loadFlowscript();
const host = { libraries: {
  'user/math': 'fn userPositiveEdge(x: number): number { return max(x, 0); }',
  'user/risk': `import "user/math";
fn userCappedEdge(x: number, cap: number): number {
  return min(userPositiveEdge(x), cap);
}`,
} };
const source = 'import "user/risk"; metric("edge", userCappedEdge(3, 2));';
const resolved = fs.resolveLibraries(source, host);
if (!resolved.ok) throw new Error(JSON.stringify(resolved.problems));
const pinnedHost = { ...host, libraries: resolved.libraries };
const compiled = fs.compile(source, pinnedHost);
if (!compiled.ok) throw new Error(JSON.stringify(compiled.problems));
try {
  const tested = fs.test(source, { tests: [{
    name: 'cap an imported positive edge', bars: 1, sources: {},
    expect: [{ metric: 'edge', at: 'last', equals: 2 }],
  }] }, pinnedHost);
  if (!tested.ok) throw new Error(tested.error);
  if (tested.report.failed) throw new Error(JSON.stringify(tested.report));
  const saved = { source, language: compiled.program.language, libraries: resolved.libraries };
  // Persist saved with the rest of your host configuration and input cursor.
} finally { fs.drop(compiled.program); }
```

`resolveLibraries` returns the selected source map without typechecking the
strategy against its data host. Compilation and the test still have to pass.
When restoring, use the saved map and language, not the current contents of a
library registry. The closure includes the exact standard-library copies used
by the program. Changed dependency implementations change program identity and
cannot silently reuse an incompatible checkpoint.

## Built-in learning and external inference

Built-in `ml.*` models fit on the program's recorded series in the evaluator.
Their training rows, parameters and work participate in its state and budgets;
replay and restored live runs use the same implementation. See
[Trained models](https://flowscript.ftw.trade/docs/reference#trained-models) for available methods, horizon and
retraining semantics. They are bounded rolling models, not an import mechanism
for arbitrary Python, ONNX, GPU or JavaScript runtimes.

An external model runs in the application's chosen backend. Publish its output
as a typed observation, just as another data source. The runnable
[external-model.ts](https://github.com/johnny-emp/flywheel/blob/main/flowscript/npm/examples/external-model.ts) example combines that
path with an imported freshness helper and a custom `research.record` action:

```sh
cd flowscript/npm
npm run build:wasm
node examples/external-model.ts
```

The example has no additional dependencies. Its fixed logistic coefficients are
illustrative and are not presented as a trained model, profitable strategy or
production inference engine. The model document owns feature order, bounds,
coefficients and intercept; its SHA-256 identity appears in the program's read
key and every recorded output. Inference reads that same document. Replacing
the document changes the compiled program identity; an old checkpoint cannot
silently resume under the new model.

The application computes a score, records `{time, asOf, model, score}`, then
steps the program. `time` is when the prediction became available to the
strategy; `asOf` is the input data's time. A response delivered at 110 cannot be
inserted into bar 109 merely because its features came from 109. All times in
this example are whole evaluation seconds. A future `asOf`, nonfinite or
out-of-range probability, wrong model identity, unsupported feature dimension
or feature outside the declared bounds is refused. Unavailable inference is
recorded explicitly as `score: null`.

The strategy checks the model identity and uses the `.flow` helper to exclude
unknown, stale, future and out-of-range observations before emitting a signal.
Omission in `run.step` holds the previous observation, so an outage must send explicit unknown values
when holding the old prediction is inappropriate. A freshness guard also
limits how long an earlier valid prediction can remain usable.

`research.record` has a real consumer: it validates the compiled action's host
operation and evaluated arguments before appending candidate signals to an
application-owned journal. It authorizes no trade. The example validates an
entire batch before retaining effects; it does not parse rendered argument IR
or report fabricated fills. A real application's storage, effect outbox,
permissions, retries and action settlement belong at this boundary.

## Reproduce the observations, not the inference request

Replay the recorded predictions at their original availability watermarks.
Do not call a remote model again during replay, substitute a newer model, or
backfill historical predictions using features or training data unavailable
then. Record model artifact identity, backend/precision configuration, feature
schema and input cursor with the inference outputs in a real integration.
The example pins a small fixed model; its test history is explicitly synthetic.

Persist the input cursor, observations, evaluator checkpoint and durable effect
outbox consistently. A Flowscript checkpoint contains evaluator state; it does
not preserve an external model server, in-progress request or model-training
state. Asynchronous inference needs the host's own correlated request/result
coordinator, recorded delivery and timeout policy. The generic `Run.report`
venue-outcome seam is not a task-result dispatcher.

The [example tests](https://github.com/johnny-emp/flywheel/blob/main/flowscript/npm/test/external-model.test.ts) exercise actual WebAssembly compilation, replay, live steps,
checkpoint restoration, delayed availability, input bounds and the action
consumer. There are no latency claims: measure the chosen inference backend,
transport and evaluation workload together before selecting a deployment mode.
