@nextcommerce/campaigns-os 1.33.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 (310) hide show
  1. package/AGENTS.md +204 -0
  2. package/CHANGELOG.md +5002 -0
  3. package/CONTEXT.md +685 -0
  4. package/LICENSE +202 -0
  5. package/NOTICE +4 -0
  6. package/README.md +368 -0
  7. package/agents/claude/CLAUDE.md +32 -0
  8. package/agents/codex/AGENTS.md +27 -0
  9. package/agents/copilot/copilot-instructions.md +14 -0
  10. package/agents/cursor/campaigns-os.mdc +13 -0
  11. package/bin/campaigns-os.mjs +38 -0
  12. package/campaign-spec/README.md +138 -0
  13. package/campaign-spec/dist/analytics-vocabulary.d.ts +47 -0
  14. package/campaign-spec/dist/analytics-vocabulary.js +74 -0
  15. package/campaign-spec/dist/index.d.ts +40 -0
  16. package/campaign-spec/dist/index.js +77 -0
  17. package/campaign-spec/dist/normalize.d.ts +22 -0
  18. package/campaign-spec/dist/normalize.js +41 -0
  19. package/campaign-spec/dist/routing.d.ts +190 -0
  20. package/campaign-spec/dist/routing.js +263 -0
  21. package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +42 -0
  22. package/campaign-spec/dist/rules/analytics-contract-shape.js +303 -0
  23. package/campaign-spec/dist/rules/assembly-hints-shape.d.ts +42 -0
  24. package/campaign-spec/dist/rules/assembly-hints-shape.js +191 -0
  25. package/campaign-spec/dist/rules/campaign-metadata.d.ts +14 -0
  26. package/campaign-spec/dist/rules/campaign-metadata.js +40 -0
  27. package/campaign-spec/dist/rules/checkout-has-success-url.d.ts +31 -0
  28. package/campaign-spec/dist/rules/checkout-has-success-url.js +66 -0
  29. package/campaign-spec/dist/rules/cycle-detection.d.ts +12 -0
  30. package/campaign-spec/dist/rules/cycle-detection.js +141 -0
  31. package/campaign-spec/dist/rules/design-source-shape.d.ts +29 -0
  32. package/campaign-spec/dist/rules/design-source-shape.js +142 -0
  33. package/campaign-spec/dist/rules/downsell-without-upsell.d.ts +11 -0
  34. package/campaign-spec/dist/rules/downsell-without-upsell.js +40 -0
  35. package/campaign-spec/dist/rules/exit-intent-validation.d.ts +23 -0
  36. package/campaign-spec/dist/rules/exit-intent-validation.js +147 -0
  37. package/campaign-spec/dist/rules/funnel-count.d.ts +9 -0
  38. package/campaign-spec/dist/rules/funnel-count.js +37 -0
  39. package/campaign-spec/dist/rules/funnel-hypothesis-length.d.ts +22 -0
  40. package/campaign-spec/dist/rules/funnel-hypothesis-length.js +60 -0
  41. package/campaign-spec/dist/rules/funnel-identity.d.ts +15 -0
  42. package/campaign-spec/dist/rules/funnel-identity.js +57 -0
  43. package/campaign-spec/dist/rules/funnel-weight-sum.d.ts +20 -0
  44. package/campaign-spec/dist/rules/funnel-weight-sum.js +66 -0
  45. package/campaign-spec/dist/rules/index.d.ts +69 -0
  46. package/campaign-spec/dist/rules/index.js +131 -0
  47. package/campaign-spec/dist/rules/offer-ref-integrity.d.ts +13 -0
  48. package/campaign-spec/dist/rules/offer-ref-integrity.js +59 -0
  49. package/campaign-spec/dist/rules/package-pricing-sanity.d.ts +12 -0
  50. package/campaign-spec/dist/rules/package-pricing-sanity.js +42 -0
  51. package/campaign-spec/dist/rules/page-count.d.ts +11 -0
  52. package/campaign-spec/dist/rules/page-count.js +31 -0
  53. package/campaign-spec/dist/rules/page-id-uniqueness.d.ts +14 -0
  54. package/campaign-spec/dist/rules/page-id-uniqueness.js +47 -0
  55. package/campaign-spec/dist/rules/promo-code-input-validation.d.ts +8 -0
  56. package/campaign-spec/dist/rules/promo-code-input-validation.js +126 -0
  57. package/campaign-spec/dist/rules/promo-codes-shape.d.ts +30 -0
  58. package/campaign-spec/dist/rules/promo-codes-shape.js +187 -0
  59. package/campaign-spec/dist/rules/route-field-ignored-for-page-type.d.ts +33 -0
  60. package/campaign-spec/dist/rules/route-field-ignored-for-page-type.js +81 -0
  61. package/campaign-spec/dist/rules/route-target-resolves.d.ts +32 -0
  62. package/campaign-spec/dist/rules/route-target-resolves.js +112 -0
  63. package/campaign-spec/dist/rules/schema-version.d.ts +21 -0
  64. package/campaign-spec/dist/rules/schema-version.js +53 -0
  65. package/campaign-spec/dist/rules/sdk-version.d.ts +26 -0
  66. package/campaign-spec/dist/rules/sdk-version.js +96 -0
  67. package/campaign-spec/dist/rules/shipping-countries-shape.d.ts +10 -0
  68. package/campaign-spec/dist/rules/shipping-countries-shape.js +30 -0
  69. package/campaign-spec/dist/rules/shipping-methods-present.d.ts +10 -0
  70. package/campaign-spec/dist/rules/shipping-methods-present.js +26 -0
  71. package/campaign-spec/dist/rules/store-profile-shape.d.ts +30 -0
  72. package/campaign-spec/dist/rules/store-profile-shape.js +127 -0
  73. package/campaign-spec/dist/rules/thank-you-requirement.d.ts +16 -0
  74. package/campaign-spec/dist/rules/thank-you-requirement.js +50 -0
  75. package/campaign-spec/dist/rules/unknown-top-level-fields.d.ts +28 -0
  76. package/campaign-spec/dist/rules/unknown-top-level-fields.js +114 -0
  77. package/campaign-spec/dist/rules/upsell-has-packages.d.ts +9 -0
  78. package/campaign-spec/dist/rules/upsell-has-packages.js +35 -0
  79. package/campaign-spec/dist/rules/upsell-routing-complete.d.ts +10 -0
  80. package/campaign-spec/dist/rules/upsell-routing-complete.js +45 -0
  81. package/campaign-spec/dist/rules/upsell-without-checkout.d.ts +11 -0
  82. package/campaign-spec/dist/rules/upsell-without-checkout.js +44 -0
  83. package/campaign-spec/dist/rules/variant-labels-shape.d.ts +28 -0
  84. package/campaign-spec/dist/rules/variant-labels-shape.js +86 -0
  85. package/campaign-spec/dist/sdk-version-parse.d.ts +43 -0
  86. package/campaign-spec/dist/sdk-version-parse.js +62 -0
  87. package/campaign-spec/dist/types.d.ts +674 -0
  88. package/campaign-spec/dist/types.js +40 -0
  89. package/campaign-spec/package.json +12 -0
  90. package/compatibility.json +25 -0
  91. package/contracts/agent-relevant-change-policy.v1.json +111 -0
  92. package/contracts/brand-theme-source-defaults.figma-sections-export.v0.json +32 -0
  93. package/contracts/brand-theme-target-tokens.next-core.v0.json +65 -0
  94. package/contracts/campaign-cart-checkout-field-contract.v0.json +45 -0
  95. package/contracts/campaign-cart-sdk-support-policy.v0.json +11 -0
  96. package/contracts/commerce-surface-catalog.json +2452 -0
  97. package/contracts/fixtures/orientation/canonicalization/v1.json +34 -0
  98. package/contracts/fixtures/orientation/envelope/current.json +95 -0
  99. package/contracts/fixtures/orientation/envelope/freshness_unknown.json +97 -0
  100. package/contracts/fixtures/orientation/envelope/legacy_baseline.json +95 -0
  101. package/contracts/fixtures/orientation/envelope/orientation_available.json +136 -0
  102. package/contracts/fixtures/orientation/envelope/recovered_interrupted_update.json +136 -0
  103. package/contracts/fixtures/orientation/envelope/refused.json +100 -0
  104. package/contracts/fixtures/orientation/envelope/restart_required.json +137 -0
  105. package/contracts/fixtures/orientation/envelope/updated.json +135 -0
  106. package/contracts/fixtures/orientation/hostile-target/README.md +61 -0
  107. package/contracts/fixtures/orientation/hostile-target/manifest.json +82 -0
  108. package/contracts/fixtures/orientation/hostile-target/repo/CHANGELOG.md +18 -0
  109. package/contracts/fixtures/orientation/hostile-target/repo/bin/intended.mjs +12 -0
  110. package/contracts/fixtures/orientation/hostile-target/repo/bin/tripwire.mjs +15 -0
  111. package/contracts/fixtures/orientation/hostile-target/repo/contracts/release-ledger.json +48 -0
  112. package/contracts/fixtures/orientation/hostile-target/repo/contracts/supported-surface.json +17 -0
  113. package/contracts/fixtures/orientation/hostile-target/repo/docs/example-contract.md +13 -0
  114. package/contracts/fixtures/orientation/hostile-target/repo/hooks/post-checkout +5 -0
  115. package/contracts/fixtures/orientation/hostile-target/repo/hooks/post-merge +5 -0
  116. package/contracts/fixtures/orientation/hostile-target/repo/hooks/pre-commit +5 -0
  117. package/contracts/fixtures/orientation/hostile-target/repo/hostile-dependency-tripwire/package.json +15 -0
  118. package/contracts/fixtures/orientation/hostile-target/repo/hostile-dependency-tripwire/tripwire.mjs +9 -0
  119. package/contracts/fixtures/orientation/hostile-target/repo/package.json +20 -0
  120. package/contracts/fixtures/orientation/hostile-target/repo/schemas/example.v0.schema.json +15 -0
  121. package/contracts/fixtures/orientation/release-gate/cases.json +1073 -0
  122. package/contracts/fixtures/runtime-recipe/accept/current.json +299 -0
  123. package/contracts/fixtures/runtime-recipe/accept/minimal.json +294 -0
  124. package/contracts/fixtures/runtime-recipe/dist-states.json +51 -0
  125. package/contracts/fixtures/runtime-recipe/manifest.json +85 -0
  126. package/contracts/fixtures/runtime-recipe/reject/advisory-enforcement.json +299 -0
  127. package/contracts/fixtures/runtime-recipe/reject/allowlist-without-hosts.json +297 -0
  128. package/contracts/fixtures/runtime-recipe/reject/committed-output-claim.json +299 -0
  129. package/contracts/fixtures/runtime-recipe/reject/engines-disagreement-warns.json +299 -0
  130. package/contracts/fixtures/runtime-recipe/reject/lifecycle-scripts-enabled.json +299 -0
  131. package/contracts/fixtures/runtime-recipe/reject/missing-required-field.json +251 -0
  132. package/contracts/fixtures/runtime-recipe/reject/unknown-kind.json +299 -0
  133. package/contracts/fixtures/runtime-recipe/reject/unknown-network-policy.json +299 -0
  134. package/contracts/fixtures/runtime-recipe/reject/unknown-output-check.json +310 -0
  135. package/contracts/fixtures/runtime-recipe/reject/unknown-revision.json +299 -0
  136. package/contracts/fixtures/runtime-recipe/reject/unknown-step-id.json +299 -0
  137. package/contracts/fixtures/runtime-recipe/reject/unperformable-check-skipped.json +299 -0
  138. package/contracts/fixtures/runtime-recipe/reject/unpinned-lockfile.json +299 -0
  139. package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/assembly-report.json +180 -0
  140. package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/build-context.json +115 -0
  141. package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/doctor-output.json +29 -0
  142. package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/qa-verdict.json +27 -0
  143. package/contracts/fixtures/sidecar-bundle/production-shaped/campaign-runtime.build.json +141 -0
  144. package/contracts/migration-sidecar-bundle.v0.json +149 -0
  145. package/contracts/orientation-limits.v1.json +41 -0
  146. package/contracts/orientation-reason-codes.v1.json +196 -0
  147. package/contracts/private-template-sources.json +8 -0
  148. package/contracts/release-ledger.json +4598 -0
  149. package/contracts/reserved-skill-names.json +13 -0
  150. package/contracts/runtime-recipe.campaigns-os-node-v1.json +299 -0
  151. package/contracts/supported-surface.json +180 -0
  152. package/contracts/template-brand-contract.apollo-mv-single-step.v0.json +27 -0
  153. package/contracts/template-brand-contract.apollo.v0.json +27 -0
  154. package/contracts/template-brand-contract.demeter.v0.json +27 -0
  155. package/contracts/template-brand-contract.olympus-mv-single-step.v0.json +27 -0
  156. package/contracts/template-brand-contract.olympus-mv-two-step.v0.json +28 -0
  157. package/contracts/template-brand-contract.olympus.v0.json +27 -0
  158. package/contracts/template-brand-contract.shared-commerce.v0.json +190 -0
  159. package/contracts/template-brand-contract.shop-single-step.v0.json +27 -0
  160. package/contracts/template-brand-contract.shop-three-step.v0.json +29 -0
  161. package/contracts/template-slot-manifest.apollo-mv-single-step.v0.json +14 -0
  162. package/contracts/template-slot-manifest.apollo.v0.json +12 -0
  163. package/contracts/template-slot-manifest.demeter.v0.json +30 -0
  164. package/contracts/template-slot-manifest.olympus-mv-single-step.v0.json +14 -0
  165. package/contracts/template-slot-manifest.olympus-mv-two-step.v0.json +15 -0
  166. package/contracts/template-slot-manifest.olympus.v0.json +12 -0
  167. package/contracts/template-slot-manifest.shared-content-core.v0.json +4254 -0
  168. package/contracts/template-slot-manifest.shop-single-step.v0.json +47 -0
  169. package/contracts/template-slot-manifest.shop-three-step.v0.json +33 -0
  170. package/docs/brand-theme-bridge.md +159 -0
  171. package/docs/build-packet.md +1300 -0
  172. package/docs/campaign-build-brief.md +145 -0
  173. package/docs/campaign-standardization-report.md +329 -0
  174. package/docs/campaigns-os-build-flow.md +117 -0
  175. package/docs/design-source-package.md +784 -0
  176. package/docs/legacy-migration.md +58 -0
  177. package/docs/migration-sidecar-bundle.md +139 -0
  178. package/docs/orientation-contract-reference.md +1220 -0
  179. package/docs/polish-evidence.md +502 -0
  180. package/docs/qa-and-test-orders.md +1691 -0
  181. package/docs/release-ledger-authoring-guide.md +274 -0
  182. package/docs/runtime-readiness.md +211 -0
  183. package/docs/supported-surface.md +83 -0
  184. package/docs/versioning.md +55 -0
  185. package/docs/workflow-findings-sidecar.md +588 -0
  186. package/package.json +135 -0
  187. package/prompts/first-build.md +27 -0
  188. package/prompts/friction-log.md +30 -0
  189. package/schemas/campaign-build-brief.v1.schema.json +149 -0
  190. package/schemas/campaign-design-source-package.v0.schema.json +697 -0
  191. package/schemas/campaign-runtime-assembly-report.v0.schema.json +390 -0
  192. package/schemas/campaign-runtime-build-context.v0.schema.json +339 -0
  193. package/schemas/campaign-runtime-build-packet.v0.schema.json +452 -0
  194. package/schemas/campaign-spec.v4.schema.json +582 -0
  195. package/schemas/campaigns-os-doctor-output.v0.schema.json +43 -0
  196. package/schemas/campaigns-os-legacy-migration-inventory.v0.schema.json +112 -0
  197. package/schemas/campaigns-os-legacy-provisioning-plan.v0.schema.json +38 -0
  198. package/schemas/campaigns-os-legacy-provisioning-receipt.v0.schema.json +57 -0
  199. package/schemas/campaigns-os-qa-verdict-sidecar.v0.schema.json +150 -0
  200. package/schemas/campaigns-os-qa-verdict.v0.schema.json +438 -0
  201. package/schemas/campaigns-os-release-ledger.v1.schema.json +162 -0
  202. package/schemas/campaigns-os-run-record.v0.schema.json +343 -0
  203. package/schemas/campaigns-os-runtime-recipe.v1.schema.json +313 -0
  204. package/schemas/campaigns-os-sidecar-bundle-conformance.v0.schema.json +78 -0
  205. package/schemas/campaigns-os-tooling-orientation.v1.schema.json +381 -0
  206. package/schemas/campaigns-os-workflow-finding.v0.schema.json +134 -0
  207. package/schemas/source-html-manifest.v0.schema.json +252 -0
  208. package/skills/next-campaigns-build/SKILL.md +73 -0
  209. package/skills/next-campaigns-os/SKILL.md +101 -0
  210. package/skills/next-campaigns-os/references/session-intake.md +160 -0
  211. package/skills/next-campaigns-os-setup/SKILL.md +22 -0
  212. package/skills/next-campaigns-polish/SKILL.md +145 -0
  213. package/skills/next-campaigns-qa/SKILL.md +92 -0
  214. package/skills.json +56 -0
  215. package/skills.sh +64 -0
  216. package/src/adapter-decision-contract.mjs +333 -0
  217. package/src/brand-theme.mjs +1151 -0
  218. package/src/browser-launch.mjs +79 -0
  219. package/src/build-brief.mjs +781 -0
  220. package/src/built-site-scope.mjs +312 -0
  221. package/src/campaign-ecosystem.mjs +734 -0
  222. package/src/campaign-identity.mjs +405 -0
  223. package/src/campaign-workspace.mjs +127 -0
  224. package/src/checkpoint-waiver.mjs +302 -0
  225. package/src/cli.mjs +13442 -0
  226. package/src/commercial-journey.mjs +1119 -0
  227. package/src/commercial-parity.mjs +965 -0
  228. package/src/consent.mjs +347 -0
  229. package/src/content-residue.mjs +322 -0
  230. package/src/deadline.mjs +81 -0
  231. package/src/design-source-package.mjs +2604 -0
  232. package/src/deviation.mjs +107 -0
  233. package/src/doctor-check-registry.mjs +49 -0
  234. package/src/doctor-sidecar.mjs +106 -0
  235. package/src/finding-cause.mjs +557 -0
  236. package/src/findings.mjs +326 -0
  237. package/src/fs-identity.mjs +68 -0
  238. package/src/gate-actions.mjs +105 -0
  239. package/src/html-scan.mjs +53 -0
  240. package/src/install-mode.mjs +272 -0
  241. package/src/legacy-migration.d.ts +128 -0
  242. package/src/legacy-migration.mjs +510 -0
  243. package/src/lifecycle.mjs +338 -0
  244. package/src/local-proof.mjs +401 -0
  245. package/src/map-pin-writeback.mjs +210 -0
  246. package/src/orchestration-stage-contract.mjs +81 -0
  247. package/src/package-install-fixture.mjs +33 -0
  248. package/src/page-kit-build-summary.mjs +175 -0
  249. package/src/page-kit-campaign-config.mjs +57 -0
  250. package/src/page-kit-sdk-version.mjs +392 -0
  251. package/src/page-kit-store-profile.mjs +369 -0
  252. package/src/page-kit-sync.mjs +162 -0
  253. package/src/polish-browser.mjs +867 -0
  254. package/src/polish-capture.mjs +1094 -0
  255. package/src/polish-deadline.mjs +51 -0
  256. package/src/polish-gate.mjs +739 -0
  257. package/src/polish-node.mjs +639 -0
  258. package/src/polish-page-load.mjs +1100 -0
  259. package/src/private-template-source.mjs +237 -0
  260. package/src/proof-policy.mjs +82 -0
  261. package/src/qa-analytics-correctness.mjs +307 -0
  262. package/src/qa-analytics-errors.mjs +38 -0
  263. package/src/qa-analytics-parity.mjs +699 -0
  264. package/src/qa-binding-evidence.mjs +140 -0
  265. package/src/qa-browser.mjs +6608 -0
  266. package/src/qa-cart-entry.mjs +406 -0
  267. package/src/qa-commercial-parity.mjs +641 -0
  268. package/src/qa-node.mjs +3620 -0
  269. package/src/qa-order-bump.mjs +381 -0
  270. package/src/qa-parity-capture.mjs +428 -0
  271. package/src/qa-parity-fixture.mjs +359 -0
  272. package/src/qa-publish.mjs +362 -0
  273. package/src/qa-purchase-data-layer.mjs +263 -0
  274. package/src/qa-route-probe.mjs +272 -0
  275. package/src/qa-sidecar.mjs +188 -0
  276. package/src/qa-test-order-topology.mjs +207 -0
  277. package/src/qa-url-privacy.mjs +13 -0
  278. package/src/qa-verdict-discovery.mjs +192 -0
  279. package/src/qa-verdict-publish.mjs +105 -0
  280. package/src/qa-verdict.mjs +287 -0
  281. package/src/remit.mjs +388 -0
  282. package/src/repo-scan.mjs +83 -0
  283. package/src/route-identity.mjs +133 -0
  284. package/src/run-record-closeout.mjs +229 -0
  285. package/src/run-record.mjs +839 -0
  286. package/src/run-session.mjs +226 -0
  287. package/src/runtime-state-ignore.mjs +113 -0
  288. package/src/sdk-attribute-index.mjs +212 -0
  289. package/src/sdk-markup.mjs +358 -0
  290. package/src/sdk-meta-tags.mjs +52 -0
  291. package/src/shell-token.mjs +7 -0
  292. package/src/sidecar-bundle.mjs +399 -0
  293. package/src/source-asset-crawl.mjs +469 -0
  294. package/src/source-html-intake.mjs +627 -0
  295. package/src/source-html-manifest.mjs +276 -0
  296. package/src/source-prep.mjs +284 -0
  297. package/src/spec-derive-store.mjs +431 -0
  298. package/src/spec-derive.mjs +514 -0
  299. package/src/spec-fetch.mjs +66 -0
  300. package/src/spec-hash.mjs +48 -0
  301. package/src/spec-identity.mjs +27 -0
  302. package/src/stage-ledger.mjs +509 -0
  303. package/src/standardization-report.mjs +1297 -0
  304. package/src/template-brand-contract.mjs +473 -0
  305. package/src/template-freshness.mjs +196 -0
  306. package/src/template-reference.mjs +81 -0
  307. package/src/template-slot-manifest.mjs +150 -0
  308. package/src/text-safety.mjs +62 -0
  309. package/src/theme-gate.mjs +185 -0
  310. package/src/upsell-selector-scope.mjs +299 -0
