---
title: "Validate and Publish an Extension"
source: https://docs.autohand.ai/tutorials/extensions/validate-and-publish
---

# Validate and publish an extension

Turn a working authoring directory into a reproducible release by proving invalid inputs fail closed, copied lifecycle operations work, and users can install from an immutable local checkout.

## Release gate

This tutorial assumes `acme.code-health` already passes the linked development loop. The release is ready only when all of these gates pass:

1.  Strict manifest and contribution validation.
2.  Negative fixtures for likely authoring mistakes.
3.  Copied installation into isolated user and project roots.
4.  Fresh-process discovery with exact provenance.
5.  Real tool approval, denial, and output checks.
6.  Agent use of the contributed tool.
7.  Disable, enable, diagnostics, replacement, and removal.
8.  An immutable tag or archive with matching manifest version.

## 1\. Validate source with stable JSON

``` bash
autohand extensions validate ./extensions/acme.code-health --json
```

Assert semantic fields rather than terminal formatting: `valid` is true, id and version match the manifest, and tool/agent arrays contain exact expected names. JSON output is ANSI-free and designed for automation.

## 2\. Build failure fixtures

Copy the package into a private test fixture for each condition. Never modify the release source in place during the suite.

| Fixture | Change | Expected result |
|---|---|---|
| Unknown field | Add a misspelled manifest property | Strict manifest validation fails |
| Wrong API | Set extensionApi to 2 | Incompatible package fails |
| Traversal | Declare ../outside.json | Path schema fails before resolution |
| Missing file | Declare a file that does not exist | Package validation fails |
| Symlink contribution | Replace a declared tool file with a symlink | Contribution is rejected |
| Duplicate key | Repeat version in raw JSON | Duplicate-key parser fails |
| Conflict | Reuse an installed tool name in another package | Install or discovery reports the owning package |

For each fixture, assert a non-zero exit and that no extension directory or state file is created by `validate`.

## 3\. Test copied user installation

Use an isolated Autohand home in CI. Do not read or mutate a developer's real `~/.autohand`.

``` bash
export AUTOHAND_HOME="$RUNNER_TEMP/autohand-extension-test"

autohand extensions install ./extensions/acme.code-health
autohand extensions list --json
autohand extensions show acme.code-health --json
autohand extensions doctor --json
```

Assert `scope: user`, `linked: false`, enabled state, exact contributions, and a root under the isolated home.

## 4\. Test project installation and precedence

``` bash
export WORKSPACE="$RUNNER_TEMP/extension-workspace"

autohand --path "$WORKSPACE" extensions install ./extensions/acme.code-health --scope project
autohand --path "$WORKSPACE" extensions show acme.code-health --json
autohand --path "$WORKSPACE" extensions doctor --json
```

With the same id installed at both scopes, the project package should be the effective whole package for this workspace. Test with intentionally distinct versions so the assertion is visible.

## 5\. Exercise runtime authorization

Start a fresh process in a controlled Git fixture. Invoke each contributed tool with valid input, invalid input, approval, and denial. Then delegate to each contributed agent and verify it can resolve only its declared active tools.

-   Check the exact command purpose and requested path shown by the approval UI.
-   Confirm denial prevents command execution.
-   Confirm tool output is bounded and contains expected fixture evidence.
-   Confirm the agent reports missing evidence when a call is denied or fails.

## 6\. Prove lifecycle and replacement

``` bash
autohand extensions disable acme.code-health
autohand extensions show acme.code-health --json
autohand extensions enable acme.code-health

# Reinstalling identical content is idempotent.
autohand extensions install ./extensions/acme.code-health

# Different content requires explicit replacement.
autohand extensions install ./release/acme.code-health --replace
autohand extensions show acme.code-health --json

autohand extensions remove acme.code-health --yes
autohand extensions list --json
```

Assert that disable removes contributions but preserves inspection, enable restores them, identical install reports existing state, a different copy fails without `--replace`, and removal leaves unrelated packages untouched.

## 7\. Review the release directory

Create a clean release directory containing only files intended for publication. Confirm:

-   The manifest version matches the planned Git tag.
-   Every declared path exists and uses the correct case.
-   The README includes validation, install, scope, link, permission, doctor, and removal guidance.
-   No secret, local absolute path, temporary state file, dependency directory, or test output is included.
-   The package validates after the authoring checkout is unavailable.

## 8\. Publish an immutable source release

``` bash
git tag -s acme.code-health-v1.0.0 -m "acme.code-health 1.0.0"
git push origin acme.code-health-v1.0.0
```

Publish release notes with contribution names, permission behavior, compatibility evidence, and checksums for any archive. In user instructions, require checkout of the immutable tag before local installation:

``` bash
git clone https://github.com/acme/code-extensions.git
cd code-extensions
git checkout acme.code-health-v1.0.0
autohand extensions validate ./extensions/acme.code-health
autohand extensions install ./extensions/acme.code-health
```

**Do not advertise direct URL installation.** Extension API v1 intentionally installs a local directory. A pinned checkout makes the reviewed source and installed content reproducible.

## 9\. Record the support evidence

For each release, retain the Autohand Code versions, operating systems, validation JSON, copied-install reports, lifecycle results, and handler smoke evidence. Use those results to populate a compatibility matrix rather than treating schema version as proof of runtime support.

If a release is faulty, tell users to disable it first, provide rollback and removal commands, publish a new immutable version, and never move the old tag.