@nextcommerce/campaigns-os 1.60.0 → 1.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/AGENTS.md +2 -1
  2. package/CHANGELOG.md +244 -0
  3. package/README.md +86 -438
  4. package/agents/claude/CLAUDE.md +20 -0
  5. package/agents/codex/AGENTS.md +19 -0
  6. package/agents/copilot/copilot-instructions.md +19 -0
  7. package/agents/cursor/campaigns-os.mdc +21 -0
  8. package/campaign-spec/README.md +7 -0
  9. package/campaign-spec/dist/analytics-vendors.d.ts +40 -0
  10. package/campaign-spec/dist/analytics-vendors.js +44 -0
  11. package/campaign-spec/dist/checkout-flow.d.ts +18 -0
  12. package/campaign-spec/dist/checkout-flow.js +28 -0
  13. package/campaign-spec/dist/index.d.ts +4 -2
  14. package/campaign-spec/dist/index.js +5 -1
  15. package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +8 -0
  16. package/campaign-spec/dist/rules/analytics-contract-shape.js +39 -0
  17. package/campaign-spec/dist/rules/campaign-currencies.d.ts +29 -0
  18. package/campaign-spec/dist/rules/campaign-currencies.js +130 -0
  19. package/campaign-spec/dist/rules/checkout-path-has-packages.d.ts +31 -0
  20. package/campaign-spec/dist/rules/checkout-path-has-packages.js +79 -0
  21. package/campaign-spec/dist/rules/index.d.ts +2 -0
  22. package/campaign-spec/dist/rules/index.js +6 -0
  23. package/campaign-spec/dist/types.d.ts +43 -9
  24. package/compatibility.json +1 -1
  25. package/contracts/brand-theme-target-tokens.next-core.v0.json +95 -25
  26. package/contracts/campaign-cart-sdk-support-policy.v0.json +2 -2
  27. package/contracts/commerce-surface-catalog.json +68 -61
  28. package/contracts/effects.v1.json +549 -165
  29. package/contracts/fixtures/runtime-recipe/accept/current.json +3 -0
  30. package/contracts/fixtures/runtime-recipe/accept/minimal.json +3 -0
  31. package/contracts/fixtures/runtime-recipe/dist-states.json +1 -1
  32. package/contracts/fixtures/runtime-recipe/reject/advisory-enforcement.json +3 -0
  33. package/contracts/fixtures/runtime-recipe/reject/allowlist-without-hosts.json +3 -0
  34. package/contracts/fixtures/runtime-recipe/reject/committed-output-claim.json +3 -0
  35. package/contracts/fixtures/runtime-recipe/reject/engines-disagreement-warns.json +3 -0
  36. package/contracts/fixtures/runtime-recipe/reject/lifecycle-scripts-enabled.json +3 -0
  37. package/contracts/fixtures/runtime-recipe/reject/unknown-kind.json +3 -0
  38. package/contracts/fixtures/runtime-recipe/reject/unknown-network-policy.json +3 -0
  39. package/contracts/fixtures/runtime-recipe/reject/unknown-output-check.json +3 -0
  40. package/contracts/fixtures/runtime-recipe/reject/unknown-revision.json +3 -0
  41. package/contracts/fixtures/runtime-recipe/reject/unknown-step-id.json +3 -0
  42. package/contracts/fixtures/runtime-recipe/reject/unperformable-check-skipped.json +3 -0
  43. package/contracts/fixtures/runtime-recipe/reject/unpinned-lockfile.json +3 -0
  44. package/contracts/release-ledger.json +1767 -0
  45. package/contracts/runtime-recipe.campaigns-os-node-v1.json +3 -0
  46. package/contracts/supported-surface.json +10 -10
  47. package/contracts/template-brand-contract.shared-commerce.v0.json +23 -1
  48. package/docs/access-model.md +20 -0
  49. package/docs/brand-theme-bridge.md +62 -3
  50. package/docs/build-packet.md +456 -36
  51. package/docs/campaign-build-brief.md +16 -0
  52. package/docs/campaign-standardization-report.md +4 -4
  53. package/docs/campaigns-os-build-flow.md +112 -2
  54. package/docs/campaignspec-authoring-examples.md +141 -0
  55. package/docs/design-source-package.md +64 -8
  56. package/docs/effects.md +17 -9
  57. package/docs/entry-points.md +188 -0
  58. package/docs/local-setup.md +1 -1
  59. package/docs/orientation-contract-reference.md +1 -1
  60. package/docs/polish-evidence.md +42 -2
  61. package/docs/progress-snapshots.md +7 -4
  62. package/docs/qa-and-test-orders.md +197 -3
  63. package/docs/quickstart.md +438 -0
  64. package/docs/readback.md +154 -6
  65. package/docs/runtime-readiness.md +6 -3
  66. package/docs/sdk-storage-compatibility.md +4 -2
  67. package/docs/skills-revision.md +10 -10
  68. package/docs/source-adapters.md +213 -0
  69. package/package.json +10 -3
  70. package/schemas/campaign-design-source-package.v0.schema.json +8 -0
  71. package/schemas/campaign-runtime-build-context.v0.schema.json +9 -0
  72. package/schemas/campaign-runtime-build-packet.v0.schema.json +9 -0
  73. package/schemas/campaign-spec.v4.schema.json +19 -3
  74. package/schemas/campaign-spec.v5.schema.json +19 -3
  75. package/schemas/campaigns-os-readback.v2.schema.json +238 -1
  76. package/schemas/source-html-manifest.v0.schema.json +3 -0
  77. package/skills/campaign-lifecycle-orientation/SKILL.md +3 -3
  78. package/skills/campaign-readback-classification/SKILL.md +3 -3
  79. package/skills/campaign-run-evidence/SKILL.md +3 -3
  80. package/skills/contribution-intake/SKILL.md +3 -3
  81. package/skills/next-campaigns-build/SKILL.md +5 -4
  82. package/skills/next-campaigns-os/SKILL.md +7 -4
  83. package/skills/next-campaigns-os-setup/SKILL.md +5 -3
  84. package/skills/next-campaigns-polish/SKILL.md +8 -10
  85. package/skills/next-campaigns-qa/SKILL.md +7 -4
  86. package/skills/next-campaigns-sdk-bump/SKILL.md +151 -25
  87. package/skills.json +11 -11
  88. package/src/brand-theme.mjs +415 -9
  89. package/src/brief-summary.mjs +6 -2
  90. package/src/browser-launch.mjs +2 -1
  91. package/src/build-brief.mjs +13 -3
  92. package/src/built-site-scope.mjs +2 -2
  93. package/src/campaign-workspace.mjs +288 -15
  94. package/src/change-scope.mjs +284 -0
  95. package/src/cli.mjs +358 -97
  96. package/src/commercial-journey.mjs +16 -3
  97. package/src/commercial-parity.mjs +15 -7
  98. package/src/content-residue.mjs +4 -2
  99. package/src/design-file-reference.mjs +93 -0
  100. package/src/design-source-package.mjs +91 -1
  101. package/src/design-source-publication.mjs +9 -6
  102. package/src/directory-lock.mjs +73 -19
  103. package/src/doctor/checks.mjs +87 -81
  104. package/src/doctor/ci.mjs +177 -0
  105. package/src/doctor/inspect.mjs +41 -6
  106. package/src/doctor/next-step.mjs +7 -0
  107. package/src/doctor/source-provenance.mjs +74 -0
  108. package/src/doctor-sidecar.mjs +3 -2
  109. package/src/evidence-debt.mjs +159 -0
  110. package/src/finding-cause.mjs +12 -11
  111. package/src/input-refresh.mjs +339 -32
  112. package/src/invocation.mjs +7 -1
  113. package/src/live-campaign-refs.mjs +7 -2
  114. package/src/local-preview-policy.mjs +35 -2
  115. package/src/page-kit-analytics-vendors.mjs +132 -0
  116. package/src/parity/compare.mjs +316 -0
  117. package/src/parity/design-access.mjs +54 -0
  118. package/src/parity/element-parity-record.mjs +569 -0
  119. package/src/parity/probe.mjs +205 -0
  120. package/src/parity/snapshot.mjs +110 -0
  121. package/src/png-header.mjs +87 -0
  122. package/src/polish-browser.mjs +189 -10
  123. package/src/polish-captures-record.mjs +52 -0
  124. package/src/polish-deadline.mjs +14 -0
  125. package/src/polish-gate.mjs +25 -6
  126. package/src/polish-node.mjs +81 -7
  127. package/src/polish-readability.mjs +221 -9
  128. package/src/polish-visual-fixtures.mjs +74 -0
  129. package/src/polish-visual.mjs +613 -0
  130. package/src/progress-node.mjs +3 -2
  131. package/src/qa-analytics-correctness.mjs +14 -2
  132. package/src/qa-analytics-parity.mjs +111 -6
  133. package/src/qa-browser.mjs +40 -4
  134. package/src/qa-commercial-parity.mjs +50 -3
  135. package/src/qa-node.mjs +235 -22
  136. package/src/qa-publish.mjs +9 -8
  137. package/src/qa-sidecar.mjs +16 -4
  138. package/src/qa-vendor-beacons.mjs +232 -0
  139. package/src/qa-vendor-endpoints.mjs +306 -0
  140. package/src/qc-accept.mjs +176 -6
  141. package/src/qc-check-registry.mjs +42 -2
  142. package/src/qc-results.mjs +477 -42
  143. package/src/readback.mjs +113 -21
  144. package/src/route-identity.mjs +10 -0
  145. package/src/run-record.mjs +18 -10
  146. package/src/runtime-state-ignore.mjs +6 -2
  147. package/src/sdk-attribute-index.mjs +11 -11
  148. package/src/sdk-markup.mjs +77 -2
  149. package/src/sdk-storage-compatibility.mjs +115 -6
  150. package/src/source-html-intake.mjs +15 -1
  151. package/src/source-html-manifest.mjs +7 -0
  152. package/src/source-html-server.mjs +129 -0
  153. package/src/spec-derive-campaign.mjs +216 -0
  154. package/src/spec-derive.mjs +473 -53
  155. package/src/spec-identity.mjs +14 -0
  156. package/src/stage-ledger.mjs +40 -1
  157. package/src/stage-record.mjs +100 -17
  158. package/src/target-lock.mjs +11 -1
  159. package/src/theme-gate.mjs +2 -2
  160. package/src/visual-diff.mjs +296 -0
  161. package/src/visual-reference.mjs +126 -0