@@ -0,0 +1,145 @@
1
+ # Campaign Build Brief
2
+
3
+ The Campaign Build Brief is the merchandising and design-presentation truth for a Campaigns OS build.
4
+
5
+ CampaignSpec remains the operational truth: packages, offers, shipping, routing, SDK hints, payment/runtime values, and API identity. The Build Brief answers business-owned presentation questions that agents should not infer silently: page authority, palette and CTA treatment, product media rules, pricing display, promo language, payment/trust surfaces, canonical names, residue policy, and QA expectations.
6
+
7
+ ## Artifact Locations
8
+
9
+ Campaigns OS accepts YAML or JSON:
10
+
11
+ ```bash
12
+ campaigns-os prepare-build \
13
+ --source ./design-export \
14
+ --spec ./campaign-spec.json \
15
+ --target ./merchant-campaign \
16
+ --template-family olympus \
17
+ --brief ./campaign-build-brief.yaml
18
+ ```
19
+
20
+ `campaigns-os build` is an intake alias for the same flow plus doctor.
21
+
22
+ If `--brief` is omitted, `prepare-build` looks for:
23
+
24
+ - `campaign-build-brief.yaml`
25
+ - `campaign-build-brief.yml`
26
+ - `campaign-build-brief.json`
27
+
28
+ It checks the source root first, then the target repo. If none exists, Campaigns OS writes a guided draft to:
29
+
30
+ ```text
31
+ .campaign-runtime/input/campaign-build-brief.normalized.json
32
+ ```
33
+
34
+ The Build Packet, Build Context, and Assembly Report all reference that normalized artifact so it survives handoff, compaction, rebuilds, polish, and QA.
35
+
36
+ ## Modes
37
+
38
+ Prepared mode is for veteran users. A complete brief should let the build proceed without business questions. If a supplied brief is incomplete or contradictory, doctor blocks with `build_brief.*` errors.
39
+
40
+ Guided mode is for new or partial inputs. Campaigns OS drafts a brief from CampaignSpec, page mappings, template family, source assets, and available runtime hints. It records only high-impact unresolved questions as warnings so existing builds still run while the business uncertainty is visible.
41
+
42
+ ## High-Impact Questions
43
+
44
+ Guided questions are intentionally short and business-readable. They prioritize:
45
+
46
+ 1. Which source controls each page?
47
+ 2. Which palette/CTA style should commerce pages use?
48
+ 3. Which product variants/colors are actually sold?
49
+ 4. How should bundle pricing be presented?
50
+ 5. What promo/savings/urgency language is approved?
51
+ 6. Which payment methods/trust badges may appear?
52
+ 7. Are runtime/catalog names allowed to override provided display names?
53
+ 8. Are there regulated claims or forbidden copy areas?
54
+
55
+ The CLI avoids SDK/page-kit jargon in questions. The implementation can resolve SDK attributes, responsive CSS, asset paths, routing, template copying, and QA reruns. Business choices should come from the brief or be escalated.
56
+
57
+ ## Risky Defaults
58
+
59
+ Doctor blocks or asks when a prepared brief leaves high-impact questions unanswered, forbids alternate variant colors without naming sold variants, or contains direct contradictions such as the same payment method being both allowed and hidden.
60
+
61
+ Doctor warns when generated guided drafts still need answers, a brief allows payment methods not observed in CampaignSpec, or a brief does not explicitly block promo/template placeholders.
62
+
63
+ Existing template residue, theme, pricing, and built-output checks continue to run. The brief gives those checks business intent instead of replacing them.
64
+
65
+ ## Content Claims Are Reviewed, Not Enforced
66
+
67
+ The doctor scans built output for content residue and raises some of it as
68
+ warnings: every content anti-pattern under the warning code
69
+ `content_residue.anti_pattern` (the finding ids include `invented_counts`
70
+ for invented counts and ratings, `verified_buyer_chrome` for "Verified
71
+ Buyer" and similar review chrome, `byline_persona`, `borrowed_authority`,
72
+ `press_marquee`, and `science_theater`; all of them are warning-only), and
73
+ promo copy claiming a discount above the CampaignSpec maximum (`template_contract.discount_claim_residue`, or
74
+ `template_contract.discount_claim_unverified` when the spec sets no maximum).
75
+
76
+ These stay warnings on purpose, and nothing downstream reads them. There is no
77
+ blocker, no `blocked_stages` entry, no QA assertion, and no test-order gate
78
+ keyed on any of them. A build carrying all of them can pass doctor, pass QA,
79
+ and deploy.
80
+
81
+ The toolkit flags the copy; it does not adjudicate it. **Responsibility for
82
+ every claim on the page — proof counts, review chrome, discount percentages,
83
+ and the rest — sits with the operator and the client, not with Campaigns OS.**
84
+ Use the brief to record which claims are approved and which language is
85
+ forbidden (see the high-impact questions above), and treat a content warning as
86
+ a prompt to check the brief, not as a gate that will stop the build if you
87
+ ignore it.
88
+
89
+ The hard content checks are separate and do block: the needs-merchant-input
90
+ marker (`content_residue.needs_merchant_input`) and countdown chrome rendered
91
+ without verified offer urgency on a brief-backed build
92
+ (`content_residue.unverified_urgency`).
93
+
94
+ ## QA Policy Scope
95
+
96
+ `qa_policy` records business expectations for the proof pass, such as desktop/mobile screenshots, checkout flow coverage, post-purchase coverage, visible-placeholder handling, and runtime-data comparison. It is not the doctor/QA enforcement contract by itself.
97
+
98
+ Normalized briefs include `qa_policy.enforcement.status: documented_expectation` so consumers do not mistake these fields for direct gates. Doctor and QA enforce the Build Packet `qa.proof_policy` and Assembly Report `report.proof_policy` contract, which names browser QA, typed-card depth, SDK origin allowlist state, order path depth, and operator approval state.
99
+
100
+ ## Generic Scenarios
101
+
102
+ Single-variant gadget:
103
+
104
+ - One physical product, one sold color.
105
+ - Landing page owns the palette.
106
+ - Checkout inherits landing CTA style.
107
+ - Carousel avoids alternate colors.
108
+ - Bundle cards emphasize unit price and simple savings badges.
109
+
110
+ Multi-variant apparel:
111
+
112
+ - Product has several colors/sizes.
113
+ - Variant selector is allowed.
114
+ - Media may show multiple colors only when the selected-variant workflow supports it.
115
+ - Product imagery must not imply unavailable sizes/colors.
116
+
117
+ Consumable subscription:
118
+
119
+ - One-time and subscribe-and-save offers may coexist.
120
+ - Pricing separates first-order savings from subscription terms.
121
+ - Promo timers avoid false urgency when the offer is evergreen.
122
+
123
+ Home goods bundle:
124
+
125
+ - Main product plus accessories.
126
+ - Bundle cards emphasize included items, not only percentage savings.
127
+ - Lifestyle images can show room scenes, but the product must remain inspectable.
128
+
129
+ Digital or service add-on:
130
+
131
+ - Physical variant imagery is not required.
132
+ - OTO copy emphasizes scope, duration, and support terms.
133
+ - Shipping copy is hidden.
134
+
135
+ Health/wellness product:
136
+
137
+ - Claims require stricter copy boundaries.
138
+ - The brief should list forbidden claims and approved benefit language.
139
+ - QA should flag unapproved medical or guaranteed-outcome wording.
140
+
141
+ High-compliance financial or regulated offer:
142
+
143
+ - Promo and urgency copy defaults conservative.
144
+ - Trust badges and claims must be source-backed.
145
+ - Savings, guarantees, or scarcity language requires explicit approval.
@@ -0,0 +1,329 @@
1
+ # Campaign Standardization Report
2
+
3
+ The Campaign Standardization Report is a read-only audit for campaign
4
+ repositories across the campaign ecosystem. It discovers campaign roots,
5
+ classifies each root's implementation, inventories source structure and
6
+ runtime contracts, validates checkout field bindings and SDK loader versions
7
+ against contracts, and names the next safe remediation category without
8
+ editing the target repo.
9
+
10
+ Two implementation kinds are recognized:
11
+
12
+ - `page_kit` — modern CPK roots (`_data/campaigns.json` or a
13
+ `next-campaign-page-kit` dependency). Existing sections and finding codes
14
+ are unchanged.
15
+ - `campaign_cart_app` — non-Page-Kit applications (Vite/React/Express apps,
16
+ static HTML funnels) detected through portable Campaign Cart evidence: the
17
+ loader script URL, `meta[name="next-campaign-id"]`, `window.nextConfig`, or
18
+ a sufficient density of `data-next-*` anchors. Evidence is rolled up to the
19
+ nearest `package.json` boundary; one strong signal (or ≥5 weak anchors)
20
+ classifies the root. Directories already claimed as Page Kit roots are never
21
+ double-claimed, nested application roots are scanned independently (a parent
22
+ root never re-reports a nested root's files), and a parent repo may contain
23
+ both kinds side by side. HTML comments are masked throughout, so
24
+ commented-out loaders, bindings, or radios never produce evidence or
25
+ findings.
26
+
27
+ Every root carries `implementation` (`kind`, `evidence`, `frameworks`) and
28
+ `capabilities` — the inspections that actually ran for that root, never a
29
+ standing list per kind. A `page_kit` root always lists
30
+ `page_kit_source_contract`, `sdk_version_policy` and
31
+ `campaign_cart_runtime_inventory`; it adds `checkout_field_contract` only
32
+ when its source inlines checkout bindings (the attributes the field
33
+ contract's `binding_attributes` names; bundled: `data-next-checkout-field` /
34
+ `os-checkout-field`), and `built_output_doctor` only once a built-output doctor result is
35
+ attached (so never under `--no-doctor`, never without a `_site`, never while
36
+ the built slug is unresolved). A `campaign_cart_app` root lists
37
+ `campaign_cart_runtime_inventory`, `sdk_loader_discovery`,
38
+ `sdk_version_policy`, `checkout_field_contract` and
39
+ `payment_interaction_risk`. Composition is capability-based rather than a
40
+ repository-type switch (see `campaign-ecosystem-standardization-design.md`).
41
+
42
+ Run it against a Page Kit root, a parent `*-cpk` repo, or any campaign
43
+ application checkout:
44
+
45
+ ```bash
46
+ campaigns-os standardize --target /path/to/example-cpk --json
47
+ campaigns-os standardize --target /path/to/example-cpk --family olympus-mv-single-step --slug example --json
48
+ ```
49
+
50
+ The command is spelled `standardize`. It had a second spelling,
51
+ `standardization-report`, which dispatched to the same code with the same
52
+ flags and the same output; that spelling was removed at supported surface
53
+ 1.27.0 and now returns the unknown-command error, so a script still using it
54
+ must be retargeted at `standardize`. The report's own
55
+ `schema_version` is unaffected.
56
+
57
+ By default, the command prints markdown for operators. Use `--json` for agents
58
+ or dashboards. When a built `_site` exists, the built slug is resolved, and a
59
+ template family is explicit or can be found in `.campaign-runtime`, the
60
+ command also runs the existing `doctor --built` checks and folds those
61
+ findings into the report. Use `--no-doctor` to keep the run to source/runtime
62
+ inventory only.
63
+
64
+ The command is read-only: it never writes into the target repository, and
65
+ tests hold it to that (every file's size, mtime and content hash are identical
66
+ before and after a run that includes the built-output doctor, and again with
67
+ `--no-doctor`, and again when the target already carries a
68
+ `.campaign-runtime/doctor-output.json`). `--no-doctor` only skips the
69
+ built-output doctor pass inside the report; there is no write for it to
70
+ skip. That last case is the one #312 reported as a write: the repro copied
71
+ an example target with `cp -R`, which copies gitignored files, and the
72
+ checkout's example carried a doctor sidecar from an earlier `doctor` run. A
73
+ `.campaign-runtime/doctor-output.json` under a target you just ran
74
+ `standardize` against was written by one of the four producers named in its
75
+ `generated_by` field (`doctor`, `next`, `start`/`build`, `qa run`) — read that
76
+ field before attributing the file.
77
+
78
+ ### Flags
79
+
80
+ `standardize` accepts exactly `--target`, `--family` (alias
81
+ `--template-family`), `--slug`, `--sdk-support-policy`, `--field-contract`,
82
+ `--no-doctor`, `--json`, and the two flags every command accepts,
83
+ `--run-id` and `--lifecycle-journal`. Any other flag is refused before the
84
+ scan starts, with the known list in the message — including the
85
+ `--flag=value` spelling, which the parser would otherwise store as an unknown
86
+ key (`--no-doctor=maybe` used to run the doctor anyway). Values follow the
87
+ flag as the next argument.
88
+
89
+ ### Exit codes
90
+
91
+ - `0` — the report was produced and `ok` is `true` (`status` is `ready` or
92
+ `ready_with_warnings`).
93
+ - `1` — the command did not run: an unknown flag, a missing `--target`, or a
94
+ missing or unparseable `--sdk-support-policy` / `--field-contract` file.
95
+ Nothing is printed on stdout; stderr carries one named error.
96
+ - `2` — the report was produced and `ok` is `false` (`status` is
97
+ `blocked`, including `campaign.root_not_found`).
98
+
99
+ ### When `--slug` matters
100
+
101
+ The built-output scope is the directory under `_site/` whose pages the
102
+ built-output doctor inspects. It is resolved from, in order: `--slug`; the
103
+ single slug `_data/campaigns.json` declares; the `campaign.public_route_slug`
104
+ the `.campaign-runtime` packets name, when every packet that names one
105
+ agrees; and, only when none of those exists, the
106
+ `_site/` layout itself (one html-bearing directory, or root-level html). The
107
+ report records the choice as `built_output.slug` and `built_output.slug_source`
108
+ (`operator_flag`, `campaigns_json`, the packet's relative path, or
109
+ `site_layout`), and `identity.campaign_slug` / `identity.campaign_slug_source`
110
+ carry the same answer. Two outcomes replace a silent guess:
111
+
112
+ - `built_output.scope_unresolved` (operator readiness) — `_site/` holds more
113
+ than one html-bearing directory and no slug source names one. This is the
114
+ case that needs `--slug`; the finding lists `slug_candidates` and the
115
+ doctor proof command carries a `--slug <slug>` placeholder.
116
+ - `built_output.slug_mismatch` (operator readiness) — the slug came from
117
+ `campaigns.json` or a packet, but `_site/` has no directory for it. The
118
+ built output belongs to some other campaign (a stale build, typically), so
119
+ the doctor is skipped and the finding names the expected slug, its source,
120
+ and the directories that are there. When `_site/<slug>/` exists but holds
121
+ no HTML pages the same finding says so (`exists but holds no HTML pages`,
122
+ evidence `slug_directory_present: true`) rather than calling the directory
123
+ missing. Rebuild, or pass `--slug` to inspect a different directory on
124
+ purpose.
125
+
126
+ Whenever a slug was needed, the doctor proof command under `remediation`
127
+ carries it (`--slug <resolved>`), so the command the report hands back is the
128
+ one that reproduces its own result.
129
+
130
+ ## Schema
131
+
132
+ Top-level shape:
133
+
134
+ ```json
135
+ {
136
+ "schema_version": "campaign-standardization-report/v0",
137
+ "generated_at": "2026-07-06T00:00:00.000Z",
138
+ "target_repo": "/path/to/example-cpk",
139
+ "status": "ready_with_warnings",
140
+ "ok": true,
141
+ "summary": {
142
+ "root_count": 1,
143
+ "blockers": 0,
144
+ "warnings": 2,
145
+ "operator_readiness": 1,
146
+ "blocked_roots": 0,
147
+ "warning_roots": 1,
148
+ "ready_roots": 0
149
+ },
150
+ "roots": [],
151
+ "errors": [],
152
+ "recommendation": {
153
+ "home": "staged_split",
154
+ "summary": "Keep the read-only source/runtime scanner in public campaigns-os first; layer private repo discovery, issue creation, and merchant ops context in an internal campaign-ops wrapper."
155
+ }
156
+ }
157
+ ```
158
+
159
+ ### Campaign Cart application roots
160
+
161
+ `campaign_cart_app` roots contain: `implementation`, `capabilities`,
162
+ `identity` (campaign IDs, loader-discovered SDK versions, runtime artifact
163
+ presence), `sdk_loader` (each loader reference with path/line/URL/version),
164
+ `version_policy` (policy source + per-version evaluations, separate from
165
+ version discovery), `checkout_fields` (every
166
+ `data-next-checkout-field`/`os-checkout-field` binding classified as
167
+ `supported`, `stale_alias`, or `unknown` against
168
+ `contracts/campaign-cart-checkout-field-contract.v0.json`), `payment`
169
+ (SDK `payment_method` radios, hidden radios, custom triggers, synchronization
170
+ script evidence, and `proof_state`), `runtime_contract`, `findings`, and
171
+ `remediation`.
172
+
173
+ `sdk_loader.references` records pinned and unpinned loader refs alike
174
+ (`version` is null for `@latest`/branch/commit pins, which raise
175
+ `version.sdk_loader_unpinned` instead of a policy evaluation). Only URLs that
176
+ point at a loader/dist artifact count; incidental `campaign-cart@x.y.z`
177
+ strings elsewhere in source are ignored.
178
+
179
+ `payment.proof_state` is one of `runtime_proof_required` (custom-control
180
+ evidence found), `undetermined` (radios exist; static scanning cannot exclude
181
+ externally-styled custom controls), or `not_applicable` (no `payment_method`
182
+ radios). The scanner never affirms that behavioral proof is unnecessary when
183
+ payment radios exist.
184
+
185
+ Ecosystem findings carry a `confidence` field:
186
+
187
+ - `static_contract` — provable from source against a named contract (stale
188
+ field aliases, SDK version below policy). Safe repair targets.
189
+ - `static_inference` — heuristic source evidence. Informs risk only.
190
+ - `runtime_proof_required` — behavior only a DOM/browser test can confirm
191
+ (custom payment controls driving the real radios). Reported as *missing
192
+ proof*, explicitly not a confirmed failure.
193
+
194
+ The SDK support policy lives in
195
+ `contracts/campaign-cart-sdk-support-policy.v0.json` and is injectable per run
196
+ via `createStandardizationReport({ sdkSupportPolicy })`; the field contract is
197
+ similarly injectable via `fieldContract`. "Latest" is never frozen into
198
+ scanner code.
199
+
200
+ Both contracts apply to both root kinds. The SDK support policy judges every
201
+ discovered SDK version — a `campaign_cart_app` root's loader pins and bundled
202
+ dependency, and a `page_kit` root's `_data/campaigns.json` `sdk_version`
203
+ values — with one rule: below `minimum_supported` is the blocker
204
+ `version.sdk_below_minimum_supported`, below `preferred_minimum` is the
205
+ warning `version.sdk_below_preferred_policy`, and each message names the
206
+ policy source. Every root records the policy it was judged by under
207
+ `version_policy` (`source`, `minimum_supported`, `preferred_minimum`,
208
+ `evaluations[]` with a `source` of `loader`, `bundled_dependency` or
209
+ `campaigns_json` per version), and the markdown prints it as
210
+ `Version policy: min X, preferred Y (source)`. The bundled policy is
211
+ `0.4.20` minimum / `0.4.30` preferred. The Page Kit dependency cutoff is
212
+ separate: `version.page_kit_below_preferred_cutoff` fires below `0.1.1`, a
213
+ constant in the scanner, because the policy contract has no Page Kit field.
214
+ The checkout field contract runs wherever inline checkout bindings exist; a
215
+ Page Kit root that inlines them gets the same `checkout_fields` block and the
216
+ same `checkout.unsupported_field_binding` / `checkout.unknown_field_binding`
217
+ findings as an application root.
218
+
219
+ Both are also injectable from the CLI: pass
220
+ `--sdk-support-policy <path-to-json>` and/or `--field-contract <path-to-json>`
221
+ to `standardize`. Each file is read and JSON-parsed
222
+ (a missing or unparseable file is a clear, named error) and overrides the
223
+ bundled contract for that run, for every root the run discovers:
224
+
225
+ ```bash
226
+ campaigns-os standardize --target /path/to/example-cpk \
227
+ --sdk-support-policy ./my-sdk-policy.json \
228
+ --field-contract ./my-field-contract.json --json
229
+ ```
230
+
231
+ When no root of either kind is detected, the report carries a single
232
+ `campaign.root_not_found` error (this replaced the earlier
233
+ `page_kit.root_not_found` code when ecosystem detection landed).
234
+
235
+ ### Page Kit root sections
236
+
237
+ Each Page Kit root contains these sections:
238
+
239
+ - `identity`: repo name, Page Kit root, slug inventory, SDK versions, Page Kit
240
+ dependency, template family evidence, certification freshness for that
241
+ family, Campaigns OS artifact presence, and built-site presence.
242
+ `template_certification_freshness` states the SDK version the family was
243
+ last verified against and the current SDK recorded by the vendored
244
+ contracts (the commerce surface catalog snapshot's per-family
245
+ `verification` blocks plus the SDK support policy) — an older evidence
246
+ record is not current certification. Families with no verification record
247
+ on the snapshot report freshness as unknown rather than inventing one.
248
+ - `source_structure`: HTML/page/include/layout counts, Liquid helper counts,
249
+ raw blocks, document wrappers, hardcoded root asset refs, unreadable files,
250
+ and payment-method include detection.
251
+ - `runtime_contract`: `data-next-*` anchor summary, checkout/upsell/receipt
252
+ surface signals, package/shipping refs, source manifest presence, and
253
+ `.campaign-runtime` inventory.
254
+ - `version_policy`: the SDK support policy the root was judged by and its
255
+ per-version evaluations (see above).
256
+ - `checkout_fields`: present only when the root inlines checkout bindings;
257
+ the same shape as on application roots.
258
+ - `built_output`: built page inventory, the resolved slug and its source,
259
+ slug-scope resolution state (`slug_candidates` when unresolved), and
260
+ optional built-output doctor result.
261
+ - `findings`: normalized blocker, warning, and operator-readiness items with
262
+ evidence and next action.
263
+ - `remediation`: safe agent repairs, clarification needed, product or merchant
264
+ risks, and proof commands.
265
+
266
+ ## Finding Taxonomy
267
+
268
+ `standardization_blocker` means an agent should not assume the repo is portable
269
+ or standard without repair. Current blockers include missing or invalid
270
+ `_data/campaigns.json`, an SDK below the policy's minimum supported version,
271
+ stale checkout field aliases, Liquid raw blocks, and built-output doctor
272
+ errors.
273
+
274
+ `standardization_warning` means the repo can be inspected but may drift from the
275
+ modern CPK contract. Current warnings include an SDK below the policy's
276
+ preferred minimum, a Page Kit dependency below `0.1.1`, missing Campaigns OS
277
+ artifacts, hardcoded `/assets/...` refs, page-level
278
+ document wrappers, unreadable source files, missing `campaign_asset`, missing
279
+ `data-next-*` anchors, and tentative payment-method include gaps.
280
+
281
+ `operator_readiness` means the repo may be technically inspectable but lacks
282
+ proof or business context. Current readiness items include missing built output,
283
+ unknown or tentative template family, missing source-html manifest, unresolved
284
+ built slug, a built slug with no matching built directory, and unknown
285
+ production proof.
286
+
287
+ ## Home Recommendation
288
+
289
+ Use a staged split:
290
+
291
+ - Put the read-only scanner, schema, markdown formatter, and built-output doctor
292
+ integration in public `campaigns-os`.
293
+ - Put private repo discovery, sample-set selection, merchant launch context,
294
+ issue creation, and workbench UI surfacing in an internal campaign-ops wrapper.
295
+
296
+ This keeps the portable contract close to the existing Campaigns OS doctor while
297
+ leaving private operational workflow outside the public package.
298
+
299
+ ## First Follow-Up Backlog
300
+
301
+ - Add schema validation once the artifact shape settles across more repos.
302
+ - Add explicit template-family evidence from CampaignSpec and Build Packet when
303
+ those artifacts are present.
304
+ - Replace crude payment-method include detection with family contract checks.
305
+ - Add optional `--output <path>` for durable JSON/markdown output.
306
+ - Add repo-set orchestration in an internal campaign-ops wrapper for private
307
+ sample sets and follow-up generation.
308
+ - Add waiver support for intentional one-off template deviations.
309
+
310
+ ## Ecosystem Follow-Up Backlog
311
+
312
+ - Packetless `qa resolve/run` for existing funnels, keyed off the
313
+ standardization report, to convert `runtime_proof_required` payment findings
314
+ into behavioral proof (deterministic DOM test of custom controls driving
315
+ `input[name="payment_method"]`).
316
+ - Deployed-URL-only assessment (no source checkout).
317
+ - Origin/environment diagnosis as operator readiness: SDK origin allowlist
318
+ rejection (CORS) must be classified as merchant/environment configuration,
319
+ never conflated with an application integration defect.
320
+ - Additional adapters: source-only exports, legacy CampaignsJS funnels,
321
+ CampaignSpec/Build Packet cross-checking for campaigns that carry full
322
+ Campaigns OS evidence.
323
+ - Provenance refresh script for the field/policy contracts, mirroring the
324
+ starter-template catalog refresh.
325
+ - Symlinked source directories are currently skipped (silent false negative)
326
+ and large vendored files are read whole; add link-following policy and a
327
+ file-size cap.
328
+ - CSS-aware hidden-control detection (external stylesheets are not scanned;
329
+ `undetermined` proof state covers the gap honestly for now).
@@ -0,0 +1,117 @@
1
+ # Campaigns OS Build Flow
2
+
3
+ The happy path is intentionally tight:
4
+
5
+ 1. Export a saved local CampaignSpec JSON from Campaign Map Builder, including Map ID and public route slug.
6
+ 2. Run `campaigns-os start` with the CampaignSpec, prepared source files, target page-kit repo, and template family.
7
+ 3. Treat doctor as the first gate. If its `next` block says `doctor-blocked` or `prepare-build` (the same stage names `campaigns-os next` uses), stop and resolve the named blocker.
8
+ 4. Run setup when doctor asks for setup; otherwise continue to assembly.
9
+ 5. Assemble the page-kit campaign from starter-template contracts, not from copied demo commerce values.
10
+ 6. Run page-kit build plus SDK/template lint and record results in the assembly report.
11
+ 7. Install the package-owned Playwright browser with `npm run qa:install-browser`.
12
+ 8. Run polish, serve the current built output, and run `campaigns-os polish capture --packet <packet> --base-url <served-current-build-url>`. Do not mark Polish terminal or begin deploy/QA until this package-owned page-load evidence passes or has an exact finding waiver.
13
+ 9. Deploy a preview.
14
+ 10. Run `campaigns-os qa resolve`, then `campaigns-os qa run --browser --test-order common` with the tested URL. QA runs publish to the QA portal by default (the QA tab records browser QA plus typed-card proof and the run prints its portal link); pass `--no-post-verdict` only for offline / dev / CI runs.
15
+ 11. Treat test-order depth as the control: global test cards bypass the gateway and create no transactions, so no approval is needed. Localhost on any port is a Campaigns App Development domain (SDK allowed, analytics suppressed); non-localhost preview/production origins must still be allowlisted for the campaign API key so the SDK loads.
16
+ 12. Promote, block, or iterate from the recorded build, polish, deploy, QA, and test-order evidence.
17
+
18
+ Pause only for missing inputs, doctor blockers, blocked deploys, out-of-scope runtime pages that block checkout proof, or merchant-specific uncertainty. The default path should not branch into external browser skills or hand-built backend order creation.
19
+
20
+ ## Partial Builds
21
+
22
+ Partial builds are valid campaign work. A pass may build only new presell pages,
23
+ landing pages, upsells, downsells, or another bounded slice that sends traffic
24
+ to an existing downstream route. In the Build Packet, map pages being built with
25
+ `source_html.pages[].path` and mark intentionally untouched pages with
26
+ `source_html.pages[].skip_reason`.
27
+
28
+ For mapped pages, `source_html.pages[].path` is source provenance. Use
29
+ `source_html.pages[].page_kit` for the Page Kit target file, public route, CPK
30
+ `page_type`, and frontmatter projection.
31
+
32
+ `doctor` classifies that as `derived.scope.mode = "partial"`. Mapped pages are
33
+ route/visual-testable after deploy. Skipped checkout, upsell, downsell, or
34
+ receipt pages keep checkout launch readiness and test-order proof blocked until
35
+ those runtime pages are built or explicitly delegated to an existing downstream
36
+ URL.
37
+
38
+ ## Commerce Ownership
39
+
40
+ - CampaignSpec/API own live campaign identity, routes, package refs, offer refs, shipping refs, payment support, tracking intent, footer links, and SEO values.
41
+ - Starter template contracts own the SDK attribute contract and protected runtime surfaces: checkout/cart/upsell/receipt/payment/address/totals/submit controls and their required data attributes.
42
+ - Designed source owns visual composition, content hierarchy, imagery, and page-level copy.
43
+
44
+ Packages identify sellable products or variants, and Offers own campaign price changes. Package Retail Price/Quantity fields may exist on older campaigns, but assembly should not introduce them for new tier pricing.
45
+
46
+ ## Offer Application Surfaces
47
+
48
+ CampaignSpec may declare checkout-level offer application behavior through
49
+ `funnels[].pages[].exit_intent` and `funnels[].pages[].promo_code_input`.
50
+ Treat these fields as intent for runtime checkout surfaces, not as separate
51
+ pricing models:
52
+
53
+ - `exit_intent.offer_ref_id` points at the configured campaign Offer.
54
+ - `exit_intent.offer_code` is the voucher/promo code the runtime should apply
55
+ when the shopper accepts the pop.
56
+ - `promo_code_input.offer_ref_id` points at the configured campaign Offer.
57
+ - `promo_code_input.offer_code` is the voucher/promo code the runtime should
58
+ accept through the manual entry surface.
59
+ - optional `notes` fields are build/QA implementation notes. Durable popup
60
+ copy, CTA labels, placeholders, success labels, and active labels belong to
61
+ the source design or selected template, not the Spec.
62
+
63
+ Build should wire the selected starter-template checkout so the accepted offer
64
+ is applied through the Campaign Cart SDK/Campaigns API path. Do not hardcode
65
+ discount math, mutate static price literals, or treat the pop as an alternate
66
+ bundle model. After the code is active, bundle selectors, totals, order summary,
67
+ and discount rows should render from SDK/API state.
68
+
69
+ Code-specific presentation belongs in SDK conditionals, for example:
70
+
71
+ ```html
72
+ <span data-next-show='cart.hasCoupon("FREESHIP")'>Free shipping applied</span>
73
+ ```
74
+
75
+ A promo-code box accepts a shopper-entered code and asks SDK/API to validate and
76
+ apply it; it does not own pricing truth. When `promo_code_input.enabled` is
77
+ declared, build should preserve or create the template/source promo-code surface,
78
+ wire it to the mapped `offer_code`, and record the implementation decision in
79
+ the assembly report.
80
+
81
+ ## Assembly Rules
82
+
83
+ - Landing and presell pages should preserve prepared HTML when it is a real standalone design. Use page-kit passthrough structure, inject the SDK/config requirements, and repoint CTAs into the CampaignSpec flow.
84
+ - **Pre-checkout pages must ship the same SDK bootstrap as the checkout layout.** Presell and landing pages are SDK `page_type: product`; they need `config.js` (before the loader), the `campaign-cart@v{sdk_version}/dist/loader.js` module script, and the `next-funnel` + `next-page-type` meta tags — not just inert `data-next-*` attributes. Without the loader the SDK silently no-ops: `data-next-hide` conditional visibility (`param.banner` / `param.seen`), `utmTransfer` UTM/query carry-through to checkout (top-of-funnel ad attribution), and SDK analytics never fire. Treat `param.banner` / `param.seen` visibility and `utmTransfer` as standard pre-checkout wiring, not per-campaign discoveries. Doctor enforces this with `built_output.pre_checkout_sdk_bootstrap`.
85
+ - **Every page names the same campaign.** A page borrowed from another funnel (a copied upsell or receipt) must not keep the other campaign's `next-api-key` / `config.js` API key, `next-funnel` meta, or `setAttribution({ funnel })` call; the SDK reads these per page and reconciles nothing, so the order lands on or attributes to the wrong campaign with no visible error. Doctor blocks this, unwaivably, with `built_output.campaign_identity` (one error per drift, naming both files and both values).
86
+ - **SDK markup must do what it says.** `data-next-checkout` goes on the `<form>`; `data-next-checkout-field` values are the SDK's fixed names (`fname`, `lname`, `postal` — never `firstName`, `lastName`, `zip`); an `add-to-cart` button linked by `data-next-selector-id` needs a selector with that id and that selector in `select` mode (swap mode plus the button writes the cart twice); one default-selected card per selector; single-brace tokens inside SDK templates. Doctor enforces the first four as blockers and the last two as warnings under `built_output.sdk_markup` (codes `SWAP_WITH_ADD_TO_CART`, `CHECKOUT_NOT_FORM`, `WRONG_FIELD_NAME`, `MISSING_SELECTOR_ID_MATCH`, `DOUBLE_SELECTED`, `TEMPLATE_DOUBLE_BRACE`).
87
+ - Checkout, upsell, downsell, and receipt pages should preserve starter-template SDK contracts while keeping the campaign/source visual language. Treat starter templates as the reference for required `data-next-*` controls and wiring, not as a mandate to carry their visual chrome into the final campaign.
88
+ - If source HTML declares SDK-owned zones such as `data-commerce-zone="checkout-form"` or `data-commerce-zone="order-summary"`, adopt the selected starter-template family shell for that runtime page. Do not build a custom checkout/upsell structure around a few borrowed includes; browser QA will check declared family structure where `agentContract.qaStructure` exists.
89
+ - If `context.theme` names a generated `brand-theme.css`, copy it into campaign assets and load it after `next-core.css` on checkout, upsell, downsell, and receipt pages. Generated brand-theme v0 is root-variable-only; do not use it as permission to edit SDK-owned selectors or runtime structure.
90
+ - Buy-more-save-more selectors should use selected quantity plus Offer-aware price displays. Do not swap in stale package-per-tier IDs unless the CampaignSpec explicitly represents an older campaign that still owns separate packages for each option.
91
+ - SDK routing meta tags should be emitted as campaign-root paths, for example `/campaign-slug/upsell/`, even when the CampaignSpec source value is slug-relative like `upsell/`.
92
+ - One-time prepurchase/order-bump packages outside the main bundle should default to fixed quantity and fixed line total display unless the spec explicitly requires syncing quantity with the main bundle.
93
+ - Checkout exit-intent pops and promo-code inputs are protected offer application surfaces. Preserve/apply the selected family's SDK coupon/voucher hooks; skin the shell and copy around them.
94
+ - Any source element dropped because the spec does not support it, such as PayPal when `available_payment_methods` excludes PayPal, should be recorded in the assembly report for polish.
95
+ - After page-kit build, doctor checks rendered local script references plus rendered package and shipping refs against the CampaignSpec. Missing built scripts, stale package IDs, stale shipping IDs, and unavailable package refs must be fixed or intentionally blocked before QA.
96
+ - Browser QA opens SDK-owned runtime pages once as a shopper and once with `?debugger=true`. The debugger pass should prove the Campaign Cart debugger overlay and selector controls mount without changing the normal checkout/test-order flow.
97
+ - Browser QA also checks template-family commerce structure when the family contract declares machine-checkable selectors. Missing required Limos checkout shell markers, for example, are treated as a warning-severity failure: the checkout may load, but it is not proven as a conformant Limos checkout.
98
+
99
+ ## Synthetic Campaigns
100
+
101
+ For AI-generated or synthetic campaigns, the static source page can be used to
102
+ exercise the design-to-page-kit path, but SDK checkout still needs a Campaigns
103
+ App campaign, store URL, packages, shipping/payment configuration, and an SDK
104
+ origin that can load the campaign API key. Localhost on any port is available for
105
+ Development-domain SDK checks, but a non-localhost preview/production origin must
106
+ be allowlisted. If the evaluator has no natural merchant/store,
107
+ reuse a designated test store and record that choice. Otherwise mark checkout,
108
+ receipt, and test-order QA as blocked instead of debugging an SDK loading state
109
+ as if it were a page-kit build failure.
110
+
111
+ ## Not Full Automation
112
+
113
+ This repo improves first-run success. It does not yet prove a campaign is live-ready.
114
+
115
+ Campaigns OS proof is not merchant launch readiness. Before launch, confirm the
116
+ production storefront URL, live payment methods, shipping markets, legal/support
117
+ URLs, analytics expectations, and merchant-side configuration.