# Running Zoho work through an AI-native SDLC

This is the artifact chain — intent → spec → plan → change → verification → review — applied to Zoho delivery, using `zone` as the tool at every stage.

## The one thing that is different about Zoho work

**A Zoho org is shared mutable state that you do not own, and it has no branches.** Code has git: you branch, you break things, you throw the branch away. An org has none of that. A field you delete is deleted for everyone, immediately, in the same org the client is working in right now.

So the playbook's loop still applies, but two stages carry the weight:

- **Design** has to be grounded in the org's *actual* metadata, because a spec that names a field that does not exist becomes a plan that fails halfway through, having already made half its changes.
- **Deploy** has to be gated by hand, because there is no revert.

Everything below follows from that.

## Stage 1 — Intent

Capture what the client asked for in `intent/<slug>.intent.md`, before touching the org. Anyone can write it: the consultant on the call, the client in an email you paste in.

```markdown
# Intent: quote approval over 50k needs a second approver

- **Problem**: Deals close at a discount no one signed off on.
- **Outcome wanted**: Above AED 50,000, a second approver is required before the stage can move to Closed Won.
- **Who is affected**: Sales team (12 users), Finance (2), the Deals module.
- **Constraints**: Must not block the existing under-50k flow. Client is live; no downtime.
- **Open questions**: Who is the second approver — a fixed user, or the deal owner's manager?
```

Do not resolve open questions by guessing. They go to the client, not into the spec.

## Stage 2 — Design, grounded in the real org

**Read the org before you write the spec.** This is the step that decides whether the rest works.

```bash
zone login crm                       # per-project session; see "Which org am I in" below
zone crm pull                        # snapshot modules, fields, layouts, functions to disk
zone crm meta modules --toon         # or read piecemeal
zone crm meta fields Deals --toon
```

`pull` exists for **crm, books, inventory, creator, mail, meetings and partner**. It writes the org's configuration to files, which means two things the playbook wants and Zoho does not give you on its own:

1. **The spec can cite exact API names** — `Deal_Name`, not "the deal name field" — because you read them.
2. **The org config is now versionable.** Commit the pull. Pull again after the change. The diff is your record of what you actually did to the client's org, which no Zoho screen will give you.

The spec names, for every change: the module, the exact field API names and types, the automation involved (workflow, blueprint, function), and what already exists that this touches.

## Stage 3 — Plan

`plan.md` lists the changes in order, and for each one: what it touches, whether it is reversible, and how it will be verified.

Order the plan so that **everything reversible happens before anything that is not**. If step 7 is destructive and step 5 fails, you want to have stopped before step 7.

## Stage 4 — The feedback loop

Give the agent a way to check its own work before a human looks. `zone` is built for this:

| Signal | Meaning |
|---|---|
| exit `0` | ok |
| exit `2` | usage error — the command or flag is wrong |
| exit `3` | not signed in for that service |
| exit `4` | signed in, but the token lacks the scope |
| exit `5` | Zoho returned an API error — a genuine rejection, do not retry blindly |
| exit `6` | blocked by policy — report which rule stopped you, do not work around it |
| exit `7` | retryable — network, 429 or 5xx. Back off and try again |
| `--json` / `--toon` | machine-readable stdout; messages go to stderr |

**A 2xx is not a success.** Zoho returns HTTP 200 with per-record failures inside the envelope. Check `data[0].code === "SUCCESS"` on every write. This is the single most common way an agent reports "done" having changed nothing.

**Verify every write with a read.** Not the write's own response — a fresh read.

```bash
zone crm record update Deals 123456 --data '{"Stage":"Negotiation"}' --json
zone crm record get Deals 123456 --json    # the actual state, now
```

Where the change is configuration rather than data, re-run `pull` and diff. That is the Zoho equivalent of a test suite passing.

## Which org am I in — the guardrail that matters most

`zone login` writes `.zone` into the current project, found the way git finds `.git`. **Each repo holds a different Zoho org.** That is the mechanism for tiered autonomy:

```
client-acme-sandbox/.zone     ← the agent may work freely here
client-acme-prod/.zone        ← gated: a human runs the writes
```

`ZONE_HOME` overrides both; `--global` uses `~/.zone`.

Zoho CRM adds a second axis: the same org has a **sandbox** on another host (`sandbox.zohoapis.<dc>`), and Zoho binds each OAuth token to the organization picked on the consent screen. Log in to the sandbox in its own store — `ZONE_HOME=./.zone-sandbox zone login crm`, choosing the Sandbox organization — and zone sets `env=sandbox` from the org's type; `zone status` shows an **ENV** column (and flags a token bound elsewhere). Rehearse a CRM change there, then replay against the production store — see `zone llm crm` for the ids caveat (sandbox and production ids differ).

