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

# Module 3 · Author .tql Correctly

> Goal: write a typed, parameterized query surface using the deployment’s own authoring skill as the grammar reference — not guesswork. (~25 min)

**Goal:** write a typed, parameterized query surface using the deployment's own authoring skill as the grammar reference — not guesswork.

## 3.1 · Pull the authoritative grammar

The deployment ships the same instruction sets the product's agent uses. Discover and fetch them — don't guess trigger names:

```text Prompt theme={null}
refinery rpc call textql.rpc.public.patches.OntologyManagementService/ListSkills --body '{"includeUnlisted":true}'
refinery rpc call textql.rpc.public.patches.OntologyManagementService/GetSkill --body '{"trigger":"writing-tql"}'
```

<Check>
  **You'll see:** the complete .tql language reference — file layout, the params grammar, fragment functions, and a "rejected forms" table of the mistakes everyone makes. Keep it open while authoring.
</Check>

## 3.2 · The guardrails that save you an hour

<table><tr><th>You'll try</th><th>The language wants</th></tr>
<tr><td>`metrics: Set&lt;"a"|"b"&gt; = ["a"]`</td><td>Set defaults may only be `[]` — handle the empty case in the body with `isEmpty`</td></tr>
<tr><td>`concatSep(", ", frags)`</td><td>Application syntax, no parens/commas: `concatSep ", " frags`</td></tr>
<tr><td>`matchSet` as a function</td><td>It's syntax: `matchSet metrics &#123; "label" -&gt; sql"...", &#125;` (arrow arms; trailing comma allowed here only)</td></tr>
<tr><td>`'$&#123;param&#125;'` in SQL</td><td>Unquoted `$&#123;param&#125;` — the renderer handles quoting; `IN $&#123;list&#125;` adds parens</td></tr>
<tr><td>`''` inside a `sql''…''` body</td><td>No adjacent single quotes there — put quoted SQL literals in `sql"…"` fragments</td></tr></table>

## 3.3 · The authoring loop

* **Write** the file (params block → `let` bindings → one `sql''…''` body ending in an explicit `LIMIT`).
* **Execute it against a live connector** with real parameters. Not "it parses" — it returns correct rows.
* **Reconcile one number** against a raw-SQL computation of the same thing before you call it done.

<Warning>
  **⚠️ Execute before you save — a true story** — A flows query surface joined a snapshot table (several rows per entity) to a fact table. It parsed, rendered, and ran — and **inflated every total \~6.5×**. The only reason no user ever saw the wrong number: the surface was executed against the live connector and reconciled before installation, the join deduplicated, re-verified to the dollar. An untested query surface is a liability wearing a governance costume.
</Warning>

### ✅ Checkpoint

* [ ] You fetched writing-tql from your own deployment and used its rejected-forms table
* [ ] Your .tql executed against a live connector with parameters
* [ ] One output number reconciled against an independent raw-SQL computation
