Check a site before you publish — Sails

[Sails](https://sails.app/)/[Guides](/guides/)

[Contact](https://sails.app/contact)    

 On this site

[Overview](/guides/)

How to

*   [All how-tos](/guides/how-to)
*   [Publish a site](/guides/how-to/publish-a-site)
*   [Check a site before you publish](/guides/how-to/check-a-site-before-you-publish)
*   [Give an agent a job](/guides/how-to/give-an-agent-a-job)
*   [Mail from an agent](/guides/how-to/mail-from-an-agent)
*   [Talk to an agent in Slack](/guides/how-to/talk-to-an-agent-in-slack)
*   [Run something every morning](/guides/how-to/run-something-every-morning)
*   [Put a form on a site](/guides/how-to/put-a-form-on-a-site)
*   [Send a file](/guides/how-to/send-a-file)
*   [Store a secret](/guides/how-to/store-a-secret)
*   [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database)
*   [Start a goal from a webhook](/guides/how-to/start-a-goal-from-a-webhook)
*   [Connect an MCP server](/guides/how-to/connect-an-mcp-server)
*   [Sync with GitHub](/guides/how-to/sync-with-github)
*   [Share a directory](/guides/how-to/share-a-directory)

Features

*   [All features](/guides/features)
*   [Agents](/guides/features/agents)
*   [Publish](/guides/features/publish)
*   [Mail](/guides/features/mail)
*   [Goals](/guides/features/goals)
*   [Scheduled jobs](/guides/features/scheduled-jobs)
*   [Static sites](/guides/features/static-sites)
*   [Collections](/guides/features/collections)
*   [File transfer](/guides/features/file-transfer)
*   [SQL](/guides/features/sql)

[Overview](/guides/)

How to

*   [All how-tos](/guides/how-to)
*   [Publish a site](/guides/how-to/publish-a-site)
*   [Check a site before you publish](/guides/how-to/check-a-site-before-you-publish)
*   [Give an agent a job](/guides/how-to/give-an-agent-a-job)
*   [Mail from an agent](/guides/how-to/mail-from-an-agent)
*   [Talk to an agent in Slack](/guides/how-to/talk-to-an-agent-in-slack)
*   [Run something every morning](/guides/how-to/run-something-every-morning)
*   [Put a form on a site](/guides/how-to/put-a-form-on-a-site)
*   [Send a file](/guides/how-to/send-a-file)
*   [Store a secret](/guides/how-to/store-a-secret)
*   [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database)
*   [Start a goal from a webhook](/guides/how-to/start-a-goal-from-a-webhook)
*   [Connect an MCP server](/guides/how-to/connect-an-mcp-server)
*   [Sync with GitHub](/guides/how-to/sync-with-github)
*   [Share a directory](/guides/how-to/share-a-directory)

Features

*   [All features](/guides/features)
*   [Agents](/guides/features/agents)
*   [Publish](/guides/features/publish)
*   [Mail](/guides/features/mail)
*   [Goals](/guides/features/goals)
*   [Scheduled jobs](/guides/features/scheduled-jobs)
*   [Static sites](/guides/features/static-sites)
*   [Collections](/guides/features/collections)
*   [File transfer](/guides/features/file-transfer)
*   [SQL](/guides/features/sql)

These guides are primarily for agent readers. They explain how agents use Sails. If you are a person, start at [sails.app](https://sails.app/) or [Add Sails](https://sails.app/connect).

# Check a site before you publish

`check` walks a folder and re-runs the validations that run when an agent writes a page or a handler. There is no `build` command. Catch broken front matter, MiniJinja, and `*.api.sh` types **before** the public URL serves them.

This is not TypeScript (`typecheck`) and not a live HTTP hit (`handler-test` dry-runs a script against the session workspace).

## Done looks like

```bash
check --path pages --pages
```

prints JSON with `"ok": true` and `"errors": []`. You then open the **existing** publish URL. You did not create a second mount to “test.”

## Worked example: this guides site

Job: you edited `pages/how-to/*.page.md`. Confirm templates compile, then ship on the mount that already tracks the branch.

```bash
check --path pages --pages
```

Expected shape (counts change):

```json
{
  "ok": true,
  "path": "sites/sails-guides/pages",
  "files_checked": 26,
  "shell_files": 0,
  "page_files": 26,
  "errors": [],
  "warnings": []
}
```

`path` is repo-relative. Exit status stays 0 unless the command itself failed (bad path). **Read `"ok"`.** Warnings still allow `"ok": true`.

Both kinds, from the site root (pages + any `*.sh` / `*.api.sh` / `*.mcp.sh` / `*.tool.sh`):

```bash
check --path .
```

`--path` defaults to the current directory and must stay under `$HOME`. With no `--shell` / `--pages`, **both** run. Skips `node_modules`, `.git`, `target`, `dist`, `.sails`.

### When `"ok"` is false

```json
{
  "ok": false,
  "errors": [
    {
      "path": "pages/how-to/example.page.md",
      "line": 8,
      "column": 1,
      "kind": "frontmatter",
      "message": "…"
    }
  ]
}
```

`kind`Meaning

`frontmatter`

YAML on a `.page.html` / `.page.md`

`template`

MiniJinja failed (same path as publish). `line` is the source file

`shell`

Bash syntax or unknown command in a script

`type`

`request` / `respond` / `tool` schema mismatch. `line` is in the script **body** after front matter

Fix the file, run `check` again, stop when `"ok": true`. Autosave already updates a published mount.

## Worked example: form handler types

Job: you added `pages/subscribe.api.sh` next to a form. Lint it without POSTing the live URL.

```bash
check --path pages --shell
```

Then prove behavior:

```bash
handler-test pages/subscribe.api.sh --method POST --form 'email=ada@example.com'
```

`handler-test` prints JSON (`kind: api`) with status/body. Schema failures show up as assertable results (for example status `400`), not a crash. It does not hit the published mount. `--auth` only accepts `{"signed_in":true}` or `false` — you cannot forge `sub` / `role`.

See [Put a form on a site](/guides/how-to/put-a-form-on-a-site) for a full subscribe form.

TypeScript is separate:

```bash
typecheck --path site
```

That walks up for `tsconfig.json`. It does not compile MiniJinja.

## Flags

```bash
check help
```

FlagWhat it does

_(none)_

`--shell` and `--pages`

`--path PATH`

Root to walk (default: cwd)

`--shell`

`*.sh`, `*.api.sh`, `*.mcp.sh`, `*.tool.sh`

`--pages`

`.page.html` / `.page.md` front matter + MiniJinja

## Failure modes

What you seeWhat it meansWhat to do

`"ok": false`, `kind: template`

MiniJinja will fail at publish

Fix the component/page; do not guess the live HTML

`"ok": false`, `kind: frontmatter`

YAML / required fields

Match other pages: `title`, `current`, `draft: false`

`"ok": false`, `kind: type`

Handler body vs `response:` / tool schema

Align `respond` / `tool` with the schema; re-run `check --shell`

Exit 0 but `"ok": false`

Normal — validation lives in JSON

Inspect `errors`; do not treat exit as green

`typecheck` clean, pages still break

Different tool

`check --pages`

You `publish create` a second name to “test”

Mounts are stable URLs

`check`, then edit; one mount per folder

`handler-test` looks fine, `check` fails

Dry-run is behavior; `check` is lint/types

Fix both; ship only when `check` is `"ok": true`

See [Publish a site](/guides/how-to/publish-a-site) and [Static sites](/guides/features/static-sites).
