# How did we publish 701 decision pages to Sanity at agent scale?

Reviewed by Martin Dejnicki · facts verified 25 October 2026 · canonical: https://deploi.ca/sanity/builds/agent-scale-publishing

## Short answer

Deploi published 701 Build vs. Buy decision pages to Sanity by having AI agents write one document per page, with slug-based IDs and weak references. From wave 5, developers imported each wave of about 100 pages as a pack they could check and roll back by ID list. Sanity runs validation rules only in Sanity Studio, not on imports ([Sanity Docs](https://www.sanity.io/docs/content-lake/schema-validation-and-the-content-lake), October 2026), so Deploi gates each wave.

- **Documents per page:** About 4.4, counted 24 October 2026. 3,057 documents across five types for 701 decision pages (Deploi arithmetic from the figures above)
- **Document quota:** 10,000 on Free, 25,000 on Growth. Growth goes to 50,000 with the Increased quota add-on at $299 a month; drafts count toward the maximum, as of October 2026 ([Sanity, Plans that scale with you](https://www.sanity.io/pricing); [Sanity Docs, Technical limits](https://www.sanity.io/docs/content-lake/technical-limits))
- **Validation on imports:** None: Studio rules don't run there. Imports, migrations and API writes are accepted without schema checks; sanity documents validate checks a dataset afterwards ([Sanity Docs, Schema validation and the Content Lake](https://www.sanity.io/docs/content-lake/schema-validation-and-the-content-lake); [Sanity Docs, Import data](https://www.sanity.io/docs/content-lake/importing-data))
- **Reference order:** Any order, with weak references. A weak reference may point at a document that isn't published yet or was deleted, so it never blocks a push and never flags a gap ([Sanity Docs, Reference](https://www.sanity.io/docs/studio/reference-type); [Sanity Docs, Import data](https://www.sanity.io/docs/content-lake/importing-data))

## Deploi's call

Model each page as one document, derive every ID from its slug, make every reference weak, and ship in waves a developer can undo by ID list. Then spend as much effort on the checks as on the content, because this design removes exactly the errors that would have stopped a bad push.

**REUSE: Reuse one Sanity document per page, with slug-based IDs and weak references, when**

- Dozens or hundreds of pages share one anatomy that fits in one document type.
- Prices, names or statuses repeat across pages and can live in one referenced document, so one edit reaches every page that joins it.
- Developers can own a wave package with an ID list, count checks and a rollback.
- You can run a dangling-reference check and a seed-to-dataset diff after every push, because Sanity does not check imported documents against schema validation rules as of October 2026.

**AVOID: Avoid this Sanity pattern when**

- Pages are one-off layouts that editors compose by hand.
- Agents would write straight to production with no review dataset or ID list.
- Nobody re-verifies the shared records that every page joins.

## What did we publish, and why one document per page?

Deploi published the [Build vs. Buy hub](https://deploi.ca/shopify/build-vs-buy) on deploi.ca as 701 `capabilityDecision` documents in Sanity, one per page, counted in the production dataset on 24 October 2026. Each page answers one Shopify capability decision: buy an app, build, customize or wait. The document's field groups (core, verdict, evidence, conversion, ops) mirror the page's sections, so a complete, valid document is a complete page and nobody assembles it afterwards. That's what makes agent authoring safe: an agent fills fields, never layouts. Shared facts sit in their own document types:

**701 pages take 3,057 documents, about 4.4 per page (Deploi arithmetic from the counts in this table)**

| Document type | Count | What it holds |
| --- | --- | --- |
| capabilityDecision | 701 | One page: verdict, scenario bands, a 12-dimension cost and risk score, FAQs |
| capability | 701 | The capability the page decides on |
| appProfile | 1,528, of which 5 are drafts | App name, status and price, stored once |
| verdictChange | 100 | One dated verdict change each, never edited |
| bvbDomain | 27 | The domain hubs that group decisions |

As of 24 October 2026.

The pages also carry 2,103 FAQ sub-questions as arrays inside the decision documents, not as separate documents, so they add nothing to the document count. The programmatic pages use case covers when this kind of hub pays off.

## How do deterministic IDs and weak references make agent pushes safe?

Deterministic IDs make a push repeatable, and weak references make it order-free. Every document ID is built from a slug, so the same content pushed twice lands on the same ID instead of creating a duplicate:

- `bvb.<slug>` for a decision page, `capability.<slug>` for its capability
- `app.<slug>` for an app profile, `bvbDomain.<slug>` for a domain
- `vc.<decision-slug>.<yyyy-mm-dd>` for a verdict change

The Sanity command-line interface (CLI) import overwrites a matching document with `--replace` and otherwise stops with an error ([Sanity Docs, Import data](https://www.sanity.io/docs/content-lake/importing-data), October 2026).

Every reference in the schema is `weak: true`. Sanity's reference type lets a weak reference point to "a document that has not yet been published or one that has been deleted" ([Sanity Docs, Reference](https://www.sanity.io/docs/studio/reference-type), October 2026), so an agent can push a decision before its app profile exists. The same import already handles order for strong references by importing them weak and strengthening them afterwards ([Sanity Docs, Import data](https://www.sanity.io/docs/content-lake/importing-data), October 2026). We keep them weak for a second reason: "A published document can't hold a strong reference to unpublished content" ([Sanity Docs, Drafts and versions](https://www.sanity.io/docs/content-lake/drafts-and-versions), October 2026), and agent-written corrections land as drafts. A draft is just a `drafts.` prefix on the decision's ID, while references keep pointing at published IDs. The cost is silence: a weak reference to a missing document fails nothing, so a check has to find it.

## How do 701 pages stay consistent with GROQ joins?

Facts that many pages share live once and join at query time with GROQ (Graph-Relational Object Queries). A decision stores only why an app fits; the app's name, status and price come from its `appProfile` through a reference, so a quarterly price check edits one document and every page that references it updates. The change log runs the other way: each verdict change is its own dated document, and the page query finds them by reverse reference.

**Part of the decision page query, from Deploi's frontend code (sanity/bvbQueries.js)**

```groq
"apps": appLandscape[]{
  "name": app->name,
  "status": app->status.label,
  "url": app->listingUrl,
  "statusNote": app->status.note,
  "pricing": app->pricing.summary,
  bestFor
},
"changeLog": *[_type == "verdictChange" && decision._ref == ^._id] | order(date desc){
  "date": date, "from": fromVerdict, "to": toVerdict, note
},
```

Across the hub, 701 decisions carry 2,401 app rows that resolve this way, counted 24 October 2026. The same fields feed structured data: each page's JSON-LD `Article.description` is the capsule field, copied verbatim, so the visible answer and the markup can't drift. JSON-LD from fields covers that build, and the GROQ feature review covers joins in general.

## How does a content wave reach production?

A content wave reaches production as a package that developers import, check and can undo. Agents wrote waves 5 to 8 to a scratch dataset, since deleted, and developers imported them; agent-written corrections now land in production as drafts for a person to publish. Agents authored about 100 pages per wave and pushed them through Sanity's MCP (Model Context Protocol) server into that dataset for review. Schema changes deploy through the Sanity command-line interface (CLI), never the MCP deploy tool.

**Agents write to a review dataset; developers move a checked package to production**

- Agent authoring -> Scratch dataset: documents with deterministic IDs
- Scratch dataset -> Wave pack: newline-delimited JSON (NDJSON) and a list of new IDs
- Wave pack -> Production dataset: CLI import, waves in order
- Production dataset -> Checks: document counts and reference checks
- Checks -> Rollback: delete the listed IDs if a check fails

The checks can't be left to the schema. On imports and API (application programming interface) writes "the Content Lake accepts the write without checking your validation rules" ([Sanity Docs, Schema validation and the Content Lake](https://www.sanity.io/docs/content-lake/schema-validation-and-the-content-lake), October 2026). Some MCP tools are schema-aware, but the Content Lake does not check writes ([Sanity Docs, Schema validation and the Content Lake](https://www.sanity.io/docs/content-lake/schema-validation-and-the-content-lake)). So each wave clears Deploi's own gates before packing: wave 8 passed lint on 101 of 101 pages and two page audits with 0 findings (5 September 2026). Rollback is deleting the pack's listed IDs, which works because the wave 8 pack added documents and modified none. See safe release scripts and the datasets review.

## What went wrong when we published 701 pages to Sanity, and how did we fix it?

Three things went wrong in Deploi's agent publishing to Sanity, all from design choices that make pushes fast.

- **One bad source multiplied across pages.** A 15 September 2026 root-cause report found 177 auto-generated app profiles whose format collapsed annual and per-order pricing into one fake monthly range. Pages join prices from those profiles, so every page that used them inherited the error. Corrected profiles go back as drafts for a person to publish; 5 awaited review on 24 October 2026.
- **A delta check missed documents.** The first wave 8 push left 97 of 101 new pages with dangling app references: the delta calculation skipped two kinds of app profile the seed generator makes. A seed-to-dataset diff found 140 missing documents, and the dataset then matched the seed at 3,052 (Deploi's wave 8 quality report, 5 September 2026).
- **Finished waves sat unpublished.** Waves 5 to 7 waited in the scratch dataset while production showed 300 pages, so none could rank. All 701 were live by 24 October 2026.

> **Weak references and joins hide failures**
>
> Imports don't flag missing targets or wrong shared records. After every push, count dangling references, diff the seed against the dataset, and run `sanity documents validate`, which Sanity suggests "after bulk operations like migrations or imports" ([Sanity Docs, Schema validation and the Content Lake](https://www.sanity.io/docs/content-lake/schema-validation-and-the-content-lake)).

## How many pages like these fit in your Sanity document quota?

A Sanity Growth plan holds about 5,700 pages built this way: its 25,000 included documents divided by this hub's ratio of about 4.4 documents per page, before drafts or other content (Deploi arithmetic from the figures above). Size the count first, because Sanity's quota counts documents across all datasets in a project ([Sanity pricing](https://www.sanity.io/pricing), October 2026), and "Drafts, release versions, and documents created by plugins count toward the maximum" ([Sanity Docs, Technical limits](https://www.sanity.io/docs/content-lake/technical-limits), October 2026). The Free and Growth plans include 2 datasets, so a scratch dataset takes the second one ([Sanity pricing](https://www.sanity.io/pricing), October 2026).

**Pages that fit each plan's documents at 4.4 documents per page (Deploi arithmetic)**

| Plan | Documents included | Pages at 4.4 documents per page |
| --- | --- | --- |
| Free | 10,000 | About 2,300 |
| Growth | 25,000 | About 5,700 |
| Growth with the Increased quota add-on, $299 a month | 50,000 | About 11,400 |
| Enterprise | Custom | Not published |

Free has no document add-on. Enterprise quotas are custom. Sources: [Sanity, Plans that scale with you](https://www.sanity.io/pricing). As of 24 October 2026.

## Deploi's point of view

**Where we disagree.** Programmatic content advice treats scale as a template problem: build one layout and pour rows in. The harder problem is provenance. A shared record that's wrong is wrong on every page that joins it, and weak references let missing records through without an error. The second mistake is trusting schema validation to guard agent writes. Sanity runs it in the Studio, not on imports or API writes, so the gates have to live in your pipeline.

**What we've seen.** deploi.ca's Build vs. Buy hub runs 701 decision pages from 3,057 documents across five types, counted 24 October 2026. Our wave 8 push on 5 September 2026 left 97 of 101 new pages with dangling app references until a seed-to-dataset diff found 140 missing documents. A 15 September 2026 root-cause report traced pricing errors to 177 auto-generated app profiles whose format turned annual and per-order prices into monthly ranges. Waves 5 to 7 also waited in a scratch dataset while only 300 pages were live.

**What we'd tell you to do first.** Write the ID convention and the rollback list format before the first page. Both cost minutes on day one and are painful to retrofit once agents have pushed a few thousand documents.

## When this answer changes

- Sanity validates schema rules on imports or API writes
- Sanity adds a bulk publish command to the CLI
- Sanity changes the document quotas or the $299 Increased quota add-on
- The Sanity MCP server changes how agents create or publish documents
- Deploi moves app prices off the shared app profiles or passes 1,000 decision pages

## How do you change live pages without breaking them?

See the dry-run scripts, expected counts and rollback steps Deploi uses to change live Sanity content safely.

## Sources

1. [Sanity Docs, Import data](https://www.sanity.io/docs/content-lake/importing-data), checked 24 October 2026
2. [Sanity Docs, Reference](https://www.sanity.io/docs/studio/reference-type), checked 24 October 2026
3. [Sanity Docs, Schema validation and the Content Lake](https://www.sanity.io/docs/content-lake/schema-validation-and-the-content-lake), checked 24 October 2026
4. [Sanity, Plans that scale with you](https://www.sanity.io/pricing), checked 24 October 2026
5. [Sanity Docs, Technical limits](https://www.sanity.io/docs/content-lake/technical-limits), checked 24 October 2026
6. [Sanity Docs, Drafts and versions](https://www.sanity.io/docs/content-lake/drafts-and-versions), checked 24 October 2026
