---
name: connector:full
description: Build a connector end to end from one request — Research, Build, Validate, gated and unattended.
triggers:
  - /connector:full
  - build the [provider] connector
  - build a connector for [provider]
  - build [provider] with [resources]
  - take [provider] from scratch to done
argument-hint: <provider> [with <resources> | full API suite]
---

# Build a connector, end to end

`$ARGUMENTS`

Three phases — **Research → Build → Validate** — each gated by a script, each looping until
its gate passes or it stops converging. Once this starts it runs to a terminal outcome
without asking anything further.

**Execute the flow yourself, in this session.** Never fork, spawn, or delegate the run — or
a whole phase of it — to a subagent or background agent, whatever else the request asked for
(telemetry, a report, a cost autopsy). A delegated run rebuilds every skill and state file
from zero context and measured at roughly **double the cost** of the same run executed
directly; the phases are strictly sequential, so delegation buys no parallelism either. If
the request also wants telemetry or an autopsy, do that work in this session too, alongside
the phases.

**That ban is about the run and its phases — NOT the flow's own worker agents.** Four
scoped agents are part of the flow, spawned by you at the step that names them, and using
them is not delegation:

| Agent | Spawned at | Mandatory? |
|---|---|---|
| `resource-endpoint-fetcher` | Research, endpoint extraction — one per in-scope resource | **Yes** at ≥2 resources or >8 in-scope endpoints |
| `use-case-discoverer` | Research, dynamic use-case catalogs (full mode) | when catalogs are needed |
| `schema-reviewer` | Validate, schema review | yes — the command says so |
| `provider-comparator` | Validate, doc comparison — one per resource | yes — the command says so |

These are the opposite of a delegated run: each has a tight contract, returns a small
JSON summary, and keeps hundreds of KB of read-once docs out of this session's context.

**Always spawn them FOREGROUND (`run_in_background: false`), batched in one message.**
Foreground calls in one message run concurrently and return together with their
summaries. A background spawn hands back an agentId and an obligation to collect that a
measured run never honored — both fetchers finished in 3 minutes and their output sat
uncollected while the parent re-did the extraction inline for 17. Background mode is
never correct in this flow.
**Skipping the mandatory fan-out is the measured kill pattern**: two runs on the same
prompt and commit — the one that researched 13 endpoints sequentially died at the 25-min
cap with research at 98% of its clock; the one that fanned out finished in 854s.

**Never read `agents/*.md` yourself** — an agent's definition loads into the agent when
it is spawned; spawning one needs only its name. The same goes for startup generally:
the first Bash call of the run is `run.js init` (then the discovery launch), and every
reference file a step needs is read in ONE parallel message while that slow work runs —
time spent reading before any work is launched is the one startup cost nothing overlaps.

## What the customer experiences

They say what they want. In the common case that is the entire interaction.

**Zero required interactions.** Nothing here deploys, connects an account or calls a live
API, so the run needs no credentials, no profile, no account and no consent. There is
exactly one question this command is ever allowed to ask, and it is rare — see
"The only permitted question" below.

What they see while it runs is five lines, one per milestone:

```
✓ Found Ashby's API docs — 3 resources, 41 endpoints
✓ Researched candidates, notes, applications
✓ Built the connector — 41 actions, 318 parameters
⟳ Re-checking every action against Ashby's documentation…
✓ Complete — no gaps found
```

**These cost no extra calls.** `set-scope` and `set-phase` already run at each transition
and now return a `narration` field — echo it verbatim. Pass `--connector-path <dir>` to the
`build` transition so the action and parameter counts are real.

Do not compose these lines yourself and do not pad them. The counts are read off the
research artifact and the connector YAML, so a narrated action count is the number of
actions that exist, while a recalled one drifts. The last line reports gaps honestly and
will say `2 gaps recorded` or `validate could not be driven to green` when that is what
happened.

Everything else is silence. No phase banners, no phase-complete blocks, no artifact paths,
no gate rosters, no check ids. That detail belongs in `run.json`, which is where anyone who
wants it can look; narrating it buries five useful lines in forty.

Gate failures and repair rounds are **working, not news** — the loop is expected to fail
and fix. Say nothing until the phase resolves. A customer who asked for a connector wants
to know it is coming, not to watch a check fail four times.

## Step 0 — Read the request

Extract **five** things from what the customer actually said:

