@nextcommerce/campaigns-os 1.53.0 → 1.55.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 +145 -0
- package/CONTEXT.md +8 -5
- package/README.md +26 -11
- package/compatibility.json +1 -1
- package/contracts/campaign-cart-checkout-field-contract.v0.json +10 -0
- package/contracts/campaign-cart-sdk-support-policy.v0.json +2 -2
- package/contracts/commerce-surface-catalog.json +27 -27
- package/contracts/effects.v1.json +96 -37
- package/contracts/release-ledger.json +774 -0
- package/contracts/supported-surface.json +4 -4
- package/contracts/template-brand-contract.shared-commerce.v0.json +1 -1
- package/docs/brand-theme-bridge.md +10 -0
- package/docs/build-packet.md +45 -22
- package/docs/campaign-standardization-report.md +32 -0
- package/docs/campaigns-os-build-flow.md +1 -1
- package/docs/design-source-package.md +7 -3
- package/docs/effects.md +4 -6
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +1 -1
- package/docs/progress-snapshots.md +3 -1
- package/docs/qa-and-test-orders.md +83 -14
- package/docs/runtime-readiness.md +1 -1
- package/docs/sdk-storage-compatibility.md +15 -0
- package/docs/skills-revision.md +10 -10
- package/docs/supported-surface.md +2 -1
- package/docs/workflow-findings-sidecar.md +57 -2
- package/package.json +3 -1
- package/schemas/campaign-runtime-assembly-report.v0.schema.json +5 -1
- package/schemas/campaigns-os-run-record.v0.schema.json +20 -15
- 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 +3 -3
- package/skills/contribution-intake/SKILL.md +3 -3
- package/skills/next-campaigns-build/SKILL.md +3 -3
- 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 +3 -3
- package/skills/next-campaigns-qa/SKILL.md +4 -4
- package/skills.json +10 -10
- package/src/adapter-decision-contract.mjs +26 -12
- package/src/brand-theme.mjs +44 -8
- package/src/campaign-ecosystem.mjs +153 -31
- package/src/campaign-identity.mjs +33 -4
- package/src/cli.mjs +58 -62
- package/src/consent.mjs +3 -2
- package/src/design-source-publication.mjs +1 -1
- package/src/deviation.mjs +2 -1
- package/src/directory-lock.mjs +79 -7
- package/src/doctor/checks.mjs +28 -8
- package/src/doctor/inspect.mjs +13 -0
- package/src/doctor/next-step.mjs +16 -6
- package/src/invocation.mjs +7 -4
- package/src/lifecycle.mjs +88 -6
- package/src/local-preview-policy.mjs +6 -0
- package/src/login.mjs +2 -1
- package/src/polish-browser.mjs +8 -2
- package/src/polish-node.mjs +6 -1
- package/src/polish-readability.mjs +2 -2
- package/src/progress-node.mjs +3 -2
- package/src/qa-browser.mjs +595 -95
- package/src/qa-cart-entry.mjs +29 -6
- package/src/qa-node.mjs +177 -43
- package/src/qa-verdict-publish.mjs +2 -1
- package/src/run-record.mjs +46 -34
- package/src/sdk-attribute-index.mjs +111 -23
- package/src/sdk-markup.mjs +89 -10
- package/src/sdk-repin.mjs +282 -0
- package/src/stage-ledger.mjs +15 -3
- package/src/stage-record.mjs +85 -17
- package/src/standardization-report.mjs +26 -1
- package/src/target-lock.mjs +9 -1
|
@@ -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.55.0",
|
|
4
4
|
"package_exports": [
|
|
5
5
|
"./commercial-journey",
|
|
6
6
|
"./commercial-parity",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"sha256": "65bcd8b5f41f1e9ecfc36f3b8838cd4ed531bd74c5a318ee5147131f6c9b4a1c"
|
|
61
61
|
},
|
|
62
62
|
"schemas/campaign-runtime-assembly-report.v0.schema.json": {
|
|
63
|
-
"sha256": "
|
|
63
|
+
"sha256": "625a6f2ed6bee726f9cddad750e64a54a7e03744c777f5a70afe4a39afdd9848"
|
|
64
64
|
},
|
|
65
65
|
"schemas/campaign-runtime-build-context.v0.schema.json": {
|
|
66
66
|
"sha256": "f310c1b7844d9b1994864d15858ff0c6f17efc1e5285ba193d181bdb2dd4be2f"
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"sha256": "d7521fe0ab4deb62485f92286cb890ee548ea290793a6ce7444ecc42ba336d49"
|
|
97
97
|
},
|
|
98
98
|
"schemas/campaigns-os-run-record.v0.schema.json": {
|
|
99
|
-
"sha256": "
|
|
99
|
+
"sha256": "390cbc9debacfbfb17d999961baa041a2138fa20a116ec463fbbefbbb05f622d"
|
|
100
100
|
},
|
|
101
101
|
"schemas/campaigns-os-runtime-recipe.v1.schema.json": {
|
|
102
102
|
"sha256": "f8a2ab5eadbb71fb6d5653b670ee71e4471c3df74dd04000d3dbd3d9ae9e71fc"
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"sha256": "dfc9abed38d456969e47a21f606d308a03bdf036f3466f7d6e47a602747dacf4"
|
|
121
121
|
},
|
|
122
122
|
"contracts/effects.v1.json": {
|
|
123
|
-
"sha256": "
|
|
123
|
+
"sha256": "10a9020aaba5780e2ee2f3ddf6d81127047f6caec0de774c48540d9b664e0a6a"
|
|
124
124
|
},
|
|
125
125
|
"schemas/campaigns-os-effects.v1.schema.json": {
|
|
126
126
|
"sha256": "3eadd22169ab98bc7c2f682af2751267605170581182158d96be035b0cfe44dc"
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"asset_pin": {
|
|
57
57
|
"repo": "NextCommerceCo/campaign-cart-starter-templates",
|
|
58
|
-
"sha": "
|
|
58
|
+
"sha": "783329017c6994a94d11e46580c33bd052aabdb0",
|
|
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
|
},
|
|
@@ -22,6 +22,16 @@ It compares source tokens against known `figma-sections-export` scaffold
|
|
|
22
22
|
defaults, maps real brand values onto a local versioned next-core target-token
|
|
23
23
|
contract, and writes evidence into a theme report.
|
|
24
24
|
|
|
25
|
+
A rule whose selector subject has a whole sale, discount, badge, rating, star,
|
|
26
|
+
strike, border or announcement name part does not supply primary or CTA unless
|
|
27
|
+
that subject also has a brand, primary, cta, button or btn part, or is a `button`
|
|
28
|
+
element or a submit input. In comma lists,
|
|
29
|
+
the other selectors still count. Primary keeps the header, navigation, brand
|
|
30
|
+
and hero matching used on main, including element selectors and ancestors;
|
|
31
|
+
button backgrounds keep the same CTA matching, including cart, buy, order and
|
|
32
|
+
submit names and IDs. If no CTA token or button background exists, the CTA uses
|
|
33
|
+
the accent fallback and the warning asks the operator to confirm it.
|
|
34
|
+
|
|
25
35
|
`campaigns-os theme generate` writes:
|
|
26
36
|
|
|
27
37
|
```text
|
package/docs/build-packet.md
CHANGED
|
@@ -827,12 +827,19 @@ Blockers (not waivable — the markup provably does not do what it says):
|
|
|
827
827
|
- `CHECKOUT_NOT_FORM` — `data-next-checkout` on an element that is not `<form>`.
|
|
828
828
|
- `WRONG_FIELD_NAME` — `data-next-checkout-field` with a value the SDK does not
|
|
829
829
|
map. The set is vendored from the SDK at a named tag
|
|
830
|
-
(`src/sdk-attribute-index.mjs`, currently v0.4.
|
|
830
|
+
(`src/sdk-attribute-index.mjs`, currently v0.4.41: `email`, `fname`, `lname`,
|
|
831
831
|
`phone`, `address1`, `address2`, `city`, `province`, `postal`, `country`,
|
|
832
832
|
`payment-method`, `accepts_marketing`, `cc-number`, `cc-month`, `cc-year`,
|
|
833
833
|
`exp-month`, `exp-year`, `cvv`, the legacy `card-*` spellings, and any
|
|
834
834
|
`billing-` prefixed name). The message names the SDK spelling for the usual
|
|
835
|
-
offenders (`firstName` → `fname`, `zip` → `postal`).
|
|
835
|
+
offenders (`firstName` → `fname`, `zip` → `postal`). Three names depend on
|
|
836
|
+
the page's SDK version: `first_name` and `last_name` are mapped from v0.4.39
|
|
837
|
+
and `phone_number` from v0.4.41. The version is the page's own exact loader
|
|
838
|
+
pin, else the campaign's `sdk_version` in `_data/campaigns.json`. Below the
|
|
839
|
+
name's version, or when neither gives an exact released version, the name
|
|
840
|
+
blocks, and the message names `fname`, `lname` or `phone`, which every
|
|
841
|
+
version maps. Each finding carries `sdk_version` and `sdk_version_source`
|
|
842
|
+
(`loader` or `campaigns_json`) in its detail.
|
|
836
843
|
- `MISSING_SELECTOR_ID_MATCH` — an `add-to-cart` button whose
|
|
837
844
|
`data-next-selector-id` names no selector on the page (an element that is a
|
|
838
845
|
bundle, package, cart or upsell selector; another element echoing the id
|
|
@@ -1246,25 +1253,39 @@ coverage blocks source readiness.
|
|
|
1246
1253
|
|
|
1247
1254
|
## Adapter And Proof Fields
|
|
1248
1255
|
|
|
1249
|
-
Fresh packets
|
|
1250
|
-
Assembly Report carry the same values as `adapter_decisions
|
|
1251
|
-
|
|
1256
|
+
Fresh packets include `source_html.adapter_contract`. Build Context and
|
|
1257
|
+
Assembly Report carry the same values as `adapter_decisions`. After building,
|
|
1258
|
+
record the true scalar decisions with `record build --adapter-decision
|
|
1259
|
+
<key>=<value>[,<key>=<value>...]` (one flag, comma-separated `key=value`
|
|
1260
|
+
pairs). For example, set `raw_html_conversion_status=completed` after source
|
|
1261
|
+
HTML conversion, or `not_required` when there is no source HTML to convert.
|
|
1262
|
+
A repeated `--adapter-decision` flag keeps only the last list. The command
|
|
1263
|
+
writes the Assembly Report only. Doctor's adapter gates and leftover-wrapper
|
|
1264
|
+
check read the effective decisions in report → Build Context → packet order;
|
|
1265
|
+
doctor still validates each copy's shape separately. Source preparation reads
|
|
1266
|
+
the packet's `wrapper_policy`, which is selected at intake with
|
|
1267
|
+
`prepare-build --wrapper-policy` or the source-html manifest's `wrapper_policy`
|
|
1268
|
+
option. The record flag refuses that key, an unknown key, or a value outside
|
|
1269
|
+
the allowed values below. The object-valued
|
|
1270
|
+
`template_files_copied` has no flag: its `status`, `required_groups`, `groups`,
|
|
1271
|
+
and `paths` remain required proof in the report and keep their existing doctor
|
|
1272
|
+
checks.
|
|
1252
1273
|
|
|
1253
1274
|
Required adapter decisions:
|
|
1254
1275
|
|
|
1255
|
-
| Field | Purpose |
|
|
1256
|
-
| --- | --- |
|
|
1257
|
-
| `raw_html_conversion_status` | Whether prepared HTML has been converted into page-kit-ready source. |
|
|
1258
|
-
| `source_asset_strategy` | How images/fonts/CSS/JS are moved and referenced.
|
|
1259
|
-
| `commerce_shell_adoption` | Whether checkout/upsell/downsell/receipt use a template-clone-first SDK surface. |
|
|
1260
|
-
| `route_rewrite_policy` | How page links, CTAs, and SDK routing values were rewritten from CampaignSpec routes. |
|
|
1261
|
-
| `template_files_copied` | Whether the selected template family was copied/verified as one atomic page-kit slice. |
|
|
1262
|
-
| `config_script_strategy` | How campaign config scripts are loaded. |
|
|
1263
|
-
| `wrapper_policy` | Whether document wrappers are stripped, preserved, or not required. |
|
|
1264
|
-
| `frontmatter_policy` | How Page Kit YAML frontmatter is created or preserved. |
|
|
1265
|
-
| `script_style_reference_policy` | How scripts/styles move into frontmatter, campaign assets, inline blocks, or passthrough. |
|
|
1266
|
-
| `cta_rewrite_policy` | How CTA destinations are rewritten from CampaignSpec routes. |
|
|
1267
|
-
| `layout_choice` | Which Page Kit layout strategy wraps the prepared source. |
|
|
1276
|
+
| Field | Purpose | `record build` allowed values |
|
|
1277
|
+
| --- | --- | --- |
|
|
1278
|
+
| `raw_html_conversion_status` | Whether prepared HTML has been converted into page-kit-ready source. | `pending`, `in_progress`, `completed`, `not_required`, `blocked` |
|
|
1279
|
+
| `source_asset_strategy` | How images/fonts/CSS/JS are moved and referenced. | `pagekit_campaign_asset_root`, `external_cdn`, `raw_passthrough`, `not_applicable`, `unknown` |
|
|
1280
|
+
| `commerce_shell_adoption` | Whether checkout/upsell/downsell/receipt use a template-clone-first SDK surface. | `not_required`, `template_clone_first_required`, `template_clone_first_verified`, `sdk_surfaces_preserved`, `custom_html_experimental` |
|
|
1281
|
+
| `route_rewrite_policy` | How page links, CTAs, and SDK routing values were rewritten from CampaignSpec routes. | `campaignspec_routes_via_campaign_link`, `pagekit_public_routes`, `raw_passthrough`, `not_applicable`, `unknown` |
|
|
1282
|
+
| `template_files_copied` | Whether the selected template family was copied/verified as one atomic page-kit slice. | Object-valued proof; no flag |
|
|
1283
|
+
| `config_script_strategy` | How campaign config scripts are loaded. | `campaign_asset`, `frontmatter_script`, `inline`, `not_required`, `unknown` |
|
|
1284
|
+
| `wrapper_policy` | Whether document wrappers are stripped, preserved, or not required. | Intake only: `strip_document_wrappers`, `preserve_document_wrappers`, `not_required`, `unknown` |
|
|
1285
|
+
| `frontmatter_policy` | How Page Kit YAML frontmatter is created or preserved. | `pagekit_yaml_frontmatter`, `raw_passthrough`, `not_required`, `unknown` |
|
|
1286
|
+
| `script_style_reference_policy` | How scripts/styles move into frontmatter, campaign assets, inline blocks, or passthrough. | `frontmatter_or_campaign_asset`, `frontmatter`, `campaign_asset`, `inline`, `raw_passthrough`, `not_required`, `unknown` |
|
|
1287
|
+
| `cta_rewrite_policy` | How CTA destinations are rewritten from CampaignSpec routes. | `campaignspec_routes_via_campaign_link`, `pagekit_public_routes`, `raw_passthrough`, `not_applicable`, `unknown` |
|
|
1288
|
+
| `layout_choice` | Which Page Kit layout strategy wraps the prepared source. | `campaign_layout`, `page_layout`, `raw_passthrough`, `not_applicable`, `unknown` |
|
|
1268
1289
|
|
|
1269
1290
|
Fresh build context also includes `source.asset_crawl`
|
|
1270
1291
|
(`source-asset-crawl/v0`). `prepare-build` scans the source HTML files and
|
|
@@ -1730,10 +1751,10 @@ hand-editing `.campaign-runtime/` JSON:
|
|
|
1730
1751
|
| Command | Writes | Refused (nothing written) when |
|
|
1731
1752
|
|---|---|---|
|
|
1732
1753
|
| `campaigns-os record setup --packet <p>` | Build Context `scaffold.required=false` (`handoff_skill` next-campaigns-build) and `stages.setup` completed | the campaign output directory (`assembly.output_dir`) does not exist, or there is no Build Context or Assembly Report |
|
|
1733
|
-
| `campaigns-os record build --packet <p> [--build-environment <development\|production>]` | `stages.assembly` completed with `build_fingerprint` = doctor's `derived.build_output_fingerprint.value`, `source_package_material_fingerprint` = the report's Design Source Package material fingerprint when present, `evidence.build_environment` = the `--build-environment` value when given (kept from the last record otherwise), and `stages.polish`
|
|
1754
|
+
| `campaigns-os record build --packet <p> [--build-environment <development\|production>] [--adapter-decision <key>=<value>[,<key>=<value>...]]` | `stages.assembly` completed with `build_fingerprint` = doctor's `derived.build_output_fingerprint.value`, `source_package_material_fingerprint` = the report's Design Source Package material fingerprint when present, `evidence.build_environment` = the `--build-environment` value when given (kept from the last record otherwise), and the specified scalar `report.adapter_decisions`; all recordable decisions go in one comma-separated flag (a repeated flag keeps only the last list); `wrapper_policy` stays an intake choice. `stages.polish` resets to `required` (`required_by` build, `required_for` qa) unless its evidence is bound to this exact output; a completed `stages.deploy` whose `source_build_fingerprint` names other output resets to `required` the same way, without the old probe's `outputs` and `evidence`, and its prior record is kept in `stages.deploy.history` (a deploy with no `source_build_fingerprint`, recorded before deploy stamped it, is kept) | doctor cannot compute the fingerprint (no `_site/<public_route_slug>/`), setup is still required, `stages.setup` is not terminal, or an adapter decision key/value is invalid |
|
|
1734
1755
|
| `campaigns-os record polish --packet <p> --evidence <file>` | `stages.polish` from the file (`docs/polish-evidence.md` §7: completed, blocked or skipped), bound to doctor's current fingerprint; `report.theme.repair_loop_defect` when the file sets it | build is not recorded for the current output, the file has a shape error (named by field), or, for a completed status, the polish gate doctor evaluates would not pass on the result |
|
|
1735
1756
|
| `campaigns-os record theme --packet <p>` | `report.theme`: status `applied`, `load_order` `after-next-core`, `css_path`, `commerce_pages` and per-page evidence read from each built commerce page's stylesheet links; any earlier theme waiver is cleared | build is not recorded for the current output, the campaign ships no commerce pages, a built commerce page that loads `next-core.css` does not load `brand-theme.css` (or `checkout-brand.css`) after it or links one missing from the built output, or no built commerce page loads `next-core.css` |
|
|
1736
|
-
| `campaigns-os record deploy --packet <p> --base-url <url>` | the packet's `deploy.preview_url` and `stages.deploy` completed with the URL in `outputs
|
|
1757
|
+
| `campaigns-os record deploy --packet <p> --base-url <url>` | the packet's `deploy.preview_url` and `stages.deploy` completed with the URL in `outputs`, one evidence line per built page that answered (each page is requested under the URL first), and `source_build_fingerprint` = the recorded `stages.assembly.build_fingerprint` it probed, so a later `record build` of different output makes deploy owed again and `next` routes back to `record deploy` before QA; a byte-identical rebuild keeps it current | the packet is not `local-serve`, the URL is not a loopback origin naming the campaign's route root, a built page does not answer 2xx, polish is not recorded, the built output changed since build was recorded, or the theme gate is blocked |
|
|
1737
1758
|
|
|
1738
1759
|
Each command also refuses a stage `next` has not reached: while doctor's
|
|
1739
1760
|
prepare-build gate is set (`next` answers prepare-build) or while an earlier
|
|
@@ -1766,7 +1787,9 @@ check exits non-zero with the problems listed, one per line; a value outside a
|
|
|
1766
1787
|
schema enum (for example an `adapter_decisions` policy) is listed with the
|
|
1767
1788
|
values the schema allows and the value it got. Re-run `record
|
|
1768
1789
|
build` after every page-kit build; a rebuild that changes the output needs
|
|
1769
|
-
`polish capture` and `record polish` again.
|
|
1790
|
+
`polish capture` and `record polish` again. After deploy is recorded for the
|
|
1791
|
+
rebuilt output, `next` asks for QA again if the last QA record names the
|
|
1792
|
+
previous build fingerprint, even while its stage status still says completed.
|
|
1770
1793
|
|
|
1771
1794
|
Stage order: `setup → build → polish → deploy → qa`. The picker walks this list and returns the first stage whose recorded status isn't terminal (`completed`, `completed_with_warnings`, `skipped`). During Polish, install the package-owned browser first, then run `campaigns-os polish capture` against the served current build before recording a terminal `stages.polish.status` or proceeding to deploy/QA; the producer attaches package-owned `visual_review.page_load` evidence and never marks the stage complete itself.
|
|
1772
1795
|
|
|
@@ -1801,7 +1824,7 @@ Result shape (with `--json`):
|
|
|
1801
1824
|
Terminal states:
|
|
1802
1825
|
|
|
1803
1826
|
- **`stage: "doctor-blocked"`** — doctor returned errors. Resolve the blockers and re-run `campaigns-os doctor` to confirm before calling `next` again.
|
|
1804
|
-
- **`stage: "done"`** — every stage is in a terminal status. Pipeline is complete. To
|
|
1827
|
+
- **`stage: "done"`** — every stage is in a terminal status. Pipeline is complete. To repeat build work, do the work and use `record build`; it makes downstream stages owed as needed. Use `record setup` or `record polish` after repeating those stages, and `qa run` for QA. `record deploy` records a local-serve target; for a hosted deploy, record the URL and stage outcome as the deploy prompt describes. Then call `next` again.
|
|
1805
1828
|
- **`stage_blocked: true`** — the picker returned a stage whose recorded status is `blocked`. Don't run the prompt as-is; clear the blocker first.
|
|
1806
1829
|
|
|
1807
1830
|
The legacy form `campaigns-os next <stage>` (e.g. `next build`) still works and is the way to force a specific stage when you want to override the picker.
|
|
@@ -170,12 +170,34 @@ version discovery), `checkout_fields` (every
|
|
|
170
170
|
script evidence, and `proof_state`), `runtime_contract`, `findings`, and
|
|
171
171
|
`remediation`.
|
|
172
172
|
|
|
173
|
+
Three names in the field contract depend on the SDK version
|
|
174
|
+
(`version_gated_aliases`): `first_name` and `last_name` from 0.4.39 and
|
|
175
|
+
`phone_number` from 0.4.41. Each is `supported` only when the binding's SDK
|
|
176
|
+
version is an exact release at or after that version; otherwise it is
|
|
177
|
+
classified as on an earlier SDK (`first_name` and `last_name` as stale aliases,
|
|
178
|
+
`phone_number` as unknown). Each binding records the `sdk_version` it was
|
|
179
|
+
judged against and its `sdk_version_source`: `loader` (the file's own exact
|
|
180
|
+
loader pin), `campaign` (for a file with no loader of its own, the lowest of
|
|
181
|
+
the root's exact loader pins and its bundled dependency's floor: an exact pin,
|
|
182
|
+
or `^`, `~` or `>=` on a released version; any other range leaves the version
|
|
183
|
+
unknown), or
|
|
184
|
+
`campaigns_json` (a Page Kit campaign's `sdk_version`; a shared file takes the
|
|
185
|
+
lowest across campaigns). An unpinned loader or a missing pin leaves
|
|
186
|
+
`sdk_version` null, judged as an earlier SDK, because `fname`, `lname` and
|
|
187
|
+
`phone` work on every version.
|
|
188
|
+
|
|
173
189
|
`sdk_loader.references` records pinned and unpinned loader refs alike
|
|
174
190
|
(`version` is null for `@latest`/branch/commit pins, which raise
|
|
175
191
|
`version.sdk_loader_unpinned` instead of a policy evaluation). Only URLs that
|
|
176
192
|
point at a loader/dist artifact count; incidental `campaign-cart@x.y.z`
|
|
177
193
|
strings elsewhere in source are ignored.
|
|
178
194
|
|
|
195
|
+
In a static repo (no `_data/campaigns.json`), `campaigns-os sdk repin --target
|
|
196
|
+
<repo> [--target-sdk <x.y.z>] [--apply]` rewrites the semver-pinned references
|
|
197
|
+
in this list that are below the target version, changing only the version
|
|
198
|
+
segment of each URL. It previews by default and leaves the unpinned refs
|
|
199
|
+
alone. See `sdk-storage-compatibility.md`.
|
|
200
|
+
|
|
179
201
|
`payment.proof_state` is one of `runtime_proof_required` (custom-control
|
|
180
202
|
evidence found), `undetermined` (radios exist; static scanning cannot exclude
|
|
181
203
|
externally-styled custom controls), or `not_applicable` (no `payment_method`
|
|
@@ -216,6 +238,16 @@ Page Kit root that inlines them gets the same `checkout_fields` block and the
|
|
|
216
238
|
same `checkout.unsupported_field_binding` / `checkout.unknown_field_binding`
|
|
217
239
|
findings as an application root.
|
|
218
240
|
|
|
241
|
+
A bundled dependency is judged by its floor, the lowest version its
|
|
242
|
+
`package.json` spec lets npm install, recorded as
|
|
243
|
+
`sdk_loader.bundled_dependency.floor_version`. Only an exact pin (`0.4.30`,
|
|
244
|
+
`=0.4.30`, `v0.4.30`) or one `^`, `~` or `>=` range on a released version has
|
|
245
|
+
a certain floor; `^0.4.25` is evaluated as `0.4.25`. Any other spec (`<0.4.39`,
|
|
246
|
+
an `||`, hyphen or x-range, a tag) has none, so it gets no evaluation and
|
|
247
|
+
raises the warning `version.sdk_dependency_floor_unknown` instead; a
|
|
248
|
+
prerelease pin raises `version.sdk_prerelease_pin`. `resolved_version` stays
|
|
249
|
+
the version text extracted from the spec, for reference.
|
|
250
|
+
|
|
219
251
|
Both are also injectable from the CLI: pass
|
|
220
252
|
`--sdk-support-policy <path-to-json>` and/or `--field-contract <path-to-json>`
|
|
221
253
|
to `standardize`. Each file is read and JSON-parsed
|
|
@@ -12,7 +12,7 @@ The happy path is intentionally tight:
|
|
|
12
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
13
|
9. Deploy a preview.
|
|
14
14
|
10. Run `campaigns-os qa resolve --packet <packet>`, then `campaigns-os qa run --packet <packet> --browser --test-order common` with the tested URL. Saved-Map QA uses the existing consent and publication controls; pass `--no-post-verdict` to keep its verdict local. Local-spec QA always keeps verdicts and progress local, even with `--post-verdict`, and `qa publish` refuses local-spec packets. Run Telemetry retains its consent controls.
|
|
15
|
-
11.
|
|
15
|
+
11. Global test cards bypass the gateway and create no transactions, but each test order leaves a real store order record. Unless the operator has already said test orders are fine for this campaign, ask once, up front, before placing test orders; then proceed. There is no permission flag; test-order depth controls coverage. 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
16
|
12. Promote, block, or iterate from the recorded build, polish, deploy, QA, and test-order evidence.
|
|
17
17
|
|
|
18
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.
|
|
@@ -396,9 +396,13 @@ a minute. The lock directory and its owner record appear together, so a lock
|
|
|
396
396
|
left by a process that died is recovered automatically, and a lock directory
|
|
397
397
|
without an owner record (only an older release leaves one) is never taken
|
|
398
398
|
over: the command refuses it after about a second and names it. Confirm no
|
|
399
|
-
campaigns-os process is working on the target, then remove it.
|
|
400
|
-
|
|
401
|
-
|
|
399
|
+
campaigns-os process is working on the target, then remove it. Anything else
|
|
400
|
+
at the lock path (a file, a symbolic link, or a directory with no owner record
|
|
401
|
+
that holds other entries) is not a lock: once the wait ends the command names
|
|
402
|
+
what is there and never removes it. The holder of the lock removes staging and
|
|
403
|
+
released directories beside it whose recorded owner process no longer exists.
|
|
404
|
+
Do not run an older Campaigns OS release against the same target at the same
|
|
405
|
+
time. A waiver's `--dry-run` preview takes no lock.
|
|
402
406
|
|
|
403
407
|
Before writing any output, `prepare-build` also requires distinct paths for the
|
|
404
408
|
Build Packet, Build Context, Assembly Report, Doctor output, normalized Build
|
package/docs/effects.md
CHANGED
|
@@ -42,6 +42,7 @@ CLI exempts from lifecycle capture (the converse does not hold: `demo` and the
|
|
|
42
42
|
`doctor` inspection and `doctor --no-write` skip it and write nothing but may
|
|
43
43
|
send the one live campaign read): `help`, `readback`,
|
|
44
44
|
`run status`, `doctor --no-live-refs` inspection, `sdk storage-check`,
|
|
45
|
+
the `sdk repin` preview (without `--apply`),
|
|
45
46
|
`tooling diagnose`, a refused invocation, `run-record --no-write`, and every
|
|
46
47
|
`--dry-run` form on the commands that implement the flag. Everything else
|
|
47
48
|
appends an entry when a journal is selected — an active run session,
|
|
@@ -160,7 +161,7 @@ Both rows are tier C, because a change makes recorded stages owed again, and eac
|
|
|
160
161
|
|
|
161
162
|
There is **one row per command and per effect-changing flag combination**. The
|
|
162
163
|
flags that change what the invocation does to the world are listed once, in
|
|
163
|
-
`vocabulary.effect_changing_flags`: `--browser`, `--built`, `--dry-run`,
|
|
164
|
+
`vocabulary.effect_changing_flags`: `--apply`, `--browser`, `--built`, `--dry-run`,
|
|
164
165
|
`--emit-packet`, `--example`, `--force`, `--from-store`, `--list`,
|
|
165
166
|
`--no-live-refs`, `--no-post-verdict`, `--no-probe`, `--no-remit`, `--no-run-session`,
|
|
166
167
|
`--no-write`, `--republish`, `--test-order`, `--write`, `--write-map`. Flags
|
|
@@ -208,9 +209,7 @@ template family is certified, and whether a named brief can be read, depend on
|
|
|
208
209
|
file content: those checks still run in the handler and are journaled.
|
|
209
210
|
The operator-facing `run-record` and `run end` commands refuse bare, empty, or
|
|
210
211
|
whitespace-only values for every value-taking inherited run-record flag before
|
|
211
|
-
packet work.
|
|
212
|
-
token and elapsed-time flags retain their non-negative-integer diagnostics;
|
|
213
|
-
`--surfaces` rejects unknown values, and `--dry-run` rejects a value. The
|
|
212
|
+
packet work. `--surfaces` rejects unknown values, and `--dry-run` rejects a value. The
|
|
214
213
|
inherited boolean flags (`--no-remit`, `--no-write`, `--dry-run`, and `--json`)
|
|
215
214
|
retain their bare-flag behavior. `run end` also rejects `--new-run` and
|
|
216
215
|
`--run-id` because the saved session fixes its run ID. `run-record` also
|
|
@@ -224,8 +223,7 @@ literal relative path, so the default context file is not read. Bare or empty
|
|
|
224
223
|
`--context` or `--report` still makes QA auto-end fail and leaves the session
|
|
225
224
|
open; bare or empty `--qa-verdict` fails a Run Record closeout when inherited,
|
|
226
225
|
though QA auto-end supplies its own verdict path. The underlying run-record
|
|
227
|
-
handler still rejects
|
|
228
|
-
integers, unknown `--surfaces`, and any valued `--dry-run` that reaches it. QA
|
|
226
|
+
handler still rejects unknown `--surfaces` and any valued `--dry-run` that reaches it. QA
|
|
229
227
|
auto-end drops `--dry-run` from inherited flags; if another inherited value
|
|
230
228
|
fails in the handler, auto-end is skipped and the session stays open. QA's own
|
|
231
229
|
journal entry is unaffected because auto-end runs after QA persistence. A named
|
package/docs/local-setup.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
For a new campaign, create an empty working folder and run this from it:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
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.
|
|
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.55.0 && npx --no-install campaigns-os tooling setup --target . --platform claude
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
`npm init -y` gives the folder its own `package.json`. Without one, npm
|
|
@@ -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.55.0`
|
|
30
30
|
|
|
31
31
|
## Forward compatibility
|
|
32
32
|
|
|
@@ -65,7 +65,9 @@ rename; a live process is never evicted. A lock directory with no owner record
|
|
|
65
65
|
a second. If one is found, or if recovery itself is interrupted, capture fails
|
|
66
66
|
closed and the warning names the affected `.allocation-lock` directory: stop
|
|
67
67
|
all Campaigns OS writers for that target, then remove that directory before
|
|
68
|
-
retrying `next`.
|
|
68
|
+
retrying `next`. A file or other non-lock entry at the lock path is named in
|
|
69
|
+
the warning and never removed: move it out of the way. Do not remove a lock
|
|
70
|
+
while a writer is active. Do not run an
|
|
69
71
|
older Campaigns OS release against the same target at the same time.
|
|
70
72
|
|
|
71
73
|
An unchanged projection reuses its ID, timestamp and sequence. Identity
|
|
@@ -59,8 +59,10 @@ the toolkit runs that loop in a fixed order:
|
|
|
59
59
|
2. **Serve and prove.** Serve `_site/` on localhost (the `next deploy` handoff
|
|
60
60
|
names the directory and any root-route rewrite) and run `polish capture`
|
|
61
61
|
against it. Once polish is recorded, `record deploy --packet <p> --base-url
|
|
62
|
-
<url>` records the URL on `deploy.preview_url` and the deploy stage
|
|
63
|
-
|
|
62
|
+
<url>` records the URL on `deploy.preview_url` and the deploy stage, bound
|
|
63
|
+
to the build it probed (a `record build` of different output makes it owed
|
|
64
|
+
again and keeps the old record in the deploy stage's history); then run `qa run --browser` and the typed-card order paths against
|
|
65
|
+
it.
|
|
64
66
|
3. **Prove the pin on the production output.** Before committing, run
|
|
65
67
|
|
|
66
68
|
```bash
|
|
@@ -339,6 +341,24 @@ npm run campaigns-os -- qa run \
|
|
|
339
341
|
|
|
340
342
|
The runner fetches deployed pages, checks route availability, verifies CampaignSpec `sdk_hints.meta_tags` (a key the Campaign Cart SDK does not read, `next-currency` or `next-predictive-address` from `src/sdk-meta-tags.mjs`, is a `warn` row at severity `warn` carrying the shared note, never `manual_review` and never a blocker, whether or not the tag rendered; doctor reports the same key as `sdk_hints.meta_tags.ignored_by_sdk`), writes a local verdict JSON under `<target-repo>/qa-output/<qa-storage-key>/<run-id>.json` (the packet's `assembly.target_repo`, else the packet's directory; `--output-dir` overrides it, and a packet-less run uses `qa-output/` under the current directory), and returns exit code `4` when the verdict is blocked. The storage key is the Map ID for saved-Map QA and `local-spec-<local_spec_id>` for local-spec packet QA. The target's managed ignore block lists `qa-output/`, because full verdicts carry live storefront URLs; the committed form is the `.campaign-runtime/qa-verdict.json` projection.
|
|
341
343
|
|
|
344
|
+
`--currency <code>` proves one currency per run: the runner adds
|
|
345
|
+
`?currency=<CODE>` to each funnel's entry URL (the HTTP fetch, the browser load
|
|
346
|
+
and `entry_urls`) and to the first page load of each test-order path (the
|
|
347
|
+
checkout probe, or the entry page or checkout when that load comes first),
|
|
348
|
+
keeping any query or tracking params already there. Campaign Cart keeps the URL
|
|
349
|
+
currency for the rest of that tab's session, so the runner adds it to no other
|
|
350
|
+
load (upsell pages and the receipt reload in recovery go without it); a later
|
|
351
|
+
load of the entry URL still carries it, because that URL is tagged. The flag takes one code per run (the
|
|
352
|
+
shared parser keeps the last one if it is given twice). The code must be three
|
|
353
|
+
letters (upper-cased: `gbp` runs as `GBP`); anything else is refused before any
|
|
354
|
+
request. A campaign set up for three currencies takes three runs. `qa parity`
|
|
355
|
+
refuses `--currency`, and so does a run whose orders come from
|
|
356
|
+
`--legacy-api-test-order`, since a direct API order would be placed in the
|
|
357
|
+
default currency. The persisted verdict redacts every URL query, so the
|
|
358
|
+
currency is recorded as `evidence.currency` on the entry page's
|
|
359
|
+
`http:<page_id>` assertion; an entry URL that does not parse gets no currency,
|
|
360
|
+
and its row records `evidence.currency_not_applied` with the reason instead.
|
|
361
|
+
|
|
342
362
|
### Automatic commercial parity
|
|
343
363
|
|
|
344
364
|
When the CampaignSpec has enabled pages with package rows, the same `qa run`
|
|
@@ -726,6 +746,16 @@ spec resolved from the portal for the run — keep publish-by-default regardless
|
|
|
726
746
|
of consent: those verdicts are the QA tab's product surface, not telemetry.
|
|
727
747
|
Explicit flags always win in both directions for saved-Map QA.
|
|
728
748
|
|
|
749
|
+
A run whose base URL is a local address (`localhost` or any `*.localhost`
|
|
750
|
+
name, any `127.x.x.x` address, `0.0.0.0`, `[::1]` or an IPv4-mapped loopback,
|
|
751
|
+
from `--base-url` or the packet's `deploy.preview_url`) keeps its verdict local
|
|
752
|
+
by default, whatever the spec source. Those runs are checks on your own
|
|
753
|
+
machine, and people watching the Map's QA tab should not see them. The output
|
|
754
|
+
names the destination and how to publish: `qa publish` for the stored verdict,
|
|
755
|
+
or `--post-verdict` on the next run. `--post-verdict` publishes a local-address
|
|
756
|
+
run, and `publish_decision.reason` reads `loopback_base_url` when the default
|
|
757
|
+
kept it local.
|
|
758
|
+
|
|
729
759
|
A packet with `local_spec_id` always keeps its verdict and progress local.
|
|
730
760
|
`qa run` suppresses portal publication even with `--post-verdict`; the flag
|
|
731
761
|
does not turn a local ID into a Map destination. `qa publish` refuses such a
|
|
@@ -792,7 +822,7 @@ prints the portal link.
|
|
|
792
822
|
The outcome lands on the run's Run Record as the `qa_verdict_publish` block —
|
|
793
823
|
`verdict_run_id`, `publisher` (`qa run` or `qa publish`), `state`
|
|
794
824
|
(`skipped` / `ok` / `failed`), `result` in the remit vocabulary, `base_kind`,
|
|
795
|
-
`published_at` — on the record whose `qa_verdict` artifact references the
|
|
825
|
+
`published_at`, and the nullable publish decision `reason` — on the record whose `qa_verdict` artifact references the
|
|
796
826
|
verdict under the packet's campaign. `qa run` records its own publish (or
|
|
797
827
|
its `--no-post-verdict` skip) the same way when the run session closes, which
|
|
798
828
|
is what `already_published` reads. A stored `ok` is never downgraded: a
|
|
@@ -1171,8 +1201,8 @@ Fixture essentials:
|
|
|
1171
1201
|
|
|
1172
1202
|
Test Orders use **global test cards** that work on any live store and integration.
|
|
1173
1203
|
They **bypass the payment gateway and create no transactions** (and no fulfillment),
|
|
1174
|
-
|
|
1175
|
-
|
|
1204
|
+
but each creates a real order record on the store. Unless the operator has already said test orders are fine for this campaign, ask once, up front, before placing test orders; then proceed. No permission flag,
|
|
1205
|
+
packet policy, or merchant sandbox routing is required. They leave a small,
|
|
1176
1206
|
easy-to-clean footprint: Test orders are deletable in bulk, and the resulting
|
|
1177
1207
|
Customer record is reused (see the test email note below) rather than multiplied.
|
|
1178
1208
|
|
|
@@ -1554,17 +1584,43 @@ Two flags target funnels the default-tier drive cannot prove:
|
|
|
1554
1584
|
|
|
1555
1585
|
- `--select-package <ref[:qty],...>` — **strict** package/bundle card selection.
|
|
1556
1586
|
Each ref is matched against rendered selector/bundle cards
|
|
1557
|
-
(`[data-next-package-id]`, `[data-next-bundle-card][data-next-bundle-id]
|
|
1558
|
-
|
|
1587
|
+
(`[data-next-package-id]`, `[data-next-bundle-card][data-next-bundle-id]`, and
|
|
1588
|
+
the package a bundle card's `data-next-bundle-items` declares, so
|
|
1589
|
+
`--select-package 1:2` finds the card declaring
|
|
1590
|
+
`[{"packageId":1,"quantity":2}]` whether or not an inner node repeats the
|
|
1591
|
+
package id) and clicked. An explicit quantity requires one unambiguous rendered card whose
|
|
1559
1592
|
`data-next-bundle-items` composition contains exactly that package and
|
|
1560
|
-
quantity; package identity alone is not enough.
|
|
1593
|
+
quantity; package identity alone is not enough. A card declaring several
|
|
1594
|
+
items matches by any one of them (`4:1` finds a kit declaring
|
|
1595
|
+
`[{"packageId":1,"quantity":2},{"packageId":4,"quantity":1}]`); a card
|
|
1596
|
+
declaring the item alone is preferred over one declaring it among others. The card must then expose a
|
|
1561
1597
|
selected-state marker (`data-next-selected="true"` / `.next-selected`) after
|
|
1562
1598
|
the click. A missing card, ambiguous composition, wrong quantity, or
|
|
1563
1599
|
unverifiable/refused selection **fails the `selected_bundle` step** instead
|
|
1564
1600
|
of silently driving the pre-selected default tier. A bundle ref remains an
|
|
1565
1601
|
authoritative identity when exactly one rendered card declares it. Use this
|
|
1566
1602
|
flag to traverse non-default tiers of a multi-tier selector. `--cart` remains
|
|
1567
|
-
the best-effort variant.
|
|
1603
|
+
the best-effort variant. For a package that bundle cards declare, `--cart
|
|
1604
|
+
<ref:qty>` clicks the card whose items carry that package at that quantity,
|
|
1605
|
+
and clicks nothing when the selected card already does; refs no bundle card
|
|
1606
|
+
declares (an order bump) are clicked as before. The cards are read again
|
|
1607
|
+
before each ref, so a selector that re-renders after a click is clicked as
|
|
1608
|
+
it now stands. Beside `--select-package`,
|
|
1609
|
+
`--cart` never clicks another card in the chosen card's selector group, and
|
|
1610
|
+
a card in no recognised selector container
|
|
1611
|
+
(`[data-next-bundle-selector]`, `[data-next-selector-id]`,
|
|
1612
|
+
`[data-next-cart-selector]`) counts as in that group: a
|
|
1613
|
+
ref the chosen card carries is left as it is, a ref an order bump or a card
|
|
1614
|
+
in another selector group carries is applied through that control, and any
|
|
1615
|
+
other ref is left unapplied and named in the `selected_bundle` detail
|
|
1616
|
+
(`--cart 3:1 not applied: ...`). The display-parity row reconciles the order
|
|
1617
|
+
against the packages and quantities every selected bundle card declares, not
|
|
1618
|
+
its `data-next-bundle-id`. Without `--select-package` it also fails when an
|
|
1619
|
+
explicit `--cart <ref:qty>` is not in the order at that quantity: a
|
|
1620
|
+
different quantity (`--cart 1:2` that ordered one unit) or no line for the
|
|
1621
|
+
package at all (`--cart 1:2,3:1` on a swap-mode selector that kept only the
|
|
1622
|
+
`3:1` card); beside `--select-package` it judges the order against the card
|
|
1623
|
+
that flag chose.
|
|
1568
1624
|
- `--apply-coupon <code>` — types the code into the rendered coupon/promo input
|
|
1569
1625
|
(the SDK's `[data-next-checkout-field="coupon"]` or
|
|
1570
1626
|
`input[data-next-coupon="input"]`, then hand-rolled `coupon`/`voucher`/`promo`
|
|
@@ -1697,7 +1753,7 @@ browser launch, prints the planned count, lists the planned paths (up to 40 ids;
|
|
|
1697
1753
|
past that the remainder is counted, never cut silently, and `--select-package
|
|
1698
1754
|
<ref[:qty]>` lists one tier's paths), and names the exact `--max-test-orders <count>` raise. For example, a linear three-offer graph has
|
|
1699
1755
|
eight terminal paths plus the checkout baseline, so it requires
|
|
1700
|
-
`--max-test-orders 9`.
|
|
1756
|
+
`--max-test-orders 9`. Unless the operator has already said test orders are fine for this campaign, ask once, up front, before placing these real store order records; then proceed. The command has no permission flag.
|
|
1701
1757
|
|
|
1702
1758
|
`--max-test-orders` bounds **planned paths**, which is not the same as real
|
|
1703
1759
|
purchases. `--max-order-creations` bounds **actual order creations**, defaults to
|
|
@@ -1810,7 +1866,12 @@ The browser driver intentionally behaves like a user:
|
|
|
1810
1866
|
proven against the persisted-order voucher read-back
|
|
1811
1867
|
- checkout is advanced through the visible cart/checkout button
|
|
1812
1868
|
- address autocomplete is settled or closed before submit
|
|
1813
|
-
-
|
|
1869
|
+
- the card and CVV iframes are filled with sequential keystrokes: Spreedly's
|
|
1870
|
+
iFrame fields on SDK 0.4.40 and earlier, and on 0.4.41 and later the
|
|
1871
|
+
Spreedly-hosted fields that NEXT's `payments.29next.com` script mounts.
|
|
1872
|
+
Typing starts once the checkout form drops `next-loading-spreedly` (the
|
|
1873
|
+
fields are ready), and both fields are read back and retyped, up to three
|
|
1874
|
+
attempts in all, before `card_fields_filled` passes
|
|
1814
1875
|
- the real submit button is clicked without fabricating SDK state
|
|
1815
1876
|
|
|
1816
1877
|
The intended QA order matrix is:
|
|
@@ -1832,7 +1893,7 @@ and hardcoded phone numbers that differ from CampaignSpec `campaign.store_phone`
|
|
|
1832
1893
|
If a static claim is intentionally preserved, wrap it in an element with
|
|
1833
1894
|
`data-skip-market-lint="true"` and record why in the assembly report.
|
|
1834
1895
|
|
|
1835
|
-
Test orders
|
|
1896
|
+
Test orders need no allowlist and no permission flag; unless the operator has already said test orders are fine for this campaign, ask once, up front, before the first one (each is a real order record). A separate concern is the
|
|
1836
1897
|
**SDK origin allowlist**: the Campaign Cart SDK must be allowed to load on the
|
|
1837
1898
|
tested origin for the campaign API key, or runtime checks (and the live page
|
|
1838
1899
|
itself) may not initialize. Localhost on any port is globally available as a
|
|
@@ -1847,8 +1908,8 @@ npm run campaigns-os -- qa policy set \
|
|
|
1847
1908
|
--allowed-domains-confirmed true
|
|
1848
1909
|
```
|
|
1849
1910
|
|
|
1850
|
-
There is no permission flag for test orders
|
|
1851
|
-
<mode
|
|
1911
|
+
There is no permission flag for test orders; unless the operator has already said test orders are fine for this campaign, ask once, up front, before placing these real store order records, then run `--test-order
|
|
1912
|
+
<mode>`. The former `--test-orders-allowed` /
|
|
1852
1913
|
`--sandbox-test-card-confirmed` flags and the `qa.test_orders_allowed` /
|
|
1853
1914
|
`qa.sandbox_test_card_confirmed` packet fields they set were removed in
|
|
1854
1915
|
supported surface 1.28.0: nothing had read their values since the gate itself
|
|
@@ -1962,6 +2023,14 @@ placeholder-text blocker and the other residue gates still run. The emitted
|
|
|
1962
2023
|
minimal packet is marked `_synthesized` — it points doctor/QA at the built
|
|
1963
2024
|
output and family, and is not a substitute for a real Build Packet.
|
|
1964
2025
|
|
|
2026
|
+
`doctor --built` reads the local built files only. `--base-url` is not
|
|
2027
|
+
fetched: it fills `deploy.preview_url` in the minimal packet, and the output
|
|
2028
|
+
says so (`derived.base_url.fetched: false` in JSON). A clean `doctor --built`
|
|
2029
|
+
run with a preview URL says nothing about the preview. To check the served
|
|
2030
|
+
pages, run `qa run --site <repo> --base-url <url> --family <family> --browser`.
|
|
2031
|
+
The emitted minimal packet does not drive packet QA: `qa resolve` and
|
|
2032
|
+
`qa run --packet` block on it, because it carries no local CampaignSpec.
|
|
2033
|
+
|
|
1965
2034
|
**Trade-off — non-packet QA is narrower than packet-driven QA.** It runs the
|
|
1966
2035
|
built-output gates (residue, placeholder text, demo-asset, pricing-CSS, brand
|
|
1967
2036
|
contract) but **skips the CampaignSpec/source-HTML-driven checks** a packet
|
|
@@ -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.55.0`.
|
|
12
12
|
|
|
13
13
|
## What this is
|
|
14
14
|
|
|
@@ -17,3 +17,18 @@ Acorn parses JavaScript ASTs; parse5 identifies executable inline HTML scripts a
|
|
|
17
17
|
Dynamic storage keys, unresolved storage methods, shadowed storage names, missing migration release evidence, unsupported target versions, and JavaScript parse failures report unknown. This bounded analysis does not execute merchant code or prove computed keys correct. It does not follow arbitrary data flow, dynamic imports, event-handler attributes, generated code or runtime-loaded script graphs. Inspect unresolved findings and include all campaign sources in scope; a clean result is only static tracked source compatibility evidence. Browser behavior, order creation, tracking and analytics still require their own proof, especially where multiple campaigns share an origin. Use the SDK public store API where the manifest recommends it; do not invent a prefix-first merchant repair.
|
|
18
18
|
|
|
19
19
|
Storage `length` property reads are unaffected metadata (`storage-metadata-read`). Storage `key()` enumeration, extracted method references, and mutations of reserved members remain unknown; enumeration cannot prove an SDK key lookup safe. Git discovery failures return one bounded CLI error. Local script diagnostics retain repository-relative spellings from HTML source, including `../` paths; excluded/outside sources are not read. The Git root and operator target are compared by filesystem device/inode identity, so supported case aliases and symlinks can name the same root. Source containment uses realpaths below the Git root without case folding. Extra positional CLI arguments are rejected so an accidental scope or version value cannot silently disappear.
|
|
20
|
+
|
|
21
|
+
## Rewriting hardcoded pins in a static repo
|
|
22
|
+
|
|
23
|
+
A static campaign repo (no `_data/campaigns.json`) pins the SDK in its HTML: `loader.js@v0.4.x` in script tags and `campaign-cart.css@v0.4.x` in link tags. Once the storage check is clean, `sdk repin` updates those pins:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npx --no-install campaigns-os sdk repin --target . --target-sdk 0.4.38 # preview: path:line, old URL -> new URL; writes nothing
|
|
27
|
+
npx --no-install campaigns-os sdk repin --target . --target-sdk 0.4.38 --apply # writes the files and the change record
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
It reads the same references `standardize` reports under `sdk_loader.references`: every Campaign Cart URL that points at a loader or dist artifact, in HTML and script files, with HTML comments, `_site/`, `node_modules/`, `dist/`, `build/` and `.campaign-runtime/` left out. Only a semver pin (`@vX.Y.Z` or `@X.Y.Z`) below the target is rewritten, and only the version segment of the URL changes; a leading `v` stays as written. These are reported and left alone: `@latest`, `@main`, commit refs, prerelease refs, and pins already at or above the target (repin never moves a pin down). Without `--target-sdk` the target is the SDK support policy's `preferred_minimum`, the version `standardize` evaluates pins against.
|
|
31
|
+
|
|
32
|
+
The default is a preview that writes nothing. `--apply` writes the files, then writes a change record to `.campaign-runtime/sdk-repin.json` (`campaigns-os-sdk-repin/v0`). The record holds the files touched, the reference count, the from and to versions, each rewrite with its path, line and artifact (`loader` or `css`), and the refs left alone. It uses repo-relative paths only, so you can commit it, and a Run Record can cite it by path and sha256 (`--json` prints the sha256). After writing, repin scans again and reports whether any pin below the target remains. Running it a second time with the same target finds nothing to change and writes nothing. `--json` prints the full result: `status` is `changes`, `nothing_to_change`, `no_references` or `refused_page_kit`.
|
|
33
|
+
|
|
34
|
+
A repo with `_data/campaigns.json`, at its root or in any folder the scan walks, is refused with exit 2 and nothing written: page-kit owns that pin, so use `campaigns-os page-kit sync --packet <campaign-runtime.build.json>` there. An invalid `--target-sdk` is refused with exit 1. Repin does not prove that the new SDK works. After rewriting, run the campaign's browser QA against the new pin.
|
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.55.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.55.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.55.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.55.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.55.0+skills.1",
|
|
94
94
|
"spelling": "bundle",
|
|
95
|
-
"on_disk": "1.
|
|
95
|
+
"on_disk": "1.55.0+skills.1",
|
|
96
96
|
"on_disk_skill": null,
|
|
97
|
-
"message": "match (1.
|
|
97
|
+
"message": "match (1.55.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.55.0+skills.1)
|
|
105
|
+
Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.55.0+skills.1 — start a fresh session
|
|
106
|
+
Skills revision: unchecked (on disk 1.55.0+skills.1)
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
`unchecked` is the state when the flag is absent. It is not an error — an
|