> For the complete documentation index, see [llms.txt](https://docs.talus.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.talus.network/guides/tap-development/build-tap-move-package.md).

# Build a Talus Agent Package in Move

{% hint style="info" %}
**Audience:** Nexus adopters and integrators.

**Goal:** Create and validate a Talus Agent Package (TAP) Move package from the CLI scaffold, then hand the artifact to the dedicated binding and execution guides.
{% endhint %}

### Prerequisites

* A configured CLI and operator-supplied network objects file; see [Developer Setup](/guides/getting-started/setup.md). For network-facing Move dependencies, use the reviewed [nexus-interface](https://www.moveregistry.com/package/@talus/nexus-interface), [nexus-primitives](https://www.moveregistry.com/package/@talus/nexus-primitives), [nexus-tool](https://www.moveregistry.com/package/@talus/nexus-tool), [nexus-registry](https://www.moveregistry.com/package/@talus/nexus-registry), [nexus-workflow](https://www.moveregistry.com/package/@talus/nexus-workflow), and [nexus-scheduler](https://www.moveregistry.com/package/@talus/nexus-scheduler) Move Registry packages. Keep `Move.lock` and the selected network's published-address records together; use the public [Nexus Move Packages](https://github.com/Talus-Network/nexus-move-packages) source only for offline inspection or provenance.
* A dotted Tool FQN such as `example.taluslabs.transfer@1` and a design for the Agent-owned package state it will mutate.
* Registered Tools and public references for the target deployment; do not assume an example FQN is live.

#### Create the CLI scaffold

Use `nexus tap scaffold` to create the package structure rather than copying a hand-written package listing from this guide:

```bash
PACKAGE_NAME=my-tap
nexus tap scaffold --name "$PACKAGE_NAME"
CONFIG="./$PACKAGE_NAME/skill.tap.json"
nexus tap validate-skill --config "$CONFIG"
```

The scaffold is the source of the package manifest, skill configuration, and generated file layout. Validation is intentionally the last network-independent step here: the scaffolded DAG still contains placeholder Tool references until you complete [Configure DAG and Skill](/guides/tap-development/dag-and-skill-config.md). Do not run `publish-skill` from an untouched scaffold, because package publication occurs before DAG publication and a later DAG failure can leave the package published. This guide does not promise a complete hand-written package implementation because Move APIs, package addresses, and deployment objects must match the selected network.

#### Preserve the package boundary

The TAP package owns its application state and may embed an Agent. Nexus owns the durable workflow lifecycle: Task creates an Occurrence, dispatch creates an Execution, and `ExecutionPayment` settles the run. Agent and skill metadata are recorded through Agent Registry APIs; the package should not recreate scheduler state or retired queue helpers.

For a standard on-chain Tool, the public entry begins with `UIDRequirements`, then `OnchainToolResult`, package state and declared inputs, and ends with `&mut TxContext`. An authorized Tool is prefixed by `ProvenValue<AgentVertexAuthorization>`. The Tool satisfies the registered requirement, builds one `TaggedOutput` from the supplied `NexusValue` payloads, and finalizes the result through the public on-chain-result surface. Treat these as interface patterns; validate the exact signature against the registered Tool schema and public references before compiling.

#### Test Task creation and inspection with an extension

Keep the released TAP manifest's `edition = "2024"`. Put module-extension tests in a sibling consumer package such as `move-tests/`, whose `Move.toml` opts into the alpha edition required by extensions:

```toml
[package]
name = "my_tap_task_tests"
edition = "2024.alpha"

[dependencies]
nexus_scheduler = { r.mvr = "@talus/nexus-scheduler" }

[addresses]
my_tap_task_tests = "0x0"
```

Add `move-tests/tests/task_extension.move`. This extension constructs the minimal stable `Task` wrapper and exposes only the identity and `TaskStatus` shapes that local application tests need:

```move
#[test_only]
extend module nexus_scheduler::task;

public fun new_for_testing(ctx: &mut sui::tx_context::TxContext): Task {
    Task { id: sui::object::new(ctx) }
}

public fun id_for_testing(task: &Task): sui::object::ID {
    sui::object::id(task)
}

public fun active_for_testing(): TaskStatus {
    TaskStatus::Active
}

public fun is_active_for_testing(status: TaskStatus): bool {
    match (status) {
        TaskStatus::Active => true,
        _ => false,
    }
}

public fun destroy_for_testing(task: Task) {
    let Task { id } = task;
    id.delete();
}
```

Call those helpers through the module they extend in `move-tests/tests/task_tests.move`:

```move
#[test_only]
module my_tap_task_tests::task_tests;

use nexus_scheduler::task;

#[test]
fun creates_and_inspects_task_value_shape() {
    let ctx = &mut sui::tx_context::dummy();
    let task = task::new_for_testing(ctx);
    let task_id = task::id_for_testing(&task);
    let status = task::active_for_testing();

    assert!(task_id != sui::object::id_from_address(@0x0));
    assert!(task::is_active_for_testing(status));

    task::destroy_for_testing(task);
}
```

Run the isolated harness against the selected dependency graph:

```bash
export SUI_BUILD_ENV="testnet"
sui move test --path move-tests --build-env "$SUI_BUILD_ENV"
```

The fixture does not recreate scheduler storage: it has no `TaskInnerV1`, authorization, payment reserve, schedule, occurrence records, or lifecycle transitions. Existing `nexus_scheduler::task` constructors and inspectors remain local stubs and abort when called in a Move unit test. Use the extension only for application logic that needs a Task identity or status value, and use a Testnet integration transaction to create and inspect a real Task. This package is intentionally Nexus-free, so its plain `sui move test` gate is safe. See [Local Move tests with module extensions](/guides/getting-started/prepare-onchain-development.md#local-move-tests-with-module-extensions) for the full evidence boundary.

#### Test a published Nexus function in a separate local package

Keep tests that call a published Nexus function out of `move-tests/`: the plain Move VM sees the interface stub and should report `ELocalExecutionUnavailable`. Create a second sibling package, `move-tests-published/`, for the released published-bytecode harness. This is an explicit local test arrangement, not a second TAP application track:

```
move-tests-published/
├── Move.toml
└── tests/
    ├── data_extension.move
    └── data_tests.move
```

Use an alpha-edition test package so the extension can construct the smallest value needed by the published data API:

```toml
[package]
name = "my_tap_published_tests"
edition = "2024.alpha"

[dependencies]
nexus_primitives = { r.mvr = "@talus/nexus-primitives" }

[addresses]
my_tap_published_tests = "0x0"
```

Add `tests/data_extension.move`:

```move
#[test_only]
extend module nexus_primitives::data;

public fun inline_length_for_testing(self: &NexusData): u64 {
    match (self) {
        NexusData::One {
            value: NexusValue::InlineData { bytes },
        } => bytes.length(),
        _ => 0,
    }
}
```

Add `tests/data_tests.move` and call the published functions directly:

```move
#[test_only]
module my_tap_published_tests::data_tests;

use nexus_primitives::data;
use std::unit_test::assert_eq;

#[test]
fun reads_published_inline_value() {
    let value = data::one(data::inline_data_value(b"hello Nexus"));
    assert_eq!(value.inline_length_for_testing(), 11);
}
```

Run only this package with the released binary from [Developer Setup](/guides/getting-started/setup.md):

```bash
nexus tap test --path move-tests-published --build-env "$SUI_BUILD_ENV"
```

The harness reads the selected public Nexus bytecode and overlays only the test extension in memory. It needs network read access to published bytecode, but no wallet, signer, gas, publication, registration, or scheduling. A green result proves the exercised published data call and local arrangement; it does not prove that the TAP package, Tool, DAG, or skill has been published or bound. Keep `sui move test --path move-tests` for the Nexus-free Task value-shape test and use a Testnet transaction for live Nexus behavior.

#### Verify the scaffolded artifact

1. Validate the scaffolded skill and inspect every referenced Tool FQN, schema, and selected mode.
2. Record the validated `skill.tap.json` path and the package/DAG files that still need deployment-specific Tool references.
3. Stop before publication. Configuring the real DAG, publishing the package and DAG, binding the artifact to an Agent, and creating Task → Occurrence → Execution evidence require additional command inputs and are covered by the linked follow-up guides.

Expected artifacts here are the local scaffolded package and a metadata-valid `skill.tap.json`; no package or DAG has been published yet. Test-only helpers belong only in test modules and are not production API guidance.

#### Failures and recovery

* **Tool schema or FQN mismatch:** correct the package/registration contract or use a new versioned dotted FQN; do not force an incompatible DAG binding.
* **Authorization failure:** verify the exact Agent, skill, vertex, recipient, and requirement proof before changing package state.
* **Settlement or ToolCashier gap:** inspect the durable Task/Occurrence/Execution evidence. There is no generic high-level non-expired reconciliation command; use the public references and approved integration boundary rather than improvising a transaction.

#### Next

Continue with [Configure DAG and Skill](/guides/tap-development/dag-and-skill-config.md), [Publish, Register, Bind](/guides/tap-development/publish-register-bind.md), and [Execute and Verify Transfer](/guides/tap-development/execute-and-verify-transfer.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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 following URL with the `ask` and `goal` query parameters:

```
GET https://docs.talus.network/guides/tap-development/build-tap-move-package.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `build a script that syncs our docs to a CMS` lets GitBook tailor the answer to that use case.

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.