| From | Take |
|---|---|
| `"Build the Ashby connector with candidates, notes and applications"` | provider `ashby`, resources `[candidates, notes, applications]`, ops `[]`, auth `[]`, sandbox `no` |
| `"Build the CascadeHR connector with the full API suite"` | provider `cascadehr`, resources `[]`, ops `[]`, auth `[]`, sandbox `no` |
| `"build an ashby connector"` | provider `ashby`, resources `[]`, ops `[]`, auth `[]`, sandbox `no` |
| `"Build the Deel connector with OAuth only, contracts and invoices"` | provider `deel`, resources `[contracts, invoices]`, ops `[]`, **auth `[oauth]`**, sandbox `no` |
| `"Build the Xero connector, API key auth, sandbox too"` | provider `xero`, resources `[]`, ops `[]`, **auth `[api key]`**, **sandbox `yes`** |
| `"Build the Attio connector with actions for searching people and companies"` | provider `attio`, resources `[people, companies]`, **ops `[search]`**, auth `[]`, sandbox `no` |

Resources named → `mode: explicit`. None named → `mode: full`.

"the full API suite", "everything", "all the endpoints" are **not** resource names. They mean
no scope was given, which is `mode: full`.

**Auth and sandbox are scope too.** Naming no mechanism means build every mechanism the
provider supports — the normal case. Naming one or more means build **exactly** those; the
rest are recorded as dropped, and gate A9 fails if an unrequested mechanism appears.

**Operations are scope too, and only when the request restricts them.** A verb attached to
the resources — "actions for *searching* people", "*read-only* access to tickets" — bounds
which operations to build. Pass it as `--operations`; the words `search`, `read`, `get`,
`create`, `update`, `delete`, `write` and `manage` are all understood.

Take care with the two shapes that are *not* restrictions:

| Request | Operations |
|---|---|
| `"actions for searching people and organizations"` | `[search]` — the verb bounds the request |
| `"actions for blobs and containers, including uploading a blob"` | `[]` — "including" adds emphasis, it does not exclude the rest |
| `"actions for tickets, agents and requesters"` | `[]` — no verb, so build every operation |

When in doubt, leave it empty. An unrequested restriction loses real coverage, while a
missing one only costs precision.

Pass what you extracted verbatim — `--auth "oauth"` is fine, it is resolved against what
research discovers. Never translate the customer's words into a mechanism id yourself.

### The only permitted question

If the provider name maps to **two or more real products with different APIs**, ask which
one — once, as a plain choice, before `init`:

> "Atlas" matches Atlas CRM and MongoDB Atlas. Which one?

That is the whole exception. It exists because guessing wrong wastes the entire run and is
not recoverable from the artifact; every other unknown has a defensible default.

It does **not** license asking about anything else. In particular, resolve these yourself:

| Unknown | What to do |
|---|---|
| No resources named | `mode: full` — the provider's core use cases, capped |
| No auth named | Build every mechanism the provider documents |
| Sandbox not mentioned | Production only |
| Where the files go | `src/configs/<provider>/` if `src/configs/` exists, else `<provider>/` |
| Which action to test with | There is no live testing in this flow |
| A gate that will not pass | Mark the phase `exhausted` and carry the gap |

A name that is merely unfamiliar is not ambiguous — research it. Ambiguity means two
plausible products, not one you have not heard of. If the documentation cannot be found at
all, say so plainly and stop; do not research a guess.

Start the run — this one goes all the way to validate, so `--target-phase` stays at its
default:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/state/run.js init \
  --provider <provider> --request "<the request, verbatim>"
```

Then say what you are about to do, in one line, and begin:

> Building the Ashby connector covering candidates, notes and applications, verified against
> Ashby's API documentation. I'll report when it's done.

**Ask nothing else.** Not which resources, not which auth, not whether to proceed.

## Step 1 — Research

**First, one capability check.** Run a single ToolSearch for `discover_actions`. If the
discovery tools (`map_provider_key`, `get_provider_actions`, `discover_actions`) are not
registered in this session, **skip discovery entirely** — do not load the discover-actions
skill, do not search again, go straight to documentation research. Loading the skill first
and finding out afterwards wastes its whole instruction text for the rest of the run.

**If the tools exist, launch action discovery before reading any documentation.**
`discover_actions` runs 5–15 minutes server-side and returns a taskId immediately, so
starting it first puts that wait underneath everything else rather than after it — and poll
it before the gate, because a task launched and never collected costs the same and yields
nothing.

Then load `research-provider` and follow it. It carries the rest: enumerating auth from the
provider's index page, and fetching independent documentation pages concurrently rather than
one per turn.


Then resolve scope:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/state/scope.js \
  --research .connector-dev/<provider>/research/<provider>_research.json \
  --mode <explicit|full> [--resources a,b,c] [--operations search] \
  [--auth "oauth,api key"] [--sandbox] [--domain <category>]
```

