@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
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,151 @@
|
|
|
2
2
|
|
|
3
3
|
Notable supported-surface changes are recorded here.
|
|
4
4
|
|
|
5
|
+
## [1.55.0] - 2026-10-07
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
Run Records now carry the optional `qa_verdict_publish.reason` from the QA publish decision and `closed_by` from the command path that closed the record. The reason distinguishes loopback, portal managed, consent, flag, default, and local spec decisions; the closer distinguishes `run end`, QA auto-end, stale sweep, and direct `run-record`. The unused `agent_usage` field and its `--agent-*` input flags are removed. Re-emitting an older record with `agent_usage` replaces it with a fresh record without carrying forward its prior remit state. `qa publish` posts its verdict but cannot stamp that older record as published.
|
|
10
|
+
|
|
11
|
+
QA observations in a Run Record now carry the verdict's `run_id`, the build fingerprint recorded for that verdict, and whether it differs from the build recorded in the Assembly Report. A rebuild without `record build` still reads `stale: false` when those recorded fingerprints match; doctor's `built_output.fingerprint_stale` checks the output on disk. Currency is unknown when either fingerprint is missing or the report's QA stage belongs to a different verdict.
|
|
12
|
+
|
|
13
|
+
Lifecycle stages now carry prompt `wait_ms` and, on a blocked doctor or QA exit, up to five distinct `finding_codes`. Run lifecycle separates exit 2 invocations in `needs_input_count` from other non-zero exits in `failure_count`. Legacy journal entries keep wait unknown, while new commands without prompts record zero. Duration continues to include prompt wait.
|
|
14
|
+
|
|
15
|
+
## [1.54.0+agent.10] - 2026-10-07
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
Generated brand themes keep sale, discount, badge, rating, star, strike, border and announcement colours out of primary and CTA roles when those names describe the selector's subject. Brand and button names keep their role. Header, navigation and button colours continue to map as they do on main, including selectors after CSS at-rules. When a design has no CTA token or button background, the existing CTA fallback warning asks the operator to confirm an accent colour that may be decorative.
|
|
20
|
+
|
|
21
|
+
## [1.54.0+agent.9] - 2026-10-07
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
After a page-kit build, `record build --adapter-decision <key>=<value>[,<key>=<value>...]` records the choices that are true for that build on the Assembly Report. Put all pairs in one flag: a repeated flag keeps only the last list. The build prompt now asks for actual values instead of supplying a completed status. Help and the Build Packet list the allowed values. `wrapper_policy` is selected at intake with `prepare-build --wrapper-policy` or the source-html manifest option; `record build` refuses it and names those options. Doctor checks leftover document wrappers against the effective recorded conversion status, and its adapter warnings name the recording command. The packet and Build Context are unchanged by `record build`. `template_files_copied` remains object-valued proof outside the flag.
|
|
26
|
+
|
|
27
|
+
## [1.54.0+agent.8] - 2026-10-07
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- `next` asks for Polish when a theme waiver or changed build leaves it owed on a local preview. It asks for deploy until that stage is recorded, even if the Build Packet already has a preview URL.
|
|
32
|
+
- After a changed build, `next` asks for QA again when the last QA verdict names the old build, even if its recorded stage still says completed.
|
|
33
|
+
- `--help` commands leave lifecycle and deviation journals untouched. Re-running `start` later in a campaign is treated as re-intake rather than a deviation.
|
|
34
|
+
|
|
35
|
+
## [1.54.0+agent.7] - 2026-10-07
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
`qa run --currency <code>` runs QA in one of a campaign's currencies. The run adds `?currency=<CODE>` to each funnel's entry URL, so the HTTP fetch, the `--browser` load and the reported `entry_urls` carry it, and each test-order path's first page load (the checkout probe, or the entry page or checkout when that load comes first) carries it too. Campaign Cart keeps the currency for the rest of that tab's session, so the runner adds it to no other load (an upsell page or a receipt reload goes without it), while a later load of the entry URL still carries it because that URL is tagged. Query and tracking parameters already on the URL are kept. The code must be three letters and is upper-cased (`gbp` runs as `GBP`); anything else, including no value, an empty value or a list, is refused before any request. The flag takes one code per run; given twice, the last one is used. `qa parity --currency` is refused, and so is `--currency` with `--legacy-api-test-order`, whose direct API order would be placed in the default currency. Before this, a run could prove only the default currency, and testing another took a hand-made copy of the built site that forced the parameter. The stored verdict removes URL queries, so the currency is recorded as `evidence.currency` on the entry page's `http:<page_id>` assertion; an entry URL that does not parse gets `evidence.currency_not_applied` with the reason instead. The verdict schema is unchanged, and a run without the flag is unchanged.
|
|
40
|
+
|
|
41
|
+
## [1.54.0+agent.6] - 2026-10-07
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
Doctor now blocks receipt order lines hidden by a cart-state visibility condition, including when only the CampaignSpec identifies the receipt page. When the campaign API key comes directly from the Build Packet or CampaignSpec, or from the environment variable named by Build Packet `campaign.api_key_source`, doctor blocks built pages with a different key even if those pages agree with one another. A mismatch names each wrong key source and the pages using it, so the built keys can be replaced before test orders begin.
|
|
46
|
+
|
|
47
|
+
## [1.54.0+agent.5] - 2026-10-07
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- `qa run --test-order` orders a multi-unit bundle card the way the checkout shows it. A starter bundle card declares its package and per-package quantity in `data-next-bundle-items` (`bundle-2x` is `[{"packageId":1,"quantity":2}]`), and the runner now reads them there:
|
|
52
|
+
- `--select-package 1:2` finds the card whose items carry package 1 at quantity 2 even when no inner node repeats the package id. A card matches when any one of its declared items is that package at that quantity, so a kit card declaring `[{"packageId":1,"quantity":2},{"packageId":4,"quantity":1}]` is found by `4:1`; when a card declares the item alone as well, that card is preferred. Before, it failed `selected_bundle` with "no rendered card exposes that package or bundle identity".
|
|
53
|
+
- `--cart 1:2` clicks that same card. Before, it clicked the first `[data-next-package-id="1"]`, which on the starter partial is the one-unit card's inner node, so the order carried one unit. Without `--select-package`, a `--cart` ref the selected card already carries at that quantity clicks nothing, and a ref that no bundle card declares is clicked as before. The cards are read again before each ref, so a card a selector renders after an earlier `--cart` click is found.
|
|
54
|
+
- Beside `--select-package`, `--cart` never clicks another card in the selector group of the card `--select-package` chose, for bundle cards and plain package cards alike; a card in no `[data-next-bundle-selector]`, `[data-next-selector-id]` or `[data-next-cart-selector]` container counts as in that group, since its own wrapper does not say which cards it swaps with. A `--cart` ref that card carries is left as it is; a ref an order bump or a card in another selector group carries is applied through that control; any other ref is left unapplied and named in the `selected_bundle` detail (`--cart 3:1 not applied: only another card in the selector --select-package chose carries it`), and the order is reconciled against the card actually selected. Before, `--select-package 1:2 --cart 3:1` switched to the package-3 card while `selected_bundle` still reported the two-unit card, and `--select-package 1:2 --cart 1:2,2:1` reset the order to one unit.
|
|
55
|
+
- The `browser-order-display-parity` row reconciles the order against the packages and quantities every selected bundle card declares, however many cards are selected. Before, it counted the card's `data-next-bundle-id` as a displayed package and resolved lines at one unit per package, so a correct two-unit order failed with "displayed but never charged: 1".
|
|
56
|
+
- Without `--select-package`, the same row now fails when an explicit `--cart <ref:qty>` is not in the order at that quantity: the order carried another quantity, as when `--cart 1:2` produced a one-unit order, or no line for the package at all, as when `--cart 1:2,3:1` on a swap-mode selector kept only the `3:1` card. Its message names the requested and persisted units, or says the order carries no line for the package. A bare `--cart <ref>` is not judged. The row's message now also names `--select-package` quantity mismatches, which already failed the row but were not named in its message.
|
|
57
|
+
- Checkouts built from plain package cards (`[data-next-package-id]`, no bundle cards) are selected and reconciled as before, except that `--cart` beside `--select-package` no longer switches to another card in the chosen card's selector group, and without `--select-package` an explicit `--cart <ref:qty>` the order does not carry at that quantity fails the display-parity row.
|
|
58
|
+
|
|
59
|
+
## [1.54.0+agent.4] - 2026-10-07
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
`qa run` now calls `ready_with_exceptions` a passing proof, lists each exception once with its id and page (and severity when present), and tells the agent to report exceptions to the operator. `next` gives the same guidance when QA completed with exceptions while keeping the closeout action.
|
|
64
|
+
|
|
65
|
+
The completed-pipeline prompt and Build Packet guide now point to `record build` for repeated build work and distinguish local-serve recording from hosted deploy instructions. The starter-palette advisory prints the named-human argument required by a recorded `theme waive`, while help and QA guidance distinguish it from the one-run `--theme-waive` form. QA prompts, help, and guides ask once, up front, before typed-card test orders unless the operator already said they are fine for this campaign. Those orders leave real store records even though no transaction or money movement occurs. `ready` and `blocked` QA text keeps its existing finding format.
|
|
66
|
+
|
|
67
|
+
## [1.54.0+agent.3] - 2026-10-07
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
The `qa run --browser` primary-CTA row (`browser-primary-cta:<page>`) no longer fails `missing_route_cta` on checkout and upsell pages that Campaign Cart routes. Since 1.53.0 those pages are checked, but the check found a route only through a control's own link, and these pages have none: a checkout advances through the submit button of its `<form data-next-checkout="form">`, and an upsell through its `data-next-upsell-action="add"` and `"skip"` controls, which are `href="#"` links. So every SDK-routed checkout and upsell failed, the starter templates' own pages included, while the `route-link:*` rows and test orders passed on the same pages. QA now takes the route a page declares for these controls, from the same page fields the `route-link:*` rows read. On a checkout, the checkout form's submit button leads to the page's next route, whether it is `type="submit"` or has no `type` attribute, which submits the form just the same; express-checkout wallet buttons are not. On an upsell or downsell, the add control leads to the page's accept route and the skip control to its decline route, but only inside a `data-next-upsell` container: Campaign Cart binds upsell actions nowhere else, so an action outside one routes nothing. The control is checked as the primary CTA when that route is the page's next route, and its contrast, size and text are measured as before. On checkout, upsell and downsell pages these declared controls are the only controls a form can route. Any other form's `action`, for example a newsletter sign-up beside the checkout form, routes nothing there, even when it is the next route. So a page whose only route to the next page is such a form still fails `missing_route_cta`. On every other page a form's `action` is now the route of that form's own submit button only, and never inside the checkout form, which Campaign Cart submits itself. So bundle cards, wallet buttons and other controls inside a form no longer become CTA candidates because the form has an action; before, they did, and the row turned to `manual_review`. A page with no control leading to its next route still fails `missing_route_cta`.
|
|
72
|
+
|
|
73
|
+
QA now recognises `data-next-upsell-action="accept"` and `data-next-upsell-action="decline"` on upsell pages. Campaign Cart treats `accept` exactly like `add` and `decline` exactly like `skip`, but QA only looked for `add` and `skip`. So on a page written with `accept` / `decline`, the test-order upsell step failed with `Missing upsell control`, the `browser-upsell-control:<page>:accept` and `:decline` rows read `not found`, the primary-CTA row failed `missing_route_cta`, and the `route-link:<page>:accept` and `:decline` rows fell to `manual_review` when the route URL was not in the page's HTML. QA now takes either spelling everywhere it looks for an upsell control: the test-order upsell step and its timeout evidence, the upsell-page readiness wait, the rendered upsell-control rows, the primary-CTA route rule, and the static `route-link:*` rows, including the check for a `data-upsell-proxy` button, which forwards to the in-offer action with its own spelling; Polish readability's contrast check also takes either spelling for its `upsell_accept` and `upsell_decline` roles. Pages written with `add` / `skip` are checked as before, and when a page has neither spelling, QA's messages still name `add` and `skip`. Other values are still not upsell controls.
|
|
74
|
+
|
|
75
|
+
## [1.54.0+agent.2] - 2026-10-07
|
|
76
|
+
|
|
77
|
+
### Changed
|
|
78
|
+
|
|
79
|
+
Repository CI and contributor tooling only; the published package is unchanged. CI runs the unit and browser lanes as two shards each on separate runners, and a newer push to a pull request cancels that pull request's older run; pushes to `main` always finish. `npm run check:fast` runs the checks of CI's types and contracts lanes and the three PR-only release gates against `origin/main` in under a minute, and `npm run hooks:install` installs it as a pre-push hook for the clone and its worktrees (`docs/small-pr-review-path.md`).
|
|
80
|
+
|
|
81
|
+
## [1.54.0+agent.1] - 2026-10-07
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
|
|
85
|
+
- When `start`, `prepare-build` or `build` refuses to overwrite a report that already carries stage evidence, the message now says that, if the brief or CampaignSpec changed, `record brief` or `record spec` binds it while keeping that evidence. Before, it offered only `--force`, which resets the recorded stages.
|
|
86
|
+
- README: the build steps and the `record` command list now include `record theme`, `record deploy` (bound to the build it probed), `record brief` and `record spec`. The SDK examples take `--target-sdk <x.y.z>` instead of 0.4.38. The `tooling status` note no longer says npm has no dist-tag, the Node floor reads 20.19.0 as in `engines`, and `--force` is described as moving completed records to stage history.
|
|
87
|
+
- `docs/quickstart.md`: a QA run against a local-address base URL keeps its verdict local by default, whatever the telemetry consent.
|
|
88
|
+
|
|
89
|
+
## [1.54.0] - 2026-10-07
|
|
90
|
+
|
|
91
|
+
Ships the same-surface changes 1.53.0+agent.1 through +agent.11 (+agent.6 and +agent.8 were never used), including the `qa run --test-order` fix for Campaign Cart SDK 0.4.41 card fields (+agent.5) and the starter catalog, SDK attribute index and support policy at 0.4.41 (+agent.10, +agent.11).
|
|
92
|
+
|
|
93
|
+
### Added
|
|
94
|
+
|
|
95
|
+
`campaigns-os sdk repin --target <repo> [--target-sdk <x.y.z>] [--apply] [--json]` updates the Campaign Cart SDK pins written into a static campaign repo's HTML, the `loader.js` and `campaign-cart.css` URLs that `standardize` already lists with path and line. It rewrites only a semver pin (`@vX.Y.Z` or `@X.Y.Z`) below the target version, and only the version part of the URL. `@latest`, `@main`, commit and prerelease refs, and pins already at or above the target, are listed and left alone. Without `--target-sdk` the target is the SDK support policy's `preferred_minimum`. By default it only previews, printing each path and line with the old and new URL. `--apply` writes the files and a change record at `.campaign-runtime/sdk-repin.json` with the files touched, the reference count and the from and to versions, which a Run Record can cite. A second run finds nothing to change and writes nothing. A repo with `_data/campaigns.json` is refused with exit 2, because page-kit owns that pin; use `page-kit sync` there. This replaces the hand-run `sed` step in the SDK bump for static repos.
|
|
96
|
+
|
|
97
|
+
### Changed
|
|
98
|
+
|
|
99
|
+
`qa run` no longer publishes a verdict to the QA portal by default when the base URL is a local address (`localhost` or any `*.localhost` name, any `127.x.x.x` address, `0.0.0.0`, `[::1]` or an IPv4-mapped loopback), whether the spec came from a saved Map or a local file. The verdict stays local, and the output names the destination plus `qa publish` and `--post-verdict`. Pass `--post-verdict` to publish a local-address run. `publish_decision.reason` reads `loopback_base_url` when this default applied. Runs against a remote base URL publish exactly as before. The `--post-verdict` help line now names the IPv4-mapped loopback too.
|
|
100
|
+
|
|
101
|
+
`record deploy` now stamps `stages.deploy.source_build_fingerprint` with the recorded build it probed. When a later `record build` records different output, a deploy stamped with the old build becomes `required` (`required_by` build, `required_for` qa), its old probe (`outputs` and `evidence`) leaves the live stage and the prior record is kept in `stages.deploy.history`, and `next` routes back to `record deploy` before QA. A rebuild with byte-identical output keeps the deploy current. A deploy recorded before this change carries no stamp and is kept as recorded. The Assembly Report schema describes the field.
|
|
102
|
+
|
|
103
|
+
The effects contract declares the `sdk repin` and `sdk repin --apply` rows, and its `record deploy` and `record build` rows say a deploy is stamped with the build it probed and owed again after a build of other output. On the eight `qa run` rows, the verdict send to `{proxy-base}/api/qa/verdicts` now names the local-address default in its condition, and the offline fixture no longer observes it because it serves the campaign from 127.0.0.1; `qa publish` proves that send.
|
|
104
|
+
|
|
105
|
+
## [1.53.0+agent.11] - 2026-10-07
|
|
106
|
+
|
|
107
|
+
### Changed
|
|
108
|
+
|
|
109
|
+
The bundled Campaign Cart SDK support policy records 0.4.41 as the latest known release (`provenance.latest_known_release`, was 0.4.38). With the starter catalog verified against 0.4.41 (1.53.0+agent.10), template freshness already took 0.4.41 as the current SDK, so no family's freshness changes; the policy now agrees with the catalog instead of naming a release three behind it, and "current SDK" is reported from the policy. `minimum_supported` (0.4.20) and `preferred_minimum` (0.4.30) are unchanged.
|
|
110
|
+
|
|
111
|
+
## [1.53.0+agent.10] - 2026-10-07
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
|
|
115
|
+
- The vendored starter-template catalog is re-synced to campaign-cart-starter-templates `7833290` (was `2c61894`). Every certified family now records Campaign Cart SDK 0.4.41 verification evidence (`sdk-0.4.41-2026-10-06.5`, a `lint-sdk` pass), and the eight agent CampaignSpec fixtures pin `sdk_version` 0.4.41. Doctor's missing-SDK-pin message therefore names 0.4.41 as the version a family was verified against. The certified-family render fixtures are regenerated at the new pin: the loader moves to 0.4.41, and the checkout and receipt pages pick up the starters' SDK address blocks (`data-next-address`) and the savings badge drawn from the summary token. The payment-chrome `asset_pin` moves to the same commit; the five hashed assets are byte-identical there.
|
|
116
|
+
- The SDK attribute index (`src/sdk-attribute-index.mjs`) moves from v0.4.38 to v0.4.41. v0.4.41 removed the generated `docs/attribute-index.md` the old recipe read, and the SDK's hand-written attribute docs leave out names its source still reads. The list is now regenerated from the SDK source at the tag (every `data-next-*` string and `dataset.next*` property in non-test TypeScript), and that recipe is in the file. It keeps all 135 names of the v0.4.38 list and adds 14: the address block (`data-next-address`, `-lang`, `-api`, `-field`, `-row`, `-state`), `data-next-i18n`, `data-next-phone-e164`, `data-next-phone-country`, `data-next-bump-section`, `data-next-button-text`, and the variant option state (`data-next-variant-name`, `-value`, `data-next-unavailable`). `built_output.sdk_markup` no longer reports these as unknown, and its findings now name `sdk_attribute_index_version` 0.4.41. The checkout field names are unchanged at v0.4.41.
|
|
117
|
+
- The raw cart placeholder check (`built_output.cart_placeholders`) accepts pages pinned to 0.4.41: all ten renderer files it was generated from have the same git blob ids at v0.4.38 and v0.4.41. Before, every 0.4.41 page reported `unexercised` (`sdk_pin_unverified`).
|
|
118
|
+
|
|
119
|
+
## [1.53.0+agent.9] - 2026-10-06
|
|
120
|
+
|
|
121
|
+
### Fixed
|
|
122
|
+
|
|
123
|
+
The per-target lock now tells an ownerless lock apart from something at the lock path that is not a lock. When the wait ends on an ordinary file, a symbolic link, or a directory with no owner record that holds other entries, the refusal names what is there and says it is never removed automatically; before, it read as a busy or ownerless lock. An empty ownerless lock directory is still refused as before. The holder of a lock also clears the staging, recovery-staging and released directories that a crashed process or a failed release left beside the lock, but only those whose recorded owner process no longer exists; a sibling with a live or unreadable owner is left alone.
|
|
124
|
+
|
|
125
|
+
## [1.53.0+agent.7] - 2026-10-06
|
|
126
|
+
|
|
127
|
+
### Changed
|
|
128
|
+
|
|
129
|
+
`doctor --built --base-url <url>` now says that it checked the local built files only. The URL is not fetched: it fills `deploy.preview_url` in the minimal Build Packet. The output names `qa run --site <repo> --base-url <url> --family <family> --browser` as the command that loads the served pages, and `--json` carries `derived.base_url` with `fetched: false`. The checks themselves are unchanged.
|
|
130
|
+
|
|
131
|
+
## [1.53.0+agent.5] - 2026-10-06
|
|
132
|
+
|
|
133
|
+
### Fixed
|
|
134
|
+
|
|
135
|
+
- `qa run --test-order` types the test card on pages running Campaign Cart SDK 0.4.41. That release draws the card number and CVV with the fields NEXT's `payments.29next.com` script mounts, whose iframes are named `spreedly-hosted-number-…` and `spreedly-hosted-cvv-…`; the runner looked only for the `spreedly-number-frame-…` and `spreedly-cvv-frame-…` iframes of 0.4.40 and earlier, so on a 0.4.41 checkout it timed out before submitting. It now finds either, and nothing changes on pages pinned to 0.4.40 or earlier. A checkout carrying both kinds of card iframe, or two of either, fails at `card_fields_filled` with the iframe ids it found instead of typing into whichever comes first, and the step's evidence records which iframes it typed into.
|
|
136
|
+
- `qa run --test-order` no longer loses card digits typed before the card fields are ready. On SDK 0.4.41 the hosted number input appears before its script has loaded, and digits typed in that window are dropped. The SDK then refuses the shortened number without tokenizing, so the run timed out at `order_submitted` 45 seconds later with no order created. This happened about once in 33 live runs, usually when the card fields loaded slowly. The runner now waits up to 15 seconds for the checkout form to drop its `next-loading-spreedly` class (SDK 0.4.38 to 0.4.41 set it until the card fields are ready); if it is still there, `card_fields_filled` fails and says the card script never finished loading. It then reads the card number and CVV back, and retypes up to three attempts in all. If the fields still don't hold the card, `card_fields_filled` fails and names what the fields held, instead of the run timing out at submit.
|
|
137
|
+
|
|
138
|
+
## [1.53.0+agent.4] - 2026-10-06
|
|
139
|
+
|
|
140
|
+
### Fixed
|
|
141
|
+
|
|
142
|
+
- The checkout field-name checks now read the SDK version of the page they judge. `first_name` and `last_name` pass on Campaign Cart SDK 0.4.39 and later, and `phone_number` passes on 0.4.41 and later; `fname`, `lname` and `phone` still pass on every version. Earlier releases blocked `first_name` and `last_name` (`built_output.sdk_markup` `WRONG_FIELD_NAME`, and `checkout.unsupported_field_binding` in `standardize`) and flagged `phone_number` on every SDK, so a page written to the 0.4.41 field names failed doctor although the SDK maps them. Doctor reads the page's own exact loader pin, then the campaign's `sdk_version` in `_data/campaigns.json`. `standardize` reads the file's own loader pin, then the lowest exact pin the campaign declares (for a bundled `campaign-cart` dependency, an exact pin or the floor of a `^`, `~` or `>=` range). When neither gives an exact released version, the checks keep their earlier behaviour, because `fname`, `lname` and `phone` work on every version. Below a name's version the result is also unchanged, and the message names the version the name needs. Findings and bindings now record the SDK version they were judged against.
|
|
143
|
+
|
|
144
|
+
## [1.53.0+agent.3] - 2026-10-06
|
|
145
|
+
|
|
146
|
+
### Fixed
|
|
147
|
+
|
|
148
|
+
- `standardize` now judges a bundled `campaign-cart` dependency against the SDK support policy by the lowest version its `package.json` spec allows. Earlier releases took the version written in the spec, so `<0.4.39` was evaluated as 0.4.39 and `0.4.41 || 0.4.10` as 0.4.41, and both could pass the minimum although npm may install an older SDK. An exact pin or one `^`, `~` or `>=` range on a released version is evaluated by that version, as before. Any other spec gets no policy evaluation and raises the new warning `version.sdk_dependency_floor_unknown`. The lowest version is recorded as `sdk_loader.bundled_dependency.floor_version`.
|
|
149
|
+
|
|
5
150
|
## [1.53.0+agent.2] - 2026-10-05
|
|
6
151
|
|
|
7
152
|
### Changed
|
package/CONTEXT.md
CHANGED
|
@@ -22,9 +22,10 @@ The deterministic decision point that blocks `next polish|deploy|qa` and
|
|
|
22
22
|
`qa run` when theme inspect proved a brand theme is generatable, the campaign
|
|
23
23
|
ships commerce pages, and the brand layer is neither applied (after
|
|
24
24
|
`next-core.css`) nor explicitly waived. Evaluated once in the doctor
|
|
25
|
-
(`derived.theme_gate`) and consumed identically by `next` and QA. A waiver
|
|
26
|
-
|
|
27
|
-
on the Assembly Report
|
|
25
|
+
(`derived.theme_gate`) and consumed identically by `next` and QA. A waiver is
|
|
26
|
+
the only sanctioned bypass: `theme waive --reason "<why>" --waived-by "<named
|
|
27
|
+
human>"` records it on the Assembly Report; `qa run --theme-waive "<reason>"`
|
|
28
|
+
waives the gate for that one run only.
|
|
28
29
|
_Avoid_: advisory warning, recommendation, soft check
|
|
29
30
|
|
|
30
31
|
**Template Brand Contract**:
|
|
@@ -676,8 +677,10 @@ and your username. The patterns are the value, not the raw dumps."
|
|
|
676
677
|
|
|
677
678
|
**Domain Expert**: "No. Campaigns OS should show QA as the Expected Proof Step.
|
|
678
679
|
Browser QA still needs the deployed URL (and SDK-origin allowlist confirmation
|
|
679
|
-
for non-localhost)
|
|
680
|
-
|
|
680
|
+
for non-localhost). Typed-card test orders use global test cards that bypass
|
|
681
|
+
the gateway and move no money, but each leaves a real store order record.
|
|
682
|
+
Unless the operator has already said test orders are fine for this campaign,
|
|
683
|
+
ask once, up front, before placing them; then proceed. There is no permission flag."
|
|
681
684
|
|
|
682
685
|
**Developer**: "Build and polish finished, but there is no QA verdict."
|
|
683
686
|
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ This toolkit gives campaign developers and AI coding tools a clear path for asse
|
|
|
13
13
|
7. Hand off to `next-campaigns-build`.
|
|
14
14
|
8. Run build/lint and record the build with `campaigns-os record build`, then install the Campaigns OS Playwright browser once with `campaigns-os qa install-browser`.
|
|
15
15
|
9. Run `next-campaigns-polish`, serve the current build, run the mandatory `campaigns-os polish capture` producer, then record Polish with `campaigns-os record polish --evidence <file>`.
|
|
16
|
-
10. Deploy a preview.
|
|
16
|
+
10. Deploy a preview. For a local preview (`--deploy-target local-serve`), serve the current build and record it with `campaigns-os record deploy --base-url <loopback route root>`; a later `record build` of different output makes the deploy owed again. `record deploy` takes only a loopback route root; for a hosted preview, deploy through your host and follow `next` for what to record.
|
|
17
17
|
11. Run `next-campaigns-qa` against the tested URL.
|
|
18
18
|
12. Record launch blockers and follow-up work.
|
|
19
19
|
|
|
@@ -289,7 +289,8 @@ npm run campaigns-os -- prepare-build --spec <spec.json> --source <html-dir> --t
|
|
|
289
289
|
npm run campaigns-os -- doctor --packet <page-kit-repo>/campaign-runtime.build.json
|
|
290
290
|
npm run campaigns-os -- page-kit sync --packet <page-kit-repo>/campaign-runtime.build.json --dry-run
|
|
291
291
|
npm run campaigns-os -- checkpoint waive --packet <packet.json> --gate source_html.producer_provenance --page <page_id> --reason "<why>" --waived-by "<named human>" --review-condition "<trigger>" --dry-run
|
|
292
|
-
npm run campaigns-os -- sdk storage-check --target <campaign-git-root> --target-sdk
|
|
292
|
+
npm run campaigns-os -- sdk storage-check --target <campaign-git-root> --target-sdk <x.y.z> --manifest <sdk-storage-manifest.json> --scope <campaign,shared> --json
|
|
293
|
+
npm run campaigns-os -- sdk repin --target <static-campaign-repo> --target-sdk <x.y.z>
|
|
293
294
|
npm run campaigns-os -- standardize --target <page-kit-repo-or-cpk-repo> --json
|
|
294
295
|
npm run campaigns-os -- theme inspect --packet <page-kit-repo>/campaign-runtime.build.json --json
|
|
295
296
|
npm run campaigns-os -- theme generate --packet <page-kit-repo>/campaign-runtime.build.json --json
|
|
@@ -301,6 +302,10 @@ npm run qa:install-browser
|
|
|
301
302
|
npm run campaigns-os -- next polish --packet <packet.json> --report <assembly-report.json>
|
|
302
303
|
npm run campaigns-os -- polish capture --packet <packet.json> --base-url <served-current-build-url>
|
|
303
304
|
npm run campaigns-os -- record polish --packet <packet.json> --evidence <polish-evidence.json>
|
|
305
|
+
npm run campaigns-os -- record theme --packet <packet.json>
|
|
306
|
+
npm run campaigns-os -- record deploy --packet <packet.json> --base-url <loopback-route-root>
|
|
307
|
+
npm run campaigns-os -- record brief --packet <packet.json> --brief <campaign-build-brief.yaml>
|
|
308
|
+
npm run campaigns-os -- record spec --packet <packet.json>
|
|
304
309
|
npm run campaigns-os -- next qa --packet <packet.json> --report <assembly-report.json>
|
|
305
310
|
npm run campaigns-os -- qa resolve --packet <packet.json>
|
|
306
311
|
npm run campaigns-os -- qa run --packet <packet.json> --base-url <preview-url> --browser --test-order common
|
|
@@ -319,14 +324,24 @@ output path alone does not create or refresh the file. Build/QA producer
|
|
|
319
324
|
commands continue to record their own stages.
|
|
320
325
|
|
|
321
326
|
Record a stage's completion with `record setup`, `record build` (after every
|
|
322
|
-
rebuild)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
327
|
+
rebuild), `record polish --evidence <file>`, `record theme` (once the brand layer
|
|
328
|
+
is linked) and, for a local preview, `record deploy --base-url <loopback route
|
|
329
|
+
root>`, rather than hand-editing `.campaign-runtime/build-context.json` or
|
|
330
|
+
`.campaign-runtime/assembly-report.json`. A recorded deploy is bound to the
|
|
331
|
+
build it probed, so a later `record build` of different output makes it owed
|
|
332
|
+
again. When the brief or the CampaignSpec changes after stages are recorded, save
|
|
333
|
+
it with `record brief` or `record spec`: an unchanged or reformatted save keeps
|
|
334
|
+
every stage, a material change moves the superseded build, Polish and QA records
|
|
335
|
+
to stage history instead of discarding them, and `next` names the stage that is
|
|
336
|
+
owed again. Re-running
|
|
337
|
+
`start` or `prepare-build` over recorded stages is refused without `--force`.
|
|
338
|
+
Each `record` command validates what it would write, refuses a stage `next` has
|
|
339
|
+
not reached, and writes nothing on failure; `--dry-run` runs the checks without
|
|
340
|
+
writing. See
|
|
326
341
|
[Build Packet](docs/build-packet.md) and [Polish evidence](docs/polish-evidence.md).
|
|
327
342
|
|
|
328
343
|
Do not use `prepare-build --force` merely to refresh a catalog path: doctor
|
|
329
|
-
already resolves the running toolkit's catalog, and force
|
|
344
|
+
already resolves the running toolkit's catalog, and `--force` resets the recorded stages (completed build, Polish and QA records move to stage history).
|
|
330
345
|
|
|
331
346
|
|
|
332
347
|
`qa run` automatically checks contract-governed authored price, recurring
|
|
@@ -338,8 +353,8 @@ Run `tooling status` before a build session. It names the install mode — a
|
|
|
338
353
|
pinned package (`npx`, or a consumer's `node_modules`) or a git checkout — and
|
|
339
354
|
checks that the package metadata, CLI entrypoint, and installed Campaigns OS
|
|
340
355
|
skills agree. For a checkout it also reports branch, upstream, and ahead/behind;
|
|
341
|
-
for a package install the pinned
|
|
342
|
-
|
|
356
|
+
for a package install the pinned version is the freshness answer, and the
|
|
357
|
+
status does not compare it with npm's `latest` dist-tag. Neither mode makes agent skills current on
|
|
343
358
|
its own: when skills are stale, run the refresh command the status output
|
|
344
359
|
prints (it names each stale platform, through the same prefix you ran
|
|
345
360
|
`tooling status` with), then read the `SKILL.md` files it lists under `Read now`
|
|
@@ -392,7 +407,7 @@ registry — so a spec rule is authored once and reaches internal teams and
|
|
|
392
407
|
third-party agencies alike. The rules are authored in TypeScript with no heavy
|
|
393
408
|
dependencies and compiled to plain ESM (`npm run build:spec`, on `prepare`) and a
|
|
394
409
|
stable subpath export `@nextcommerce/campaigns-os/campaign-spec`, so consumers run
|
|
395
|
-
them on `engines.node` (>=20) with no type-stripping or build step of their own.
|
|
410
|
+
them on `engines.node` (>=20.19.0) with no type-stripping or build step of their own.
|
|
396
411
|
See [`campaign-spec/README.md`](campaign-spec/README.md).
|
|
397
412
|
|
|
398
413
|
## Docs
|
|
@@ -421,7 +436,7 @@ See [`campaign-spec/README.md`](campaign-spec/README.md).
|
|
|
421
436
|
|
|
422
437
|
## Status
|
|
423
438
|
|
|
424
|
-
Developer preview. Build output still needs the normal proof gates: build/lint evidence, polish plus package-owned page-load capture against the served current build, preview deploy or local dev URL, Playwright browser QA, and typed-card test-order proof via `--test-order common
|
|
439
|
+
Developer preview. Build output still needs the normal proof gates: build/lint evidence, polish plus package-owned page-load capture against the served current build, preview deploy or local dev URL, Playwright browser QA, and typed-card test-order proof via `--test-order common`. 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. Localhost on any port is a Campaigns App Development domain, so SDK calls are allowed and analytics are suppressed there; non-localhost preview/production origins still need SDK origin allowlist confirmation.
|
|
425
440
|
|
|
426
441
|
Launch readiness is separate from Campaigns OS proof. Before real shoppers see a campaign, confirm the production storefront URL, live payment methods, shipping markets, legal/support URLs, analytics expectations, and merchant-side configuration.
|
|
427
442
|
|
package/compatibility.json
CHANGED
|
@@ -31,6 +31,16 @@
|
|
|
31
31
|
"cc-year": "exp-year"
|
|
32
32
|
},
|
|
33
33
|
"prefixes": ["billing-"],
|
|
34
|
+
"version_gated_aliases": {
|
|
35
|
+
"first_name": { "canonical": "fname", "since": "0.4.39" },
|
|
36
|
+
"last_name": { "canonical": "lname", "since": "0.4.39" },
|
|
37
|
+
"phone_number": { "canonical": "phone", "since": "0.4.41" }
|
|
38
|
+
},
|
|
39
|
+
"version_gated_provenance": {
|
|
40
|
+
"source": "NextCommerceCo/campaign-cart src/utils/checkout-field-names.ts at v0.4.39 (first_name, last_name; v0.4.40 is the same SDK) and v0.4.41 (phone_number), and v0.4.41 docs/guides/reference/data-attributes.md (Field names)",
|
|
41
|
+
"rule": "A version-gated alias is supported when the binding's SDK version is an exact release at or after since. Below since, or when the version is unknown, the alias is judged as on an earlier SDK: a name also listed in stale_aliases is a stale alias, any other is unknown. The canonical name is supported on every version.",
|
|
42
|
+
"captured_at": "2026-10-06"
|
|
43
|
+
},
|
|
34
44
|
"stale_aliases": {
|
|
35
45
|
"state": "province",
|
|
36
46
|
"region": "province",
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"schema_version": "campaign-cart-sdk-support-policy/v0",
|
|
3
3
|
"provenance": {
|
|
4
4
|
"source": "NextCommerceCo/campaign-cart release tags",
|
|
5
|
-
"latest_known_release": "0.4.
|
|
6
|
-
"captured_at": "2026-
|
|
5
|
+
"latest_known_release": "0.4.41",
|
|
6
|
+
"captured_at": "2026-10-07"
|
|
7
7
|
},
|
|
8
8
|
"source": "contracts/campaign-cart-sdk-support-policy.v0.json",
|
|
9
9
|
"minimum_supported": "0.4.20",
|
|
@@ -848,9 +848,9 @@
|
|
|
848
848
|
}
|
|
849
849
|
},
|
|
850
850
|
"verification": {
|
|
851
|
-
"sdk_version": "0.4.
|
|
852
|
-
"verified_at": "2026-10-
|
|
853
|
-
"evidence": "sdk-0.4.
|
|
851
|
+
"sdk_version": "0.4.41",
|
|
852
|
+
"verified_at": "2026-10-06T23:03:41Z",
|
|
853
|
+
"evidence": "sdk-0.4.41-2026-10-06.5",
|
|
854
854
|
"status": "certified",
|
|
855
855
|
"source": "template-verification.json"
|
|
856
856
|
}
|
|
@@ -861,7 +861,7 @@
|
|
|
861
861
|
"id": "template-reference-apollo",
|
|
862
862
|
"family": "apollo",
|
|
863
863
|
"version": "sdk-0.4.38-composable-upsells-2026-09-25",
|
|
864
|
-
"source_commit": "
|
|
864
|
+
"source_commit": "1a3bf7218e7f10ef5733ceba84d2856eeca2cfbd",
|
|
865
865
|
"provenance_url": "https://raw.githubusercontent.com/NextCommerceCo/campaign-cart-starter-templates/main/docs/template-references/apollo/README.md",
|
|
866
866
|
"contract_path": "contracts/commerce-surface-catalog.json",
|
|
867
867
|
"standard_viewport_refs": [
|
|
@@ -1272,9 +1272,9 @@
|
|
|
1272
1272
|
}
|
|
1273
1273
|
},
|
|
1274
1274
|
"verification": {
|
|
1275
|
-
"sdk_version": "0.4.
|
|
1276
|
-
"verified_at": "2026-10-
|
|
1277
|
-
"evidence": "sdk-0.4.
|
|
1275
|
+
"sdk_version": "0.4.41",
|
|
1276
|
+
"verified_at": "2026-10-06T23:03:41Z",
|
|
1277
|
+
"evidence": "sdk-0.4.41-2026-10-06.5",
|
|
1278
1278
|
"status": "certified",
|
|
1279
1279
|
"source": "template-verification.json"
|
|
1280
1280
|
}
|
|
@@ -1583,9 +1583,9 @@
|
|
|
1583
1583
|
}
|
|
1584
1584
|
},
|
|
1585
1585
|
"verification": {
|
|
1586
|
-
"sdk_version": "0.4.
|
|
1587
|
-
"verified_at": "2026-10-
|
|
1588
|
-
"evidence": "sdk-0.4.
|
|
1586
|
+
"sdk_version": "0.4.41",
|
|
1587
|
+
"verified_at": "2026-10-06T23:03:41Z",
|
|
1588
|
+
"evidence": "sdk-0.4.41-2026-10-06.5",
|
|
1589
1589
|
"status": "certified",
|
|
1590
1590
|
"source": "template-verification.json"
|
|
1591
1591
|
}
|
|
@@ -1886,9 +1886,9 @@
|
|
|
1886
1886
|
}
|
|
1887
1887
|
},
|
|
1888
1888
|
"verification": {
|
|
1889
|
-
"sdk_version": "0.4.
|
|
1890
|
-
"verified_at": "2026-10-
|
|
1891
|
-
"evidence": "sdk-0.4.
|
|
1889
|
+
"sdk_version": "0.4.41",
|
|
1890
|
+
"verified_at": "2026-10-06T23:03:41Z",
|
|
1891
|
+
"evidence": "sdk-0.4.41-2026-10-06.5",
|
|
1892
1892
|
"status": "certified",
|
|
1893
1893
|
"source": "template-verification.json"
|
|
1894
1894
|
}
|
|
@@ -2227,9 +2227,9 @@
|
|
|
2227
2227
|
}
|
|
2228
2228
|
},
|
|
2229
2229
|
"verification": {
|
|
2230
|
-
"sdk_version": "0.4.
|
|
2231
|
-
"verified_at": "2026-10-
|
|
2232
|
-
"evidence": "sdk-0.4.
|
|
2230
|
+
"sdk_version": "0.4.41",
|
|
2231
|
+
"verified_at": "2026-10-06T23:03:41Z",
|
|
2232
|
+
"evidence": "sdk-0.4.41-2026-10-06.5",
|
|
2233
2233
|
"status": "certified",
|
|
2234
2234
|
"source": "template-verification.json"
|
|
2235
2235
|
}
|
|
@@ -2666,9 +2666,9 @@
|
|
|
2666
2666
|
}
|
|
2667
2667
|
},
|
|
2668
2668
|
"verification": {
|
|
2669
|
-
"sdk_version": "0.4.
|
|
2670
|
-
"verified_at": "2026-10-
|
|
2671
|
-
"evidence": "sdk-0.4.
|
|
2669
|
+
"sdk_version": "0.4.41",
|
|
2670
|
+
"verified_at": "2026-10-06T23:03:41Z",
|
|
2671
|
+
"evidence": "sdk-0.4.41-2026-10-06.5",
|
|
2672
2672
|
"status": "certified",
|
|
2673
2673
|
"source": "template-verification.json"
|
|
2674
2674
|
}
|
|
@@ -3023,9 +3023,9 @@
|
|
|
3023
3023
|
}
|
|
3024
3024
|
},
|
|
3025
3025
|
"verification": {
|
|
3026
|
-
"sdk_version": "0.4.
|
|
3027
|
-
"verified_at": "2026-10-
|
|
3028
|
-
"evidence": "sdk-0.4.
|
|
3026
|
+
"sdk_version": "0.4.41",
|
|
3027
|
+
"verified_at": "2026-10-06T23:03:41Z",
|
|
3028
|
+
"evidence": "sdk-0.4.41-2026-10-06.5",
|
|
3029
3029
|
"status": "certified",
|
|
3030
3030
|
"source": "template-verification.json"
|
|
3031
3031
|
}
|
|
@@ -3313,9 +3313,9 @@
|
|
|
3313
3313
|
}
|
|
3314
3314
|
},
|
|
3315
3315
|
"verification": {
|
|
3316
|
-
"sdk_version": "0.4.
|
|
3317
|
-
"verified_at": "2026-10-
|
|
3318
|
-
"evidence": "sdk-0.4.
|
|
3316
|
+
"sdk_version": "0.4.41",
|
|
3317
|
+
"verified_at": "2026-10-06T23:03:41Z",
|
|
3318
|
+
"evidence": "sdk-0.4.41-2026-10-06.5",
|
|
3319
3319
|
"status": "certified",
|
|
3320
3320
|
"source": "template-verification.json"
|
|
3321
3321
|
}
|
|
@@ -3504,6 +3504,6 @@
|
|
|
3504
3504
|
}
|
|
3505
3505
|
},
|
|
3506
3506
|
"_synced_from_repo": "NextCommerceCo/campaign-cart-starter-templates",
|
|
3507
|
-
"_synced_from_ref": "
|
|
3508
|
-
"_synced_from_sha": "
|
|
3507
|
+
"_synced_from_ref": "783329017c6994a94d11e46580c33bd052aabdb0",
|
|
3508
|
+
"_synced_from_sha": "783329017c6994a94d11e46580c33bd052aabdb0"
|
|
3509
3509
|
}
|