@nextcommerce/campaigns-os 1.57.0 → 1.59.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 +60 -0
- package/campaign-spec/dist/checkout-flow.d.ts +7 -0
- package/campaign-spec/dist/checkout-flow.js +17 -1
- package/compatibility.json +1 -1
- package/contracts/campaign-cart-sdk-support-policy.v0.json +2 -2
- package/contracts/commerce-surface-catalog.json +26 -27
- package/contracts/effects.v1.json +5 -5
- package/contracts/release-ledger.json +432 -0
- package/contracts/supported-surface.json +4 -4
- package/contracts/template-brand-contract.shared-commerce.v0.json +1 -1
- package/docs/build-packet.md +22 -8
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +1 -1
- package/docs/qa-and-test-orders.md +54 -12
- package/docs/runtime-readiness.md +1 -1
- package/docs/skills-revision.md +10 -10
- package/package.json +1 -1
- package/schemas/campaign-runtime-build-context.v0.schema.json +2 -1
- package/schemas/campaign-runtime-build-packet.v0.schema.json +2 -1
- package/skills/campaign-lifecycle-orientation/SKILL.md +3 -3
- package/skills/campaign-readback-classification/SKILL.md +3 -3
- package/skills/campaign-run-evidence/SKILL.md +3 -3
- package/skills/contribution-intake/SKILL.md +3 -3
- package/skills/next-campaigns-build/SKILL.md +6 -6
- 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 +3 -3
- package/skills.json +10 -10
- package/src/built-site-scope.mjs +91 -0
- package/src/cli.mjs +15 -23
- package/src/doctor/checks.mjs +73 -3
- package/src/doctor/next-step.mjs +3 -14
- package/src/polish-node.mjs +5 -4
- package/src/qa-browser.mjs +137 -2
- package/src/qa-cart-entry.mjs +105 -0
- package/src/qa-node.mjs +3 -1
- package/src/qa-verdict.mjs +24 -1
- package/src/sdk-attribute-index.mjs +8 -7
- package/src/source-html-intake.mjs +27 -18
- package/src/stage-record.mjs +200 -33
|
@@ -119,15 +119,14 @@ checks are carried forward:
|
|
|
119
119
|
|
|
120
120
|
A carried-forward gate has status `carried_forward`. Doctor reports it as a
|
|
121
121
|
warning starting "Carried forward on the local preview" or "Carried forward on the hosted preview"; `next` moves past
|
|
122
|
-
polish to deploy and QA
|
|
122
|
+
polish to deploy and QA; QA records it as a `warn` row, so the verdict is at
|
|
123
123
|
best `ready_with_exceptions`. The evidence is reported as missing, never as
|
|
124
124
|
passed. On a hosted all-template preview, first record an unset preview URL
|
|
125
125
|
with `campaigns-os qa policy set --packet <packet> --preview-url <url>`, then
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
carried forward. The hosted exception
|
|
126
|
+
record the deploy with `campaigns-os record deploy --packet <packet> --base-url <preview-url>`
|
|
127
|
+
(on a hosted target it takes an https URL, requires every built page to answer
|
|
128
|
+
200, and compares the served scripts and stylesheets with the build by sha256),
|
|
129
|
+
then run `campaigns-os qa run --packet <packet> --base-url <preview-url> --browser --test-order common`. After QA records the current-build verdict, run `campaigns-os next --packet <packet>` for closeout. The hosted exception
|
|
131
130
|
requires a distinct packet `deploy.preview_url`; a query or fragment on the production page does not count as distinct. QA with `--base-url` equal to
|
|
132
131
|
the packet's `deploy.production_url` stays blocked. A `local-serve` packet
|
|
133
132
|
served from any other host also gets strict gates. `record polish` and the
|
|
@@ -1345,6 +1344,7 @@ Assembly Report's `stages.qa`:
|
|
|
1345
1344
|
"declared_order_path_depth": "common",
|
|
1346
1345
|
"declared_typed_card_depth": "common",
|
|
1347
1346
|
"order_paths_executed": 3,
|
|
1347
|
+
"test_order_attempts": 3,
|
|
1348
1348
|
"orders_created": 3,
|
|
1349
1349
|
"orders_verified": 3,
|
|
1350
1350
|
"all_orders_test_mode": true
|
|
@@ -1357,7 +1357,9 @@ readback bundle, where the verdict's own order arrays are deliberately emptied
|
|
|
1357
1357
|
(see the committed verdict sidecar above) — so the signal that a purchase
|
|
1358
1358
|
happened has to be numbers, not the orders themselves. `all_orders_test_mode` is
|
|
1359
1359
|
`null`, not `false`, when nothing ran: "no order left test mode" and "no order
|
|
1360
|
-
ran" are different facts.
|
|
1360
|
+
ran" are different facts. `order_paths_executed` counts planned paths and
|
|
1361
|
+
`test_order_attempts` counts every attempt, so a re-run adds an attempt and not a
|
|
1362
|
+
path.
|
|
1361
1363
|
|
|
1362
1364
|
Beside it, `stages.qa.evidence` carries the build the verdict judged and the
|
|
1363
1365
|
outcome of the gates doctor's static scan can only approximate:
|
|
@@ -1554,16 +1556,49 @@ Evidence: `landing_url`, `landing_page_id`, `landing_page_type`,
|
|
|
1554
1556
|
`landing_resolution` (`routes_into_checkout`, `entry_page_fallback`,
|
|
1555
1557
|
`first_page_fallback`), `control_text`, `control_kind` (`add_to_cart`,
|
|
1556
1558
|
`checkout_link`, or `checkout_button`), `package_id`, `bundle_card_selection`
|
|
1557
|
-
when `--select-package` clicked a bundle card, `
|
|
1559
|
+
when `--select-package` clicked a bundle card, `variant_selections` when the
|
|
1560
|
+
runner chose slot variants (below), `sdk_ready`, `arrived_url`, the
|
|
1558
1561
|
`checkout_selection_surface` probe result, `selection_surface_probe`
|
|
1559
1562
|
(`loaded`, `reused`, or `failed`), and `selection_surface_probe_error` when it
|
|
1560
1563
|
failed. The failure codes are
|
|
1561
1564
|
`cart_entry_unresolved` (no selection surface on checkout and no entry page
|
|
1562
1565
|
resolves from the topology), `cart_entry_control_missing` (the entry page
|
|
1563
1566
|
renders no control, or none carrying the requested ref), and
|
|
1564
|
-
`cart_entry_no_navigation` (the click did not reach the checkout URL)
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
+
`cart_entry_no_navigation` (the click did not reach the checkout URL), and
|
|
1568
|
+
`cart_entry_variant_unfilled` (a bundle slot's variant control has no in-stock
|
|
1569
|
+
option or row, or its row could not be clicked, named by slot). Each fails the path inside the step budget with the
|
|
1570
|
+
code as the first word of the error, never as a step timeout.
|
|
1571
|
+
|
|
1572
|
+
On a select page whose bundle slots each need a variant (size, colour) before
|
|
1573
|
+
Next, the runner chooses one per slot after any bundle card click and before
|
|
1574
|
+
the checkout control, one field at a time. It looks at every visible, enabled
|
|
1575
|
+
slot variant select (`[data-next-variant-selectors] select` or
|
|
1576
|
+
`.next-slot-variant-field select`, the native select the SDK renders, counted
|
|
1577
|
+
visible when its field is):
|
|
1578
|
+
|
|
1579
|
+
- When the field shows a dropdown UI (an `os-dropdown` with a
|
|
1580
|
+
`.os-card__variant-dropdown-toggle` and `os-dropdown-item` rows, as the
|
|
1581
|
+
olympus-mv-two-step starter renders), the runner clicks the toggle and then a
|
|
1582
|
+
row, like a shopper, even when the select is already filled. A page may count
|
|
1583
|
+
a field as chosen only when a row was clicked. It clicks the row matching the
|
|
1584
|
+
select's current value when that row is in stock, so the cart stays what the
|
|
1585
|
+
SDK chose, and otherwise the first in-stock row. After the toggle click it
|
|
1586
|
+
finds the field again by slot identity and the row again by its value, and
|
|
1587
|
+
waits up to 2 s for that row to show, so a menu that opens late or re-renders
|
|
1588
|
+
its rows is handled.
|
|
1589
|
+
- With no dropdown UI, an empty select gets its first in-stock option, with
|
|
1590
|
+
`input` and `change` dispatched. A filled native select is left alone.
|
|
1591
|
+
|
|
1592
|
+
In stock means enabled, not hidden, and with a non-empty value. For a row it
|
|
1593
|
+
also means not `[disabled]`, `aria-disabled`, `.next-oos`,
|
|
1594
|
+
`.next-variant-unavailable` or `data-available="false"`, and that the row's
|
|
1595
|
+
native option is not disabled. Each choice is recorded in
|
|
1596
|
+
`variant_selections` as `{ bundle_id, slot, variant_code, value, via }`, where
|
|
1597
|
+
`via` is `dropdown` or `select`, so the ordered variants can be checked against
|
|
1598
|
+
the cart lines. A field with nothing to choose, or a row that does not show
|
|
1599
|
+
or cannot be clicked, fails the path with `cart_entry_variant_unfilled` before Next is
|
|
1600
|
+
clicked. A page whose filled selects have no dropdown UI is left untouched and
|
|
1601
|
+
`variant_selections` is omitted.
|
|
1567
1602
|
|
|
1568
1603
|
Which page a funnel enters the cart from is still inferred from topology and
|
|
1569
1604
|
the rendered checkout. Recording it authoritatively on the spec is the open
|
|
@@ -1840,7 +1875,7 @@ deciding what to do next, it classifies what the attempt did to the store:
|
|
|
1840
1875
|
|
|
1841
1876
|
| Classification | What it means | What the runner does |
|
|
1842
1877
|
|---|---|---|
|
|
1843
|
-
| `not_created` | The path failed before the checkout was submitted (including the runner's own named refusals: `cart_entry_unresolved`, `cart_entry_control_missing`, `cart_entry_no_navigation`, `cart_empty_before_submit`), or the platform rejected every order create it saw. Nothing reached the store. | Re-runs the path once, if the creation budget has a slot no still-unrun planned path needs. This is the bounded retry for a transient miss; the re-run decides the assertion. |
|
|
1878
|
+
| `not_created` | The path failed before the checkout was submitted (including the runner's own named refusals: `cart_entry_unresolved`, `cart_entry_control_missing`, `cart_entry_no_navigation`, `cart_entry_variant_unfilled`, `cart_empty_before_submit`), or the platform rejected every order create it saw. Nothing reached the store. | Re-runs the path once, if the creation budget has a slot no still-unrun planned path needs. This is the bounded retry for a transient miss; the re-run decides the assertion. |
|
|
1844
1879
|
| `created` | An order exists and was read back — the failure happened after the purchase (most often a receipt that did not render its line items). | Runs a **read-only recovery pass**: reloads the receipt the order already produced, re-reads the persisted order, and re-checks the buyer-visible receipt surface and the voucher read-back. It clicks nothing, applies nothing, and submits nothing. |
|
|
1845
1880
|
| `ambiguous` | The submit may have created an order this runner cannot see: a ref id with an unusable read-back, a lost create response, a network-failed create, or a 4xx that follows an earlier 2xx on the same endpoint. | Stops. It never resubmits, and the assertion names the check an operator should run — look for an existing order against the run's QA email or the observed ref id. |
|
|
1846
1881
|
|
|
@@ -1865,6 +1900,13 @@ is not re-run and its assertion records why under
|
|
|
1865
1900
|
re-runs for those paths. A re-run that stops on the budget never becomes the
|
|
1866
1901
|
deciding result: it proved nothing, so the first attempt's real failure stands.
|
|
1867
1902
|
|
|
1903
|
+
Every attempt is its own `test_orders[]` entry, because a re-run can place its
|
|
1904
|
+
own real order. Each entry carries `plan_id`, the planned path it belongs to,
|
|
1905
|
+
and `attempt`, 1 for the first try and 2 for the re-run, so a run with five
|
|
1906
|
+
planned paths that each re-ran once holds ten entries for five paths. The
|
|
1907
|
+
`qa run` summary prints the three counts apart, for example
|
|
1908
|
+
`Test orders: 5 paths, 10 attempts, 0 orders created`.
|
|
1909
|
+
|
|
1868
1910
|
Recovery may only clear a failure on evidence it actually re-read. If the
|
|
1869
1911
|
receipt reload's persisted-order read-back fails or never happens, the pass stops
|
|
1870
1912
|
honestly: the read-back failure is itself a remaining failure, and the receipt
|
|
@@ -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.3`, 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.3`, validated by `schemas/campaigns-os-runtime-recipe.v1.schema.json` (`Campaigns OS Runtime Recipe v1`). Supported surface at generation time: `1.59.0`.
|
|
12
12
|
|
|
13
13
|
## What this is
|
|
14
14
|
|
package/docs/skills-revision.md
CHANGED
|
@@ -16,7 +16,7 @@ that the copy on disk moved.
|
|
|
16
16
|
`skills.json` carries one top-level field:
|
|
17
17
|
|
|
18
18
|
```json
|
|
19
|
-
"bundle_revision": "1.
|
|
19
|
+
"bundle_revision": "1.59.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.59.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.59.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.59.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.59.0+skills.1",
|
|
94
94
|
"spelling": "bundle",
|
|
95
|
-
"on_disk": "1.
|
|
95
|
+
"on_disk": "1.59.0+skills.1",
|
|
96
96
|
"on_disk_skill": null,
|
|
97
|
-
"message": "match (1.
|
|
97
|
+
"message": "match (1.59.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.59.0+skills.1)
|
|
105
|
+
Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.59.0+skills.1 — start a fresh session
|
|
106
|
+
Skills revision: unchecked (on disk 1.59.0+skills.1)
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
`unchecked` is the state when the flag is absent. It is not an error — an
|
package/package.json
CHANGED
|
@@ -192,7 +192,8 @@
|
|
|
192
192
|
"permalink": { "type": "string", "minLength": 1 },
|
|
193
193
|
"next_url": { "type": "string", "minLength": 1 },
|
|
194
194
|
"decline_url": { "type": "string", "minLength": 1 },
|
|
195
|
-
"
|
|
195
|
+
"next_step": { "type": "string", "minLength": 1, "description": "On a select or checkout_step page on a path to a Checkout: this page's forward link, for the step form's data-next-checkout-step or the select page's checkout link. next_url on such a page is the Checkout's post-payment destination, for the next-success-url meta tag the SDK reads for express orders (campaign-cart-starter-templates#223)." },
|
|
196
|
+
"success_url": { "type": "string", "minLength": 1, "description": "No longer emitted: the starter templates read the post-payment destination from next_url and the forward link from next_step. Accepted so packets written by 1.56.0 and 1.57.0 still validate." },
|
|
196
197
|
"step_number": { "type": "integer", "minimum": 1, "description": "On a checkout_step page: its position among its path's steps, from 1, for the step form's data-next-step-number." }
|
|
197
198
|
}
|
|
198
199
|
}
|
|
@@ -194,7 +194,8 @@
|
|
|
194
194
|
"permalink": { "type": "string", "minLength": 1 },
|
|
195
195
|
"next_url": { "type": "string", "minLength": 1 },
|
|
196
196
|
"decline_url": { "type": "string", "minLength": 1 },
|
|
197
|
-
"
|
|
197
|
+
"next_step": { "type": "string", "minLength": 1, "description": "On a select or checkout_step page on a path to a Checkout: this page's forward link, for the step form's data-next-checkout-step or the select page's checkout link. next_url on such a page is the Checkout's post-payment destination, for the next-success-url meta tag the SDK reads for express orders (campaign-cart-starter-templates#223)." },
|
|
198
|
+
"success_url": { "type": "string", "minLength": 1, "description": "No longer emitted: the starter templates read the post-payment destination from next_url and the forward link from next_step. Accepted so packets written by 1.56.0 and 1.57.0 still validate." },
|
|
198
199
|
"step_number": { "type": "integer", "minimum": 1, "description": "On a checkout_step page: its position among its path's steps, from 1, for the step form's data-next-step-number." }
|
|
199
200
|
}
|
|
200
201
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-lifecycle-orientation
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.37
|
|
4
4
|
description: Orient a reader to the Campaigns OS lifecycle artifacts a run has already emitted, without advancing any stage or changing any state.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.59.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.59.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-readback-classification
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.37
|
|
4
4
|
description: Classify a selected campaign from the readback projection's v2 fields and write a read-only handoff without turning diagnosis into permission.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.59.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.59.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-run-evidence
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.37
|
|
4
4
|
description: Interpret existing Campaigns OS doctor, QA and proof-depth evidence without claiming more proof than the artifacts contain.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.59.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.59.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: contribution-intake
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.37
|
|
4
4
|
description: Turn a suggestion about the agent surface into a classified, evidence-checked proposal and, only with attended approval, one issue on this repository's tracker.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.59.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.59.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-build
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.42
|
|
4
4
|
description: Assemble a NEXT campaign from a doctor-cleared Build Packet, CampaignSpec/API values, prepared HTML/assets, page-kit, and starter-template contracts.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.59.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.59.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -100,9 +100,9 @@ Build rules:
|
|
|
100
100
|
- If setup/build needs starter-template files, copy the template family atomically with its dependent pages, `_includes/`, `_layouts/`, `assets/css/`, and `assets/js/`; copying only checkout/receipt pages is incomplete.
|
|
101
101
|
- Resolve SDK routing meta tags to deployed campaign-root paths, not spec literals. For example, `next-success-url: upsell/` in the spec should become `/<public_route_slug>/upsell/` in built HTML.
|
|
102
102
|
- If an order bump package comes from `packages.prepurchase_*` and is not one of the main `bundles[]`, default `package_sync=false` and `show_line_total_price=false` unless the CampaignSpec explicitly says the add-on quantity must sync with the main bundle.
|
|
103
|
-
- Multi-step checkout paths run `select` → `checkout_step`… → `checkout`; only the `checkout` page takes payment. On a `select` page the swap-mode bundle selector (`data-next-bundle-selector` with `data-next-selection-mode="swap"`) writes the cart and the SDK persists it between pages; a checkout button or link (`href` from the page's `
|
|
104
|
-
- Each `checkout_step` page's checkout form (`<form data-next-checkout="form">`) carries `data-next-checkout-step="<its
|
|
105
|
-
- On
|
|
103
|
+
- Multi-step checkout paths run `select` → `checkout_step`… → `checkout`; only the `checkout` page takes payment. On a `select` page the swap-mode bundle selector (`data-next-bundle-selector` with `data-next-selection-mode="swap"`) writes the cart and the SDK persists it between pages; a checkout button or link (`href` from the page's `next_step`) goes to the next page. Use `forcePackageId` only on a page with no selector, such as a landing call-to-action into a checkout that renders none.
|
|
104
|
+
- Each `checkout_step` page's checkout form (`<form data-next-checkout="form">`) carries `data-next-checkout-step="<its next_step>"` and `data-next-step-number="<step_number>"` (frontmatter `next_step` and `step_number`, 1 for the first step on its path). The `checkout` page's form carries neither.
|
|
105
|
+
- On `select` and `checkout_step` pages, frontmatter `next_url` is the Checkout's post-payment destination, the same meaning it has on the `checkout` page, and the layout renders it as the `next-success-url` meta tag: the SDK reads that tag for express orders placed on the page. `next_step` is the page's forward link. A landing page keeps `next_url` as its call-to-action target; if it has express checkout buttons, set its `next-success-url` to the Checkout's own `next_url` from the Build Context `page_map`, never to the landing page's `next_url`. Forward navigation uses the step form attribute or a plain link, never that meta tag. Page Kit frontmatter has no `success_url` key; `success_url` stays a CampaignSpec field on `checkout` pages.
|
|
106
106
|
- Record intentional drops from source HTML in the assembly report, especially payment/provider changes such as "PayPal removed because CampaignSpec available_payment_methods excludes it." Polish must inherit these decisions.
|
|
107
107
|
- Preserve any existing Build Context `theme` inspection state and Assembly Report `theme` application state. If build applies, skips, or invalidates generated theme CSS, update `report.theme` rather than leaving stale evidence.
|
|
108
108
|
- After page-kit build, inspect rendered `_site` output: body exists, Campaign Cart runtime markers exist, `sdk_hints.meta_tags` rendered, route meta points at the campaign root, and copied funnel attribution/runtime baggage is gone.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-os
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.57
|
|
4
4
|
description: Coordinate Campaigns OS lifecycle workflows from CampaignSpec, Build Packet, starter-template contracts, stage reports, deploy evidence, and QA proof depth.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.59.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.59.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-os-setup
|
|
3
|
-
version: 2.0.
|
|
3
|
+
version: 2.0.40
|
|
4
4
|
description: Bootstrap or prepare a target page-kit campaign repo from a doctor-cleared Campaigns OS Build Packet before full build wiring. Formerly installed as next-campaigns-setup; renamed 2026-08 to stop colliding with the published NextCommerceCo/skills scaffolder of that name.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.59.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.59.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-polish
|
|
3
|
-
version: 1.1.
|
|
3
|
+
version: 1.1.42
|
|
4
4
|
description: Run the visual/runtime polish pass after build and before QA for a Campaigns OS campaign.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.59.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.59.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: next-campaigns-qa
|
|
3
|
-
version: 1.3.
|
|
3
|
+
version: 1.3.41
|
|
4
4
|
description: Run spec-aware QA from a saved Map or local-spec Build Packet and tested campaign URL after build, polish, and deploy/local evidence exist, including Playwright typed-card test-order proof.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Bundle revision: 1.
|
|
8
|
-
Run `npx --no-install campaigns-os tooling status --skills-revision 1.
|
|
7
|
+
Bundle revision: 1.59.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.59.0+skills.1`
|
|
9
9
|
from the campaign's Page Kit folder, where it runs the project's pinned copy and
|
|
10
10
|
never installs one, at the start of each task. Start a fresh session if it
|
|
11
11
|
reports `mismatch`: this text is already in your context and is never re-read
|
package/skills.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "campaigns-os-skills",
|
|
3
3
|
"description": "Skills bundled with Campaigns OS. `skills.sh` installs these into the shared agent skill directories (~/.claude/skills, ~/.codex/skills), so each one is a versioned package: bump the version whenever a package changes. `bundle_revision` identifies the bundle as a whole — `<package version>+skills.<n>`, where the prefix is this package's version and `<n>` counts the skill-text revisions published against it. It advances whenever ANY bundled skill changes, every SKILL.md states it on its first body line, and `campaigns-os tooling status --skills-revision <value>` compares the value an agent read from a skill against the bundle on disk. See docs/skills-revision.md.",
|
|
4
|
-
"bundle_revision": "1.
|
|
4
|
+
"bundle_revision": "1.59.0+skills.1",
|
|
5
5
|
"homepage": "https://github.com/NextCommerceCo/campaigns-os",
|
|
6
6
|
"retired_skills": [
|
|
7
7
|
{
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
{
|
|
17
17
|
"id": "next-campaigns-os",
|
|
18
18
|
"name": "Campaigns OS Lifecycle",
|
|
19
|
-
"version": "1.0.
|
|
19
|
+
"version": "1.0.57",
|
|
20
20
|
"path": "skills/next-campaigns-os/SKILL.md",
|
|
21
21
|
"domain": "campaigns",
|
|
22
22
|
"description": "Coordinate Campaigns OS lifecycle workflows from CampaignSpec, Build Packet, starter-template contracts, stage reports, deploy evidence, and QA proof depth."
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
{
|
|
25
25
|
"id": "next-campaigns-os-setup",
|
|
26
26
|
"name": "Campaigns OS Setup",
|
|
27
|
-
"version": "2.0.
|
|
27
|
+
"version": "2.0.40",
|
|
28
28
|
"path": "skills/next-campaigns-os-setup/SKILL.md",
|
|
29
29
|
"domain": "campaigns",
|
|
30
30
|
"description": "Bootstrap or prepare a target page-kit campaign repo from a doctor-cleared Campaigns OS Build Packet before full build wiring. Formerly next-campaigns-setup; renamed to release that name to the published NextCommerceCo/skills scaffolder."
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
{
|
|
33
33
|
"id": "next-campaigns-build",
|
|
34
34
|
"name": "Campaign Build",
|
|
35
|
-
"version": "1.0.
|
|
35
|
+
"version": "1.0.42",
|
|
36
36
|
"path": "skills/next-campaigns-build/SKILL.md",
|
|
37
37
|
"domain": "campaigns",
|
|
38
38
|
"description": "Assemble a NEXT campaign from a doctor-cleared Build Packet, CampaignSpec/API values, prepared HTML/assets, page-kit, and starter-template contracts."
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
{
|
|
41
41
|
"id": "next-campaigns-polish",
|
|
42
42
|
"name": "Campaign Polish",
|
|
43
|
-
"version": "1.1.
|
|
43
|
+
"version": "1.1.42",
|
|
44
44
|
"path": "skills/next-campaigns-polish/SKILL.md",
|
|
45
45
|
"domain": "campaigns",
|
|
46
46
|
"description": "Run the visual/runtime polish pass after build and before QA for a Campaigns OS campaign."
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
{
|
|
49
49
|
"id": "next-campaigns-qa",
|
|
50
50
|
"name": "Campaign QA",
|
|
51
|
-
"version": "1.3.
|
|
51
|
+
"version": "1.3.41",
|
|
52
52
|
"path": "skills/next-campaigns-qa/SKILL.md",
|
|
53
53
|
"domain": "campaigns",
|
|
54
54
|
"description": "Run spec-aware QA from a saved Map or local-spec Build Packet and tested campaign URL after build, polish, and deploy/local evidence exist, including Playwright typed-card test-order proof."
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
{
|
|
57
57
|
"id": "campaign-lifecycle-orientation",
|
|
58
58
|
"name": "Campaign Lifecycle Orientation",
|
|
59
|
-
"version": "1.0.
|
|
59
|
+
"version": "1.0.37",
|
|
60
60
|
"path": "skills/campaign-lifecycle-orientation/SKILL.md",
|
|
61
61
|
"domain": "campaigns",
|
|
62
62
|
"description": "Orient a reader to the Campaigns OS lifecycle artifacts a run has already emitted, without advancing their state: the shared vocabulary, doctor as a gate, the stage record, and the store-theme/Page Kit two-worlds trap."
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
{
|
|
65
65
|
"id": "campaign-run-evidence",
|
|
66
66
|
"name": "Campaign Run Evidence",
|
|
67
|
-
"version": "1.0.
|
|
67
|
+
"version": "1.0.37",
|
|
68
68
|
"path": "skills/campaign-run-evidence/SKILL.md",
|
|
69
69
|
"domain": "campaigns",
|
|
70
70
|
"description": "Interpret existing doctor, QA verdict and proof-depth evidence without claiming more proof than the artifacts contain, and keep funnel proof apart from merchant launch readiness."
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
{
|
|
73
73
|
"id": "campaign-readback-classification",
|
|
74
74
|
"name": "Campaign Readback Classification",
|
|
75
|
-
"version": "1.0.
|
|
75
|
+
"version": "1.0.37",
|
|
76
76
|
"path": "skills/campaign-readback-classification/SKILL.md",
|
|
77
77
|
"domain": "campaigns",
|
|
78
78
|
"description": "Classify a selected campaign from the campaigns-os-readback/v2 fields (artifacts, staleness.stale_keys, clean, doctor, divergences, skip_cascades) and write a read-only handoff without turning diagnosis into permission."
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
{
|
|
81
81
|
"id": "contribution-intake",
|
|
82
82
|
"name": "Contribution Intake",
|
|
83
|
-
"version": "1.0.
|
|
83
|
+
"version": "1.0.37",
|
|
84
84
|
"path": "skills/contribution-intake/SKILL.md",
|
|
85
85
|
"domain": "campaigns",
|
|
86
86
|
"description": "Turn a suggestion about the agent surface into a classified, evidence-checked and redacted proposal, filed on this repository tracker only with attended approval."
|
package/src/built-site-scope.mjs
CHANGED
|
@@ -355,3 +355,94 @@ export function computeBuildFingerprint(outputDir, { exclude = [] } = {}) {
|
|
|
355
355
|
error: null,
|
|
356
356
|
};
|
|
357
357
|
}
|
|
358
|
+
|
|
359
|
+
// The manifest as `{ path, sha256 }` entries, the shape `record build` keeps
|
|
360
|
+
// on stages.assembly.build_manifest beside build_fingerprint so a later
|
|
361
|
+
// mismatch can name the files that differ instead of only two hashes.
|
|
362
|
+
export function buildManifestEntries(fingerprint) {
|
|
363
|
+
const lines = String(fingerprint?.manifest || "").split("\n");
|
|
364
|
+
const entries = [];
|
|
365
|
+
for (let index = 0; index + 1 < lines.length; index += 2) {
|
|
366
|
+
if (lines[index]) entries.push({ path: lines[index], sha256: lines[index + 1] });
|
|
367
|
+
}
|
|
368
|
+
return entries;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
372
|
+
|
|
373
|
+
// The recorded manifest, only when it is evidence about the recorded build:
|
|
374
|
+
// well-formed entries whose canonical manifest hashes to the recorded
|
|
375
|
+
// fingerprint. A record made before build kept a manifest, or one rewritten
|
|
376
|
+
// by hand, reads as unavailable (null) rather than as a misleading diff.
|
|
377
|
+
export function recordedBuildManifest(assembly) {
|
|
378
|
+
const entries = assembly?.build_manifest;
|
|
379
|
+
const recorded = assembly?.build_fingerprint;
|
|
380
|
+
if (!Array.isArray(entries) || typeof recorded !== "string") return null;
|
|
381
|
+
if (!entries.every((entry) => typeof entry?.path === "string" && entry.path && typeof entry?.sha256 === "string" && SHA256_HEX.test(entry.sha256))) return null;
|
|
382
|
+
const manifest = entries.map((entry) => `${entry.path}\n${entry.sha256}\n`).join("");
|
|
383
|
+
return `sha256:${sha256Hex(Buffer.from(manifest, "utf8"))}` === recorded ? entries : null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// macOS and iCloud name a sync conflict copy `<name> 2.<ext>` (then ` 3`,
|
|
387
|
+
// ...), on files and on folders. A trailing " N" alone is too common in real
|
|
388
|
+
// names ("chapter 12.md"), so a path counts as a copy only when the same path
|
|
389
|
+
// without the suffix also exists. The flag is a hint, never a verdict.
|
|
390
|
+
const CONFLICT_COPY_SEGMENT = /^(.+) (?:[2-9]|[1-9]\d+)(\.[^.]+)?$/;
|
|
391
|
+
export function isSyncConflictCopyPath(path, knownPaths) {
|
|
392
|
+
const segments = String(path).split("/");
|
|
393
|
+
const known = [...knownPaths];
|
|
394
|
+
return segments.some((segment, index) => {
|
|
395
|
+
const match = CONFLICT_COPY_SEGMENT.exec(segment);
|
|
396
|
+
if (!match) return false;
|
|
397
|
+
const original = [...segments.slice(0, index), `${match[1]}${match[2] ?? ""}`].join("/");
|
|
398
|
+
return index === segments.length - 1
|
|
399
|
+
? known.includes(original)
|
|
400
|
+
: known.some((candidate) => candidate.startsWith(`${original}/`));
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export const BUILD_DRIFT_PATH_LIMIT = 20;
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Compare the output on disk (a computeBuildFingerprint result) with the
|
|
408
|
+
* manifest stages.assembly recorded. Returns
|
|
409
|
+
* { manifest: "recorded", extra, missing, changed, conflict_copies, summary }
|
|
410
|
+
* with sorted path lists, or { manifest: "unavailable", summary } when the
|
|
411
|
+
* record carries no usable manifest. `summary` is one sentence for a
|
|
412
|
+
* refusal: each list capped at BUILD_DRIFT_PATH_LIMIT with "+N more".
|
|
413
|
+
*/
|
|
414
|
+
export function describeBuildOutputDrift(assembly, current) {
|
|
415
|
+
const recorded = recordedBuildManifest(assembly);
|
|
416
|
+
if (!recorded) {
|
|
417
|
+
return {
|
|
418
|
+
manifest: "unavailable",
|
|
419
|
+
summary: "The build record carries no file manifest (it was recorded before record build kept one), so the differing paths cannot be named; re-run record build to record one.",
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const before = new Map(recorded.map((entry) => [entry.path, entry.sha256]));
|
|
423
|
+
const now = new Map(buildManifestEntries(current).map((entry) => [entry.path, entry.sha256]));
|
|
424
|
+
const extra = [...now.keys()].filter((path) => !before.has(path));
|
|
425
|
+
const missing = [...before.keys()].filter((path) => !now.has(path));
|
|
426
|
+
const changed = [...now.keys()].filter((path) => before.has(path) && before.get(path) !== now.get(path));
|
|
427
|
+
const knownPaths = new Set([...before.keys(), ...now.keys()]);
|
|
428
|
+
const isCopy = (path) => isSyncConflictCopyPath(path, knownPaths);
|
|
429
|
+
const conflictCopies = extra.filter(isCopy);
|
|
430
|
+
const list = (label, paths) => {
|
|
431
|
+
if (paths.length === 0) return null;
|
|
432
|
+
const shown = paths.slice(0, BUILD_DRIFT_PATH_LIMIT).map((path) => (isCopy(path) ? `${path} [sync conflict copy]` : path));
|
|
433
|
+
const more = paths.length > BUILD_DRIFT_PATH_LIMIT ? ` (+${paths.length - BUILD_DRIFT_PATH_LIMIT} more)` : "";
|
|
434
|
+
return `${paths.length} ${label}: ${shown.join(", ")}${more}`;
|
|
435
|
+
};
|
|
436
|
+
const parts = [list("extra", extra), list("missing", missing), list("changed", changed)].filter(Boolean);
|
|
437
|
+
const hint = conflictCopies.length
|
|
438
|
+
? ` ${conflictCopies.length} extra file name(s) look like macOS/iCloud sync conflict copies ("<name> 2.<ext>" beside "<name>.<ext>"); if they are, remove them (or move the repo out of the synced folder) and rebuild.`
|
|
439
|
+
: "";
|
|
440
|
+
return {
|
|
441
|
+
manifest: "recorded",
|
|
442
|
+
extra,
|
|
443
|
+
missing,
|
|
444
|
+
changed,
|
|
445
|
+
conflict_copies: conflictCopies,
|
|
446
|
+
summary: `Against the manifest build recorded: ${parts.length ? parts.join("; ") : "no path differs"}.${hint}`,
|
|
447
|
+
};
|
|
448
|
+
}
|