Pass `--operations`, `--auth` and `--sandbox` **whenever the customer named them**, using
their words. This is the only place those constraints become durable — omit them and the run
reverts to building every operation and every mechanism the provider supports, whatever the
prompt said. A request that named an operation but reached `scope.js` without
`--operations` builds the resource's whole surface, and every unasked-for action counts
against precision.

Pipe the contract straight into the run — `run.json` cannot be edited by hand:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/state/scope.js --research <path> --mode <mode> [...] \
  | node ${CLAUDE_PLUGIN_ROOT}/scripts/state/run.js set-scope --provider <provider>
```

It echoes back what was recorded — check `authBuild` and `authDropped` match what the
customer asked for before continuing. Then loop:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/gates/phase.js --phase research --provider <provider> \
  && node ${CLAUDE_PLUGIN_ROOT}/scripts/state/run.js set-phase \
       --provider <provider> --phase research --status complete
```

**One chained call, not two turns**: `&&` records the transition the moment the gate
passes — the gate report and the milestone line arrive together, and a turn of round-trip
latency disappears at every boundary. On a non-zero exit the chain stops and you fix what
the gate printed, exactly as before.

The reply carries `narration` — echo it. On the `build` transition add
`--connector-path <dir>` so the counts come from the YAML.

## Step 2 — Build

Load `build-connector` and follow it.

**Load `connector-patterns` before writing YAML** — `references/authentication.md` for the
auth block, `SKILL.md` for actions. It is not Skill-invocable — Read the files directly
from `${CLAUDE_PLUGIN_ROOT}/skills/connector-patterns/`. It is mined from the shipped corpus and it is the only
reference that distinguishes a shape that works from one that merely parses. Loading it
after a gate fails is too late: the wrong shapes are valid YAML, so they survive
`stackone validate` and fail silently at runtime.

**Auth first, but in one call.** A wrong auth block breaks every action at once, so it is
driven to green before actions are written — and `phase.js` already runs `auth` first and
reports it first. Do not run `auth.js` separately beforehand: that evaluates auth twice per
round for no extra signal.

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/gates/phase.js --phase build \
  --provider <provider> --connector-path <dir> --cli-ok <true|false>
```

Prefer this to invoking the gates one at a time. Each separate call is a round-trip, repair
rounds are the normal case rather than the exception, and the time they cost is the time
budget — a run that spends it on round-trips can be cut off before it writes a connector.
**Run auto-fix before each gate round.** The mechanical violations — a lowercase label, a
description missing its capital, an input with no `required` line, a non-preview
`releaseStage` — have exactly one correct output, so a model does not need to be involved:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/gates/autofix.js \
  --provider <provider> --connector-path <dir>
```

It edits line-by-line, so comments and key order survive, and logs every change to
`.connector-dev/<provider>/build/fix-log.json` — `--revert` undoes the run. It never
touches anything needing judgement: no renames (that would break the `$ref` declaring the
partial) and no invented descriptions.

Each fix it applies is a repair round that never happens, and repair rounds are the budget
that decides whether validate is reached at all.

### The repair round

A round is: read **every** failing check the gate reported, fix **all** of them, re-run.
Fixing one finding per round is what turns a two-round repair into fifteen.

Each gate reports a `fixes` list alongside the failing checks — the check's `detail` is a
locator (`oauth2.authorizationParams.state`), `fixes` is the explanation and the remedy.
**Read `fixes`. Never open a gate's source to work out why it failed** — that is a several-
minute, several-dollar detour, and one observed run spent 28% of its budget doing it and
never reached validate. If a message genuinely does not say what to change, that is a defect
in the message: record it as a gap and move on rather than reverse-engineering the check.