```bash
zone status              # which store answered, and which services are authenticated
zone status --live       # probe real endpoints — proves the token works, not just that it exists
```

**Run `zone status` before the first write of any session.** An agent that assumes it is in the sandbox and is actually in production is the worst failure mode available here, and it is one command to rule out.

## The gate is built in — use it before reaching for a hook

zone enforces this itself, so a change to a live org does not depend on the agent's judgement.

**Dry-run the write first.** `--dry-run` works on every command: it resolves the request and prints the exact method, path and body instead of sending it, then exits 0. Reads still run, so a command that reads before it writes does its reads and then shows you the write it would have made.

```bash
zone crm record delete Leads 123 --dry-run --json
# {"dry_run":true,"service":"crm","method":"DELETE","path":"/Leads/123",...}
```

That output is what you put in front of the human for a yes. Summarize it in words too — "this deletes lead 123, permanently" — because a JSON blob is not informed consent.

**Run the session read-only until a write is actually approved.**

```bash
export ZONE_POLICY='*:GET'        # every write now exits 6
zone crm coql "select id, Stage from Deals where Amount > 50000"
```

Deny always wins over allow, and no policy set means no restriction. Reads that Zoho implements as POST (COQL, bulk read) still work under `*:GET`, so a read-only session is genuinely usable rather than half-broken. To open exactly one thing:

```bash
zone crm record update Deals 123 --data '{"Stage":"Won"}' --allow 'crm:GET,crm:PUT' --deny '*:DELETE'
```

**Keep the transcript.** `--audit` appends one JSONL line per request — including denials and dry runs, tagged — with no bodies, so the file is not itself a leak.

```bash
export ZONE_AUDIT=./client-acme.jsonl
```

This is the answer to "what did the agent change in that org on Tuesday", which Zoho's own audit log cannot attribute to a session.

**If you are an agent reading this:** `shell(zone)` in your harness granted you every write to every authenticated Zoho service. Your harness cannot express "zone but reads only" — that granularity does not exist at the shell-prefix level. `ZONE_POLICY='*:GET'` is how you get it. Set it yourself at the start of investigative work; ask before lifting it.

A `PreToolUse` hook still has a place — enforcing that `ZONE_POLICY` is *set at all* in a production repo, which the CLI cannot check for you — but the per-command blocking is zone's job now.

## What is reversible, and what is not

Order the plan by this table. Anything in the bottom two rows is a human step.

| Change | Reversible? |
|---|---|
| Record create / update | Yes — you have the prior values if you read before writing |
| Record delete | Recycle Bin, for a limited window — recoverable, not free |
| Adding a field, layout, view, workflow | Yes — remove it |
| Bulk / mass update, bulk write | **No** — there is no undo; it is many writes |
| Deleting a custom field | **No in practice** — the data in it goes with it |
| Removing a picklist value in use | **No** — records holding it are affected |
| Blueprint transition | Forward only, unless a reverse transition was built |
| Deleting a module, or emptying a bin | **No** |

Before any bulk operation: read the affected set first, write it to a file, and keep it. That file is the only rollback that exists.

```bash
zone crm coql --query "select id, Stage, Amount from Deals where Amount > 50000" --all --json > backup-deals.json
```

## Stage 5 — Review

The reviewable artifact for Zoho work is not only the code. It is:

- the Deluge diff (see the `zoho-deluge` skill for what to look for),
- the **config diff from re-pulling the org**,
- and the plan, with each step marked verified.

Review Deluge against the language skill's trap list, not against JavaScript instincts. Review widget code against its subtype skill.

## Stage 6 — Maintain

Scheduled, read-only checks that raise an intent when they find something, rather than acting:

```bash
zone status --live --json                  # tokens still valid across services
zone grants                                # stored OAuth grants; Zoho caps at 20 per user
zone crm pull && git diff --stat           # did anyone change the org outside our process?
```

That last one is worth a schedule on any client org. A config diff you did not author is a finding, and it becomes the next `intent.md`.

## The loop, in short

1. Write `intent.md`. Do not guess the open questions.
2. `pull` the org. Ground the spec in real API names.
3. Plan reversible-first, with a verification per step.
4. Work in the sandbox session (`zone ctx crm env=sandbox` for a CRM sandbox), `ZONE_POLICY='*:GET'` until a write is approved. `zone status` before the first write.
5. `--dry-run` the write, get a yes, then run it. Check `data[0].code`. Verify with a fresh read.
6. Re-pull, diff, commit. The diff is the record of what you did.

> Exit codes, output contract, session resolution, the services with `pull`, and every command shown are read from this installed CLI, so they are exact. The reversibility table reflects how Zoho behaves across CRM and the finance apps and is deliberately conservative — where an entry says "no", treat it as a human decision even if your edition offers some recovery path.
