> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Lints and consent

> Every publish lint code with its cause, what installers consent to, how update consent works, and how visibility and review gate a template

Publishing an [agent template](/concepts/agent-templates) runs every lint before a version number is taken: a refused publish creates nothing. The CLI prints the refusal as `✖ http_400: <CODE>: <detail>; <detail>` and exits 10; every violation is in the one message with its field path.

*Verified against lua-cli 3.33.0.*

## What installers agree to and keep

The install screen shows, and the installer agrees to: every connection with its scopes, the instruction of every trigger, the workflows the version ships with their schedules, the required env contract keys, the `onInstall` and `onUninstall` hooks, and the persona template text. The first four form the [consent surface](#consent-surface) whose hash decides whether a later version needs re-consent.

A creator push (a publish that auto-applies, or `lua marketplace template apply`) replaces the template's managed primitives (skills, webhooks, jobs, preprocessors, postprocessors, triggers, workflows) and the frozen `modelSettings`. It never changes what the installer owns:

* env values, set with `lua env`, including the keys the install introduced;
* a persona the installer edited or already had (`personaSkipped: true` on the result);
* which triggers are armed and their schedule values; an added trigger lands disabled, and a paused or auto-disabled schedule stays parked;
* which connection satisfies each capability;
* the model: the template's model seeds only an agent that had none, on its first install;
* channels and the agent's name.

A hand edit to a managed primitive is drift: the next version-changing update is refused with `MODIFIED_SINCE_INSTALL`, naming the primitives, until the installer re-sends the install over REST with `acceptModifiedPrimitives` (`"<kind>/<key>"` entries) on the [agent templates API](/reference/rest/agent-templates). When a version changes the consent surface, the installer re-consents by installing that version from a project on the agent:

```bash theme={null}
lua marketplace template install --template-id <template-id> --version <n> --force --ci
```

The re-install keeps the installer's stored answers and clears the pending update.

## Lint codes

| Code                                                                   | Raised when                                                                                                                                                                                                | Fix                                                                  |
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `SECRET_DETECTED: <pattern> in <kind>/<key>`                           | A frozen string matches a credential pattern                                                                                                                                                               | Read it through `env('KEY')` and declare `KEY` with `--env-contract` |
| `CODE_BLOB_MISSING: <kind>/<key> references <hash>`                    | The frozen code behind a primitive is missing on the server                                                                                                                                                | Push the primitive again, promote, and republish                     |
| `SCHEDULE_INVALID`                                                     | A job with a `once` schedule; a job or workflow interval under 60 seconds or not a multiple of 60; a `cron` expression that does not compile (5 fields)                                                    | Change the schedule, push, promote                                   |
| `TRIGGER_PRESET_INVALID`                                               | A preset key that is no trigger in the version, or an entry outside the [triggerPresets](/marketplace/manifest#triggerpresets) rules; the message names the path                                           | Edit `triggerPresets`                                                |
| `PERSONA_TEMPLATE_INVALID`                                             | A `{{TOKEN}}` with no `vars[]` entry or a var in no branch; a var or `paramsMeta` entry outside the [manifest](/marketplace/manifest#personatemplate) rules; a `showIf` rule failing the following section | Edit `personaTemplate` or `paramsMeta`                               |
| `PERSONA_VARS_INVALID`                                                 | The slot mismatch of the row above, raised only for the REST `persona` field; a `personaTemplate` reports it as `PERSONA_TEMPLATE_INVALID`                                                                 | Add or remove the declaration                                        |
| `CONNECTION_DECLARATION_INVALID`                                       | A `connections[]` entry outside the [connections](/marketplace/manifest#connections) rules, or a `boundSkills` or `boundWorkflows` key naming nothing in the version                                       | Edit `connections`                                                   |
| `OUTCOMES_MANIFEST_INVALID`                                            | An outcome `match.connectionKey` naming no connection, or a reversal with no evidence source                                                                                                               | Edit the outcomes declaration                                        |
| `ENV_CONTRACT_RESERVED_PREFIX`                                         | A contract key under `LUA_TRIGGER_URL__`                                                                                                                                                                   | Rename the key                                                       |
| `ON_INSTALL_INVALID`, `ON_UNINSTALL_INVALID`, `INSTALL_POLICY_INVALID` | See [Lifecycle hooks](/marketplace/lifecycle-hooks#lints)                                                                                                                                                  |                                                                      |
| `UNSUPPORTED_SOURCE`                                                   | The source agent has a voice binding or device triggers                                                                                                                                                    | Remove them from the source agent                                    |

The secret patterns are named `private-key-block`, `aws-access-key-id`, `google-api-key`, `slack-token`, `jwt`, and `generic-secret-assignment` (an `api_key`, `secret`, `token`, or `password` assigned a quoted value of 16 or more characters). They run over skill context, conditions, tool code and descriptions, webhook, job, processor and trigger code and descriptions, job prompts, workflow definitions, and env contract descriptions.

Two refusals are not lints: ``Source agent has no active version — run `lua version create` + `lua version promote` first`` when nothing is promoted, and `Missing required environment variables: <keys>` at install when the env contract is unmet.

## showIf rules

A `paramsMeta` entry or a persona var may carry `showIf: { param, equals }`. The rules, each reported as `<where>.showIf…`:

* `param` names another declared key, never itself. Env contract keys and persona var names share one namespace; a param may depend only on another env contract key, a persona var on either.
* `equals` is a non-empty list of at most 20 unique, non-empty strings. When the controller declares an `enum`, every value must be a member; when it is a `boolean`, only `true` and `false` match.
* The chain of `showIf` rules is acyclic; a cycle is named in the message.
* A conditional persona var carries a `default`, because its slot must resolve while hidden.

At install a field is visible only when its controller is visible and has one of `equals`, judged on the submitted value, else the stored value, else the declared default. A hidden field is neither required nor validated, and any value for it is cleared; the install result lists those keys as `hiddenParamsCleared`.

## Consent surface

Each version carries a hash of its consent surface. The surface is:

* every connection: `key`, `capability`, `required`, `ownerType`, and its platforms with their scopes;
* the union of declared OAuth scopes;
* the instruction of every trigger and declared connection-event trigger;
* the required env contract keys;
* for versions that include workflows, each workflow's definition identity, agent-step prompts, approvers, role references, schedule, and structural settings.

The install and uninstall screens also show `onInstall` and `onUninstall` and the persona template text, and the manifest's `runNote` says whether a workflow schedule runs as the installer or as the system.

When a version's hash differs from the one an install consented to, a creator push onto that install is refused with `RECONSENT_REQUIRED` and the install is marked as waiting, whether or not it allows creator updates; the installer re-installs at that version to accept. A version whose surface is unchanged, however much code changed, flows under standing consent.

## Update consent

An install opts in with `--allow-creator-updates` on `lua marketplace template install`; the default is off. It can be changed later with `PATCH /agents/:agentId/templates/:templateId` on the [agent templates API](/reference/rest/agent-templates); no CLI action does it.

* Publishing a private template applies the version to every install that allows creator updates and is on an older version, unless `--skip-auto-apply` is passed. Each target goes through the same gates as a manual `apply`: consent, the target organization's template policy, and the re-consent check.
* Publishing a public template defers the same fan-out until the version is approved, and skips it if a newer version has been approved by then.
* A manual `apply` onto an install that does not allow creator updates, or whose organization's policy blocks the template, fails that target with `Target agent is not eligible for creator updates`.
* What a creator update may and may not change is listed under [What installers agree to and keep](#what-installers-agree-to-and-keep).

## Visibility and review

A template is `private` by default: only the creator organization can find and install it. A `public` template is reviewed per version; each version carries `approvalStatus` of `pending`, `approved`, or `rejected`, and installers outside the creator organization are offered approved versions only. An install that resolves to no approved version is refused with `TEMPLATE_NOT_APPROVED`; a template with no version at all with `TEMPLATE_NO_VERSIONS`. The creator organization can install a pending version to test it.

The served manifest's `installable` is `true` when the version is not deprecated and, for a public template, approved. Unlisting a template refuses fresh installs with `TEMPLATE_UNLISTED`; agents that already have it keep running and can still be updated. An organization's template policy can allow a template, require approval (the install is queued as a request), or block it (`POLICY_BLOCKED`).

## See also

* [Template manifest](/marketplace/manifest) — the fields these lints guard
* [Lifecycle hooks](/marketplace/lifecycle-hooks) — hook and `installPolicy` lints
* [Publish a template](/marketplace/publish-a-template) — where the refusals appear
* [Install and apply](/marketplace/install-and-apply) — the installer's side of consent
* [`lua marketplace`](/reference/cli/marketplace) — exit codes
