@nextcommerce/campaigns-os 1.54.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 +84 -0
- package/CONTEXT.md +8 -5
- package/README.md +26 -12
- package/compatibility.json +1 -1
- package/contracts/release-ledger.json +441 -0
- package/contracts/supported-surface.json +2 -2
- package/docs/brand-theme-bridge.md +10 -0
- package/docs/build-packet.md +35 -19
- package/docs/campaigns-os-build-flow.md +1 -1
- package/docs/effects.md +2 -5
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +1 -1
- package/docs/qa-and-test-orders.md +55 -11
- package/docs/runtime-readiness.md +1 -1
- package/docs/skills-revision.md +10 -10
- package/docs/supported-surface.md +1 -1
- package/docs/workflow-findings-sidecar.md +57 -2
- package/package.json +3 -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-identity.mjs +33 -4
- package/src/cli.mjs +36 -59
- package/src/consent.mjs +3 -2
- package/src/design-source-publication.mjs +1 -1
- package/src/deviation.mjs +2 -1
- package/src/doctor/checks.mjs +16 -6
- package/src/doctor/next-step.mjs +16 -6
- package/src/invocation.mjs +4 -3
- 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/qa-browser.mjs +521 -92
- package/src/qa-cart-entry.mjs +29 -6
- package/src/qa-node.mjs +144 -39
- package/src/qa-verdict-publish.mjs +2 -1
- package/src/run-record.mjs +46 -34
- package/src/sdk-markup.mjs +41 -1
- package/src/stage-ledger.mjs +14 -2
- package/src/stage-record.mjs +50 -13
|
@@ -341,6 +341,24 @@ npm run campaigns-os -- qa run \
|
|
|
341
341
|
|
|
342
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.
|
|
343
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
|
+
|
|
344
362
|
### Automatic commercial parity
|
|
345
363
|
|
|
346
364
|
When the CampaignSpec has enabled pages with package rows, the same `qa run`
|
|
@@ -804,7 +822,7 @@ prints the portal link.
|
|
|
804
822
|
The outcome lands on the run's Run Record as the `qa_verdict_publish` block —
|
|
805
823
|
`verdict_run_id`, `publisher` (`qa run` or `qa publish`), `state`
|
|
806
824
|
(`skipped` / `ok` / `failed`), `result` in the remit vocabulary, `base_kind`,
|
|
807
|
-
`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
|
|
808
826
|
verdict under the packet's campaign. `qa run` records its own publish (or
|
|
809
827
|
its `--no-post-verdict` skip) the same way when the run session closes, which
|
|
810
828
|
is what `already_published` reads. A stored `ok` is never downgraded: a
|
|
@@ -1183,8 +1201,8 @@ Fixture essentials:
|
|
|
1183
1201
|
|
|
1184
1202
|
Test Orders use **global test cards** that work on any live store and integration.
|
|
1185
1203
|
They **bypass the payment gateway and create no transactions** (and no fulfillment),
|
|
1186
|
-
|
|
1187
|
-
|
|
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,
|
|
1188
1206
|
easy-to-clean footprint: Test orders are deletable in bulk, and the resulting
|
|
1189
1207
|
Customer record is reused (see the test email note below) rather than multiplied.
|
|
1190
1208
|
|
|
@@ -1566,17 +1584,43 @@ Two flags target funnels the default-tier drive cannot prove:
|
|
|
1566
1584
|
|
|
1567
1585
|
- `--select-package <ref[:qty],...>` — **strict** package/bundle card selection.
|
|
1568
1586
|
Each ref is matched against rendered selector/bundle cards
|
|
1569
|
-
(`[data-next-package-id]`, `[data-next-bundle-card][data-next-bundle-id]
|
|
1570
|
-
|
|
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
|
|
1571
1592
|
`data-next-bundle-items` composition contains exactly that package and
|
|
1572
|
-
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
|
|
1573
1597
|
selected-state marker (`data-next-selected="true"` / `.next-selected`) after
|
|
1574
1598
|
the click. A missing card, ambiguous composition, wrong quantity, or
|
|
1575
1599
|
unverifiable/refused selection **fails the `selected_bundle` step** instead
|
|
1576
1600
|
of silently driving the pre-selected default tier. A bundle ref remains an
|
|
1577
1601
|
authoritative identity when exactly one rendered card declares it. Use this
|
|
1578
1602
|
flag to traverse non-default tiers of a multi-tier selector. `--cart` remains
|
|
1579
|
-
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.
|
|
1580
1624
|
- `--apply-coupon <code>` — types the code into the rendered coupon/promo input
|
|
1581
1625
|
(the SDK's `[data-next-checkout-field="coupon"]` or
|
|
1582
1626
|
`input[data-next-coupon="input"]`, then hand-rolled `coupon`/`voucher`/`promo`
|
|
@@ -1709,7 +1753,7 @@ browser launch, prints the planned count, lists the planned paths (up to 40 ids;
|
|
|
1709
1753
|
past that the remainder is counted, never cut silently, and `--select-package
|
|
1710
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
|
|
1711
1755
|
eight terminal paths plus the checkout baseline, so it requires
|
|
1712
|
-
`--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.
|
|
1713
1757
|
|
|
1714
1758
|
`--max-test-orders` bounds **planned paths**, which is not the same as real
|
|
1715
1759
|
purchases. `--max-order-creations` bounds **actual order creations**, defaults to
|
|
@@ -1849,7 +1893,7 @@ and hardcoded phone numbers that differ from CampaignSpec `campaign.store_phone`
|
|
|
1849
1893
|
If a static claim is intentionally preserved, wrap it in an element with
|
|
1850
1894
|
`data-skip-market-lint="true"` and record why in the assembly report.
|
|
1851
1895
|
|
|
1852
|
-
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
|
|
1853
1897
|
**SDK origin allowlist**: the Campaign Cart SDK must be allowed to load on the
|
|
1854
1898
|
tested origin for the campaign API key, or runtime checks (and the live page
|
|
1855
1899
|
itself) may not initialize. Localhost on any port is globally available as a
|
|
@@ -1864,8 +1908,8 @@ npm run campaigns-os -- qa policy set \
|
|
|
1864
1908
|
--allowed-domains-confirmed true
|
|
1865
1909
|
```
|
|
1866
1910
|
|
|
1867
|
-
There is no permission flag for test orders
|
|
1868
|
-
<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` /
|
|
1869
1913
|
`--sandbox-test-card-confirmed` flags and the `qa.test_orders_allowed` /
|
|
1870
1914
|
`qa.sandbox_test_card_confirmed` packet fields they set were removed in
|
|
1871
1915
|
supported surface 1.28.0: nothing had read their values since the gate itself
|
|
@@ -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
|
|
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
|
|
@@ -13,7 +13,7 @@ implementation detail, however stable it looks.
|
|
|
13
13
|
|
|
14
14
|
| Surface | Contract | Change discipline |
|
|
15
15
|
|---|---|---|
|
|
16
|
-
| `schemas/*.schema.json` (all of them) | The portable contract catalog: CampaignSpec, Design Source Package, Build Packet, Build Context, Assembly Report, Doctor Output, sidecar-bundle conformance, Run Record, Workflow Finding, Build Brief, Source-HTML Manifest, Tooling Orientation, Release Ledger, QA Verdict, the QA Verdict sidecar projection, Runtime Recipe, and the legacy-migration inventory/plan/receipt trio. | Hashed. Any content change requires updating the recorded hash **and** bumping `surface_version` in the same PR. A shape change that alters meaning gets a new schema-version const — one version identifier must never cover two shapes (the 2026-08 assembly-report drift is the incident this rule encodes). Additions to an open `v0` schema are expected and consumers must tolerate unknown fields; the security-sensitive legacy-migration schemas are closed, so additions there require a new lineage. 1.28.0 (RL entry `surface_version: 1.28.0`, breaking) removed the two required Build Packet booleans `qa.test_orders_allowed` and `qa.sandbox_test_card_confirmed` (nothing read them; test orders run from `--test-order <mode>` alone), added `local-serve` to the `deploy.target` enum, and added the optional `remit_result` / `remit_base_kind` fields to the Run Record. 1.30.0 (additive) added the optional `data_layer` record to the QA Verdict's `test_orders[]` entries — the order's `dl_purchase` reading (#325). 1.33.0 (additive) added the optional `qa_verdict_publish` block to the Run Record — which verdict was posted to the QA portal, by `qa run` or `qa publish`, and what the portal answered, in the `remit_result` vocabulary (#328). |
|
|
16
|
+
| `schemas/*.schema.json` (all of them) | The portable contract catalog: CampaignSpec, Design Source Package, Build Packet, Build Context, Assembly Report, Doctor Output, sidecar-bundle conformance, Run Record, Workflow Finding, Build Brief, Source-HTML Manifest, Tooling Orientation, Release Ledger, QA Verdict, the QA Verdict sidecar projection, Runtime Recipe, and the legacy-migration inventory/plan/receipt trio. | Hashed. Any content change requires updating the recorded hash **and** bumping `surface_version` in the same PR. A shape change that alters meaning gets a new schema-version const — one version identifier must never cover two shapes (the 2026-08 assembly-report drift is the incident this rule encodes). Additions to an open `v0` schema are expected and consumers must tolerate unknown fields; the security-sensitive legacy-migration schemas are closed, so additions there require a new lineage. 1.28.0 (RL entry `surface_version: 1.28.0`, breaking) removed the two required Build Packet booleans `qa.test_orders_allowed` and `qa.sandbox_test_card_confirmed` (nothing read them; test orders run from `--test-order <mode>` alone), added `local-serve` to the `deploy.target` enum, and added the optional `remit_result` / `remit_base_kind` fields to the Run Record. 1.30.0 (additive) added the optional `data_layer` record to the QA Verdict's `test_orders[]` entries — the order's `dl_purchase` reading (#325). 1.33.0 (additive) added the optional `qa_verdict_publish` block to the Run Record — which verdict was posted to the QA portal, by `qa run` or `qa publish`, and what the portal answered, in the `remit_result` vocabulary (#328). 1.55.0 (breaking) adds the optional publish decision `reason`, record `closed_by` closer, QA observation `verdict_run_id`, `build_fingerprint`, and `stale`, lifecycle `stages[].wait_ms` and `stages[].finding_codes`, and run-level `needs_input_count` and `failure_count` while removing the unused `agent_usage` field from the same v0 Run Record shape. |
|
|
17
17
|
| CLI commands: `start`, `prepare-build`, `build`, `polish`, `checkpoint`, `page-kit`, `spec`, `doctor`, `bundle`, `next`, `theme`, `tooling`, `install-skills`, `install-agent-context`, `validate-assembly-report`, `telemetry`, `standardize`, `qa`, `findings`, `run-record`, `run`, `sdk`, `demo`, `login`, `logout` | Scriptable entry points. The founding list (1.0.0) was the argv surface Campaigns Agent's remit fixture pins; 1.25.0 adds the partner entry path the public guides instruct — `install-skills` (skill install), `tooling status` (preflight), `next --packet` (the runtime cursor) — plus `theme`, `install-agent-context`, `validate-assembly-report`, and `telemetry`, since consent is part of the partner contract. `validate-build-packet`, formerly an undocumented and unsupported alias of `doctor`, was removed in 1.25.0+agent.7 (RL-0036) and now gets the unknown-command error; use `doctor`. `standardization-report`, a supported but redundant second spelling of `standardize` — same flags, same output, same exit codes — was removed in 1.27.0 and now gets the same unknown-command error; use `standardize`. Removing a supported command is a breaking change and is why that release moved the minor. `bundle check` validates the canonical JSON readback set and makes QA required only under `--require-qa`. `polish capture` owns page-load evidence production. `checkpoint waive` currently accepts five registered gates: `page_kit.store_profile`, `page_kit.sdk_version`, `polish.hidden_eager_media`, `built_output.upsell_selector_scope`, and `source_html.producer_provenance` (per page, with `--page <page_id>`). `page-kit sync` (added in 1.29.0) writes the CampaignSpec's Store Profile fields and SDK pin into the target's `_data/campaigns.json` entry — the repair the first two gates name — and is the only `page-kit` subcommand. `spec derive` (added in 1.31.0) is the reverse write for the repo-derived field class (#432): the target's SDK pin, page routes and analytics ids into the packet's local CampaignSpec; it is the only `spec` subcommand, and the `page_kit.sdk_version.repo_newer` advisory names it; its `--write-map` flag (1.33.0+agent.2) also records the derived pin in the saved Map's Build hints field through the proxy Worker, never moving a Map pin backwards. `qa publish` (added in 1.33.0) posts an already-stored verdict to the QA portal without a re-run or an order, refusing a stale `spec_hash` or a verdict its Run Record already records as published (#328). Within Polish, only the broader Source Freshness waiver remains on its existing report lane; theme and QA decisions also retain their existing lanes. | Any change to this list — adding, renaming, or removing a command — bumps `surface_version` in the same PR, and since 1.25.0 `check-supported-surface.mjs --base` enforces that (before, only hashed and named entries owed a bump, so `checkpoint` landed unbumped). Subcommands, registered gates, and flags may grow freely beneath a listed command. Removing a listed command from dispatch fails the gate outright. Do not infer support for an unregistered checkpoint from the top-level command. |
|
|
18
18
|
| `sdk storage-check` and `docs/sdk-storage-compatibility.md` | Read-only AST compatibility report for explicitly scoped tracked campaign HTML/JS before an SDK bump; consumes the supplied SDK-owned manifest and records its SHA-256 and verified or unverified local Git provenance. Incompatible and unknown findings exit 2; a clean scan is static source evidence only. | Additive supported CLI and named documentation; no automatic merchant integration repair. |
|
|
19
19
|
| `sdk repin` | Rewrites the version segment of semver-pinned Campaign Cart loader.js / campaign-cart.css URLs in a static repo (no `_data/campaigns.json`) to a target version, default the SDK support policy's `preferred_minimum`. It previews by default. `--apply` writes the files and the change record `.campaign-runtime/sdk-repin.json` (`campaigns-os-sdk-repin/v0`). `@latest`, `@main`, commit and prerelease refs, and pins at or above the target, are reported and never rewritten. A page-kit repo is refused with exit 2. Usage is in `docs/sdk-storage-compatibility.md`. | Additive supported subcommand under the listed `sdk` command. It never changes anything in a URL except the version segment. |
|
|
@@ -51,7 +51,8 @@ own schemas and evolve independently). Instead it carries:
|
|
|
51
51
|
|
|
52
52
|
- **Stable envelope** — `schema_version` (`campaigns-os-run-record/v0`),
|
|
53
53
|
`run_id`, package version, the command that ran, an `argv` *shape* (flag names
|
|
54
|
-
present, not raw values), `created_at`, consent state,
|
|
54
|
+
present, not raw values), `created_at`, consent state, remit status, and
|
|
55
|
+
optional `closed_by` (`run_end`, `qa_auto_end`, `stale_sweep`, or `manual`).
|
|
55
56
|
- **Run identity** — `map_id`, `campaign_slug`, `template_family`,
|
|
56
57
|
`entry_point_shape`. (Best-effort; missing identity never blocks capture.)
|
|
57
58
|
- **Source artifact refs** — for the Build Packet, Build Context, Assembly
|
|
@@ -64,6 +65,17 @@ own schemas and evolve independently). Instead it carries:
|
|
|
64
65
|
this run.
|
|
65
66
|
- **Findings snapshot** — this run's Workflow Findings (see channel below).
|
|
66
67
|
|
|
68
|
+
When a QA verdict is present, `observations.qa.verdict_run_id` names the same
|
|
69
|
+
verdict that supplies `disposition`. `build_fingerprint` is the QA stage's
|
|
70
|
+
recorded build fingerprint only when the Assembly Report binds that stage to
|
|
71
|
+
the same verdict; otherwise it is `null`. `stale` compares that fingerprint
|
|
72
|
+
with the Assembly Report's recorded build fingerprint using the same comparison
|
|
73
|
+
as `next`: `true` when they differ, `false` when they match, and `null` when
|
|
74
|
+
either fingerprint is unknown. A rebuild without `record build` therefore reads
|
|
75
|
+
`stale: false` when the recorded fingerprints still match; doctor's
|
|
76
|
+
`built_output.fingerprint_stale` checks the output on disk. All three fields
|
|
77
|
+
are optional for older records; without a verdict, `observations.qa` is absent.
|
|
78
|
+
|
|
67
79
|
### Run identity
|
|
68
80
|
|
|
69
81
|
A single canonical `campaigns_os_run_id` is minted at the run boundary and
|
|
@@ -78,6 +90,45 @@ Stage timings and repair-loop count are captured from the command lifecycle
|
|
|
78
90
|
journal when a run session or explicit lifecycle journal is active. They remain
|
|
79
91
|
best-effort signal: telemetry records the commands Campaigns OS can observe, not
|
|
80
92
|
every thought, browser click, or external editor action in an agent session.
|
|
93
|
+
Each new journal entry records `wait_ms` as non-negative whole milliseconds
|
|
94
|
+
blocked on terminal input; an invocation without a prompt records 0. Legacy
|
|
95
|
+
entries may omit it or hold null, and Run Record stages preserve unknown wait
|
|
96
|
+
as absent or null rather than inventing 0. `duration_ms` still includes prompt
|
|
97
|
+
wait, so observed command work is `duration_ms - wait_ms` when wait is known. Tier-2 phases carry their
|
|
98
|
+
own measured wait when the prompt occurs while that phase is active; the journal
|
|
99
|
+
also retains the invocation total. Nested prompt hooks count their outer wait
|
|
100
|
+
interval once. Authentication prompts are outside lifecycle capture by policy.
|
|
101
|
+
|
|
102
|
+
On a non-zero doctor or QA exit, optional `finding_codes` is an array of at most
|
|
103
|
+
five distinct string codes in first-seen order; null is invalid. Codes belong
|
|
104
|
+
to the phase active when they were recorded. Codes recorded outside any phase,
|
|
105
|
+
and codes in a legacy entry, are invocation-level only; aggregation assigns them
|
|
106
|
+
to the entry's last phase. A successful stage has no finding codes. The optional run-level
|
|
107
|
+
`needs_input_count` and `failure_count` are non-negative integer counts; null is
|
|
108
|
+
invalid and absence means a legacy record did not report the count.
|
|
109
|
+
`needs_input_count` counts journal entries with exit 2 and `failure_count`
|
|
110
|
+
counts entries with any other non-zero exit. Current command
|
|
111
|
+
paths use 0 for success, 1 for operational errors, 2 for missing or blocked
|
|
112
|
+
inputs, and 4 for blocked QA; the other-non-zero rule also covers a future
|
|
113
|
+
status without reclassifying exit 2. Both counts are per command invocation,
|
|
114
|
+
including a command that emits multiple Tier-2 stages. `repair_loop_count`
|
|
115
|
+
continues to count repeated commands plus explicit repair-loop hooks.
|
|
116
|
+
|
|
117
|
+
| Terminal input site | Wait hook | Capture context |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| Consent question (`src/consent.mjs`) | Around the injectable `ask` call | Active command, including `run-record` |
|
|
120
|
+
| Store question (`src/login.mjs`) | Around readline `question` | Authentication runs outside lifecycle capture |
|
|
121
|
+
| Finding stage, kind, summary, details (`src/cli.mjs`) | Around each readline `question` | Active `findings add` command; omitted fields do not prompt |
|
|
122
|
+
|
|
123
|
+
| Non-zero finding source | Codes recorded |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `doctor` | `result.errors[].code` before rendering and exit 2 |
|
|
126
|
+
| `start` and `build` | Their embedded doctor `errors[].code` before the prepare result sets exit 2 |
|
|
127
|
+
| `qa run` and `qa parity` | Blocked verdict assertions: `evidence.code`, or assertion `id` when no code is present, before returning from QA dispatch with exit 4 |
|
|
128
|
+
|
|
129
|
+
Spec resolution exceptions and prepare-build without a doctor result do not
|
|
130
|
+
expose doctor or QA codes. They keep their existing exit and timing evidence,
|
|
131
|
+
with no invented finding code.
|
|
81
132
|
|
|
82
133
|
### Validation
|
|
83
134
|
|
|
@@ -246,7 +297,11 @@ remit(path, payload, proxyBase) // mirrors qa-node.mjs postVerdict
|
|
|
246
297
|
for a later post of the stored verdict), `attempted` / `ok` / `error` /
|
|
247
298
|
`endpoint` (`/api/qa/verdicts`), a `state` (`skipped` when the run's
|
|
248
299
|
publish was off, `ok`, `failed`), the `result` in the same vocabulary as
|
|
249
|
-
`remit_result`, the `base_kind`,
|
|
300
|
+
`remit_result`, the `base_kind`, `published_at`, and optional `reason`.
|
|
301
|
+
The reason records the actual QA publish decision: `loopback_base_url`,
|
|
302
|
+
`portal_managed_default`, `consent_off`, `flag_opt_out`, `flag_opt_in`,
|
|
303
|
+
`default`, or `local_spec`. A later `qa publish` has no QA run decision and
|
|
304
|
+
records a null reason. `qa run` hands the
|
|
250
305
|
block to the session through its QA attempt, so `run end` and the auto-end
|
|
251
306
|
stamp it; `qa publish` stamps the record whose `qa_verdict` artifact
|
|
252
307
|
references the verdict, reads `state: "ok"` as already published, and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextcommerce/campaigns-os",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.55.0",
|
|
4
4
|
"description": "Toolkit for agent-assisted NEXT campaign builds.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -66,6 +66,8 @@
|
|
|
66
66
|
"check:spec": "node --test \"campaign-spec/test/**/*.test.ts\"",
|
|
67
67
|
"check": "npm run check:provenance && npm run check:workflows && npm run check:changelog-structure && npm run build:spec && npm run check:tests && npm run check:spec && npm run check:fixtures && npm run check:legacy-migration && npm run check:spec-conformance && npm run check:private-strings && npm run check:template-doctrine && npm run check:slot-manifest && npm run check:skill-versions && npm run check:cart-readiness && npm run check:sidecar-bundle && npm run check:supported-surface && npm run check:effects && npm run check:release-ledger && npm run check:runtime-recipe && npm run check:orientation-docs && npm run check:runtime-docs && npm run check:pack -- --skip-prepare",
|
|
68
68
|
"check:tests": "node ./scripts/check-tests.mjs",
|
|
69
|
+
"check:fast": "node ./scripts/check-fast.mjs",
|
|
70
|
+
"hooks:install": "node ./scripts/install-git-hooks.mjs",
|
|
69
71
|
"check:legacy-migration": "node ./scripts/check-legacy-migration.mjs",
|
|
70
72
|
"check:fixtures": "node ./scripts/check-fixtures.mjs",
|
|
71
73
|
"check:spec-conformance": "node ./scripts/check-campaign-spec-conformance.mjs",
|
|
@@ -134,6 +134,11 @@
|
|
|
134
134
|
"enum": ["skipped", "ok", "failed"],
|
|
135
135
|
"description": "skipped: publish was off for the run (no send). ok: the receiver's answer counts as stored. failed: refused or never answered."
|
|
136
136
|
},
|
|
137
|
+
"reason": {
|
|
138
|
+
"type": ["string", "null"],
|
|
139
|
+
"enum": ["loopback_base_url", "portal_managed_default", "consent_off", "flag_opt_out", "flag_opt_in", "default", "local_spec", null],
|
|
140
|
+
"description": "The QA publish decision's reason. null when no decision was recorded, such as a later qa publish of a stored verdict."
|
|
141
|
+
},
|
|
137
142
|
"result": {
|
|
138
143
|
"type": ["string", "null"],
|
|
139
144
|
"enum": ["stored", "already_stored", "ok_unparsed_ack", "refused", "transport_error", null],
|
|
@@ -226,6 +231,9 @@
|
|
|
226
231
|
"additionalProperties": false,
|
|
227
232
|
"properties": {
|
|
228
233
|
"disposition": { "type": ["string", "null"] },
|
|
234
|
+
"verdict_run_id": { "type": ["string", "null"], "minLength": 1, "description": "Run ID of the verdict supplying disposition." },
|
|
235
|
+
"build_fingerprint": { "type": ["string", "null"], "minLength": 1, "description": "QA build fingerprint from the Assembly Report stage bound to that verdict." },
|
|
236
|
+
"stale": { "type": ["boolean", "null"], "description": "Whether that QA fingerprint differs from the current recorded build; null when either fingerprint is unknown." },
|
|
229
237
|
"gap_classes": {
|
|
230
238
|
"type": "array",
|
|
231
239
|
"items": { "type": "string" },
|
|
@@ -286,19 +294,10 @@
|
|
|
286
294
|
"type": ["string", "null"],
|
|
287
295
|
"description": "Optional free-form confidence hint for primary_surface, e.g. \"low\"."
|
|
288
296
|
},
|
|
289
|
-
"
|
|
290
|
-
"type": "
|
|
291
|
-
"
|
|
292
|
-
"description": "
|
|
293
|
-
"properties": {
|
|
294
|
-
"input_tokens": { "type": "integer", "minimum": 0 },
|
|
295
|
-
"output_tokens": { "type": "integer", "minimum": 0 },
|
|
296
|
-
"tool_output_tokens": { "type": "integer", "minimum": 0 },
|
|
297
|
-
"total_tokens": { "type": "integer", "minimum": 0 },
|
|
298
|
-
"elapsed_ms": { "type": "integer", "minimum": 0 },
|
|
299
|
-
"model": { "type": "string" },
|
|
300
|
-
"source": { "type": "string" }
|
|
301
|
-
}
|
|
297
|
+
"closed_by": {
|
|
298
|
+
"type": "string",
|
|
299
|
+
"enum": ["run_end", "qa_auto_end", "stale_sweep", "manual"],
|
|
300
|
+
"description": "The command path that closed this Run Record. Absent on records written before this field existed."
|
|
302
301
|
},
|
|
303
302
|
"lifecycle": {
|
|
304
303
|
"type": ["object", "null"],
|
|
@@ -327,17 +326,23 @@
|
|
|
327
326
|
"type": "object",
|
|
328
327
|
"additionalProperties": false,
|
|
329
328
|
"required": ["name"],
|
|
329
|
+
"if": { "required": ["finding_codes"], "properties": { "finding_codes": {} } },
|
|
330
|
+
"then": { "required": ["exit_status"], "properties": { "exit_status": { "type": "integer", "not": { "const": 0 } } } },
|
|
330
331
|
"properties": {
|
|
331
332
|
"name": { "type": "string" },
|
|
332
333
|
"duration_ms": { "type": ["number", "null"] },
|
|
333
|
-
"exit_status": { "type": ["integer", "null"] }
|
|
334
|
+
"exit_status": { "type": ["integer", "null"] },
|
|
335
|
+
"wait_ms": { "type": ["integer", "null"], "minimum": 0, "description": "Whole milliseconds blocked on operator input. Absent or null means unknown for legacy entries; duration_ms includes it." },
|
|
336
|
+
"finding_codes": { "type": "array", "maxItems": 5, "uniqueItems": true, "items": { "type": "string" }, "description": "First five distinct doctor or QA finding codes, only on a non-zero exit." }
|
|
334
337
|
}
|
|
335
338
|
}
|
|
336
339
|
},
|
|
337
340
|
"repair_loop_count": {
|
|
338
341
|
"type": ["integer", "null"],
|
|
339
342
|
"description": "Re-runs of any command within the run (a re-run is a repair loop: doctor -> fix -> doctor). 0 when each command ran once."
|
|
340
|
-
}
|
|
343
|
+
},
|
|
344
|
+
"needs_input_count": { "type": "integer", "minimum": 0, "description": "Number of command invocations with exit_status 2." },
|
|
345
|
+
"failure_count": { "type": "integer", "minimum": 0, "description": "Number of command invocations with a non-zero exit_status other than 2." }
|
|
341
346
|
}
|
|
342
347
|
}
|
|
343
348
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-lifecycle-orientation
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.32
|
|
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.55.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.55.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.32
|
|
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.55.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.55.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.32
|
|
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.55.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.55.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.32
|
|
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.55.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.55.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.37
|
|
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.55.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.55.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
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.52
|
|
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.55.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.55.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.35
|
|
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.55.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.55.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.36
|
|
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.55.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.55.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.36
|
|
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.55.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.55.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
|
|
@@ -80,7 +80,7 @@ Rules:
|
|
|
80
80
|
- Parity capture blocking proof is the voucher-adjusted persisted line from typed-card order readback. Browser totals and client state do not replace the persisted-line voucher guard.
|
|
81
81
|
- Read client purchase values per event. A whole-cart `dl_purchase` must not mask or supply an offer-level upsell purchase expectation.
|
|
82
82
|
- Live `qa parity` runs publish to the QA portal by default like other QA runs. Pass `--no-post-verdict` for dev, replay, negative-control, and other local proof runs.
|
|
83
|
-
- Theme gate: `qa run` refuses to run when a generatable brand theme is not applied to commerce pages and no waiver exists. Apply the brand layer or
|
|
83
|
+
- Theme gate: `qa run` refuses to run when a generatable brand theme is not applied to commerce pages and no waiver exists. Apply the brand layer or use the recorded waiver `campaigns-os theme waive --packet <p> --reason "<why>" --waived-by "<named human>"` (tier `C`: it overwrites the assembly report and doctor output). `qa run --theme-waive "<reason>"` is the one-run ephemeral form. Do not bypass the gate another way. A waived run still reports template-residue findings at warn severity.
|
|
84
84
|
- Template residue is a QA dimension, not advice: promoted starter families must have a brand/residue/pricing contract (`contracts/template-brand-contract.<family>.v0.json`). Browser QA inspects computed styles on commerce surfaces and fails pages that still render starter defaults (`#3c7dff`/`#0a265c`, starter `next-logo.png`, paypal/klarna chrome absent from the spec).
|
|
85
85
|
- Pricing visibility is a blocker: an upsell/downsell offer with zero visible price rows fails QA. Pricing surfaces render via template pricing modes (`full_price`, `compare_at_current`, `unit_price_plus_total`, `savings_badge_amount`, `code_discounted_post_checkout`), never via campaign CSS `display:none` on price wrappers.
|
|
86
86
|
- The upsell and checkout bundle price checks count the SDK's `data-next-bundle-display` price as a price row when it is visible and not empty.
|