Between rounds, re-check just what you touched — it is far cheaper than the whole surface:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/gates/build.js --provider <provider> --only B4,B7
```

**A scoped run is not a verdict.** It answers "did my fix land?", nothing more. It reports
`verdict: scoped-pass` and says so on stderr, precisely because a green subset while other
checks fail is how a phase gets marked complete over live blocking gaps. **The phase advances
only on a full `phase.js` run exiting 0.**

`stackone validate` only needs re-running after the YAML actually changed. Re-running it
after a no-op round buys nothing and costs a round-trip.

Each full run reports `round`, and `noProgressRounds` once rounds stop resolving anything.
At `stalled: true` the loop has converged as far as it will: mark the phase `exhausted` with
what remains and move on — the gate prints the exact command.

`conventions.js` enforces what all 484 shipped connectors do: snake_case actionIds, no
brackets in input names, `required` always stated, a `condition` on every optional arg, and
**production only** — sandbox is built only when `scope.sandbox` records that the customer
asked for it. Read `connector-patterns/references/naming-conventions.md` before writing, not
after it fails.

`--cli-ok` carries the `stackone validate` verdict. Run it, then pass the result — B1 fails
when it is omitted, because an unrun check must never read as a passing one.

Loop 1 needs `.connector-dev/<provider>/build/auth-evidence.json` citing the provider's auth
documentation per mechanism; A10 fails without it, so the auth block cannot be signed off on
assumption.

## Step 3 — Validate

Load `validate-config` and follow it.


Loop:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/gates/phase.js --phase validate \
  --provider <provider> --connector-path <dir>
```

This phase re-reads the provider's **live documentation** and compares it against what was
built. It does not check against the research report — that report produced the connector, so
comparing the two would certify research's own omissions as correct.

## The loop, for every phase

```
do the phase's work
run its gate
  exit 0  →  mark the phase complete, move to the next
  exit 1  →  fix what the gate reported, run it again
  exit 2  →  the gate could not evaluate; that is a bug, fix it and rerun
```

Record each transition. A **complete** transition chains onto the passing gate run in the
same Bash call — never its own turn:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/gates/phase.js --phase <phase> --provider <provider> [...] \
  && node ${CLAUDE_PLUGIN_ROOT}/scripts/state/run.js set-phase \
       --provider <provider> --phase <research|build|validate> --status complete
```

An **exhausted** transition follows a *failing* round (two rounds resolved nothing), so it
cannot chain on `&&` — record it directly:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/state/run.js set-phase \
  --provider <provider> --phase <research|build|validate> \
  --status exhausted [--unmet B3,B7]
```

The reply carries `nextPhase` and a directive. **While `nextPhase` is not null, the run is
not finished — start that phase immediately, in the same turn.** A phase boundary is where
an unattended run is most likely to stop by accident, and stopping there is fatal: in a
headless session the turn ending is the run ending, so a connector that was one phase away
is never written at all. The Stop hook refuses a turn that still owes a phase, so a summary
written at a phase boundary is wasted work.

**Stop looping when two consecutive rounds resolve nothing.** Mark the phase `exhausted`,
record what remains, and move to the next phase anyway. A gate that never passes is not a
reason to stop the run — a connector plus an honest gap list beats no connector.

After a targeted fix, re-check only what changed rather than the whole surface:

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/gates/build.js --provider <provider> --scope candidates
```

## What this command never does

- ❌ Ask "shall I continue?" or "proceed to the next phase?"
- ❌ Fork or delegate the run, or a whole phase of it, to a subagent or background agent
  (the four scoped worker agents above are part of the flow, not delegation — and the
  research fan-out is mandatory, not optional)
- ❌ Offer retry / skip / pause on a failure
- ❌ Ask which resource, action or auth type to handle first
- ❌ Ask the customer to interpret an error
- ❌ Stop because a gate refused — that is what `exhausted` is for
- ❌ **End the turn at a phase boundary** — a passing gate is a handover, not a finish line
- ❌ Deploy, connect an account, or execute anything against a live API

The only legitimate stop before a terminal outcome is a provider whose documentation cannot
be found at all. Say so plainly and stop; do not research a guess.

## Terminal outcomes

| Outcome | When |
|---|---|
| **complete** | All three gates passed |
| **partial** | At least one phase exhausted — connector delivered with its gap list |

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/state/run.js summary --provider <provider>
```

The final `set-phase --phase validate` already returned the closing line — echo that.

The narrated line is the whole report when the run is clean. Add prose **only** when there
is something the customer must act on:

| Situation | Say |
|---|---|
| No gaps, nothing capped | Nothing further — the five lines are the report |
| Scope was capped | How many endpoints were left out, and that naming resources gets them |
| Gaps recorded | What is unresolved and why, in one or two sentences |
| A phase exhausted | Which one, and what remains |

Then close with the limitation, in words:

> Built and verified against <Provider>'s API documentation. Not executed against a live
> account — behaviour under real credentials has not been confirmed.

## Resuming

Re-issuing the same request resumes from `run.json`; completed phases are skipped.

```bash
node ${CLAUDE_PLUGIN_ROOT}/scripts/state/run.js summary --provider <provider>
```