@@ -0,0 +1,438 @@
1
+ # Quickstart
2
+
3
+ Read [activation, access, and evidence](activation-and-evidence.md) before
4
+ interpreting an installed toolkit, saved Map, demo, or preview as campaign proof.
5
+
6
+ This path is optimized for a developer using Claude Code or another AI coding tool with a prepared campaign design.
7
+
8
+ For Claude Code, [local campaign setup](local-setup.md) combines the pinned
9
+ dependency install, skills, connected project instructions and QA browser in
10
+ one setup entry. It preserves authored source and project instructions. The
11
+ detailed/manual paths below remain supported.
12
+
13
+ ## Install without a clone
14
+
15
+ Requirements: Node `>=20.19.0` and npm 10 or 11 (Node 22 ships npm 10). The
16
+ primary way to run Campaigns OS is pinned as a devDependency of the campaign
17
+ folder — a page-kit project — and run through `npx --no-install campaigns-os …`
18
+ from that folder. Nothing is cloned, nothing goes on PATH, and the pin is
19
+ committed in `package.json`, so CI and the deploy host install the same package
20
+ bytes. `--no-install` is part of the command: `campaigns-os` is only the bin
21
+ name of `@nextcommerce/campaigns-os`, so in a folder without the pinned copy a
22
+ plain `npx campaigns-os` looks that name up on the registry and, with no
23
+ terminal to ask, installs what it finds. With the flag, npx fails instead.
24
+
25
+ Do the steps in this order:
26
+
27
+ 1. **Orient.** Read [`AGENTS.md`](../AGENTS.md), `contracts/supported-surface.json`,
28
+ `contracts/release-ledger.json` and `CHANGELOG.md` on GitHub at one commit.
29
+ Follow its canonical reading order. That is a read of declarative data,
30
+ not a run of toolkit code. Keep the commit sha.
31
+ 2. **Pin the toolkit, preflight, and install skills** at the exact reviewed release version.
32
+ 3. **Start** at the exact reviewed release version.
33
+
34
+ New campaign folder:
35
+
36
+ ```bash
37
+ mkdir "<route>" && cd "<route>"
38
+ npm init -y && npm i --save-exact next-campaign-page-kit@0.3.0
39
+ npx campaign-init --non-interactive --template <family> --slug "<route>" --name "<campaign name>"
40
+ ```
41
+
42
+ Existing page-kit campaign: `cd` into it (its `package.json` declares
43
+ `next-campaign-page-kit`). Then, in the campaign folder:
44
+
45
+ ```bash
46
+ npm install --save-dev --save-exact @nextcommerce/campaigns-os@<version>
47
+ npx --no-install campaigns-os tooling status --platform claude
48
+ ```
49
+
50
+ `<version>` is the exact release you reviewed. The newest published release is
51
+ npm's `latest` (`npm view @nextcommerce/campaigns-os version`), and
52
+ `contracts/release-ledger.json` and `CHANGELOG.md` list every release. Verify
53
+ its tag/provenance against the source commit; do not use a floating dist-tag. Commit both `package.json` and `package-lock.json`. An unreleased
54
+ reviewed commit may instead be pinned with
55
+ `npm install --save-dev --save-exact "github:NextCommerceCo/campaigns-os#<full-sha>"`.
56
+ The package install runs its own lifecycle build; it is separate from the
57
+ checkout-only runtime preparation recipe and makes no claim under that recipe.
58
+ Diagnostics and corrected global invocation rendering require 1.35.0 or later;
59
+ `demo` requires 1.37.0 or later.
60
+
61
+ For an optional visual walkthrough,
62
+ `npx --no-install campaigns-os demo --target ./apollo-sample`
63
+ writes an offline sample. Open the printed local
64
+ `landing/index.html` file. This offline sample has no live commerce or campaign
65
+ proof. Preserve sample edits and create a separate new Page Kit folder for the
66
+ real campaign. See [offline sample preview](demo-preview.md).
67
+
68
+ Global use is also supported, with an exact release:
69
+
70
+ ```bash
71
+ npm install -g @nextcommerce/campaigns-os@<version>
72
+ campaigns-os tooling status --platform claude
73
+ campaigns-os install-skills --platform claude
74
+ ```
75
+
76
+ Global Git-source installation is not the supported path: npm's nested build
77
+ can inherit global mode. Global registry installations ship without Chromium;
78
+ run `campaigns-os qa install-browser` once. If optional Playwright was omitted,
79
+ ordinary commands still work and browser commands explain the missing package.
80
+ When another install shadows the global binary, status prints an explicit
81
+ invocation of the inspected copy. In a campaign folder,
82
+ `npx --no-install campaigns-os` selects the project-local dependency ahead of
83
+ the global binary on PATH.
84
+
85
+ `tooling status` is the preflight for "am I current?". It names the install
86
+ mode and checks package identity, CLI entrypoint, and installed Campaigns OS
87
+ skills. It also reports local gateway login metadata across saved store bindings,
88
+ without a `--store` flag or a remote validity check. No credential values are
89
+ shown; unavailable storage is reported without guessing that the user is logged
90
+ out. See [gateway login](gateway-login.md).
91
+
92
+ - From a campaign folder it reports `Install mode: package install
93
+ (node_modules), pinned at <version> @ <sha>`; git freshness is
94
+ `not_applicable` because the pinned commit is the freshness answer, and
95
+ there is no npm dist-tag to compare against. An npm release reports its version even when a source commit is not derivable.
96
+ Commands are spelled `npx --no-install campaigns-os <command>`; if a
97
+ different install of the toolkit is on PATH, it says so and points you back
98
+ to `npx`.
99
+ - From a git checkout it reports `Install mode: git checkout at <path>` plus
100
+ branch, upstream, ahead/behind, and whether the tree is dirty.
101
+
102
+ Exit code 2 from `tooling status` means attention is required — on a fresh
103
+ profile that is the not-yet-installed skills — and the output prints the exact
104
+ refresh command for the mode you ran it in. Pass the same `--platform` to
105
+ status that you install skills for; without it, status checks every agent
106
+ profile and stays at exit 2 until each is installed.
107
+
108
+ Everything the toolkit prints for you to run (`next`, gate remediations, the
109
+ browser-missing hints) is spelled for the install it came from: `npx
110
+ --no-install campaigns-os …` from a campaign folder (releases before 1.41.2
111
+ print it without `--no-install`; add the flag when you copy one), bare
112
+ `campaigns-os …` from a global installation whose binary matches PATH, and the
113
+ documented checkout translation below.
114
+
115
+ ## Contributor / local checkout
116
+
117
+ To change the toolkit, run `npm run check`, or work against an unpushed commit,
118
+ use a checkout:
119
+
120
+ ```bash
121
+ git clone https://github.com/NextCommerceCo/campaigns-os.git
122
+ cd campaigns-os
123
+ npm install
124
+ npm run campaigns-os -- --help
125
+ npm run campaigns-os -- tooling status
126
+ ```
127
+
128
+ Every `npm run campaigns-os -- <command>` example in this repository is the
129
+ checkout form. From a campaign folder the same command is `npx --no-install
130
+ campaigns-os <command>`; the arguments are identical. `npm run
131
+ qa:install-browser`, a checkout script, is `npx --no-install campaigns-os qa
132
+ install-browser` from a campaign folder (`npx playwright install chromium` is
133
+ equivalent only when it resolves to the toolkit's own Playwright; prefer
134
+ `qa install-browser` on pins that have it). A fresh `git pull`
135
+ does not refresh copied agent skills in either mode; `tooling status` tells
136
+ you when they are stale.
137
+
138
+ ## Install Skills
139
+
140
+ After installing or updating the CLI, refresh the Campaigns OS skills in Claude Code:
141
+
142
+ ```bash
143
+ npx --no-install campaigns-os install-skills --platform claude
144
+ ```
145
+
146
+ This syncs bundled `skills/*` directories from the installed package into `~/.claude/skills/<skill-name>/` (`--platform codex` writes `~/.codex/skills`), replacing same-name folders, and reports which skills were created, updated, or unchanged. It lists each `SKILL.md` it wrote under `Read now`: read those files now in the same session, because a running agent does not load skills installed after it started (a new session loads them on its own). Preview changes without writing files:
147
+
148
+ ```bash
149
+ npx --no-install campaigns-os install-skills --dry-run
150
+ ```
151
+
152
+ Use `--platform` for other local agent profiles:
153
+
154
+ ```bash
155
+ npx --no-install campaigns-os install-skills --platform codex
156
+ npx --no-install campaigns-os install-skills --platform agents
157
+ npx --no-install campaigns-os install-skills --platform all --dry-run
158
+ ```
159
+
160
+ If `tooling status` reports stale skills, run the refresh command it prints —
161
+ it names each stale platform, through the same prefix — and read the
162
+ `SKILL.md` files it lists under `Read now` in the running session so the new
163
+ instructions are loaded (new sessions load them on their own). The build after `start` is
164
+ agent-driven — `next` names the skill for each stage — so install skills before
165
+ the first `start`.
166
+
167
+ From a local checkout, `./skills.sh status` previews every known local target
168
+ and `./skills.sh install codex` refreshes Codex skills. Use `--target <dir>` to
169
+ sync into another skills directory for testing or a managed profile.
170
+
171
+ ## Inputs
172
+
173
+ You need:
174
+
175
+ - A Campaigns App campaign with shipping methods, payment methods, an API key, and Offers for its price tiers. The Offers bring in the campaign's products (packages in the API); see the setup note below.
176
+ - CampaignSpec JSON exported from a saved Map or authored by the coding agent through the [local-spec entry](build-packet.md#local-spec-entry), including `campaign.store_url` for page-kit `campaigns.json`. Local-spec identity requires a reviewed 1.43.0-or-later release.
177
+ - Prepared HTML/CSS/assets for the campaign pages.
178
+ - A target `next-campaign-page-kit` repo or local directory.
179
+ - A starter template family decision, usually `olympus` unless `demeter` or `shop-single-step` better matches the campaign shape.
180
+
181
+ If the checkout should have an exit-intent offer or typed promo-code box,
182
+ configure it in Campaign Map Builder before export, or declare it in the local
183
+ spec using verified campaign Offer values, so the checkout page carries
184
+ the mapped `exit_intent.offer_ref_id` / `exit_intent.offer_code` or
185
+ `promo_code_input.offer_ref_id` / `promo_code_input.offer_code`.
186
+
187
+ Campaigns API keys are public, browser-side, domain-allowlisted keys. If your CampaignSpec includes `campaign.campaigns_api_key`, `doctor` uses it directly and does not require a `CAMPAIGNS_API_KEY` shell env var.
188
+
189
+ The Store Profile is campaign metadata entered by the operator or derived from the store with `spec derive --from-store` (see "Create The Packet" below), not Campaigns API data. Before the target is scaffolded, `doctor` requires only `campaign.store_url`; `store_name`, `store_terms`, `store_privacy`, `store_contact`, `store_returns`, `store_shipping`, `store_phone`, and `store_phone_tel` are optional storefront/legal metadata used by templates when present. Once the target's `campaigns.json` entry exists, `page_kit.store_profile` checks every one of those fields the CampaignSpec provides against the target: a field the spec carries that the target lacks, or carries with a different value, blocks (the spec is the authority; run `npx --no-install campaigns-os page-kit sync --packet <campaign-runtime.build.json>` to write the spec's values into the target entry, or fix the spec, then re-run `doctor`), a field present only in the target warns as `target_only` — unless the value is starter demo residue (a placeholder storefront URL or phone number), which blocks as `demo_residue` whatever the spec says — and a field absent from both is clean. An optional field (any of the eight besides `store_url`) the spec sets to `""` says the merchant has none: `page-kit sync` blanks a recognised starter demo value (the placeholder storefront URLs and phone number; the starter's demo store name is not recognised and stays a `target_only` warning) and doctor reads the blank field as `intentionally_empty`, while a real, non-demo target value is left as it is (still a `target_only` warning, and sync reports the `""` as not applied; remove the value by hand if the merchant has none); an absent or null field still means "not provided". `campaign.store_url` stays required: `store_url: ""` still fails doctor's `spec.store_profile` check. So a spec that fills all nine fields makes all nine required after scaffold. A discrepancy the spec cannot yet resolve can be recorded with `campaigns-os checkpoint waive --packet <campaign-runtime.build.json> --gate page_kit.store_profile --reason "<why>" --waived-by "<named human>" --review-condition "<trigger>"` (or `--expires-at <ISO timestamp>` instead of the review condition; see [docs/build-packet.md](./build-packet.md), "Page Kit Store Profile checkpoint").
190
+
191
+ In the Campaigns App dashboard ([Campaigns App docs](https://docs.nextcommerce.com/docs/apps/campaigns-app), which name these screens and tabs), setup takes two steps: **Create Campaign** (name, currency, shipping, payments), then **Add Offer** on the campaign's **Offers** tab. Picking store products in the Offer's condition adds each selected variant to the campaign as one product at its store price when the Offer is saved, and later Offers reuse it. Pages reference those products by package ID, which the campaign's **Products** tab shows on each row. Offers set the customer's final price: Buy 1, Buy 2 and Buy 3 are separate Offers over the same products. Do not create separate `1x` / `2x` / `3x` products to express tier pricing, and do not rely on Retail Price/Quantity fields unless the campaign already uses that legacy setting (**Enable Package Retail Price & Quantity**). A campaign set up over the Admin API still creates its packages first and points Offers at their package IDs.
192
+
193
+ For synthetic or AI-generated evaluation campaigns, create or reuse a known test
194
+ store/API key before checkout work. A purely static source page can validate the
195
+ landing-page assembly path, but SDK checkout, package, shipping, payment, and
196
+ receipt surfaces need Campaigns App data; without it, checkout can remain in a
197
+ loading state. Record the test store/key choice in the Build Packet or
198
+ CampaignSpec so QA knows whether runtime checkout proof is expected or blocked.
199
+
200
+ For partial campaign work, map only the pages being built with
201
+ `source_html.pages[].path` and give intentionally untouched CampaignSpec pages a
202
+ clear `skip_reason`. Doctor will surface those pages under `derived.scope`,
203
+ label mapped routes as previewable, and keep checkout launch/test-order proof
204
+ blocked when runtime pages are out of scope.
205
+
206
+ `source_html.pages[].path` is the source/provenance path. Use
207
+ `source_html.pages[].page_kit` for the Page Kit target file, public route, CPK
208
+ `page_type`, and frontmatter projection. This matters for mixed sources where a
209
+ producer path such as `checkout/index.html` should still target
210
+ `src/<slug>/checkout.html` unless the CampaignSpec route requires a permalink.
211
+
212
+ ## Prepare Raw HTML Source
213
+
214
+ **Standalone HTML mockups: keep them whole, set
215
+ `wrapper_policy: preserve_document_wrappers`.** Put it in the source-html
216
+ manifest at `<source>/.campaigns-os/source-html-manifest.json` (or pass
217
+ `--wrapper-policy preserve_document_wrappers` to `start` / `prepare-build`).
218
+ Source screenshot proof must be of the standalone document, so a page kept whole
219
+ needs no conversion, and doctor reports its document wrappers as a warning that
220
+ names the decision. For pages without a Figma `design_source`, no exporter is
221
+ required: for hand-written HTML, write the manifest yourself. When any active
222
+ page's `design_source` is Figma, the manifest must pass the Figma provenance
223
+ gate (`source_html.producer_provenance`), which needs the exporter's handoff
224
+ manifest. A minimal one (schema:
225
+ `schemas/source-html-manifest.v0.schema.json`):
226
+
227
+ ```json
228
+ {
229
+ "schema_version": "source-html-manifest/v0",
230
+ "wrapper_policy": "preserve_document_wrappers",
231
+ "pages": [{ "page_id": "landing", "path": "landing.html" }]
232
+ }
233
+ ```
234
+
235
+ Decide this before capturing screenshots or computing `source_hash`; the rest
236
+ of this section is the conversion for source that is not kept whole.
237
+
238
+ Otherwise, `html_funnel` source files should be page-kit-ready source, not full browser
239
+ documents copied verbatim from an AI tool. This is not a wholesale Liquid
240
+ rewrite. Use Liquid only for page-kit helpers such as `campaign_link`,
241
+ `campaign_asset`, and `campaign_include`.
242
+
243
+ The conversion process used in prior builds is:
244
+
245
+ - Strip document-level wrappers: `<!doctype>`, `<html>`, `<head>`, and `<body>`.
246
+ - Add page frontmatter for title, layout, route/meta values, and any source mapping notes.
247
+ - Move shared CSS into the campaign asset tree or an include when it is reused; inline page-specific CSS only when the target page-kit style allows it.
248
+ - Move local images/fonts/assets into the campaign asset tree and root paths with `campaign_asset` when needed. Page Kit publishes `src/<slug>/assets/config.js` as `/<slug>/config.js` and `src/<slug>/assets/products/foo.png` as `/<slug>/products/foo.png`; raw `/assets/...` paths from AI/exported HTML are source paths, not built campaign URLs.
249
+ - Check `context.source.asset_crawl` in `.campaign-runtime/build-context.json` before rewriting assets. `prepare-build` crawls source HTML plus referenced local CSS, records raw refs, resolved source files, missing/out-of-root assets, and `pagekit_asset_path` hints so assembly does not have to rediscover `/assets/...` path behavior page by page.
250
+ - Replace internal links/CTAs with CampaignSpec routes, usually via `campaign_link`.
251
+ - Keep source landing/presell composition and copy intact when it is a real design.
252
+ - Use starter-template SDK contracts for checkout, upsell, downsell, receipt, payment, totals, and submit controls.
253
+ - Run page-kit build and inspect `_site/<slug>/` before handing off to polish.
254
+
255
+ Doctor checks this preparation deterministically: unstripped document wrappers
256
+ and leftover/broken frontmatter block with `source_html.prep.*` error codes,
257
+ and source-file internal links warn. See the "Source preparation check" section
258
+ in [docs/source-adapters.md](./source-adapters.md) for each code and its fix.
259
+ If the source is meant to stay a full browser document (raw HTML handed over
260
+ as-is, or pages whose `source_screenshot` proof must be of the standalone
261
+ document), do not strip it: record the decision instead with
262
+ `--wrapper-policy preserve_document_wrappers` on `start` / `prepare-build`, or
263
+ `wrapper_policy` in the source-html manifest, and the wrapper finding becomes a
264
+ warning that names the decision. Choose before capturing screenshots or
265
+ computing `source_hash`; the "Selecting the wrapper policy at intake" section of
266
+ [docs/source-adapters.md](./source-adapters.md) gives the order of operations.
267
+
268
+ ## Create The Packet
269
+
270
+ > **Heads up — `start` turns on run telemetry, and remit is ON by default.**
271
+ > `start` (and `prepare-build`) opens an ambient Run Session in the target repo.
272
+ > When that session's Run Record is assembled — by a ready `qa run`, by
273
+ > `campaigns-os run end`, or by the automatic closeout of a stale session — it is
274
+ > **remitted to the canonical Next Commerce endpoint,
275
+ > `https://campaign-map.nextcommerce.com`**, without asking again.
276
+ >
277
+ > What is sent: the Run Record (the run's own system signal and workflow
278
+ > findings — stage timings, findings, tool/environment shape), plus the packet's
279
+ > Campaigns API key in the `X-Campaign-Key` header so the record lands in your
280
+ > tenant scope and you can read it back with `campaigns-os telemetry list
281
+ > --packet <json>`. Campaigns API keys are public, browser-side,
282
+ > domain-allowlisted keys by design, so this is attribution, not a secret.
283
+ >
284
+ > Why: those run records are what the toolkit learns from — they are how
285
+ > templates, gates, and guidance get fixed.
286
+ >
287
+ > Three ways out, any of which is enough:
288
+ >
289
+ > - `npx --no-install campaigns-os telemetry off` — machine-level, sticks.
290
+ > - `CAMPAIGNS_OS_TELEMETRY=off` — per shell or per CI job.
291
+ > - `--no-remit` on the remitting command (`qa run`, `run-record`, `run end`).
292
+ >
293
+ > Consent gates remit only. Capture is always local, so opting out costs you
294
+ > nothing locally: sessions, journals and Run Records are still written and
295
+ > the Run Record's `remit_state` reads `skipped`. Turning consent off at the
296
+ > machine or environment level (`telemetry off`, `CAMPAIGNS_OS_TELEMETRY=off`)
297
+ > also makes saved-Map `qa run` default to a local-only verdict unless the campaign is
298
+ > portal-managed or `--post-verdict` is passed; `--no-remit` does not — it
299
+ > skips this command's remit only, and the verdict still publishes. Whatever
300
+ > the consent, a run whose base URL is a local address (`localhost` or any
301
+ > `*.localhost` name, any `127.x.x.x`, `0.0.0.0`, `[::1]` or an IPv4-mapped
302
+ > loopback) keeps its verdict local by default; pass
303
+ > `--post-verdict`, or run `qa publish` afterwards, to send it. To keep
304
+ > one saved-Map verdict local, pass `--no-post-verdict` (or `--local-only`) to `qa run`.
305
+ > A `local_spec_id` packet always keeps its QA verdict and progress local,
306
+ > including with `--post-verdict`; `qa publish` refuses it. Run Record remit
307
+ > still follows the consent controls above.
308
+ > `--no-run-session` on `start` skips opening the session altogether. Full
309
+ > contract: [Run Telemetry](./workflow-findings-sidecar.md).
310
+
311
+ ```bash
312
+ mkdir -p source
313
+ npx --no-install campaigns-os start --map-id <map-id> --target . --source ./source --template-family <family>
314
+ ```
315
+
316
+ `--map-id <id>` starts from a map saved in Campaign Map Builder (add
317
+ `--proxy-base <origin>` when the map was saved on a non-production map store);
318
+ `--spec <campaignspec.json>` starts from a local saved-Map export or an
319
+ agent-authored local CampaignSpec instead. One of the two is required.
320
+ `--target .` is the campaign folder, the
321
+ page-kit project that pins the toolkit; with `--spec` it must already be a
322
+ directory (`Target repo is not a directory` otherwise). `--source` is always
323
+ required: the folder of prepared HTML/CSS/assets for the pages you are
324
+ building, with a source manifest carrying desktop and mobile screenshot proof
325
+ for each designed page — see [Design Source Package](./design-source-package.md).
326
+ It must exist even when every page is template stock; those pages are declared
327
+ out of source scope (manifest `skip_reason`, or CampaignSpec
328
+ `build_scope.mode: "partial"`), never left out of `--source`.
329
+
330
+ `start` creates the packet, context, report, doctor output, and target-repo agent context. It does not edit campaign pages, deploy, run QA, or place test orders.
331
+
332
+ `start` finishes by running doctor, and on a fresh target doctor's first verdict
333
+ is normally `BLOCKED` with a list of what to supply — missing screenshot proof,
334
+ demo values to replace, a scaffold to run. That list is the intake checklist,
335
+ not a failed install; work through it and re-run. The demo values are the
336
+ store profile and SDK pin `campaign-init` seeded into `_data/campaigns.json`
337
+ after a fresh scaffold; doctor's required actions print the command that
338
+ replaces them from the CampaignSpec — `npx --no-install campaigns-os page-kit
339
+ sync --packet campaign-runtime.build.json` (`--dry-run` to see the diff first)
340
+ — and after it `page_kit.store_profile` and `page_kit.sdk_version` pass without a waiver.
341
+
342
+ The other direction exists too. Once the campaign is configured, the repo is
343
+ the authority for the SDK pin, the page routes and the analytics ids, and
344
+ `npx --no-install campaigns-os spec derive --packet campaign-runtime.build.json` writes
345
+ those into the local CampaignSpec with a field-by-field diff (`--dry-run`
346
+ first). After a bump in `_data/campaigns.json`, that is the one command that
347
+ brings the spec back in line; doctor's `page_kit.sdk_version.repo_newer`
348
+ warning names it. Add `--write-map` and the pin is also recorded in the saved
349
+ Map's Build hints field, so the Map and its next export stop reading stale.
350
+ The store profile (`campaign.store_*`) comes from the store
351
+ itself: the 1.38.0 candidate uses gateway login for `--from-store <subdomain>`
352
+ within the admitted owned-store private pilot. Existing direct callers must add
353
+ `--store-token-source env:<VAR>` explicitly; there is no implicit environment
354
+ fallback. See [gateway login and migration](gateway-login.md). A successful read
355
+ writes the available store name,
356
+ primary domain, phone and policy-page URLs into the spec too, for `page-kit
357
+ sync` to carry into the repo.
358
+
359
+ It also runs brand-theme discovery in inspect-only mode. When source tokens are
360
+ available, the build context records `context.theme` and the target repo gets
361
+ `.campaign-runtime/theme/theme-report.json`. It does not write
362
+ `brand-theme.css` by default.
363
+
364
+ To inspect or generate the optional commerce-page brand bridge:
365
+
366
+ ```bash
367
+ npx --no-install campaigns-os theme inspect --packet ./campaign-runtime.build.json --json
368
+ npx --no-install campaigns-os theme generate --packet ./campaign-runtime.build.json --json
369
+ ```
370
+
371
+ Use `--theme-policy auto` on `start` / `prepare-build` only when you want
372
+ Campaigns OS to write `brand-theme.css` automatically from high-confidence
373
+ source tokens. Generated CSS is root-variable-only and must be loaded after
374
+ `next-core.css` on checkout, upsell, downsell, and receipt pages.
375
+
376
+ ## Continue In Your AI Tool
377
+
378
+ Run `next` after `start` and after every stage, and do what it prints — the
379
+ commands are already spelled for this install:
380
+
381
+ ```bash
382
+ npx --no-install campaigns-os next --packet ./campaign-runtime.build.json --json
383
+ ```
384
+
385
+ When it names the setup stage, that is:
386
+
387
+ ```bash
388
+ npx --no-install campaigns-os next setup --packet ./campaign-runtime.build.json
389
+ ```
390
+
391
+ If doctor says setup is not required, run:
392
+
393
+ ```bash
394
+ npx --no-install campaigns-os next build --packet ./campaign-runtime.build.json
395
+ ```
396
+
397
+ Paste the generated handoff into your AI tool. From here the build is
398
+ agent-driven: each `next` names the Campaigns OS skill for the stage
399
+ (`next-campaigns-os-setup`, `next-campaigns-build`, `next-campaigns-polish`,
400
+ `next-campaigns-qa`) and the exact commands, which is why the skills are
401
+ installed before the first `start`.
402
+
403
+ ## Gates
404
+
405
+ Build is not launch readiness. A complete run still needs:
406
+
407
+ - page-kit build
408
+ - starter-template/SDK lint from the target repo, for example `npm run lint:sdk`,
409
+ `npm run lint:sdk:promoted`, or `npm run lint:sdk:ci` when those scripts are
410
+ available. There is no separate `campaign-lint` package in the current flow.
411
+ - Campaigns OS Playwright browser install
412
+ - formal polish pass against the served current build
413
+ - mandatory package-owned `polish capture` evidence before Polish becomes
414
+ terminal or deploy/QA begins
415
+ - preview deploy, or a local serve of the built `_site/` output with the packet's
416
+ `deploy.target` set to `local-serve` (`qa policy set --deploy-target local-serve
417
+ --preview-url http://localhost:<port>/<slug>/`); localhost on any port is a
418
+ Development domain, so no SDK origin allowlist entry is needed there
419
+ - Node/npm QA with a saved Map ID or local-spec Build Packet, and the tested URL
420
+ - typed-card test-order proof via `--test-order common` (global test cards bypass the gateway and move no money, but each leaves a real store order record; unless the operator has already said test orders are fine for this campaign, ask once, up front, before placing test orders, then proceed; there is no permission flag)
421
+
422
+ ```bash
423
+ npx --no-install campaigns-os qa install-browser
424
+ npx --no-install campaigns-os polish capture --packet ./campaign-runtime.build.json --base-url <served-current-build-url>
425
+ npx --no-install campaigns-os qa resolve --packet ./campaign-runtime.build.json
426
+ npx --no-install campaigns-os qa run --packet ./campaign-runtime.build.json --base-url https://preview.example.com/campaign/ --browser --test-order common
427
+ ```
428
+
429
+ `npx --no-install campaigns-os qa install-browser` (`npm run qa:install-browser` from a checkout; `npx playwright install chromium` only when it resolves to the toolkit's Playwright)
430
+ is a one-time local setup step after install/update. It installs the Chromium
431
+ binary used by package-owned polish capture and QA.
432
+ Run it before `polish capture`, `--browser`, or `--test-order`; the CLI will tell
433
+ you to run it if the browser binary is missing. The capture producer must point
434
+ at the served current build and complete before Polish hands off to deploy/QA.
435
+
436
+ After the browser is installed, `npm run smoke:polish-capture` is an optional
437
+ real-Chromium package smoke. It needs permission to open a loopback HTTP
438
+ listener and is deliberately excluded from `npm run check` and CI.