@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.
- package/AGENTS.md +204 -0
- package/CHANGELOG.md +5002 -0
- package/CONTEXT.md +685 -0
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +368 -0
- package/agents/claude/CLAUDE.md +32 -0
- package/agents/codex/AGENTS.md +27 -0
- package/agents/copilot/copilot-instructions.md +14 -0
- package/agents/cursor/campaigns-os.mdc +13 -0
- package/bin/campaigns-os.mjs +38 -0
- package/campaign-spec/README.md +138 -0
- package/campaign-spec/dist/analytics-vocabulary.d.ts +47 -0
- package/campaign-spec/dist/analytics-vocabulary.js +74 -0
- package/campaign-spec/dist/index.d.ts +40 -0
- package/campaign-spec/dist/index.js +77 -0
- package/campaign-spec/dist/normalize.d.ts +22 -0
- package/campaign-spec/dist/normalize.js +41 -0
- package/campaign-spec/dist/routing.d.ts +190 -0
- package/campaign-spec/dist/routing.js +263 -0
- package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +42 -0
- package/campaign-spec/dist/rules/analytics-contract-shape.js +303 -0
- package/campaign-spec/dist/rules/assembly-hints-shape.d.ts +42 -0
- package/campaign-spec/dist/rules/assembly-hints-shape.js +191 -0
- package/campaign-spec/dist/rules/campaign-metadata.d.ts +14 -0
- package/campaign-spec/dist/rules/campaign-metadata.js +40 -0
- package/campaign-spec/dist/rules/checkout-has-success-url.d.ts +31 -0
- package/campaign-spec/dist/rules/checkout-has-success-url.js +66 -0
- package/campaign-spec/dist/rules/cycle-detection.d.ts +12 -0
- package/campaign-spec/dist/rules/cycle-detection.js +141 -0
- package/campaign-spec/dist/rules/design-source-shape.d.ts +29 -0
- package/campaign-spec/dist/rules/design-source-shape.js +142 -0
- package/campaign-spec/dist/rules/downsell-without-upsell.d.ts +11 -0
- package/campaign-spec/dist/rules/downsell-without-upsell.js +40 -0
- package/campaign-spec/dist/rules/exit-intent-validation.d.ts +23 -0
- package/campaign-spec/dist/rules/exit-intent-validation.js +147 -0
- package/campaign-spec/dist/rules/funnel-count.d.ts +9 -0
- package/campaign-spec/dist/rules/funnel-count.js +37 -0
- package/campaign-spec/dist/rules/funnel-hypothesis-length.d.ts +22 -0
- package/campaign-spec/dist/rules/funnel-hypothesis-length.js +60 -0
- package/campaign-spec/dist/rules/funnel-identity.d.ts +15 -0
- package/campaign-spec/dist/rules/funnel-identity.js +57 -0
- package/campaign-spec/dist/rules/funnel-weight-sum.d.ts +20 -0
- package/campaign-spec/dist/rules/funnel-weight-sum.js +66 -0
- package/campaign-spec/dist/rules/index.d.ts +69 -0
- package/campaign-spec/dist/rules/index.js +131 -0
- package/campaign-spec/dist/rules/offer-ref-integrity.d.ts +13 -0
- package/campaign-spec/dist/rules/offer-ref-integrity.js +59 -0
- package/campaign-spec/dist/rules/package-pricing-sanity.d.ts +12 -0
- package/campaign-spec/dist/rules/package-pricing-sanity.js +42 -0
- package/campaign-spec/dist/rules/page-count.d.ts +11 -0
- package/campaign-spec/dist/rules/page-count.js +31 -0
- package/campaign-spec/dist/rules/page-id-uniqueness.d.ts +14 -0
- package/campaign-spec/dist/rules/page-id-uniqueness.js +47 -0
- package/campaign-spec/dist/rules/promo-code-input-validation.d.ts +8 -0
- package/campaign-spec/dist/rules/promo-code-input-validation.js +126 -0
- package/campaign-spec/dist/rules/promo-codes-shape.d.ts +30 -0
- package/campaign-spec/dist/rules/promo-codes-shape.js +187 -0
- package/campaign-spec/dist/rules/route-field-ignored-for-page-type.d.ts +33 -0
- package/campaign-spec/dist/rules/route-field-ignored-for-page-type.js +81 -0
- package/campaign-spec/dist/rules/route-target-resolves.d.ts +32 -0
- package/campaign-spec/dist/rules/route-target-resolves.js +112 -0
- package/campaign-spec/dist/rules/schema-version.d.ts +21 -0
- package/campaign-spec/dist/rules/schema-version.js +53 -0
- package/campaign-spec/dist/rules/sdk-version.d.ts +26 -0
- package/campaign-spec/dist/rules/sdk-version.js +96 -0
- package/campaign-spec/dist/rules/shipping-countries-shape.d.ts +10 -0
- package/campaign-spec/dist/rules/shipping-countries-shape.js +30 -0
- package/campaign-spec/dist/rules/shipping-methods-present.d.ts +10 -0
- package/campaign-spec/dist/rules/shipping-methods-present.js +26 -0
- package/campaign-spec/dist/rules/store-profile-shape.d.ts +30 -0
- package/campaign-spec/dist/rules/store-profile-shape.js +127 -0
- package/campaign-spec/dist/rules/thank-you-requirement.d.ts +16 -0
- package/campaign-spec/dist/rules/thank-you-requirement.js +50 -0
- package/campaign-spec/dist/rules/unknown-top-level-fields.d.ts +28 -0
- package/campaign-spec/dist/rules/unknown-top-level-fields.js +114 -0
- package/campaign-spec/dist/rules/upsell-has-packages.d.ts +9 -0
- package/campaign-spec/dist/rules/upsell-has-packages.js +35 -0
- package/campaign-spec/dist/rules/upsell-routing-complete.d.ts +10 -0
- package/campaign-spec/dist/rules/upsell-routing-complete.js +45 -0
- package/campaign-spec/dist/rules/upsell-without-checkout.d.ts +11 -0
- package/campaign-spec/dist/rules/upsell-without-checkout.js +44 -0
- package/campaign-spec/dist/rules/variant-labels-shape.d.ts +28 -0
- package/campaign-spec/dist/rules/variant-labels-shape.js +86 -0
- package/campaign-spec/dist/sdk-version-parse.d.ts +43 -0
- package/campaign-spec/dist/sdk-version-parse.js +62 -0
- package/campaign-spec/dist/types.d.ts +674 -0
- package/campaign-spec/dist/types.js +40 -0
- package/campaign-spec/package.json +12 -0
- package/compatibility.json +25 -0
- package/contracts/agent-relevant-change-policy.v1.json +111 -0
- package/contracts/brand-theme-source-defaults.figma-sections-export.v0.json +32 -0
- package/contracts/brand-theme-target-tokens.next-core.v0.json +65 -0
- package/contracts/campaign-cart-checkout-field-contract.v0.json +45 -0
- package/contracts/campaign-cart-sdk-support-policy.v0.json +11 -0
- package/contracts/commerce-surface-catalog.json +2452 -0
- package/contracts/fixtures/orientation/canonicalization/v1.json +34 -0
- package/contracts/fixtures/orientation/envelope/current.json +95 -0
- package/contracts/fixtures/orientation/envelope/freshness_unknown.json +97 -0
- package/contracts/fixtures/orientation/envelope/legacy_baseline.json +95 -0
- package/contracts/fixtures/orientation/envelope/orientation_available.json +136 -0
- package/contracts/fixtures/orientation/envelope/recovered_interrupted_update.json +136 -0
- package/contracts/fixtures/orientation/envelope/refused.json +100 -0
- package/contracts/fixtures/orientation/envelope/restart_required.json +137 -0
- package/contracts/fixtures/orientation/envelope/updated.json +135 -0
- package/contracts/fixtures/orientation/hostile-target/README.md +61 -0
- package/contracts/fixtures/orientation/hostile-target/manifest.json +82 -0
- package/contracts/fixtures/orientation/hostile-target/repo/CHANGELOG.md +18 -0
- package/contracts/fixtures/orientation/hostile-target/repo/bin/intended.mjs +12 -0
- package/contracts/fixtures/orientation/hostile-target/repo/bin/tripwire.mjs +15 -0
- package/contracts/fixtures/orientation/hostile-target/repo/contracts/release-ledger.json +48 -0
- package/contracts/fixtures/orientation/hostile-target/repo/contracts/supported-surface.json +17 -0
- package/contracts/fixtures/orientation/hostile-target/repo/docs/example-contract.md +13 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hooks/post-checkout +5 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hooks/post-merge +5 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hooks/pre-commit +5 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hostile-dependency-tripwire/package.json +15 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hostile-dependency-tripwire/tripwire.mjs +9 -0
- package/contracts/fixtures/orientation/hostile-target/repo/package.json +20 -0
- package/contracts/fixtures/orientation/hostile-target/repo/schemas/example.v0.schema.json +15 -0
- package/contracts/fixtures/orientation/release-gate/cases.json +1073 -0
- package/contracts/fixtures/runtime-recipe/accept/current.json +299 -0
- package/contracts/fixtures/runtime-recipe/accept/minimal.json +294 -0
- package/contracts/fixtures/runtime-recipe/dist-states.json +51 -0
- package/contracts/fixtures/runtime-recipe/manifest.json +85 -0
- package/contracts/fixtures/runtime-recipe/reject/advisory-enforcement.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/allowlist-without-hosts.json +297 -0
- package/contracts/fixtures/runtime-recipe/reject/committed-output-claim.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/engines-disagreement-warns.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/lifecycle-scripts-enabled.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/missing-required-field.json +251 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-kind.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-network-policy.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-output-check.json +310 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-revision.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-step-id.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unperformable-check-skipped.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unpinned-lockfile.json +299 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/assembly-report.json +180 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/build-context.json +115 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/doctor-output.json +29 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/qa-verdict.json +27 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/campaign-runtime.build.json +141 -0
- package/contracts/migration-sidecar-bundle.v0.json +149 -0
- package/contracts/orientation-limits.v1.json +41 -0
- package/contracts/orientation-reason-codes.v1.json +196 -0
- package/contracts/private-template-sources.json +8 -0
- package/contracts/release-ledger.json +4598 -0
- package/contracts/reserved-skill-names.json +13 -0
- package/contracts/runtime-recipe.campaigns-os-node-v1.json +299 -0
- package/contracts/supported-surface.json +180 -0
- package/contracts/template-brand-contract.apollo-mv-single-step.v0.json +27 -0
- package/contracts/template-brand-contract.apollo.v0.json +27 -0
- package/contracts/template-brand-contract.demeter.v0.json +27 -0
- package/contracts/template-brand-contract.olympus-mv-single-step.v0.json +27 -0
- package/contracts/template-brand-contract.olympus-mv-two-step.v0.json +28 -0
- package/contracts/template-brand-contract.olympus.v0.json +27 -0
- package/contracts/template-brand-contract.shared-commerce.v0.json +190 -0
- package/contracts/template-brand-contract.shop-single-step.v0.json +27 -0
- package/contracts/template-brand-contract.shop-three-step.v0.json +29 -0
- package/contracts/template-slot-manifest.apollo-mv-single-step.v0.json +14 -0
- package/contracts/template-slot-manifest.apollo.v0.json +12 -0
- package/contracts/template-slot-manifest.demeter.v0.json +30 -0
- package/contracts/template-slot-manifest.olympus-mv-single-step.v0.json +14 -0
- package/contracts/template-slot-manifest.olympus-mv-two-step.v0.json +15 -0
- package/contracts/template-slot-manifest.olympus.v0.json +12 -0
- package/contracts/template-slot-manifest.shared-content-core.v0.json +4254 -0
- package/contracts/template-slot-manifest.shop-single-step.v0.json +47 -0
- package/contracts/template-slot-manifest.shop-three-step.v0.json +33 -0
- package/docs/brand-theme-bridge.md +159 -0
- package/docs/build-packet.md +1300 -0
- package/docs/campaign-build-brief.md +145 -0
- package/docs/campaign-standardization-report.md +329 -0
- package/docs/campaigns-os-build-flow.md +117 -0
- package/docs/design-source-package.md +784 -0
- package/docs/legacy-migration.md +58 -0
- package/docs/migration-sidecar-bundle.md +139 -0
- package/docs/orientation-contract-reference.md +1220 -0
- package/docs/polish-evidence.md +502 -0
- package/docs/qa-and-test-orders.md +1691 -0
- package/docs/release-ledger-authoring-guide.md +274 -0
- package/docs/runtime-readiness.md +211 -0
- package/docs/supported-surface.md +83 -0
- package/docs/versioning.md +55 -0
- package/docs/workflow-findings-sidecar.md +588 -0
- package/package.json +135 -0
- package/prompts/first-build.md +27 -0
- package/prompts/friction-log.md +30 -0
- package/schemas/campaign-build-brief.v1.schema.json +149 -0
- package/schemas/campaign-design-source-package.v0.schema.json +697 -0
- package/schemas/campaign-runtime-assembly-report.v0.schema.json +390 -0
- package/schemas/campaign-runtime-build-context.v0.schema.json +339 -0
- package/schemas/campaign-runtime-build-packet.v0.schema.json +452 -0
- package/schemas/campaign-spec.v4.schema.json +582 -0
- package/schemas/campaigns-os-doctor-output.v0.schema.json +43 -0
- package/schemas/campaigns-os-legacy-migration-inventory.v0.schema.json +112 -0
- package/schemas/campaigns-os-legacy-provisioning-plan.v0.schema.json +38 -0
- package/schemas/campaigns-os-legacy-provisioning-receipt.v0.schema.json +57 -0
- package/schemas/campaigns-os-qa-verdict-sidecar.v0.schema.json +150 -0
- package/schemas/campaigns-os-qa-verdict.v0.schema.json +438 -0
- package/schemas/campaigns-os-release-ledger.v1.schema.json +162 -0
- package/schemas/campaigns-os-run-record.v0.schema.json +343 -0
- package/schemas/campaigns-os-runtime-recipe.v1.schema.json +313 -0
- package/schemas/campaigns-os-sidecar-bundle-conformance.v0.schema.json +78 -0
- package/schemas/campaigns-os-tooling-orientation.v1.schema.json +381 -0
- package/schemas/campaigns-os-workflow-finding.v0.schema.json +134 -0
- package/schemas/source-html-manifest.v0.schema.json +252 -0
- package/skills/next-campaigns-build/SKILL.md +73 -0
- package/skills/next-campaigns-os/SKILL.md +101 -0
- package/skills/next-campaigns-os/references/session-intake.md +160 -0
- package/skills/next-campaigns-os-setup/SKILL.md +22 -0
- package/skills/next-campaigns-polish/SKILL.md +145 -0
- package/skills/next-campaigns-qa/SKILL.md +92 -0
- package/skills.json +56 -0
- package/skills.sh +64 -0
- package/src/adapter-decision-contract.mjs +333 -0
- package/src/brand-theme.mjs +1151 -0
- package/src/browser-launch.mjs +79 -0
- package/src/build-brief.mjs +781 -0
- package/src/built-site-scope.mjs +312 -0
- package/src/campaign-ecosystem.mjs +734 -0
- package/src/campaign-identity.mjs +405 -0
- package/src/campaign-workspace.mjs +127 -0
- package/src/checkpoint-waiver.mjs +302 -0
- package/src/cli.mjs +13442 -0
- package/src/commercial-journey.mjs +1119 -0
- package/src/commercial-parity.mjs +965 -0
- package/src/consent.mjs +347 -0
- package/src/content-residue.mjs +322 -0
- package/src/deadline.mjs +81 -0
- package/src/design-source-package.mjs +2604 -0
- package/src/deviation.mjs +107 -0
- package/src/doctor-check-registry.mjs +49 -0
- package/src/doctor-sidecar.mjs +106 -0
- package/src/finding-cause.mjs +557 -0
- package/src/findings.mjs +326 -0
- package/src/fs-identity.mjs +68 -0
- package/src/gate-actions.mjs +105 -0
- package/src/html-scan.mjs +53 -0
- package/src/install-mode.mjs +272 -0
- package/src/legacy-migration.d.ts +128 -0
- package/src/legacy-migration.mjs +510 -0
- package/src/lifecycle.mjs +338 -0
- package/src/local-proof.mjs +401 -0
- package/src/map-pin-writeback.mjs +210 -0
- package/src/orchestration-stage-contract.mjs +81 -0
- package/src/package-install-fixture.mjs +33 -0
- package/src/page-kit-build-summary.mjs +175 -0
- package/src/page-kit-campaign-config.mjs +57 -0
- package/src/page-kit-sdk-version.mjs +392 -0
- package/src/page-kit-store-profile.mjs +369 -0
- package/src/page-kit-sync.mjs +162 -0
- package/src/polish-browser.mjs +867 -0
- package/src/polish-capture.mjs +1094 -0
- package/src/polish-deadline.mjs +51 -0
- package/src/polish-gate.mjs +739 -0
- package/src/polish-node.mjs +639 -0
- package/src/polish-page-load.mjs +1100 -0
- package/src/private-template-source.mjs +237 -0
- package/src/proof-policy.mjs +82 -0
- package/src/qa-analytics-correctness.mjs +307 -0
- package/src/qa-analytics-errors.mjs +38 -0
- package/src/qa-analytics-parity.mjs +699 -0
- package/src/qa-binding-evidence.mjs +140 -0
- package/src/qa-browser.mjs +6608 -0
- package/src/qa-cart-entry.mjs +406 -0
- package/src/qa-commercial-parity.mjs +641 -0
- package/src/qa-node.mjs +3620 -0
- package/src/qa-order-bump.mjs +381 -0
- package/src/qa-parity-capture.mjs +428 -0
- package/src/qa-parity-fixture.mjs +359 -0
- package/src/qa-publish.mjs +362 -0
- package/src/qa-purchase-data-layer.mjs +263 -0
- package/src/qa-route-probe.mjs +272 -0
- package/src/qa-sidecar.mjs +188 -0
- package/src/qa-test-order-topology.mjs +207 -0
- package/src/qa-url-privacy.mjs +13 -0
- package/src/qa-verdict-discovery.mjs +192 -0
- package/src/qa-verdict-publish.mjs +105 -0
- package/src/qa-verdict.mjs +287 -0
- package/src/remit.mjs +388 -0
- package/src/repo-scan.mjs +83 -0
- package/src/route-identity.mjs +133 -0
- package/src/run-record-closeout.mjs +229 -0
- package/src/run-record.mjs +839 -0
- package/src/run-session.mjs +226 -0
- package/src/runtime-state-ignore.mjs +113 -0
- package/src/sdk-attribute-index.mjs +212 -0
- package/src/sdk-markup.mjs +358 -0
- package/src/sdk-meta-tags.mjs +52 -0
- package/src/shell-token.mjs +7 -0
- package/src/sidecar-bundle.mjs +399 -0
- package/src/source-asset-crawl.mjs +469 -0
- package/src/source-html-intake.mjs +627 -0
- package/src/source-html-manifest.mjs +276 -0
- package/src/source-prep.mjs +284 -0
- package/src/spec-derive-store.mjs +431 -0
- package/src/spec-derive.mjs +514 -0
- package/src/spec-fetch.mjs +66 -0
- package/src/spec-hash.mjs +48 -0
- package/src/spec-identity.mjs +27 -0
- package/src/stage-ledger.mjs +509 -0
- package/src/standardization-report.mjs +1297 -0
- package/src/template-brand-contract.mjs +473 -0
- package/src/template-freshness.mjs +196 -0
- package/src/template-reference.mjs +81 -0
- package/src/template-slot-manifest.mjs +150 -0
- package/src/text-safety.mjs +62 -0
- package/src/theme-gate.mjs +185 -0
- 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.
|