Campaigns OS agency enablement guide
v0.12.0Updated Oct 1, 2026
Build a NEXT campaign from approved HTML, Figma, a hybrid source, or a maintained starter template. Campaigns OS keeps commerce, source, build, and QA evidence tied to the same campaign.
Begin with Campaigns OS Start: configure products and offers in the NEXT dashboard, map and save the campaign in Workspace, then copy the Handoff for a developer or agent to build, preview, and run QA.
Overview
Campaigns OS is the supported workflow for custom performance campaigns on NEXT. It prepares the build contract, checks inputs, guides each stage, and records proof. It does not decide pricing, rewrite the approved design, or replace Campaigns App configuration.
The workflow has four sources of truth:
1. Campaign data
Campaigns App owns packages, Offers, shipping methods, payment support, API keys, and allowed domains.
2. Campaign intent
Campaign Map Builder exports the CampaignSpec and Map ID for routes, page roles, packages, Offers, and QA identity.
3. Design source
Approved HTML, Figma, or template contributions own composition, imagery, copy, and visual hierarchy.
4. Runtime proof
The Build Packet, sidecars, preview, browser QA, test orders, and Run Record show what was built and tested.
Current supported surface
This guide is written against the Campaigns OS supported surface, not against one pinned release. For the version in force today, read the toolkit CHANGELOG and the surface_version field in contracts/supported-surface.json. Start every build from the Campaigns OS Start page: it provides the instructions an operator or agent follows to pin the toolkit in the campaign folder and install the agent skills. Run npx --no-install campaigns-os tooling status before work. If installed help or npx --no-install campaigns-os next --packet differs from this guide, follow the installed toolkit.
Let the runtime choose the next stage
Do not choose setup, build, polish, deploy, or QA from memory. Run npx --no-install campaigns-os next --packet campaign-runtime.build.json --json. The command reads the packet and its sidecars, reports blocked gates, and returns exact next_actions.
Run npx --no-install campaigns-os tooling status before starting. If the bundled agent Skills are stale, refresh them from the pinned toolkit. This matters because Skills and CLI contracts can change independently of a campaign repo.
- Claude Code:
npx --no-install campaigns-os tooling setup --target . --platform claudeinstalls the skills, the project context, and the QA browser in one step, and keeps existing pages and project instructions. When it reportsrestart_required, restart Claude Code in the campaign folder. - Codex, Cursor, and other agents: run
install-skills --platform codex(or--platform agents),install-agent-context --target ., andqa install-browser, then point the project'sAGENTS.mdor Cursor rules at the matching file in.campaign-runtime/agent-context/.install-skillslists eachSKILL.mdit wrote underRead now. Read those files in the current session: a running agent does not load skills installed after it started. Restart only if the agent cannot read them.
The toolkit ships five lifecycle Skills for Claude and Codex, covering orchestration, setup, build, polish, and QA, plus agent context for Copilot and Cursor. Install them from the pinned toolkit, as the Start page does, rather than pasting workflow steps into a prompt. An agent joining a campaign cold should read the toolkit's AGENTS.md orientation contract first: it names what to read, in what order, and which paths are safe to depend on. It also sets the discipline for the read itself. Resolve one commit OID and read every contract at that OID, so nothing shifts underneath you mid-orientation. Orientation is a read of contracts and schemas, not a toolkit command run to vouch for the toolkit.
Commerce rules for current builds
| Need | Current pattern |
|---|---|
| Product or variant identity | One package for each sellable product or variant. |
| Checkout quantity pricing | Cart quantity plus an automatic Campaign Offer. |
| Upsell, downsell, or exit-pop discount | A code Offer applied through the supported SDK voucher path. |
| Displayed savings and totals | Campaigns and SDK state. Do not calculate or hardcode discount math in the page. |
Package Retail Price and Quantity fields exist for older campaigns. Do not introduce them for a current build unless the target campaign already depends on that compatibility model. Scope Offers to the intended packages and test both qualifying and non-qualifying carts.
Boundaries
- Campaigns OS does not make an incomplete campaign launch ready. Production domains, payment methods, shipping markets, support links, analytics, and legal copy still need owner review.
- A custom design does not authorize custom checkout or payment behavior. Preserve SDK-owned cart, address, payment, submit, upsell, and receipt contracts.
- Manual notes can explain a decision. They do not replace generated JSON evidence, and stage completion is recorded with
campaigns-os record, not by editing sidecars. - A committed QA sidecar records a disposition. It is not automatically a passing result.
Before you start
- A saved CampaignSpec or Map ID with the public route slug.
- Campaigns App commerce data, including package and Offer references, shipping methods, payment support, and the Campaign API key source.
- The approved design source and a clear assembly mode: HTML, full Figma, hybrid, or template led.
- A target Page Kit repo and the public route the preview will serve.
- A Campaign Build Brief for decisions that do not belong in CampaignSpec.
- A deploy and QA plan, including who owns non-local domain authorization and how much test-order coverage is required.
Stop when a missing input would force you to guess a price, package, Offer code, route, payment method, shipping promise, legal URL, or analytics requirement.
Sources of truth and evidence
Campaigns App and CampaignSpec
Campaigns App is live commerce truth. CampaignSpec is approved funnel intent. Fix conflicts before assembly.
Brief and design source
The Campaign Build Brief records presentation decisions. The Design Source Package records source files, page coverage, visual references, gaps, waivers, and freshness.
Runtime envelope
The root Build Packet and the Build Context, Assembly Report, and Doctor Output sidecars bind the campaign to one target and source set.
QA and closeout
The QA verdict projection, bundle check, and Run Record preserve the final result for another operator or CI consumer.
Standard workflow
The toolkit is pinned in the campaign folder as an exact dev dependency and every command is npx --no-install campaigns-os … run from there (see the Start page). --no-install makes sure the pinned copy is the one that runs. Contributors working on a toolkit checkout use npm run campaigns-os -- instead.
Check tooling and start
Check installed tooling, then create the packet from the Map ID or CampaignSpec, prepared source directory, target repo, Build Brief, and the commerce contract family used by runtime pages.
Clear doctor
start prepares the artifacts and runs doctor. Resolve every collect-inputs or setup gate before editing campaign code.
Follow the runtime cursor
Run next --packet and execute its returned actions. Repeat after each stage instead of carrying a hand-written command sequence forward.
Assemble and build
Keep the approved source composition, replace demo commerce data with CampaignSpec or API values, preserve the SDK contract, then run the target repo build and SDK checks. Record setup once the campaign output directory exists, and record the build after every rebuild (see Record each stage).
Polish and capture
Run the dedicated polish pass. Serve the current build and attach package-owned desktop and mobile page-load evidence with polish capture, then record the result with record polish --packet campaign-runtime.build.json --evidence <file>.
Prove locally, then deploy
Serve the development build on localhost and prove it there first (see Prove on localhost first). Then deploy a preview, record its URL in the runtime artifacts, and run qa resolve. Fix a route-root mismatch or unreachable preview before full QA.
Run QA and test orders
Run browser QA and the selected typed-card path against the preview as the second check. Review the verdict rather than counting assertions.
Check the evidence bundle
After QA, run bundle check --require-qa. Commit the canonical JSON sidecars required by the repo's workflow.
npx --no-install campaigns-os tooling status
npx --no-install campaigns-os start \
--map-id <map-id> \
--source <prepared-source-dir> \
--target <page-kit-repo> \
--template-family <commerce-contract-family> \
--brief <campaign-build-brief.yaml>
npx --no-install campaigns-os next \
--packet campaign-runtime.build.json \
--jsonRecord each stage
Record a stage's completion with campaigns-os record. Do not hand-edit .campaign-runtime/build-context.json or .campaign-runtime/assembly-report.json to mark a stage done.
# Setup: once the campaign output directory exists
npx --no-install campaigns-os record setup --packet campaign-runtime.build.json
# Build: after every page-kit build, so the build fingerprint matches _site/
npx --no-install campaigns-os record build --packet campaign-runtime.build.json
# Polish: after polish capture, from a polish evidence file
npx --no-install campaigns-os record polish \
--packet campaign-runtime.build.json \
--evidence <polish-evidence.json>
# Theme: once the brand layer is linked and the build is recorded
npx --no-install campaigns-os record theme --packet campaign-runtime.build.json
# Deploy (local preview): GETs every built page under the served route root
npx --no-install campaigns-os record deploy \
--packet campaign-runtime.build.json \
--base-url http://localhost:4173/
# A changed brief or CampaignSpec after stages are recorded
npx --no-install campaigns-os record brief --packet campaign-runtime.build.json --brief <campaign-build-brief.yaml>
npx --no-install campaigns-os record spec --packet campaign-runtime.build.jsonrecord buildstamps the build fingerprint doctor computes from_site/<slug>/and makes polish required again. Rerun it after every rebuild.record deployis bound to the build it probed. A laterrecord buildof different output makes the deploy required again, so rebuild, record, polish and deploy again before QA. It is refused unless the URL is a loopback route root that answers 2xx for every built page.record briefandrecord specbind a changed brief or CampaignSpec without starting over. An unchanged save keeps every stage; a material change moves the superseded build, Polish and QA records to stage history, andnextnames the stage that is owed again. Re-runningstartorprepare-buildover recorded stages is refused without--force.- The polish evidence file carries the status (
completed,completed_with_warnings,blockedwithblockers, orskippedwithskip_reason), the seven evidence fields, and an optionalrepair_loop_defect. The capturedpage_loadstays package-owned. Acompletedstatus is refused unless the polish gate would pass. The field rules are in the toolkit'sdocs/polish-evidence.md. - Each command validates what it would write and refuses a stage
nexthas not reached yet, listing each problem by field. On any failure it writes nothing. Add--dry-runto run every check without writing.
--template-family selects the protected commerce structure for runtime pages. It does not make that family the visual source when approved HTML or Figma governs the design.
A packet created on another machine needs no catalog path fix: doctor resolves the toolkit's commerce catalog itself. A stale machine-local path is reported as a ready line, never a blocker. To clear it, re-run npx --no-install campaigns-os prepare-build with the same --map-id, --source, --target, and --template-family as start; it refuses to overwrite an assembly report that carries stage evidence unless --force is passed, which sets the recorded stages back to pending (so they are owed again) while keeping the completed build, Polish and QA records in stage history.
Assembly rules
- Approved HTML or Figma owns composition, imagery, copy, and visual hierarchy.
- CampaignSpec and Campaigns App own routes, packages, Offers, shipping, payment support, tracking intent, footer links, and SEO values.
- The selected commerce contract owns SDK attributes and protected checkout, cart, payment, upsell, and receipt behavior.
- Pre-checkout pages need the declared SDK bootstrap when they use SDK conditions, attribution transfer, or analytics.
- Remove template features that the campaign does not support. This includes unused payment methods, Offers, footer links, tracking, and SEO demo values.
- Load a generated brand theme after
next-core.css. A theme may change variables and presentation, but it must not rewrite SDK selectors or runtime structure.
A page whose CampaignSpec design_source is Figma, but whose approved source is hand-written HTML, has no Figma export provenance, so doctor blocks it under source_html.producer_provenance. Write the source-html manifest by hand, set wrapper_policy: preserve_document_wrappers for full-document HTML, and waive the gate for that page only. The waiver needs a named human, a reason, and an expiry (--expires-at) or a review condition; --dry-run checks it without writing.
npx --no-install campaigns-os checkpoint waive \
--packet campaign-runtime.build.json \
--gate source_html.producer_provenance \
--page <page_id> \
--reason "<why the approved source is hand-written HTML>" \
--waived-by "<named human>" \
--review-condition "<what triggers a review>"For hybrid work, record which source owns each page or component. If two sources could plausibly govern the same surface, stop and ask. Do not let template inference displace stronger approved design evidence.
Doctor and QA checks to expect
- Host-prefixed routes. Some saved Maps store a
page_urlsuch asshop.example.com/route/upsell/. Intake from--map-idstrips the host, keeps the rooted path, and records each change asrouting_meta.host_stripped. A local--specfile or a--cached-speccopy is never rewritten: doctor blocks withrouting_meta.host_prefixeduntil you edit the file, or rerun without--cached-specso the Map is fetched again. - Store Profile fields left blank on purpose. An explicit
""in one of the eight optional Store Profile fields (campaign.store_name,store_terms,store_privacy,store_contact,store_returns,store_shipping,store_phone,store_phone_tel) means the merchant has no such value.page-kit syncblanks the starter's demo URL or phone number in that field, and doctor reads the field as intentionally empty. An absent ornullfield still means "not provided".campaign.store_urlstays required. - Decline links outside the offer. Doctor blocks under
built_output.sdk_markup.orphaned_upsell_actionwhen an element withdata-next-upsell-actionhas nodata-next-upsellancestor. The SDK binds upsell actions only inside that container, so a "No thanks" link beside it goes nowhere. Move the element inside its container; the check cannot be waived. - Live campaign refs. When the built
_site/<route>/exists and a public Campaigns API key resolves, doctor andqa runmake one read-only request for the live campaign. A built page that names a shipping method or package the live campaign does not serve blocks (built_output.shipping_ref_live_missing,built_output.package_ref_live_missing). Differences between the CampaignSpec and the live campaign are the separate warningspec.campaign_drift. A read that fails is reported as not run, never as a pass.--no-live-refsskips the read. - Checkout bumps tagged as upsells. A checkout order bump carries
data-next-is-upsell="true". That is intended: it tags the bump's line as an upsell in order reports, so reports show upsell items apart from core items. It is a reporting tag, not a billing change. Leave the attribute in place. To report a bump as a core item instead, passis_upsell: falseto the bump include. Doctor reports nothing either way. - Subscription claims. QA commercial parity warns
commercial_parity.recurring_claim_absent, naming the package, when a page renders a subscription package with no readable recurring price claim, and reports parity as incomplete.
Polish, deploy, and QA
Install the toolkit's Playwright browser once per pin (older pins: npx playwright install chromium). Prove the campaign on localhost first, commit, then use the PR preview as the second check.
npx --no-install campaigns-os qa install-browserProve on localhost first
Set the deploy target
Set deploy.target to local-serve and record the localhost URL you will serve on.
Build the development environment
Run the development build command campaigns-os next prints at the build stage (CPK_ENV=development npx campaign-build --json > .campaign-runtime/page-kit-build-summary.json). campaign-build is the page-kit's own binary, installed with next-campaign-page-kit in the campaign repo. The render goes into _site/.
Serve and prove
Serve _site/ on localhost with any static server. Run polish capture, qa run --browser, and the typed-card orders against it.
Check production parity
Run page-kit parity. It must print PASS; a failure names the first non-gated difference and exits 2. Fix the source, rebuild, and rerun.
Commit, then open the PR
Commit the source and the sidecars. The PR preview is the second check.
npx --no-install campaigns-os qa policy set \
--packet campaign-runtime.build.json \
--deploy-target local-serve \
--preview-url http://localhost:4173/
# page-kit's own binary (next-campaign-page-kit), not a campaigns-os subcommand
CPK_ENV=development npx campaign-build --json \
> .campaign-runtime/page-kit-build-summary.json
npx --no-install campaigns-os record build --packet campaign-runtime.build.json
# serve _site/ on http://localhost:4173/ with any static server, then:
npx --no-install campaigns-os polish capture \
--packet campaign-runtime.build.json \
--base-url http://localhost:4173/
# write the polish evidence file (see Record each stage), then:
npx --no-install campaigns-os record polish \
--packet campaign-runtime.build.json \
--evidence <polish-evidence.json>
npx --no-install campaigns-os record deploy \
--packet campaign-runtime.build.json \
--base-url http://localhost:4173/
npx --no-install campaigns-os qa run \
--packet campaign-runtime.build.json \
--base-url http://localhost:4173/ \
--browser \
--test-order common
npx --no-install campaigns-os page-kit parity \
--packet campaign-runtime.build.json.campaign-runtime/page-kit-build-summary.jsonis a build-local artifact: doctor reads it (built_output.build_summary); it is not part of the canonical bundle andbundle checkdoes not validate it.- A QA run against a localhost base URL keeps its verdict local by default. Pass
--post-verdict, or runqa publishafterwards, to put it on the Map's QA tab. - Localhost on any port is a Development origin: the SDK loads and analytics are suppressed. Non-local previews and production origins still need domain authorization in Campaigns App.
- Never edit a generated include (
analytics-head.html,analytics-body.html, or any_includes/file marked GENERATED) to get a capture to pass. A capture that fails on a cross-originhttp:loader means a production build was served locally: rebuild in development and recapture.
Second check: the PR preview
Deploy the branch, resolve the deployed routes, and run QA against the preview.
npx --no-install campaigns-os qa resolve \
--packet campaign-runtime.build.json \
--base-url https://preview.example.com/campaign/
npx --no-install campaigns-os qa run \
--packet campaign-runtime.build.json \
--base-url https://preview.example.com/campaign/ \
--browser \
--test-order commonqa resolvederives and probes entry routes. A derived route is not proof that the preview serves it.--test-order commonis the default depth, and what it runs depends on the order cap (--max-test-orders, 6 by default). When the checkout topology has no more terminal paths than the cap, it runs all of them. When it has more, it runs checkout, the first offer's accept and decline, and the shortest receipt path, then adds a decline path for each upsell or downsell page not yet declined, up to the cap, and names any page left out. Use targeted, tier, or full coverage only when the handoff calls for it.- Every test-order run with offer pages records a
browser-test-order:upsell-action-coveragerow. It warns, naming the page, when no order clicked an upsell or downsell decline, and the verdict becomesready_with_exceptions. A run that never proves a decline, such as--test-order accept, no longer reads asready. - For a browser-only run, declare it first with
npx --no-install campaigns-os qa policy set --packet campaign-runtime.build.json --order-path-depth off, then run--test-order off.--test-order offalone leaves purchase proof unknown andnextcannot reachdone. - Global test cards bypass the gateway and create no transaction. The order count cap prevents accidental floods; it is not an approval gate.
- Browser QA checks declared checkout Offer surfaces and compares displayed packages and totals with the persisted order.
Bundle closeout and CI
Campaigns OS defines one canonical migration and readback bundle:
campaign-runtime.build.json.campaign-runtime/build-context.json.campaign-runtime/assembly-report.json.campaign-runtime/doctor-output.json.campaign-runtime/qa-verdict.jsonafter QA
# Check the base bundle during the build
npx --no-install campaigns-os bundle check \
--packet campaign-runtime.build.json \
--json
# Require the QA projection at closeout or in post-QA CI
npx --no-install campaigns-os bundle check \
--packet campaign-runtime.build.json \
--require-qa \
--jsonThe checker validates canonical paths, schemas, timestamps, freshness, and identity across the files. It also emits exact file digests and a stable material digest. ok alone is conformance: the sidecars agree with each other and with the contract. A blocked doctor or a blocked QA verdict is reported as a warning, and as an error under --require-qa (exit 2); the text output carries a Readiness: line under Status:. CI must still inspect stage_blocked and the warnings, not the exit code alone.
Do not substitute markdown reports for missing JSON. Do not select a packet or historical verdict by file modification time. If CI carries forward a historical verdict, name that file explicitly with qa promote before running the strict bundle check.
Run Record
Close the run, then emit its record. After run end, a bare run-record re-emits the closed run's record under the same run ID; --new-run starts a new run under a fresh ID; --list prints every record on disk for the packet and writes nothing.
npx --no-install campaigns-os run end --packet campaign-runtime.build.json
# Re-emit the closed run's record (same run ID)
npx --no-install campaigns-os run-record --packet campaign-runtime.build.json
# Inspect every record for this packet
npx --no-install campaigns-os run-record --packet campaign-runtime.build.json --list
# Start a new run under a fresh ID
npx --no-install campaigns-os run-record --packet campaign-runtime.build.json --new-runComplex cases
Partial builds
Map only the pages being built and give every omitted page an explicit skip reason. A presell-only or landing-only preview can pass route and visual checks, but it is not checkout-launch-ready.
Existing or non-Page-Kit campaigns
Run campaigns-os standardize --target <campaign-repo> --json before choosing a repair or migration path. Treat runtime_proof_required as missing browser evidence, not a confirmed defect.
Multiple markets
Confirm the store and Campaigns App can serve every intended currency and country before build. Check package and shipping prices, payment methods, and market-specific claims. QA should cover at least one non-default market when the campaign has one.
Mixed source contributions
Record the assembly mode and source owner for each page or protected component. Use a certified template family only for the surfaces it contributes. Remove unsupported optional surfaces rather than leaving their demo configuration in the build.
Agency handoff
Give the builder one current package of inputs:
- CampaignSpec or Map ID, public route slug, and target repo.
- Campaign Build Brief, approved source files, and page mapping.
- Campaigns App commerce references and domain owner.
- Assembly mode and the contract family for protected runtime surfaces.
- Deploy target, preview URL discovery method, and QA depth.
The handoff should name missing inputs and manual decisions. It should not require the builder to reconstruct them from chat history.
Suggested agent prompt
Use the current Campaigns OS runtime to build or evaluate this NEXT campaign from the supplied CampaignSpec or Map ID, Build Brief, approved design source, target repo, and commerce contract.
Read the toolkit's
AGENTS.mdorientation contract, runcampaigns-os tooling status, install or refresh the lifecycle Skills, then start or reopen the Build Packet. Followcampaigns-os next --packetand its exact actions, and record each stage withcampaigns-os record setup|build|polish|theme|deployinstead of editing sidecars, and save a changed brief or CampaignSpec withrecord brieforrecord specrather than re-runningstart. Preserve SDK-owned commerce behavior. Replace demo commerce, tracking, footer, and SEO values only from approved sources.Prove on localhost first: set the deploy target to
local-serve, build the development environment, serve it, run the dedicated polish pass, package-owned polish capture, and browser QA with the requested typed-card depth, and requirepage-kit parityto pass before commit. Then use preview deployment,qa resolve, and browser QA on the preview as the second check. Finish withbundle check --require-qa. Stop when a missing input would require a guess or an unapproved commerce change.
Definition of done
- The CampaignSpec, Map ID, public route, target directory, and assembly mode agree.
- Campaign commerce and design sources are current, and no demo values remain.
- Doctor, development build, SDK checks, polish capture, production parity, deploy, and route resolution completed.
- Browser QA and the required typed-card paths produced a reviewed verdict.
- The committed QA projection matches that verdict.
bundle check --require-qapasses and CI also accepts the recorded disposition.- The Run Record is closed and remaining launch decisions have named owners.
Resources
Start
The one starting point: the setup line for an empty folder and the prompt for your coding agent.
Campaign Map Builder
Create or reopen the campaign map and export its CampaignSpec.
Campaigns App guide
Configure packages, Offers, shipping, payments, domains, and API access.
Campaigns developer docs
Use the current SDK, Campaign Cart API, and Admin API reference.
Campaigns OS toolkit
Read the supported command, artifact, QA, and sidecar contracts.
Campaigns OS portal
Browse the starter template families, the pattern library, and the tracking setup path.
Agent orientation contract
Start an agent here: what to read, in what order, and which paths it may depend on.
Use the runtime cursor for the next action and the strict bundle check for closeout.
Reporting & attribution guide
Choose the report that matches the question, then keep the date range and attribution fields consistent through export and reconciliation.
Shopify to NEXT theme migration
A translation guide for Shopify Liquid developers moving an existing storefront to Next Commerce. Use it to map theme concepts and protect storefront behavior. Follow the storefront development workflow for setup, implementation, testing, and handoff.