@nextcommerce/campaigns-os 1.46.0 → 1.48.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/CHANGELOG.md +347 -3
- package/README.md +31 -4
- package/agents/claude/CLAUDE.md +2 -0
- package/agents/codex/AGENTS.md +1 -0
- package/agents/copilot/copilot-instructions.md +1 -0
- package/agents/cursor/campaigns-os.mdc +1 -1
- package/compatibility.json +1 -1
- package/contracts/commerce-surface-catalog.json +1204 -129
- package/contracts/effects.v1.json +3 -3
- package/contracts/release-ledger.json +803 -0
- package/contracts/supported-surface.json +2 -2
- package/contracts/template-brand-contract.shared-commerce.v0.json +3 -3
- package/docs/build-packet.md +23 -3
- package/docs/campaign-build-brief.md +25 -28
- package/docs/local-setup.md +13 -5
- package/docs/orientation-contract-reference.md +1 -1
- package/docs/qa-and-test-orders.md +68 -3
- package/docs/runtime-readiness.md +1 -1
- package/docs/sdk-storage-compatibility.md +1 -1
- package/docs/skills-revision.md +10 -10
- package/package.json +1 -1
- package/skills/campaign-lifecycle-orientation/SKILL.md +3 -3
- package/skills/campaign-readback-classification/SKILL.md +3 -3
- package/skills/campaign-run-evidence/SKILL.md +9 -4
- package/skills/contribution-intake/SKILL.md +3 -3
- package/skills/next-campaigns-build/SKILL.md +5 -4
- package/skills/next-campaigns-os/SKILL.md +3 -3
- package/skills/next-campaigns-os-setup/SKILL.md +3 -3
- package/skills/next-campaigns-polish/SKILL.md +4 -4
- package/skills/next-campaigns-qa/SKILL.md +4 -4
- package/skills.json +10 -10
- package/src/built-script-syntax.mjs +116 -15
- package/src/cli.mjs +6 -3
- package/src/content-residue.mjs +18 -90
- package/src/diagnostic.mjs +2 -1
- package/src/doctor/checks.mjs +21 -30
- package/src/doctor/inspect.mjs +13 -3
- package/src/doctor/next-step.mjs +4 -0
- package/src/gate-actions.mjs +8 -0
- package/src/local-preview-policy.mjs +92 -0
- package/src/page-kit-sdk-version.mjs +8 -1
- package/src/polish-node.mjs +26 -2
- package/src/progress-node.mjs +5 -1
- package/src/qa-analytics-parity.mjs +37 -2
- package/src/qa-binding-evidence.mjs +5 -3
- package/src/qa-browser.mjs +136 -25
- package/src/qa-node.mjs +33 -4
- package/src/readback.mjs +19 -10
- package/src/sdk-markup.mjs +6 -45
- package/src/sdk-storage-compatibility.mjs +3 -2
- package/src/source-prep.mjs +1 -1
- package/src/tooling-setup.mjs +9 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "The downstream contract manifest. Everything listed here is SUPPORTED SURFACE: consumers (campaigns-agent, campaign-builder, the private ops repo, page-kit campaign repos) may depend on it, and changing it is a deliberate act — hashed entries require a surface_version bump in the same change (check-supported-surface.mjs --base, mirroring the skills.json bump gate), named entries must keep existing at their path, cli_commands must keep resolving in the CLI dispatch, package_exports must stay exported, and every entry must ship in the npm pack (files[] coverage). Anything NOT listed here — src/** internals, scripts/** checkers, examples/**, prompts/**, contracts/** other than this file and the entries named[] below (the orientation contract, the release ledger, and the consumer-facing orientation fixtures) — is implementation: consumers may read it for context but must not build on it, and it can change without notice. Rationale and the compatibility promise: docs/supported-surface.md.",
|
|
3
|
-
"surface_version": "1.
|
|
3
|
+
"surface_version": "1.48.0",
|
|
4
4
|
"package_exports": [
|
|
5
5
|
"./commercial-journey",
|
|
6
6
|
"./commercial-parity",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"sha256": "dfc9abed38d456969e47a21f606d308a03bdf036f3466f7d6e47a602747dacf4"
|
|
121
121
|
},
|
|
122
122
|
"contracts/effects.v1.json": {
|
|
123
|
-
"sha256": "
|
|
123
|
+
"sha256": "5f8c6f2f88fc9e0c11c09e265640071d3c1d81fd96c6e05283ed5cfed21d1d59"
|
|
124
124
|
},
|
|
125
125
|
"schemas/campaigns-os-effects.v1.schema.json": {
|
|
126
126
|
"sha256": "3eadd22169ab98bc7c2f682af2751267605170581182158d96be035b0cfe44dc"
|
|
@@ -55,12 +55,12 @@
|
|
|
55
55
|
},
|
|
56
56
|
"asset_pin": {
|
|
57
57
|
"repo": "NextCommerceCo/campaign-cart-starter-templates",
|
|
58
|
-
"sha": "
|
|
58
|
+
"sha": "37a8d945db4c360739ded98348814cfd632da94f",
|
|
59
59
|
"path": "src/<family>/assets/<asset>",
|
|
60
60
|
"note": "asset_sha256 is the sha256 of each shipped starter asset at this commit (the commerce-surface-catalog _synced_from_sha pin; the bytes are identical across every family). A served asset whose bytes hash to the shipped value is the untouched starter asset and counts as residue even though its markup names no method; different bytes mean it was edited in place. check-template-doctrine verifies these hashes against the pinned checkout."
|
|
61
61
|
},
|
|
62
|
-
"rule": "Remove unless the method is listed in CampaignSpec available_payment_methods / available_express_payment_methods. Template chrome outside the SDK payment-method include counts as implied payment-method residue.",
|
|
63
|
-
"note": "paypal intentionally has two selectors. QA fails if any selector for an unsupported method remains visible."
|
|
62
|
+
"rule": "Remove unless the method is listed in CampaignSpec available_payment_methods / available_express_payment_methods. Template chrome outside the SDK payment-method include counts as implied payment-method residue. The starter payment-logos.html row renders one <img data-payment-logo> per method and hides the ones the campaign does not offer, so it needs no removal; when one of its logos is flagged, set payment_flags.show_<method>: false in the page frontmatter instead of deleting markup.",
|
|
63
|
+
"note": "paypal intentionally has two selectors. QA fails if any selector for an unsupported method remains visible. images/upsell-payment-logos.svg stays listed: the starter still renders it ungated under payment_flags.style: flat and on the olympus-mv-two-step select page."
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"demo_assets": {
|
package/docs/build-packet.md
CHANGED
|
@@ -858,15 +858,35 @@ file. A referenced local script that is not in the built output is a warning,
|
|
|
858
858
|
not a blocker, under `built_output.script_syntax.missing_script`, one warning
|
|
859
859
|
per src naming the pages that load it: the browser gets a 404 for it and
|
|
860
860
|
nothing it would define runs, but whether the page needs it is not known here.
|
|
861
|
+
Missing scripts are grouped by the URL the browser resolves, not the raw src,
|
|
862
|
+
so `check	out.js` and `checkout.js` (the URL parser removes the tab) are one
|
|
863
|
+
warning; the src shown is the first spelling met.
|
|
861
864
|
The src is also listed in `scripts_unresolved[]`. While a parse failure blocks
|
|
862
865
|
the gate, the missing scripts stay on the gate's `warned[]` rather than also
|
|
863
866
|
surfacing as warnings.
|
|
864
867
|
|
|
868
|
+
A `<script>` the page ends inside, with no `</script>` before end of file, is
|
|
869
|
+
not parsed: the browser never runs a script element whose end tag never
|
|
870
|
+
arrives. Doctor warns about it under `built_output.script_syntax.unclosed_script`,
|
|
871
|
+
one warning per page, because a page that ends mid-script is usually truncated
|
|
872
|
+
output. QA neither fetches nor parses such a script.
|
|
873
|
+
|
|
874
|
+
**Symlinks under `_site`.** A script that is a symlink, or sits under a
|
|
875
|
+
symlinked directory, is read the way a static server serves it, by following
|
|
876
|
+
the link, as long as its real path stays inside the site root (`_site/`). A
|
|
877
|
+
parse failure in it is reported under the path the page loads. A script whose
|
|
878
|
+
real path resolves outside the site root is not read. Doctor warns, and does
|
|
879
|
+
not block, under `built_output.script_syntax.symlink_outside_site`, naming the
|
|
880
|
+
link (never its target) and saying the target is outside the site root. The
|
|
881
|
+
link is listed in `scripts_outside_site[]` with `file`, `src` and `pages`, not
|
|
882
|
+
in `scripts_unresolved[]`.
|
|
883
|
+
|
|
865
884
|
The gate's evidence lands beside the other checkpoint gates at
|
|
866
885
|
`derived.checkpoint_gates[]` (`id: built_output.script_syntax`, status `pass` |
|
|
867
886
|
`blocked` | `not_applicable`, `findings[]` with `file`, `line`, `column`,
|
|
868
|
-
`source_type` and `pages`, `warned[]` with `src` and `pages
|
|
869
|
-
`
|
|
887
|
+
`source_type` and `pages`, `warned[]` with `code`, `src` and `pages` (and
|
|
888
|
+
`file` for a symlink outside the site root), `scripts_scanned`,
|
|
889
|
+
`scripts_unresolved[]`, `scripts_outside_site[]`, `pages_scanned`). Fixtures: `fixtures/script-syntax/{good,bad}`. It passes, parsing every
|
|
870
890
|
local script the pages load and with no missing-script warning, on the
|
|
871
891
|
canonical rendered output of every certified starter family
|
|
872
892
|
(`fixtures/certified-families/`). QA applies the same rule to the page scripts
|
|
@@ -1433,7 +1453,7 @@ Each `manifest.pages[]` entry MAY carry a `source_hash` field — the sha256 hex
|
|
|
1433
1453
|
Behavior:
|
|
1434
1454
|
|
|
1435
1455
|
- Optional on the producer side. Producers that don't emit `source_hash` (pre-Slice-6 manifests, template-stock, hand-authored) keep working; doctor's drift check is silent without a hash to compare.
|
|
1436
|
-
- Warning severity only. A drift never blocks a build
|
|
1456
|
+
- Warning severity only. A drift never blocks a build. The hash doctor compares is the one intake recorded in the packet, so editing the manifest alone does not clear the warning: re-running `start` or `prepare-build` with `--force` records the current file, and also clears recorded stage evidence. A revision made after build belongs in the page-kit source under `src/<route>/`; the source HTML stays the design provenance.
|
|
1437
1457
|
- The warning names the file path and includes both hashes (truncated to 12 chars) so the operator can confirm which file diverged without re-running the producer.
|
|
1438
1458
|
|
|
1439
1459
|
### Reference AI-generated producer
|
|
@@ -62,34 +62,31 @@ Doctor warns when generated guided drafts still need answers, a brief allows pay
|
|
|
62
62
|
|
|
63
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
64
|
|
|
65
|
-
##
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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`).
|
|
65
|
+
## The Merchant's Own Claims Are Not Reviewed
|
|
66
|
+
|
|
67
|
+
Proof and urgency content the merchant supplies is the merchant's
|
|
68
|
+
responsibility, not Campaigns OS's: reviews and testimonials, ratings and
|
|
69
|
+
counts, "Verified Purchase" labels, recent-purchase popups, stock counters,
|
|
70
|
+
countdowns, guarantees and press mentions. The doctor does not scan for it and
|
|
71
|
+
QA does not assert on it. When the prepared source design carries these
|
|
72
|
+
elements, the build reproduces them as designed. The starter templates ship
|
|
73
|
+
without some of them; that is not a reason to drop the source's.
|
|
74
|
+
|
|
75
|
+
What the doctor does scan built output for is template residue: the starter
|
|
76
|
+
templates' own demo strings and bracket-style stubs
|
|
77
|
+
(`content_residue.demo_residue`, a warning). It also warns on promo copy
|
|
78
|
+
claiming a discount above the CampaignSpec maximum
|
|
79
|
+
(`template_contract.discount_claim_residue`, or
|
|
80
|
+
`template_contract.discount_claim_unverified` when the spec sets no maximum),
|
|
81
|
+
because that copy disagrees with the campaign's own pricing. Nothing downstream
|
|
82
|
+
blocks on either.
|
|
83
|
+
|
|
84
|
+
Two content checks do block: the needs-merchant-input marker
|
|
85
|
+
(`content_residue.needs_merchant_input`), and, on a brief-backed build only,
|
|
86
|
+
starter countdown chrome the brief payload does not verify
|
|
87
|
+
(`content_residue.unverified_urgency`). Use the brief to record which claims
|
|
88
|
+
are approved and which language is forbidden (see the high-impact questions
|
|
89
|
+
above).
|
|
93
90
|
|
|
94
91
|
## QA Policy Scope
|
|
95
92
|
|
package/docs/local-setup.md
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
# Local campaign setup
|
|
2
2
|
|
|
3
|
-
For a new campaign,
|
|
3
|
+
For a new campaign, create an empty working folder and run this from it:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.
|
|
6
|
+
npm init -y && npm install --save-exact next-campaign-page-kit@0.2.0 && npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.48.0 && npx --no-install campaigns-os tooling setup --target . --platform claude
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
+
`npm init -y` gives the folder its own `package.json`. Without one, npm
|
|
10
|
+
installs into the nearest parent folder that has a `package.json` or
|
|
11
|
+
`node_modules`, so a campaign folder created inside another project would add
|
|
12
|
+
page-kit and the toolkit to that project instead.
|
|
13
|
+
|
|
9
14
|
Review the release source/provenance before installation as described in
|
|
10
15
|
`AGENTS.md`. npm installs the dependencies first; `--no-install` then runs only
|
|
11
|
-
the project's installed CLI.
|
|
12
|
-
|
|
16
|
+
the project's installed CLI. Page-kit stays a runtime dependency and the
|
|
17
|
+
toolkit a dev dependency: installing page-kit with `--save-dev` would move it
|
|
18
|
+
out of `dependencies` and break builds that run `npm ci --omit=dev`. Keep
|
|
19
|
+
`package.json` and `package-lock.json` in Git. For an existing project, preserve its reviewed pin: run `npm ci`, then
|
|
13
20
|
`npx --no-install campaigns-os tooling setup --target . --platform claude`
|
|
14
21
|
on a release that supports setup. Changing the pin is a separate update.
|
|
15
22
|
|
|
16
23
|
Setup checks the exact toolkit pin, its lockfile version and the installed
|
|
17
|
-
page-kit dependency before it changes files
|
|
24
|
+
page-kit dependency before it changes files, and warns when page-kit is
|
|
25
|
+
declared only in `devDependencies`. It composes the existing
|
|
18
26
|
installers to:
|
|
19
27
|
|
|
20
28
|
1. Install the QA browser through this toolkit's own Playwright package.
|
|
@@ -26,7 +26,7 @@ Ledger schema id: `campaigns-os-release-ledger/v1`
|
|
|
26
26
|
Change policy version: `1.0.0`
|
|
27
27
|
Reason-code vocabulary version: `1.0.0`
|
|
28
28
|
Limits version: `1.0.0`
|
|
29
|
-
Supported surface at generation time: `1.
|
|
29
|
+
Supported surface at generation time: `1.48.0`
|
|
30
30
|
|
|
31
31
|
## Forward compatibility
|
|
32
32
|
|
|
@@ -96,6 +96,32 @@ recapture. A hosted target (`netlify`, `cloudflare-pages`, …) is unaffected:
|
|
|
96
96
|
its build stage renders production as before and `page-kit parity` refuses the
|
|
97
97
|
packet (`local_proof.parity.not_local_serve`).
|
|
98
98
|
|
|
99
|
+
### Missing evidence carried forward on the local preview
|
|
100
|
+
|
|
101
|
+
On a `local-serve` packet served from a loopback host (`localhost`,
|
|
102
|
+
`127.0.0.1`, `[::1]`, for both `deploy.preview_url` and `--base-url`), some
|
|
103
|
+
missing evidence is carried forward as a warning instead of blocking the
|
|
104
|
+
loop. The campaign must still prove its commerce there: store and campaign
|
|
105
|
+
binding, routes, SDK loading, prices and a typed-card order. Only these
|
|
106
|
+
checks are carried forward:
|
|
107
|
+
|
|
108
|
+
| Check | When |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| `polish.evidence_missing`, `polish.report_missing` | Polish was never recorded for this build. |
|
|
111
|
+
| `polish.hidden_eager_media.no_capturable_routes` | Every mapped page is template stock (`skip_reason`), so polish capture has no design route to capture. |
|
|
112
|
+
| `polish.hidden_eager_media.capture_malformed` | Only when no page-load capture was recorded at all. |
|
|
113
|
+
| Template-residue severity | With `theme_gate.nothing_generatable`, the starter template is the design, so residue findings are warnings rather than blockers. |
|
|
114
|
+
|
|
115
|
+
A carried-forward gate has status `carried_forward`. Doctor reports it as a
|
|
116
|
+
warning starting "Carried forward on the local preview"; `next` moves past
|
|
117
|
+
polish to deploy and QA; QA records it as a `warn` row, so the verdict is at
|
|
118
|
+
best `ready_with_exceptions`. The evidence is reported as missing, never as
|
|
119
|
+
passed. Any other check keeps its meaning. A hosted preview or production
|
|
120
|
+
packet, and a `local-serve` packet served from any other host, gets the strict
|
|
121
|
+
gates. `record polish` and the waiver commands also keep them strict. Progress
|
|
122
|
+
snapshots record a carried-forward gate as `not_applicable`, because their
|
|
123
|
+
schema has no carried-forward state.
|
|
124
|
+
|
|
99
125
|
## Resolve
|
|
100
126
|
|
|
101
127
|
Use resolve before a full run:
|
|
@@ -482,7 +508,15 @@ the three checks and each `evidence.checks[]` entry carries `kind`
|
|
|
482
508
|
(`family_shell` or `sdk_wiring`).
|
|
483
509
|
The upsell and checkout bundle price checks count the SDK's
|
|
484
510
|
`[data-next-bundle-display*='price']` alongside the contract's price rows; a
|
|
485
|
-
hidden, zero-size or empty bundle-display node does not count.
|
|
511
|
+
hidden, zero-size or empty bundle-display node does not count. A checkout that
|
|
512
|
+
shows no price because QA opened it directly, with an empty SDK cart and no
|
|
513
|
+
package selection of its own, is `skipped` rather than failed:
|
|
514
|
+
`pricing.checkout_price_visible` records `cart_count` and
|
|
515
|
+
`checkout_selection_surface` and says so. That checkout's cart is filled on
|
|
516
|
+
an earlier page, for example by a landing link carrying `forcePackageId`, and
|
|
517
|
+
the test order enters it from there. A checkout with its own package
|
|
518
|
+
selection, or a filled cart, still fails when no price shows. If the cart
|
|
519
|
+
probe itself fails, the row stays failed and records `empty_cart_probe_error`.
|
|
486
520
|
Promoted template families must also have
|
|
487
521
|
`contracts/template-brand-contract.<family>.v0.json`; QA emits a blocker if the
|
|
488
522
|
selected family is missing its brand/residue/pricing contract instead of
|
|
@@ -1004,6 +1038,22 @@ the baseline, and when no candidate page is captured the leg emits
|
|
|
1004
1038
|
`no_in_scope_page_captured` (skipped) or `no_capture_page_answered`
|
|
1005
1039
|
(`FAIL`/`BLOCKER`).
|
|
1006
1040
|
|
|
1041
|
+
The automatic candidate is never a receipt page: the campaign root is not one,
|
|
1042
|
+
and a built entry counts as one only when its topology `page_type` is `receipt`
|
|
1043
|
+
or `thankyou`. The leg does not substitute the topology's receipt step, because
|
|
1044
|
+
a receipt loaded without an order fires no Purchase. So when the automatic
|
|
1045
|
+
candidate is not a receipt and fires no Purchase, `purchase-present` is
|
|
1046
|
+
`MANUAL_REVIEW`/`WARN` instead of a blocker, and its `evidence.page_mismatch`
|
|
1047
|
+
names the mismatch: `reason` (`receipt_baseline_non_receipt_candidate` when the
|
|
1048
|
+
baseline fired a Purchase, else `candidate_not_receipt`),
|
|
1049
|
+
`baseline_fired_purchase`, `candidate_receipt: false`, `candidate_source`
|
|
1050
|
+
(`campaign_root` or `built_entry`) and `candidate_page_type`. To compare
|
|
1051
|
+
Purchase, pass the candidate receipt with `--analytics-candidate`. An explicit
|
|
1052
|
+
candidate, or an automatic one whose page type is a receipt, still blocks on a
|
|
1053
|
+
missing Purchase, and a non-receipt candidate that does fire a Purchase gets the
|
|
1054
|
+
full set of Purchase checks, with its page recorded in `evidence.candidate_page`
|
|
1055
|
+
on `purchase-present`.
|
|
1056
|
+
|
|
1007
1057
|
| Flag | Meaning |
|
|
1008
1058
|
|---|---|
|
|
1009
1059
|
| `--analytics-baseline <url>` | Legacy funnel URL to capture as the parity baseline (enables the leg) |
|
|
@@ -1019,7 +1069,9 @@ Point both at the **thank-you / receipt page** for the highest-value `dl_purchas
|
|
|
1019
1069
|
check, or drive the same offer through each funnel so client-fired values line up.
|
|
1020
1070
|
|
|
1021
1071
|
What the diff asserts (BLOCKER unless noted):
|
|
1022
|
-
- `purchase-present` — candidate fires a purchase event
|
|
1072
|
+
- `purchase-present` — candidate fires a purchase event (`MANUAL_REVIEW`
|
|
1073
|
+
naming the page mismatch when the automatic candidate is not a receipt and
|
|
1074
|
+
fires none; see above).
|
|
1023
1075
|
- `purchase-value` / `purchase-currency` — match the baseline's **client-fired**
|
|
1024
1076
|
value (compared client-vs-client; never vs a backend total, since tax is
|
|
1025
1077
|
computed backend and is not in the client value on headless checkouts).
|
|
@@ -1731,6 +1783,17 @@ shared real inbox rather than a synthetic one). When neither is set, the runner
|
|
|
1731
1783
|
falls back to a single stable synthetic address — still one reused customer, but
|
|
1732
1784
|
not deliverable.
|
|
1733
1785
|
|
|
1786
|
+
Reusing one customer has one cost. The platform refuses an order whose customer,
|
|
1787
|
+
items and total match one it accepted or is still processing in the last 30
|
|
1788
|
+
minutes ("Duplicate order detected, order not created"). A successful test order
|
|
1789
|
+
does not hold that window, so the paths of one run do not collide. Two QA runs
|
|
1790
|
+
against the same campaign at once do, and so can a rerun soon after an attempt
|
|
1791
|
+
that died mid-submit. QA reports it on the path's `browser-test-order` row as
|
|
1792
|
+
`order create rejected: HTTP 400: Duplicate order detected …` followed by
|
|
1793
|
+
`duplicate_order` and the remedy: re-run with a different `--test-email-prefix`
|
|
1794
|
+
(or `--test-email`), or wait. The shipping address is not part of the match, so
|
|
1795
|
+
changing `--test-address1` does not help.
|
|
1796
|
+
|
|
1734
1797
|
The browser driver intentionally behaves like a user:
|
|
1735
1798
|
|
|
1736
1799
|
- package selection uses rendered `[data-next-package-id]` controls when
|
|
@@ -1901,7 +1964,9 @@ ASCII whitespace and compared case-insensitively as the browser does; classic
|
|
|
1901
1964
|
`built_output.script_syntax` in [the Build Packet doc](build-packet.md).
|
|
1902
1965
|
|
|
1903
1966
|
External executable scripts other than the recognized jsDelivr Campaign Cart
|
|
1904
|
-
loader
|
|
1967
|
+
loader or index are inspected only on the page's origin. Three Campaign Cart
|
|
1968
|
+
paths count as the SDK and are never fetched: `dist/loader.js` (the one the
|
|
1969
|
+
starter templates load), `dist/index.js` and `public/loader.js`. Each page admits at most
|
|
1905
1970
|
6 such references; each run fetches at most 24 distinct URLs (deduplicated),
|
|
1906
1971
|
256 KiB per response and 6 MiB aggregate, 5 seconds per request including body
|
|
1907
1972
|
read (at most 30 seconds of sequential config requests per page). Redirects,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
How a checkout of this repository at one commit becomes a usable installed runtime, and how a consumer decides whether a prepared one is still trustworthy. Everything below is generated from `contracts/runtime-recipe.campaigns-os-node-v1.json`, which is the only authority for these values.
|
|
10
10
|
|
|
11
|
-
Recipe kind `campaigns-os-node-v1`, revision `1.0.2`, validated by `schemas/campaigns-os-runtime-recipe.v1.schema.json` (`Campaigns OS Runtime Recipe v1`). Supported surface at generation time: `1.
|
|
11
|
+
Recipe kind `campaigns-os-node-v1`, revision `1.0.2`, validated by `schemas/campaigns-os-runtime-recipe.v1.schema.json` (`Campaigns OS Runtime Recipe v1`). Supported surface at generation time: `1.48.0`.
|
|
12
12
|
|
|
13
13
|
## What this is
|
|
14
14
|
|
|
@@ -8,7 +8,7 @@ npx --no-install campaigns-os sdk storage-check --target . --target-sdk 0.4.38 -
|
|
|
8
8
|
|
|
9
9
|
Omit `--json` for the concise human report. Exit 0 means source-compatible; exit 2 means incompatible or unknown; invalid arguments or manifests exit 1. No merchant files or pins are rewritten. This is independent of doctor's built HTML markup check.
|
|
10
10
|
|
|
11
|
-
The manifest is generated and owned by Campaign Cart, from its storage registry. The scanner has no independent SDK key list and fetches no remote code. Supply `docs/compatibility/storage-migrations.v1.json` from an unmodified checkout of a Campaign Cart release tag, v0.4.40 or later. The report records the full file SHA-256, SDK version, supported target range, registry/extractor/input digest, and Git provenance. When the supplied bytes equal their repository HEAD blob, provenance is `verified-git-blob` with commit and repository path. Proposed, modified, or copied manifests are labeled `unverified-local-file`; the label never certifies a release. Review/pin SDK provenance separately before acting on findings. Targets outside the manifest's supported SDK range report unknown.
|
|
11
|
+
The manifest is generated and owned by Campaign Cart, from its storage registry. The scanner has no independent SDK key list and fetches no remote code. Supply `docs/compatibility/storage-migrations.v1.json` from an unmodified checkout of a Campaign Cart release tag, v0.4.40 or later. The report records the full file SHA-256, SDK version, supported target range, registry/extractor/input digest, and Git provenance. When the supplied bytes equal their repository HEAD blob, provenance is `verified-git-blob` with commit and repository path. Proposed, modified, or copied manifests are labeled `unverified-local-file`; the label never certifies a release. Review/pin SDK provenance separately before acting on findings. A release manifest covers the target range it declares, which may end below its own SDK version: the v0.4.40 manifest, for example, covers 0.4.38 only. Targets outside the manifest's supported SDK range report unknown. A manifest whose SDK version is below its supported maximum is invalid.
|
|
12
12
|
|
|
13
13
|
`--target` must name the Git root. `--scope` is mandatory: comma-separated literal repository-relative directory/file paths; use `.` only when the complete repository is intended. Add shared JavaScript directories explicitly. `--exclude` uses the same syntax and records explicit exclusions. Archives receive no implicit exemption. Only Git-tracked `.html`, `.htm`, `.js`, `.mjs`, and `.cjs` files in the selected scope are scanned, using current working-tree bytes and per-file digests; untracked files and built dependencies are outside this evidence. A selected HTML page's local script outside the selected files reports unknown. Relative scripts affected by an HTML `<base href>` also report unknown; review their actual dependency paths. Remote SDK and third-party scripts are not fetched or analyzed.
|
|
14
14
|
|
package/docs/skills-revision.md
CHANGED
|
@@ -16,7 +16,7 @@ that the copy on disk moved.
|
|
|
16
16
|
`skills.json` carries one top-level field:
|
|
17
17
|
|
|
18
18
|
```json
|
|
19
|
-
"bundle_revision": "1.
|
|
19
|
+
"bundle_revision": "1.48.0+skills.1"
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
The spelling is `<package version>+skills.<n>`:
|
|
@@ -25,7 +25,7 @@ The spelling is `<package version>+skills.<n>`:
|
|
|
25
25
|
skills ship with (`check-skill-versions.mjs` fails if the two disagree);
|
|
26
26
|
- `<n>` is a plain counter, not a semver component. It says "this is the *n*th
|
|
27
27
|
skill-text revision published against that package version" and it **resets
|
|
28
|
-
with the prefix**. `1.
|
|
28
|
+
with the prefix**. `1.48.0+skills.1` is therefore ahead of `1.40.0+skills.7`.
|
|
29
29
|
|
|
30
30
|
It is one identity for the bundle as a whole, on purpose. Per-skill versions
|
|
31
31
|
still exist and still gate per-skill changes, but an agent that loaded one skill
|
|
@@ -37,7 +37,7 @@ The first body line of every bundled `SKILL.md`, immediately after the
|
|
|
37
37
|
frontmatter, is exactly:
|
|
38
38
|
|
|
39
39
|
```
|
|
40
|
-
Bundle revision: 1.
|
|
40
|
+
Bundle revision: 1.48.0+skills.1
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
followed by a short paragraph telling the agent to run the check below at the
|
|
@@ -48,7 +48,7 @@ text the agent is actually reading, not from a file it would have to go and open
|
|
|
48
48
|
## The check
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
51
|
+
npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
The value is compared against the bundle revision of the **CLI the command runs
|
|
@@ -90,20 +90,20 @@ reports the choice as `skills.scope` (`requested`, `installed_platforms`, or
|
|
|
90
90
|
"revision_check": "match",
|
|
91
91
|
"skills_revision": {
|
|
92
92
|
"status": "match",
|
|
93
|
-
"requested": "1.
|
|
93
|
+
"requested": "1.48.0+skills.1",
|
|
94
94
|
"spelling": "bundle",
|
|
95
|
-
"on_disk": "1.
|
|
95
|
+
"on_disk": "1.48.0+skills.1",
|
|
96
96
|
"on_disk_skill": null,
|
|
97
|
-
"message": "match (1.
|
|
97
|
+
"message": "match (1.48.0+skills.1)"
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
The text view prints one named line, as a header above the rest of the status:
|
|
102
102
|
|
|
103
103
|
```
|
|
104
|
-
Skills revision: match (1.
|
|
105
|
-
Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.
|
|
106
|
-
Skills revision: unchecked (on disk 1.
|
|
104
|
+
Skills revision: match (1.48.0+skills.1)
|
|
105
|
+
Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.48.0+skills.1 — start a fresh session
|
|
106
|
+
Skills revision: unchecked (on disk 1.48.0+skills.1)
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
`unchecked` is the state when the flag is absent. It is not an error — an
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-lifecycle-orientation
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.22
|
|
4
4
|
description: Orient a reader to the Campaigns OS lifecycle artifacts a run has already emitted, without advancing any stage or changing any state.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.48.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-readback-classification
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.22
|
|
4
4
|
description: Classify a selected campaign from the readback projection's v2 fields and write a read-only handoff without turning diagnosis into permission.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.48.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-run-evidence
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.22
|
|
4
4
|
description: Interpret existing Campaigns OS doctor, QA and proof-depth evidence without claiming more proof than the artifacts contain.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.48.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -65,7 +65,12 @@ Saved-Map QA may publish it under the existing consent and flag controls; a
|
|
|
65
65
|
publication failure does not erase the local one. Local-spec packet verdicts
|
|
66
66
|
stay local even with `--post-verdict`, and `qa publish` refuses those packets.
|
|
67
67
|
The readback projects `.campaign-runtime/qa-verdict.json` when
|
|
68
|
-
that sidecar has been copied into the campaign repository.
|
|
68
|
+
that sidecar has been copied into the campaign repository. The sidecar is a
|
|
69
|
+
committable projection: it always empties `test_orders`, `entry_urls`,
|
|
70
|
+
`page_urls` and `tested_urls`, so an empty `test_orders` there says nothing
|
|
71
|
+
about ordering. Its typed-card proof is the `browser-test-order:<path>`
|
|
72
|
+
assertions, each with a `browser-order-total-parity:<path>` row beside it; the
|
|
73
|
+
full verdict under `qa-output/` keeps the order records. A markdown QA
|
|
69
74
|
report, a ledger or a gate script is not a verdict and must not be scanned for
|
|
70
75
|
a disposition, a run id or a blocker. Where two JSON verdicts exist, interpret
|
|
71
76
|
the one the projection loaded; do not walk a report looking for a later rerun.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: contribution-intake
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.22
|
|
4
4
|
description: Turn a suggestion about the agent surface into a classified, evidence-checked proposal and, only with attended approval, one issue on this repository's tracker.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.48.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-build
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.27
|
|
4
4
|
description: Assemble a NEXT campaign from a doctor-cleared Build Packet, CampaignSpec/API values, prepared HTML/assets, page-kit, and starter-template contracts.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.48.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -86,8 +86,9 @@ Build rules:
|
|
|
86
86
|
- Preserve SDK-owned checkout/cart/upsell/receipt/payment/address/totals/submit surfaces.
|
|
87
87
|
- If `doctor` (tier `A`: inspection that writes nothing, plus one read-only live campaign request once the site is built and a public campaign key resolves; `--write` is also tier `A` and writes doctor output; `--built` is tier `B`) reports `derived.scope.mode = "partial"`, build the pages listed in `derived.scope.built_pages` from their prepared source. Keep every out-of-scope route unbuilt by default, including pages marked `template_stock: true`; never publish placeholder presell/landing pages that remain on another host. Materialize a stock page only with explicit per-page operator opt-in (including a required pre-checkout `select` stand-in). For opted-in pages use the locked family's own page for that role with its dependent `_includes/`, `_layouts/`, and assets, wire it from CampaignSpec, and build a required select step first. Do not attest stock screenshots as design source. Built stock pages rejoin preview QA once their HTML exists. Carry remaining `skip_reason` declarations into the report and label the preview route/visual-testable rather than full-funnel launch-ready.
|
|
88
88
|
- For `landing` and `presell` pages, prefer the prepared source HTML when `source_html.pages[].path` points at a real standalone page. Preserve the design/content through a passthrough page-kit layout, inject the SDK loader/config as needed, and repoint CTAs into the CampaignSpec flow. Treat `source_html.pages[].path` and `context.page_map[].source_path` as source provenance. Treat `source_html.pages[].page_kit`, `context.page_map[].page_kit`, and `context.page_map[].output_path` as the Page Kit target file, route, CPK `page_type`, and frontmatter projection.
|
|
89
|
+
- Reproduce the source design's own proof and urgency elements as designed: reviews and testimonials, ratings, "Verified Purchase" labels, recent-purchase popups, stock counters, countdowns and guarantees. They are the merchant's content and the merchant's responsibility; Campaigns OS does not review them, so do not remove, soften or flag them. The starter templates shipping without some of these elements is not a reason to drop the source's.
|
|
89
90
|
- Prepared source HTML means page-kit-ready markup, not a wholesale Liquid rewrite. Standalone HTML mockups that are meant to stay whole (their `source_screenshot` proof is of the full document) keep their document wrappers: the source-html manifest records `wrapper_policy: preserve_document_wrappers` (or `--wrapper-policy preserve_document_wrappers` on `start` / `prepare-build`), and doctor reports the wrappers as a warning. Otherwise, standalone AI/exported HTML should keep page-owned body markup, remove document wrappers, add YAML frontmatter, move shared CSS/assets into the campaign structure, and use Liquid helpers only where page-kit needs campaign-rooted links/assets/includes.
|
|
90
|
-
- For `checkout`, `upsell`, `downsell`, and `receipt` pages, treat the selected starter-template commerce surface as the SDK contract reference: preserve required `data-next-*` controls, hidden fields, payment/address/totals/submit wiring, and `next_dont_touch` regions. The surrounding HTML wrapper, page composition, imagery, copy hierarchy, and brand layer are campaign/source-owned. Do not carry starter visual chrome forward when prepared source design should own that surface. QA checks what the checkout does, not family class names: do not add family shell classes (`.checkout-wrapper`, `.checkout-layout__left/right`) to the campaign's own grid or swap source-owned field markup for family includes to satisfy QA. Keep the checkout working instead: a `<form data-next-checkout="form">`, the contact and shipping fields bound with `data-next-checkout-field` inside it, and a visible cart total.
|
|
91
|
+
- For `checkout`, `upsell`, `downsell`, and `receipt` pages, treat the selected starter-template commerce surface as the SDK contract reference: preserve required `data-next-*` controls, hidden fields, payment/address/totals/submit wiring, and `next_dont_touch` regions. The surrounding HTML wrapper, page composition, imagery, copy hierarchy, and brand layer are campaign/source-owned. Do not carry starter visual chrome forward when prepared source design should own that surface. QA checks what the checkout does, not family class names: do not add family shell classes (`.checkout-wrapper`, `.checkout-layout__left/right`) to the campaign's own grid or swap source-owned field markup for family includes to satisfy QA. Keep the checkout working instead: a `<form data-next-checkout="form">`, the contact and shipping fields bound with `data-next-checkout-field` inside it, and a visible cart total. A required field bound only on a `type="hidden"` input, a disabled control, a read-only input or read-only textarea, or a control with `aria-disabled="true"` does not count as bound: QA reports it in `fields_bound.missing`.
|
|
91
92
|
- Read `context.theme` and `.campaign-runtime/theme/theme-report.json` when present. If a fresh `brand-theme.css` artifact exists, copy it into the campaign asset tree and load it after `next-core.css` on checkout, upsell, downsell, and receipt pages. If policy is `inspect_only`, either run `campaigns-os theme generate` (tier `B`: writes the theme artifacts and doctor output under the target; `--force` is tier `C`) or record an explicit skipped reason before applying a new brand layer.
|
|
92
93
|
- Generated brand-theme v0 is root-variable-only. It may skin commerce pages through next-core custom properties, but it is not permission to edit SDK-owned selectors, package controls, payment fields, totals, submit controls, receipt templates, route meta tags, or SDK JavaScript.
|
|
93
94
|
- Payment, express checkout, bundle selectors, and order bumps must start from the selected family's canonical component DOM/classes, not from raw custom/source HTML with `data-next-*` added afterward. For payment specifically, preserve the family payment-method wrapper, hosted field classes, and iframe geometry assumptions (for example `input-flds spreedly-field` in shop-style templates). Skin these components with campaign tokens; do not rebuild Spreedly/card fields as arbitrary divs.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-os
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.42
|
|
4
4
|
description: Coordinate Campaigns OS lifecycle workflows from CampaignSpec, Build Packet, starter-template contracts, stage reports, deploy evidence, and QA proof depth.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.48.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-os-setup
|
|
3
|
-
version: 2.0.
|
|
3
|
+
version: 2.0.25
|
|
4
4
|
description: Bootstrap or prepare a target page-kit campaign repo from a doctor-cleared Campaigns OS Build Packet before full build wiring. Formerly installed as next-campaigns-setup; renamed 2026-08 to stop colliding with the published NextCommerceCo/skills scaffolder of that name.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.48.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-polish
|
|
3
|
-
version: 1.1.
|
|
3
|
+
version: 1.1.26
|
|
4
4
|
description: Run the visual/runtime polish pass after build and before QA for a Campaigns OS campaign.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.48.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -56,7 +56,7 @@ Responsibilities:
|
|
|
56
56
|
- Compare prepared source design against built campaign pages.
|
|
57
57
|
- Scan the prepared source assets for brand marks such as `logo*.png`, `logo*.svg`, and obvious header/logo images before leaving starter-template logos in place.
|
|
58
58
|
- Scan **visible** rendered text inside the family's content/commerce surfaces (the selectors enumerated in `contracts/template-brand-contract.<family>.v0.json`) for placeholder/residue copy the build should have replaced — the *literal starter defaults*: lorem-ipsum, the unmodified starter headings (`Product Name` / `Package Title` / `Your headline`), `[VERIFY …]` author notes, `TODO` markers. Match the literal starter strings, not any authored copy that merely contains those words, and skip `<script>` / `<style>` / JSON-LD / `data-*` attributes. Treat a surviving literal starter default on a content/commerce surface as a polish blocker — replace it from the prepared source / CampaignSpec; do not draft substitute copy (if the design's authored copy is genuinely missing, flag it rather than invent it). This is *copy* residue, complementary to the computed-style / asset residue gated by `next-campaigns-qa` + the brand contract — not a duplicate of it.
|
|
59
|
-
- Flag template *defaults* left where they disagree with the prepared design — e.g. the same benefit icon repeated across a grid *when the design uses distinct icons*, or a guarantee badge/term that disagrees with the design — as polish defects, not just logos. Judge against the prepared source, not taste: copy or imagery the source/CampaignSpec does not supply (e.g. a placeholder testimonial name/quote/role) is residue; "looks generic" on its own is not.
|
|
59
|
+
- Flag template *defaults* left where they disagree with the prepared design — e.g. the same benefit icon repeated across a grid *when the design uses distinct icons*, or a guarantee badge/term that disagrees with the design — as polish defects, not just logos. Judge against the prepared source, not taste: copy or imagery the source/CampaignSpec does not supply (e.g. a placeholder testimonial name/quote/role) is residue; "looks generic" on its own is not. Copy the source does supply, including its proof and urgency elements (reviews, "Verified Purchase" labels, recent-purchase popups, stock counters, countdowns, guarantees), is the merchant's content: keep it as designed and do not record it as an issue or an unconfirmed claim.
|
|
60
60
|
- **Brand-bleed (cloned-source de-brand) pass.** When a campaign is cloned from a proven sibling, the sibling's brand defaults ride along. Inspect the built pages and assets for residual cross-brand bleed and clear it before recording: (1) a residual promo/sale banner or coupon code/copy from the source campaign (including a baked-in *fake* code); (2) a prior-campaign / sibling favicon left in place; (3) scaffold or non-design fonts the design did not specify (e.g. starter `Plus Jakarta`); (4) hardcoded non-token colors — any brand color literal that should be a token, such as next-core's `#C670FE` "Most Popular" pill. Clear each from the prepared source / CampaignSpec and brand theme (tokens, not literals); flag — do not invent — anything the design genuinely doesn't supply. Treat surviving bleed as a polish blocker. This complements the favicon/logo and copy-residue checks above; it is the cross-brand contamination angle, not a duplicate.
|
|
61
61
|
- Read the assembly report decisions before polishing. Do not reintroduce source-HTML elements that build intentionally dropped because CampaignSpec/API did not support them, such as unavailable payment methods.
|
|
62
62
|
- **Remove or rename a contract-listed payment-chrome asset; never edit one in place.** The assets named in `contracts/template-brand-contract.<family>.v0.json` under `default_residue.payment_chrome.assets` are keyed by QA on the *referenced basename*, not on their contents. Stripping the unsupported marks from inside a shared file such as `upsell-payment-logos.svg` leaves the reference in place, so QA reports residue for an asset that no longer carries any — and on 2026-09-06 the repair loop's remedy for that report deleted a cards-only trust strip that was correct. Delete the asset, or write a new one under a new name and repoint the reference. QA downgrades an edited-in-place asset to `manual_review` rather than a blocker, but that is a safety net for a mistake, not the supported way to do this.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-qa
|
|
3
|
-
version: 1.3.
|
|
3
|
+
version: 1.3.26
|
|
4
4
|
description: Run spec-aware QA from a saved Map or local-spec Build Packet and tested campaign URL after build, polish, and deploy/local evidence exist, including Playwright typed-card test-order proof.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.48.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.48.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -83,7 +83,7 @@ Rules:
|
|
|
83
83
|
- Template residue is a QA dimension, not advice: promoted starter families must have a brand/residue/pricing contract (`contracts/template-brand-contract.<family>.v0.json`). Browser QA inspects computed styles on commerce surfaces and fails pages that still render starter defaults (`#3c7dff`/`#0a265c`, starter `next-logo.png`, paypal/klarna chrome absent from the spec).
|
|
84
84
|
- Pricing visibility is a blocker: an upsell/downsell offer with zero visible price rows fails QA. Pricing surfaces render via template pricing modes (`full_price`, `compare_at_current`, `unit_price_plus_total`, `savings_badge_amount`, `code_discounted_post_checkout`), never via campaign CSS `display:none` on price wrappers.
|
|
85
85
|
- The upsell and checkout bundle price checks count the SDK's `data-next-bundle-display` price as a price row when it is visible and not empty.
|
|
86
|
-
- `browser-commerce-structure` checks what the checkout does, not family class names; the checkout wrapper and page composition are source-owned. A missing family shell selector (only `.checkout-wrapper`, `.checkout-layout__left/right`, `.checkout__layout`, `.checkout__column--left/right` and the shipping field row marker) is `warn` when the checkout passes the behaviour checks in `evidence.behaviour`: a `<form data-next-checkout="form">`, `email`/`fname`/`lname`/`country`/`address1`/`city`/`province`/`postal` bound with `data-next-checkout-field` on an input, select or textarea inside it that is not `type="hidden"` or disabled (it may be hidden until a country is chosen), and a visible cart total. It stays `fail` when any of those fails or no checkout form is found, and any other missing selector (SDK attributes, payment field classes) is always `fail`. Do not ask the build to add family classes to clear a `warn`; fix the failing behaviour check.
|
|
86
|
+
- `browser-commerce-structure` checks what the checkout does, not family class names; the checkout wrapper and page composition are source-owned. A missing family shell selector (only `.checkout-wrapper`, `.checkout-layout__left/right`, `.checkout__layout`, `.checkout__column--left/right` and the shipping field row marker) is `warn` when the checkout passes the behaviour checks in `evidence.behaviour`: a `<form data-next-checkout="form">`, `email`/`fname`/`lname`/`country`/`address1`/`city`/`province`/`postal` bound with `data-next-checkout-field` on an input, select or textarea inside it that is not `type="hidden"` or disabled (it may be hidden until a country is chosen), and a visible cart total. A required field bound only on a `type="hidden"` input, a disabled control, a read-only input or read-only textarea, or a control with `aria-disabled="true"` does not count as bound: QA reports it in `fields_bound.missing`. It stays `fail` when any of those fails or no checkout form is found, and any other missing selector (SDK attributes, payment field classes) is always `fail`. Do not ask the build to add family classes to clear a `warn`; fix the failing behaviour check.
|
|
87
87
|
- Exit-pop widgets are governed offer surfaces. If the selected family ships or copies a default exit-pop and CampaignSpec has no checkout `exit_intent` or `promo_code_input`, QA/doctor must report it as residue; strip it or wire the mapped offer/code through the SDK coupon path.
|
|
88
88
|
- Typed-card runs emit a per-step ladder (`[qa:test-order] step=... status=...`) with bounded per-step and per-path timeouts, and always produce a verdict — a hung or crashed path is a blocked verdict with the step ladder as evidence, not a silent exit. Read the last completed step before re-running.
|
|
89
89
|
- A typed-card path that fails is classified by **what it did to the store** before the runner decides what to do about it. A failure the runner can prove happened before submit (`not_created`) is **re-run once, if the creation budget has a slot no still-unrun planned path needs** — so a transient miss is not reported as a defect in the build, without an early path eating budget the last planned paths need. Under the default budget a path whose submit was *rejected* has already spent its own slot, so it is not re-run and records `evidence.order_creation.rerun_skipped` instead. When the re-run does happen, both attempts appear in `test_orders[]` and `evidence.retry` names the first attempt's error and ref id. A failure that happened **after** the order was created (`created` — most often a receipt that did not render) is **never resubmitted**: the runner reloads that order's receipt and re-runs only the read-only checks, and `evidence.recovery` carries the original failure, the checks re-run, and whether it cleared. Read `evidence.order_creation` for two separate counts: `submissions_reserved` (platform-side creation slots charged to this path — reserved before a submit click, or charged for a hosted-checkout redirect where no submit click happens — which stand even when the create then failed) and `orders_confirmed_created` (creates the platform was observed to accept) — a spent slot with no confirmed order is the ambiguous case, not an order to reconcile. Recovery clears only on persisted evidence it re-read on that pass: a failed or absent order read-back stops it honestly rather than re-deciding against the original attempt's numbers. An outcome it cannot prove either way (`ambiguous` — an unusable read-back, a lost create response, a network-failed create, a 4xx after an earlier 2xx) stops the path and names the operator check instead of buying again. A pass that only came back after recovery is never indistinguishable from a first-attempt pass, and a failure that survives recovery still blocks.
|