@nextcommerce/campaigns-os 1.52.0 → 1.54.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 +109 -0
- package/README.md +1 -0
- package/agents/claude/CLAUDE.md +2 -0
- package/agents/codex/AGENTS.md +2 -0
- package/agents/copilot/copilot-instructions.md +2 -0
- package/agents/cursor/campaigns-os.mdc +2 -0
- 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 +295 -47
- package/contracts/release-ledger.json +660 -0
- package/contracts/supported-surface.json +3 -3
- package/contracts/template-brand-contract.shared-commerce.v0.json +1 -1
- package/docs/brand-theme-bridge.md +2 -0
- package/docs/build-packet.md +67 -19
- package/docs/campaign-build-brief.md +48 -17
- package/docs/campaign-standardization-report.md +32 -0
- package/docs/design-source-package.md +7 -3
- package/docs/effects.md +10 -1
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +1 -1
- package/docs/polish-evidence.md +2 -0
- package/docs/progress-snapshots.md +5 -5
- package/docs/qa-and-test-orders.md +30 -3
- package/docs/readback.md +19 -2
- 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 +1 -0
- package/package.json +1 -1
- package/schemas/campaign-runtime-assembly-report.v0.schema.json +5 -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 +5 -3
- package/skills/next-campaigns-os/SKILL.md +10 -19
- package/skills/next-campaigns-os/references/session-intake.md +10 -6
- package/skills/next-campaigns-os-setup/SKILL.md +4 -3
- package/skills/next-campaigns-polish/SKILL.md +7 -5
- package/skills/next-campaigns-qa/SKILL.md +5 -3
- package/skills.json +10 -10
- package/src/brand-theme.mjs +21 -20
- package/src/brief-summary.mjs +275 -0
- package/src/build-brief.mjs +167 -15
- package/src/campaign-ecosystem.mjs +153 -31
- package/src/cli.mjs +182 -115
- package/src/contrast.mjs +649 -0
- package/src/design-source-publication.mjs +110 -3
- package/src/directory-lock.mjs +79 -7
- package/src/doctor/checks.mjs +141 -16
- package/src/doctor/inspect.mjs +13 -0
- package/src/doctor/next-step.mjs +15 -6
- package/src/input-currency.mjs +394 -0
- package/src/input-refresh.mjs +708 -0
- package/src/invocation.mjs +6 -2
- package/src/polish-browser.mjs +503 -201
- package/src/polish-deadline.mjs +7 -0
- package/src/polish-node.mjs +326 -46
- package/src/polish-readability.mjs +828 -0
- package/src/progress-node.mjs +18 -5
- package/src/qa-browser.mjs +211 -93
- package/src/qa-node.mjs +47 -7
- package/src/qc-accept.mjs +44 -8
- package/src/qc-check-registry.mjs +15 -7
- package/src/qc-results.mjs +326 -34
- package/src/readback.mjs +58 -10
- package/src/sdk-attribute-index.mjs +111 -23
- package/src/sdk-markup.mjs +49 -10
- package/src/sdk-repin.mjs +282 -0
- package/src/stage-ledger.mjs +235 -44
- package/src/stage-record.mjs +154 -30
- package/src/standardization-report.mjs +26 -1
- package/src/target-lock.mjs +9 -1
- package/src/tooling-setup.mjs +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "The downstream contract manifest. Everything listed here is SUPPORTED SURFACE: consumers (campaigns-agent, campaign-builder, the private ops repo, page-kit campaign repos) may depend on it, and changing it is a deliberate act — hashed entries require a surface_version bump in the same change (check-supported-surface.mjs --base, mirroring the skills.json bump gate), named entries must keep existing at their path, cli_commands must keep resolving in the CLI dispatch, package_exports must stay exported, and every entry must ship in the npm pack (files[] coverage). Anything NOT listed here — src/** internals, scripts/** checkers, examples/**, prompts/**, contracts/** other than this file and the entries named[] below (the orientation contract, the release ledger, and the consumer-facing orientation fixtures) — is implementation: consumers may read it for context but must not build on it, and it can change without notice. Rationale and the compatibility promise: docs/supported-surface.md.",
|
|
3
|
-
"surface_version": "1.
|
|
3
|
+
"surface_version": "1.54.0",
|
|
4
4
|
"package_exports": [
|
|
5
5
|
"./commercial-journey",
|
|
6
6
|
"./commercial-parity",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"sha256": "65bcd8b5f41f1e9ecfc36f3b8838cd4ed531bd74c5a318ee5147131f6c9b4a1c"
|
|
61
61
|
},
|
|
62
62
|
"schemas/campaign-runtime-assembly-report.v0.schema.json": {
|
|
63
|
-
"sha256": "
|
|
63
|
+
"sha256": "625a6f2ed6bee726f9cddad750e64a54a7e03744c777f5a70afe4a39afdd9848"
|
|
64
64
|
},
|
|
65
65
|
"schemas/campaign-runtime-build-context.v0.schema.json": {
|
|
66
66
|
"sha256": "f310c1b7844d9b1994864d15858ff0c6f17efc1e5285ba193d181bdb2dd4be2f"
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"sha256": "dfc9abed38d456969e47a21f606d308a03bdf036f3466f7d6e47a602747dacf4"
|
|
121
121
|
},
|
|
122
122
|
"contracts/effects.v1.json": {
|
|
123
|
-
"sha256": "
|
|
123
|
+
"sha256": "10a9020aaba5780e2ee2f3ddf6d81127047f6caec0de774c48540d9b664e0a6a"
|
|
124
124
|
},
|
|
125
125
|
"schemas/campaigns-os-effects.v1.schema.json": {
|
|
126
126
|
"sha256": "3eadd22169ab98bc7c2f682af2751267605170581182158d96be035b0cfe44dc"
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"asset_pin": {
|
|
57
57
|
"repo": "NextCommerceCo/campaign-cart-starter-templates",
|
|
58
|
-
"sha": "
|
|
58
|
+
"sha": "783329017c6994a94d11e46580c33bd052aabdb0",
|
|
59
59
|
"path": "src/<family>/assets/<asset>",
|
|
60
60
|
"note": "asset_sha256 is the sha256 of each shipped starter asset at this commit (the commerce-surface-catalog _synced_from_sha pin; the bytes are identical across every family). A served asset whose bytes hash to the shipped value is the untouched starter asset and counts as residue even though its markup names no method; different bytes mean it was edited in place. check-template-doctrine verifies these hashes against the pinned checkout."
|
|
61
61
|
},
|
|
@@ -94,6 +94,8 @@ used and reported with `theme.foreground.low_contrast`. With no declared CTA
|
|
|
94
94
|
foreground, the output is unchanged. Declarations inside CSS comments are not
|
|
95
95
|
read, so a commented-out token or rule never supplies a CTA or body text colour.
|
|
96
96
|
|
|
97
|
+
Contrast is computed with the same unrounded measurement Polish and QA use, so a declared colour at 4.495:1 now warns. The generator's colour choices are unchanged. Polish measures the rendered text at its real size and weight: a qualifying large label at 3:1 passes there, and smaller text produces a warning the operator can accept.
|
|
98
|
+
|
|
97
99
|
### Body text prefers the darkest declared text token
|
|
98
100
|
|
|
99
101
|
A declared text token is a `:root` custom property in the selected source whose
|
package/docs/build-packet.md
CHANGED
|
@@ -48,8 +48,10 @@ report writes, polish capture, progress, run closeout and QA compare that local
|
|
|
48
48
|
identity. Material spec hashes still bind the current revision; a changed ID or
|
|
49
49
|
content cannot reuse earlier proof. After a material revision, doctor and
|
|
50
50
|
`next` warn `spec.material_stale` (the spec no longer has the material hash
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
the Assembly Report binds, which QA refuses), and `next` routes to the build
|
|
52
|
+
and names the refresh: run `campaigns-os record spec --packet <packet>` to bind
|
|
53
|
+
the new content and keep stage history (see
|
|
54
|
+
[Refreshing after a CampaignSpec change](#refreshing-after-a-campaignspec-change)). Keep the spec, source, dependency
|
|
53
55
|
pins and canonical sidecars in Git. Use `readback` and `next` after a fresh
|
|
54
56
|
checkout; identity survives the move, but proof freshness is assessed again.
|
|
55
57
|
|
|
@@ -523,16 +525,13 @@ writing it would declare an analytics contract QA then blocks on. A derived
|
|
|
523
525
|
field the entry does not carry at all is listed under `not_in_target[]` and
|
|
524
526
|
left as it is.
|
|
525
527
|
|
|
526
|
-
|
|
528
|
+
`spec derive` writes the spec alone and no longer rebinds identity: the
|
|
527
529
|
Build Context's `spec.hash` / `spec.material_hash` and the Assembly Report's
|
|
528
|
-
`identity.spec_hash` / `identity.spec_material_hash`
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
is what keeps a derive-then-QA run conformant. A sidecar already carrying
|
|
534
|
-
another identity is left alone with a `spec.derive.identity_not_rebound`
|
|
535
|
-
warning naming `prepare-build`. A derived route change also warns
|
|
530
|
+
`identity.spec_hash` / `identity.spec_material_hash` keep the spec they were
|
|
531
|
+
bound to, so every build, Polish and QA record made against the earlier
|
|
532
|
+
content reads owed again. Run `record spec` after it (the result's `next`
|
|
533
|
+
names it) to bind the derived spec; QA's verdict and `bundle check` correlate
|
|
534
|
+
against the bound material hash. A derived route change also warns
|
|
536
535
|
`spec.derive.projection_stale`: the packet's page-kit projection
|
|
537
536
|
(`source_html.pages[].page_kit`) and the Build Context page map were prepared
|
|
538
537
|
from the old routes, and `prepare-build` (or `start`) regenerates them. A
|
|
@@ -552,8 +551,8 @@ the spec resolves to, which must lie inside the spec's own directory or the
|
|
|
552
551
|
target repo (`spec.derive.spec_escapes_boundary` otherwise, so a symlinked
|
|
553
552
|
`spec.local_path` cannot redirect the write); the retained doctor sidecar is
|
|
554
553
|
marked stale after a write, and a terminal build gets a
|
|
555
|
-
`spec.derive.build_stale` warning naming
|
|
556
|
-
|
|
554
|
+
`spec.derive.build_stale` warning naming `record spec`, after which `next`
|
|
555
|
+
routes back to the build. `--dry-run` prints the
|
|
557
556
|
same diff and writes nothing; unknown flags and a valued `--dry-run` are
|
|
558
557
|
rejected. Exit 2 with `spec.derive.*` error codes and nothing written when the
|
|
559
558
|
packet cannot be read, `spec.local_path` is absent or not a file, the spec is
|
|
@@ -828,12 +827,19 @@ Blockers (not waivable — the markup provably does not do what it says):
|
|
|
828
827
|
- `CHECKOUT_NOT_FORM` — `data-next-checkout` on an element that is not `<form>`.
|
|
829
828
|
- `WRONG_FIELD_NAME` — `data-next-checkout-field` with a value the SDK does not
|
|
830
829
|
map. The set is vendored from the SDK at a named tag
|
|
831
|
-
(`src/sdk-attribute-index.mjs`, currently v0.4.
|
|
830
|
+
(`src/sdk-attribute-index.mjs`, currently v0.4.41: `email`, `fname`, `lname`,
|
|
832
831
|
`phone`, `address1`, `address2`, `city`, `province`, `postal`, `country`,
|
|
833
832
|
`payment-method`, `accepts_marketing`, `cc-number`, `cc-month`, `cc-year`,
|
|
834
833
|
`exp-month`, `exp-year`, `cvv`, the legacy `card-*` spellings, and any
|
|
835
834
|
`billing-` prefixed name). The message names the SDK spelling for the usual
|
|
836
|
-
offenders (`firstName` → `fname`, `zip` → `postal`).
|
|
835
|
+
offenders (`firstName` → `fname`, `zip` → `postal`). Three names depend on
|
|
836
|
+
the page's SDK version: `first_name` and `last_name` are mapped from v0.4.39
|
|
837
|
+
and `phone_number` from v0.4.41. The version is the page's own exact loader
|
|
838
|
+
pin, else the campaign's `sdk_version` in `_data/campaigns.json`. Below the
|
|
839
|
+
name's version, or when neither gives an exact released version, the name
|
|
840
|
+
blocks, and the message names `fname`, `lname` or `phone`, which every
|
|
841
|
+
version maps. Each finding carries `sdk_version` and `sdk_version_source`
|
|
842
|
+
(`loader` or `campaigns_json`) in its detail.
|
|
837
843
|
- `MISSING_SELECTOR_ID_MATCH` — an `add-to-cart` button whose
|
|
838
844
|
`data-next-selector-id` names no selector on the page (an element that is a
|
|
839
845
|
bundle, package, cart or upsell selector; another element echoing the id
|
|
@@ -1058,8 +1064,13 @@ full.
|
|
|
1058
1064
|
and auto-discover `campaign-build-brief.yaml`, `.yml`, or `.json` from the
|
|
1059
1065
|
source root or target repo. When none is present, Campaigns OS creates a guided
|
|
1060
1066
|
draft at `.campaign-runtime/input/campaign-build-brief.normalized.json`.
|
|
1061
|
-
|
|
1062
|
-
prepared
|
|
1067
|
+
A brief file is guided unless it sets `"brief_mode": "prepared"` (a campaign
|
|
1068
|
+
whose Assembly Report already records a prepared brief stays prepared). Save
|
|
1069
|
+
answers later with `record brief`, which keeps stage evidence unless the brief's
|
|
1070
|
+
material content changes; a presentation change makes build, Polish and QA
|
|
1071
|
+
owed again, and a `qa_policy` change makes QA alone owed again. See
|
|
1072
|
+
[Campaign Build Brief](./campaign-build-brief.md) for the schema, the mode
|
|
1073
|
+
rule and the stage map.
|
|
1063
1074
|
|
|
1064
1075
|
`start` / `prepare-build` also prepares the normalized Design Source Package at
|
|
1065
1076
|
`.campaign-runtime/input/design-source-package.json`. When that path is absent,
|
|
@@ -1681,6 +1692,43 @@ records it (`campaigns-os record setup|build|polish`; deploy and QA record their
|
|
|
1681
1692
|
own stages) → calls `next` again → repeat until stage="done"
|
|
1682
1693
|
```
|
|
1683
1694
|
|
|
1695
|
+
### Refreshing after a CampaignSpec change
|
|
1696
|
+
|
|
1697
|
+
When the CampaignSpec's material content changes, every build, Polish and QA record made against the earlier content stops counting as current, in local-spec, saved-Map and gateway packets alike. For a saved Map or gateway packet, Campaigns OS compares the copy intake fetched; it does not check whether that copy is the latest remote revision, and reports the remote as unconfirmed. Doctor warns `spec.material_stale`, and `next`, progress, readback, the Assembly Report's own status and the QA gate show the stages that are owed again; `next` routes to the first one. Run `campaigns-os record spec --packet <packet>` to bind the new content: it updates the build context and report identity, re-reads the brief file last saved (by intake or `record brief`), keeps waivers, warning accepts, the applied theme and deploy settings, and moves each superseded stage record into that stage's `history`, which is never used as current proof. `spec derive` no longer rebinds identity; run `record spec` after it. A change to the set, order or routes of active pages still needs intake. Re-recording an unchanged build after a spec change keeps the build owed, because the change has not reached the pages, unless the operator records a reason with `--deviation-reason`; this holds even when `record spec` was not run, and that `record build` also marks Polish and QA owed again. Polish stays owed until a new `polish capture`, and QA must produce a new verdict against the current content. Polish measurements of byte-identical output keep their values, but the Polish stage is owed again. These checks are tamper evidence, not proof: a hand-written record that copies the current values, or a packet pointed at an older copy of the spec, is not detected.
|
|
1698
|
+
|
|
1699
|
+
The CampaignSpec's material content is the whole spec except `spec_identity`,
|
|
1700
|
+
`slug`, `map_id` and `saved_at`; a page `label` edit is material. A spec
|
|
1701
|
+
change affects the stages as follows:
|
|
1702
|
+
|
|
1703
|
+
| Stage | After a spec material change |
|
|
1704
|
+
|---|---|
|
|
1705
|
+
| `prepare_build` | unchanged (`record spec` refuses a page-scope change, which needs intake) |
|
|
1706
|
+
| `doctor` | recomputed on every read |
|
|
1707
|
+
| `setup` | kept |
|
|
1708
|
+
| `assembly` | owed: `required`, `required_by: "spec"`, `required_for: ["polish", "qa"]` |
|
|
1709
|
+
| `polish` | owed: `required`, `required_by: "spec"`, `required_for: ["qa"]` |
|
|
1710
|
+
| `deploy` | kept |
|
|
1711
|
+
| `qa` | owed: `required`, `required_by: "spec"`, `required_for: []` |
|
|
1712
|
+
|
|
1713
|
+
`record spec` refuses, writing nothing, in this order: `spec_unreadable` (the
|
|
1714
|
+
spec does not parse), `spec_identity_changed` (its map id or local spec id
|
|
1715
|
+
names another campaign than the packet or the report), `page_scope_changed`,
|
|
1716
|
+
then, for the brief file last saved (by intake or `record brief`), `brief_too_large`,
|
|
1717
|
+
`brief_source_is_package_artifact` and `brief_file_missing` (the recorded
|
|
1718
|
+
path is missing or is not a readable regular file: a directory, a pipe or
|
|
1719
|
+
socket, a symlink to nothing, or a file without read permission). When it must
|
|
1720
|
+
re-derive the brief, it also refuses `brief_inputs_unavailable` (the Build
|
|
1721
|
+
Context lacks an intake product re-deriving the brief needs). It reads
|
|
1722
|
+
`unchanged` and writes nothing when the bound material equals the spec and no
|
|
1723
|
+
stage's spec or brief stamp differs from the current content; a stage that
|
|
1724
|
+
does not record which content it was made against is named in a
|
|
1725
|
+
`binding_unknown` notice instead. Otherwise it refreshes, re-deriving the
|
|
1726
|
+
normalized brief from the brief file last saved, so an edit made to the
|
|
1727
|
+
normalized brief itself is replaced by what its source gives. A stage whose spec
|
|
1728
|
+
stamp already equals the new content (a build recorded after the edit) is not
|
|
1729
|
+
demoted. `record spec` does not write the Build Packet. `--dry-run` runs every
|
|
1730
|
+
check and writes nothing.
|
|
1731
|
+
|
|
1684
1732
|
### Recording stage completion
|
|
1685
1733
|
|
|
1686
1734
|
Setup, build and polish completion is recorded with one command each, never by
|
|
@@ -1689,10 +1737,10 @@ hand-editing `.campaign-runtime/` JSON:
|
|
|
1689
1737
|
| Command | Writes | Refused (nothing written) when |
|
|
1690
1738
|
|---|---|---|
|
|
1691
1739
|
| `campaigns-os record setup --packet <p>` | Build Context `scaffold.required=false` (`handoff_skill` next-campaigns-build) and `stages.setup` completed | the campaign output directory (`assembly.output_dir`) does not exist, or there is no Build Context or Assembly Report |
|
|
1692
|
-
| `campaigns-os record build --packet <p> [--build-environment <development\|production>]` | `stages.assembly` completed with `build_fingerprint` = doctor's `derived.build_output_fingerprint.value`, `source_package_material_fingerprint` = the report's Design Source Package material fingerprint when present, `evidence.build_environment` = the `--build-environment` value when given (kept from the last record otherwise), and `stages.polish` reset to `required` (`required_by` build, `required_for` qa) unless its evidence is bound to this exact output | doctor cannot compute the fingerprint (no `_site/<public_route_slug>/`), setup is still required, or `stages.setup` is not terminal |
|
|
1740
|
+
| `campaigns-os record build --packet <p> [--build-environment <development\|production>]` | `stages.assembly` completed with `build_fingerprint` = doctor's `derived.build_output_fingerprint.value`, `source_package_material_fingerprint` = the report's Design Source Package material fingerprint when present, `evidence.build_environment` = the `--build-environment` value when given (kept from the last record otherwise), and `stages.polish` reset to `required` (`required_by` build, `required_for` qa) unless its evidence is bound to this exact output; a completed `stages.deploy` whose `source_build_fingerprint` names other output is reset to `required` the same way, without the old probe's `outputs` and `evidence`, and its prior record is kept in `stages.deploy.history` (a deploy with no `source_build_fingerprint`, recorded before deploy stamped it, is kept) | doctor cannot compute the fingerprint (no `_site/<public_route_slug>/`), setup is still required, or `stages.setup` is not terminal |
|
|
1693
1741
|
| `campaigns-os record polish --packet <p> --evidence <file>` | `stages.polish` from the file (`docs/polish-evidence.md` §7: completed, blocked or skipped), bound to doctor's current fingerprint; `report.theme.repair_loop_defect` when the file sets it | build is not recorded for the current output, the file has a shape error (named by field), or, for a completed status, the polish gate doctor evaluates would not pass on the result |
|
|
1694
1742
|
| `campaigns-os record theme --packet <p>` | `report.theme`: status `applied`, `load_order` `after-next-core`, `css_path`, `commerce_pages` and per-page evidence read from each built commerce page's stylesheet links; any earlier theme waiver is cleared | build is not recorded for the current output, the campaign ships no commerce pages, a built commerce page that loads `next-core.css` does not load `brand-theme.css` (or `checkout-brand.css`) after it or links one missing from the built output, or no built commerce page loads `next-core.css` |
|
|
1695
|
-
| `campaigns-os record deploy --packet <p> --base-url <url>` | the packet's `deploy.preview_url` and `stages.deploy` completed with the URL in `outputs
|
|
1743
|
+
| `campaigns-os record deploy --packet <p> --base-url <url>` | the packet's `deploy.preview_url` and `stages.deploy` completed with the URL in `outputs`, one evidence line per built page that answered (each page is requested under the URL first), and `source_build_fingerprint` = the recorded `stages.assembly.build_fingerprint` it probed, so a later `record build` of different output makes deploy owed again and `next` routes back to `record deploy` before QA; a byte-identical rebuild keeps it current | the packet is not `local-serve`, the URL is not a loopback origin naming the campaign's route root, a built page does not answer 2xx, polish is not recorded, the built output changed since build was recorded, or the theme gate is blocked |
|
|
1696
1744
|
|
|
1697
1745
|
Each command also refuses a stage `next` has not reached: while doctor's
|
|
1698
1746
|
prepare-build gate is set (`next` answers prepare-build) or while an earlier
|
|
@@ -35,7 +35,7 @@ The Build Packet, Build Context, and Assembly Report all reference that normaliz
|
|
|
35
35
|
|
|
36
36
|
## Modes
|
|
37
37
|
|
|
38
|
-
Prepared mode is for veteran users. A complete brief should let the build proceed without business questions. If a
|
|
38
|
+
Prepared mode is for veteran users, and a brief file selects it with `"brief_mode": "prepared"` (see [Brief mode](#brief-mode)). A complete brief should let the build proceed without business questions. If a prepared brief is incomplete or contradictory, doctor blocks with `build_brief.*` errors.
|
|
39
39
|
|
|
40
40
|
Guided mode is for new or partial inputs. Campaigns OS drafts a brief from CampaignSpec, page mappings, template family, source assets, and available runtime hints. It records only high-impact unresolved questions as warnings so existing builds still run while the business uncertainty is visible.
|
|
41
41
|
|
|
@@ -56,27 +56,58 @@ The CLI avoids SDK/page-kit jargon in questions. The implementation can resolve
|
|
|
56
56
|
|
|
57
57
|
Question 5 is asked only when the CampaignSpec maps a surface that fills the template's promo placeholders: a `funnels[].promo_codes` roster, or a checkout page's enabled `exit_intent` or `promo_code_input`. It never asks for approval of the source design's own promo, proof or urgency copy, which is built as designed (see below). Without such a surface the guided draft sets `header_claim_source` and `timer_label` to `"none"`: the template's promo placeholders are removed.
|
|
58
58
|
|
|
59
|
-
##
|
|
59
|
+
## Saving Answers
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Save brief answers with `campaigns-os record brief --packet <packet>`. Copy the normalized draft to `campaign-build-brief.json` in the target repository, set the fields named in the open questions, and run `record brief`. You do not need to re-run `start` or `prepare-build`, and no stage evidence is cleared unless the saved answers change the brief's material content. `record brief` refuses a path that points at one of the files Campaigns OS writes itself, such as the normalized brief.
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
2. Set the fields that close each open question:
|
|
63
|
+
A brief is prepared, where every open question blocks, only when the file sets `"brief_mode": "prepared"`. A brief file without `brief_mode` is guided: questions you leave open remain warnings. One exception keeps existing campaigns as they were built: when the campaign's Assembly Report already records a prepared brief, a file without `brief_mode` stays prepared. Set `"brief_mode": "guided"` to make it guided.
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|---|---|
|
|
68
|
-
| `page_design_authority` | `design_authority.<page_id>.source` for each page named in the question |
|
|
69
|
-
| `brand_palette_cta` | `brand.commerce_palette_source`, `brand.cta_style` |
|
|
70
|
-
| `variant_media_rules` | `media.sold_variants`, `media.allow_other_variant_colors` |
|
|
71
|
-
| `bundle_pricing_presentation` | `offer_presentation.bundle_cards.primary_price` |
|
|
72
|
-
| `promo_urgency_copy` | `promo_urgency.header_claim_source` (`campaign_offers` or `none`), `promo_urgency.timer_label` (the template timer's label, or `none`) |
|
|
73
|
-
| `payment_methods_trust` | `commerce_surfaces.payment_methods_allowed` |
|
|
74
|
-
| `canonical_display_names` | `canonical_display.product_name_source` |
|
|
75
|
-
| `regulated_claims` | one of `campaign_intent.compliance.approved_benefit_language`, `.forbidden_claims`, `.approved_claims`, `.copy_rules` |
|
|
65
|
+
Saving a brief whose content is unchanged keeps all stage evidence and statuses. Reformatting, reordering keys, writing a default value explicitly, or changing only `_meta` also keeps all stage evidence and statuses; the save may rewrite the brief files. When the presentation sections change (`campaign_intent`, `design_authority`, `brand`, `media`, `offer_presentation`, `promo_urgency`, `commerce_surfaces`, `canonical_display`, `template_residue_policy`, or any section you add), build, Polish and QA are owed again. When only `qa_policy` changes, QA alone is owed again. Setup and deploy records are kept. Each affected stage records when its inputs changed. Each replaced record is kept under the stage's `history`; the last five superseded records per stage are kept, and older ones are dropped. History is display only and is never used as current proof.
|
|
76
66
|
|
|
77
|
-
|
|
67
|
+
Recording build again over output that has not changed since the brief or CampaignSpec changed does not make the build current: `next` keeps asking for a build, because the change has not reached the pages. If the operator decides the change needs no change to the built pages, record build with `--deviation-reason "<the operator's reason>"`; the build is then recorded with a warning, and the reason is shown in `next` and the QC handoff. `--deviation-reason` is a general flag any caller can pass: Campaigns OS records the reason, but cannot tell whether the operator chose it. A CampaignSpec change that was never saved with `record spec` is recorded by the next `record build`, `record polish` or QA run, with the same effect; `record build` then also marks Polish and QA owed again. An edit to the brief file is not read until it is saved with `record brief`: until then the build reads the saved brief, and doctor warns `build_brief.input_unsaved`. The same applies after `prepare-build --force`, which now keeps stage history, archives the build, Polish and QA records it clears, and keeps the record that the inputs changed. Recording Polish again does not make it current until a new `polish capture` runs after the change. Recording Polish as skipped, with a skip reason, while it is owed is the operator's decision to skip it: the input change no longer makes Polish owed. The Polish gate still passes only a completed Polish, so `next` stays at Polish until one is recorded (see `docs/polish-evidence.md`).
|
|
78
68
|
|
|
79
|
-
|
|
69
|
+
A build, Polish or QA record that does not say which brief and CampaignSpec content it was made against counts as unconfirmed, not current. Records made by earlier releases are unconfirmed, so after upgrading, `next` routes back to build once; record build and Polish again and run QA again. These checks are tamper evidence, not proof: a record written by hand that copies the current values or removes the record of an input change, or a packet pointed at an older copy of the spec or brief, is not detected.
|
|
70
|
+
|
|
71
|
+
The fields that close each open question (doctor's `build_brief.guided_questions` warning names the open questions and their fields):
|
|
72
|
+
|
|
73
|
+
| Question | Fields |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `page_design_authority` | `design_authority.<page_id>.source` for each page named in the question |
|
|
76
|
+
| `brand_palette_cta` | `brand.commerce_palette_source`, `brand.cta_style` |
|
|
77
|
+
| `variant_media_rules` | `media.sold_variants`, `media.allow_other_variant_colors` |
|
|
78
|
+
| `bundle_pricing_presentation` | `offer_presentation.bundle_cards.primary_price` |
|
|
79
|
+
| `promo_urgency_copy` | `promo_urgency.header_claim_source` (`campaign_offers` or `none`), `promo_urgency.timer_label` (the template timer's label, or `none`) |
|
|
80
|
+
| `payment_methods_trust` | `commerce_surfaces.payment_methods_allowed` |
|
|
81
|
+
| `canonical_display_names` | `canonical_display.product_name_source` |
|
|
82
|
+
| `regulated_claims` | one of `campaign_intent.compliance.approved_benefit_language`, `.forbidden_claims`, `.approved_claims`, `.copy_rules` |
|
|
83
|
+
|
|
84
|
+
### Brief mode
|
|
85
|
+
|
|
86
|
+
The mode is decided in this order:
|
|
87
|
+
|
|
88
|
+
1. `"brief_mode": "prepared"` in the file: prepared.
|
|
89
|
+
2. `"brief_mode": "guided"` in the file: guided.
|
|
90
|
+
3. No `brief_mode`, and the campaign's Assembly Report on disk records a prepared brief: prepared.
|
|
91
|
+
4. No `brief_mode` otherwise, including the generated draft and every new hand-written file: guided.
|
|
92
|
+
5. Any other `brief_mode` value is the error `build_brief.brief_mode`, and the brief reads `invalid`.
|
|
93
|
+
|
|
94
|
+
The file's own `_meta` (including a copied draft's `_meta.mode`) is never read for the mode; the normalized brief records how the mode was decided in `_meta.mode_source` (`field`, `legacy_report` or `generated`). Input errors block in both modes. In guided mode, open questions and evaluation gates stay warnings.
|
|
95
|
+
|
|
96
|
+
### What a brief change makes owed
|
|
97
|
+
|
|
98
|
+
The brief's material content is the normalized brief without `_meta`, `status`, `questions`, `gates`, `confidence`, `schema_version`, `brief_mode` and `qa_policy.enforcement`, in two parts: `qa_policy`, and every other section (presentation).
|
|
99
|
+
|
|
100
|
+
| Stage | Presentation changed | `qa_policy` changed |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `prepare_build` | brief blockers re-derived; other blockers kept | re-derived |
|
|
103
|
+
| `doctor` | recomputed on every read | recomputed on every read |
|
|
104
|
+
| `setup` | kept | kept |
|
|
105
|
+
| `assembly` | owed (`required`, `required_by: "brief"`) | kept |
|
|
106
|
+
| `polish` | owed (`required`, `required_by: "brief"`) | kept |
|
|
107
|
+
| `deploy` | kept | kept |
|
|
108
|
+
| `qa` | owed (`required`, `required_by: "brief"`) | owed |
|
|
109
|
+
|
|
110
|
+
Only completed records are made owed. A skipped stage stays skipped.
|
|
80
111
|
|
|
81
112
|
## Risky Defaults
|
|
82
113
|
|
|
@@ -170,12 +170,34 @@ version discovery), `checkout_fields` (every
|
|
|
170
170
|
script evidence, and `proof_state`), `runtime_contract`, `findings`, and
|
|
171
171
|
`remediation`.
|
|
172
172
|
|
|
173
|
+
Three names in the field contract depend on the SDK version
|
|
174
|
+
(`version_gated_aliases`): `first_name` and `last_name` from 0.4.39 and
|
|
175
|
+
`phone_number` from 0.4.41. Each is `supported` only when the binding's SDK
|
|
176
|
+
version is an exact release at or after that version; otherwise it is
|
|
177
|
+
classified as on an earlier SDK (`first_name` and `last_name` as stale aliases,
|
|
178
|
+
`phone_number` as unknown). Each binding records the `sdk_version` it was
|
|
179
|
+
judged against and its `sdk_version_source`: `loader` (the file's own exact
|
|
180
|
+
loader pin), `campaign` (for a file with no loader of its own, the lowest of
|
|
181
|
+
the root's exact loader pins and its bundled dependency's floor: an exact pin,
|
|
182
|
+
or `^`, `~` or `>=` on a released version; any other range leaves the version
|
|
183
|
+
unknown), or
|
|
184
|
+
`campaigns_json` (a Page Kit campaign's `sdk_version`; a shared file takes the
|
|
185
|
+
lowest across campaigns). An unpinned loader or a missing pin leaves
|
|
186
|
+
`sdk_version` null, judged as an earlier SDK, because `fname`, `lname` and
|
|
187
|
+
`phone` work on every version.
|
|
188
|
+
|
|
173
189
|
`sdk_loader.references` records pinned and unpinned loader refs alike
|
|
174
190
|
(`version` is null for `@latest`/branch/commit pins, which raise
|
|
175
191
|
`version.sdk_loader_unpinned` instead of a policy evaluation). Only URLs that
|
|
176
192
|
point at a loader/dist artifact count; incidental `campaign-cart@x.y.z`
|
|
177
193
|
strings elsewhere in source are ignored.
|
|
178
194
|
|
|
195
|
+
In a static repo (no `_data/campaigns.json`), `campaigns-os sdk repin --target
|
|
196
|
+
<repo> [--target-sdk <x.y.z>] [--apply]` rewrites the semver-pinned references
|
|
197
|
+
in this list that are below the target version, changing only the version
|
|
198
|
+
segment of each URL. It previews by default and leaves the unpinned refs
|
|
199
|
+
alone. See `sdk-storage-compatibility.md`.
|
|
200
|
+
|
|
179
201
|
`payment.proof_state` is one of `runtime_proof_required` (custom-control
|
|
180
202
|
evidence found), `undetermined` (radios exist; static scanning cannot exclude
|
|
181
203
|
externally-styled custom controls), or `not_applicable` (no `payment_method`
|
|
@@ -216,6 +238,16 @@ Page Kit root that inlines them gets the same `checkout_fields` block and the
|
|
|
216
238
|
same `checkout.unsupported_field_binding` / `checkout.unknown_field_binding`
|
|
217
239
|
findings as an application root.
|
|
218
240
|
|
|
241
|
+
A bundled dependency is judged by its floor, the lowest version its
|
|
242
|
+
`package.json` spec lets npm install, recorded as
|
|
243
|
+
`sdk_loader.bundled_dependency.floor_version`. Only an exact pin (`0.4.30`,
|
|
244
|
+
`=0.4.30`, `v0.4.30`) or one `^`, `~` or `>=` range on a released version has
|
|
245
|
+
a certain floor; `^0.4.25` is evaluated as `0.4.25`. Any other spec (`<0.4.39`,
|
|
246
|
+
an `||`, hyphen or x-range, a tag) has none, so it gets no evaluation and
|
|
247
|
+
raises the warning `version.sdk_dependency_floor_unknown` instead; a
|
|
248
|
+
prerelease pin raises `version.sdk_prerelease_pin`. `resolved_version` stays
|
|
249
|
+
the version text extracted from the spec, for reference.
|
|
250
|
+
|
|
219
251
|
Both are also injectable from the CLI: pass
|
|
220
252
|
`--sdk-support-policy <path-to-json>` and/or `--field-contract <path-to-json>`
|
|
221
253
|
to `standardize`. Each file is read and JSON-parsed
|
|
@@ -396,9 +396,13 @@ a minute. The lock directory and its owner record appear together, so a lock
|
|
|
396
396
|
left by a process that died is recovered automatically, and a lock directory
|
|
397
397
|
without an owner record (only an older release leaves one) is never taken
|
|
398
398
|
over: the command refuses it after about a second and names it. Confirm no
|
|
399
|
-
campaigns-os process is working on the target, then remove it.
|
|
400
|
-
|
|
401
|
-
|
|
399
|
+
campaigns-os process is working on the target, then remove it. Anything else
|
|
400
|
+
at the lock path (a file, a symbolic link, or a directory with no owner record
|
|
401
|
+
that holds other entries) is not a lock: once the wait ends the command names
|
|
402
|
+
what is there and never removes it. The holder of the lock removes staging and
|
|
403
|
+
released directories beside it whose recorded owner process no longer exists.
|
|
404
|
+
Do not run an older Campaigns OS release against the same target at the same
|
|
405
|
+
time. A waiver's `--dry-run` preview takes no lock.
|
|
402
406
|
|
|
403
407
|
Before writing any output, `prepare-build` also requires distinct paths for the
|
|
404
408
|
Build Packet, Build Context, Assembly Report, Doctor output, normalized Build
|
package/docs/effects.md
CHANGED
|
@@ -42,6 +42,7 @@ CLI exempts from lifecycle capture (the converse does not hold: `demo` and the
|
|
|
42
42
|
`doctor` inspection and `doctor --no-write` skip it and write nothing but may
|
|
43
43
|
send the one live campaign read): `help`, `readback`,
|
|
44
44
|
`run status`, `doctor --no-live-refs` inspection, `sdk storage-check`,
|
|
45
|
+
the `sdk repin` preview (without `--apply`),
|
|
45
46
|
`tooling diagnose`, a refused invocation, `run-record --no-write`, and every
|
|
46
47
|
`--dry-run` form on the commands that implement the flag. Everything else
|
|
47
48
|
appends an entry when a journal is selected — an active run session,
|
|
@@ -127,6 +128,14 @@ itself carries QA's own read).
|
|
|
127
128
|
|
|
128
129
|
**Accepting a warning.** `campaigns-os checkpoint accept` records an operator's decision about a warning that a Campaigns OS check measured. It does not change readiness, does not remove the warning, does not change the status `next` reports, and cannot be used on review, unexercised, or excluded results. The warning must already be on record (in the doctor snapshot `next` writes, the Polish capture, or the full QA verdict) before it can be accepted. The accept is bound to the exact measured state and lapses when that state changes, or when its evidence is stale or cannot be reproduced. An optional expiry or review condition adds a second bound. Run it only with the operator's explicit decision, made after the warning was shown to them. A plausible name is not authorization. The command refuses placeholder and automation identities. Results are re-derived from the package's raw captures on every read, and a result whose capture is missing or does not match is listed as unexercised. These checks are tamper evidence, not proof of authorship: a hand-written record that copies every value and recomputes the checksum, or a hand-written result written together with a matching capture, is not detected.
|
|
129
130
|
|
|
131
|
+
### `record brief` and `record spec`
|
|
132
|
+
|
|
133
|
+
**Saving brief answers.** `campaigns-os record brief --packet <packet>` saves Campaign Build Brief answers without re-running `start` or `prepare-build`. It writes the normalized brief under `{target}/.campaign-runtime/input/`, the Build Packet, the build context, the Assembly Report and the doctor-sidecar stale stamp, and sends nothing. It refuses a path that points at one of the files Campaigns OS writes itself, such as the normalized brief. Saving a brief whose content is unchanged keeps all stage evidence and statuses. Reformatting, reordering keys, writing a default value explicitly, or changing only `_meta` also keeps all stage evidence and statuses; the save may rewrite the brief files. When the presentation sections change, build, Polish and QA are owed again. When only `qa_policy` changes, QA alone is owed again. Setup and deploy records are kept. Each replaced record is kept under the stage's `history`; the last five superseded records per stage are kept, and history is never used as current proof.
|
|
134
|
+
|
|
135
|
+
**Binding a CampaignSpec change.** `campaigns-os record spec --packet <packet>` binds the new CampaignSpec content: it updates the build context and report identity, re-reads the brief file last saved (by intake or `record brief`), keeps waivers, warning accepts, the applied theme and deploy settings, and moves each superseded stage record into that stage's `history`. It writes the build context, the Assembly Report, the normalized brief and the doctor-sidecar stale stamp; it does not write the Build Packet, and it sends nothing. `spec derive` no longer rebinds identity; run `record spec` after it. A change to the set, order or routes of active pages still needs intake.
|
|
136
|
+
|
|
137
|
+
Both rows are tier C, because a change makes recorded stages owed again, and each `--dry-run` form is tier none: it writes nothing and appends no lifecycle entry. A CampaignSpec change that was never saved with `record spec` is recorded by the next `record build`, `record polish` or QA run, with the same effect; `record build` then also marks Polish and QA owed again. An edit to the brief file is not read until it is saved with `record brief`: until then the build reads the saved brief, and doctor warns `build_brief.input_unsaved`. Intake now also refuses, without `--force`, to discard recorded waivers, warning accepts, stage history, an applied theme, or a recorded deploy target, preview URL, production URL, order-path depth or allowed-domains confirmation; with `--force`, it keeps stage history, archives the build, Polish and QA records it clears, and keeps the record that the inputs changed.
|
|
138
|
+
|
|
130
139
|
## How to read a row
|
|
131
140
|
|
|
132
141
|
```jsonc
|
|
@@ -152,7 +161,7 @@ itself carries QA's own read).
|
|
|
152
161
|
|
|
153
162
|
There is **one row per command and per effect-changing flag combination**. The
|
|
154
163
|
flags that change what the invocation does to the world are listed once, in
|
|
155
|
-
`vocabulary.effect_changing_flags`: `--browser`, `--built`, `--dry-run`,
|
|
164
|
+
`vocabulary.effect_changing_flags`: `--apply`, `--browser`, `--built`, `--dry-run`,
|
|
156
165
|
`--emit-packet`, `--example`, `--force`, `--from-store`, `--list`,
|
|
157
166
|
`--no-live-refs`, `--no-post-verdict`, `--no-probe`, `--no-remit`, `--no-run-session`,
|
|
158
167
|
`--no-write`, `--republish`, `--test-order`, `--write`, `--write-map`. Flags
|
package/docs/local-setup.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
For a new campaign, create an empty working folder and run this from it:
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npm init -y && npm install --save-exact next-campaign-page-kit@0.2.0 && npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.
|
|
6
|
+
npm init -y && npm install --save-exact next-campaign-page-kit@0.2.0 && npm install --save-dev --save-exact @nextcommerce/campaigns-os@1.54.0 && npx --no-install campaigns-os tooling setup --target . --platform claude
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
`npm init -y` gives the folder its own `package.json`. Without one, npm
|
|
@@ -26,7 +26,7 @@ Ledger schema id: `campaigns-os-release-ledger/v1`
|
|
|
26
26
|
Change policy version: `1.0.0`
|
|
27
27
|
Reason-code vocabulary version: `1.0.0`
|
|
28
28
|
Limits version: `1.0.0`
|
|
29
|
-
Supported surface at generation time: `1.
|
|
29
|
+
Supported surface at generation time: `1.54.0`
|
|
30
30
|
|
|
31
31
|
## Forward compatibility
|
|
32
32
|
|
package/docs/polish-evidence.md
CHANGED
|
@@ -119,6 +119,8 @@ It warns when a video, or an image over 500,000 transfer bytes, comes from the p
|
|
|
119
119
|
|
|
120
120
|
The size check covers `<img>` elements in the page, including those inside open and closed shadow roots; images inside an iframe, `<input type=image>`, `<object>`, SVG `<image>` and CSS background images are not checked for size, and only their transfer bytes are weighed.
|
|
121
121
|
|
|
122
|
+
**Readability.** `polish capture` also measures text contrast on every page under `_site/<slug>/` (up to 128 pages per run), at desktop (1440×1200) and mobile (390×844) widths, device pixel ratio 1, including campaigns whose pages are all template stock: the Polish route plan now covers the mapped pages plus every built page, while the page-load capture keeps its own page set. On a campaign with no mapped pages, `polish capture` measures readability, prints the page-load message as a warning, and exits successfully. The text it reads: purchase, accept and decline controls; submit buttons and submit-type inputs; SDK action controls; bundle cards (names, prices, badges and the card selected at load); order bumps; prices; checkout labels and placeholder hints; and other body text. Ratios follow WCAG 2.x and are compared unrounded: 4.5:1 for normal text, 3:1 for large text (at least 24 CSS px, or at least 18.66 CSS px at weight 700 or more; semibold does not qualify). Colours in any CSS Color 4 form the browser reports are converted to sRGB; values outside sRGB are clipped and marked. Backgrounds are composited through transparent layers. SVG text is measured with its fill colour, and placeholder hints with their own colour, size and weight. Gradients, background images, pseudo-element backgrounds, a `::first-line` or `::first-letter` style that changes the text's colour, background or opacity, overlapping positioned layers or images (and the shapes of an SVG drawing under its text), text clipped to a background, colours that cannot be read (including a text fill colour, and an SVG fill that is not a plain colour or is combined with a stroke), and controls marked disabled only by the SDK's `next-disabled` class produce a review result with a reason and a crop; so do opacity, filters, blend modes and masks on the text or on any element around it. `clip-path` is ignored. Disabled controls (`disabled` or `aria-disabled="true"`) are listed as excluded (inactive control) and are never accepted; `readonly` is not disabled. Polish measures the states present when the page loads — default, selected, active, and open panels — without clicking, scrolling or typing. Text that has not rendered, controls still loading, controls hidden at load, text supplied by CSS `content`, selection states and collapsed panels not present at load, text inside closed shadow roots or cross-origin frames, pages measured before the SDK, stylesheets or fonts were ready, and pages not captured are listed as unexercised. Overlapping layers are detected by box intersection: a layer the page paints over text in another way is not detected. A ratio under the requirement is a warning. Warnings never block Polish or change readiness. The probe reads the page without changing it, and adds at most about five minutes of work per run; pages it could not reach in that time are listed as unexercised. The stored measurements are re-derived on every read, and the page set, widths and campaign are checked against the current build; but the colours the browser reported are tamper-evident only, like other Polish captures. Readability and media weight results read as stale when the built output differs from the recorded build, even before a new `record build`.
|
|
123
|
+
|
|
122
124
|
### Collector response records (the producer's wire form)
|
|
123
125
|
|
|
124
126
|
Inside the producer, the browser adapter's CDP collector emits one
|
|
@@ -18,8 +18,7 @@ blocked QA keeps it open. A progress delivery cannot close a run.
|
|
|
18
18
|
|
|
19
19
|
The strict v0 object carries its schema, package version, observation timestamp,
|
|
20
20
|
producer (`next` or `qa`), opaque progress stream and sequence, prior snapshot ID,
|
|
21
|
-
content digest, Map ID and separate revision/spec/build hashes. Six independent
|
|
22
|
-
stage records retain observed status and explicit build binding. Continuation
|
|
21
|
+
content digest, Map ID and separate revision/spec/build hashes. Six independent stage records carry each stage's effective status and explicit build binding: the status the Assembly Report records, except that a stage made owed again by a brief or CampaignSpec change reads `required`, and a stage whose inputs cannot be confirmed reads `unknown`. Continuation
|
|
23
22
|
retains the canonical stage, blocked/divergent flags, fixed action IDs and gate
|
|
24
23
|
states. Preview carries only presence and a hash of the URL. An optional QA block
|
|
25
24
|
retains the exact verdict ID, disposition (including `ready_with_exceptions`),
|
|
@@ -28,8 +27,7 @@ retained verdict; recovery uses `qa publish`, without repeating an order.
|
|
|
28
27
|
|
|
29
28
|
No commands, prompts, free text, content, local paths, URLs, query strings,
|
|
30
29
|
credentials, order values or customer payloads enter the wire. Unknown vocabulary
|
|
31
|
-
becomes a fixed `unknown` marker and cannot imply a passing continuation. A
|
|
32
|
-
stage's observed `completed` status is a ledger claim, not independent verification.
|
|
30
|
+
becomes a fixed `unknown` marker and cannot imply a passing continuation. A stage's `completed` status is a ledger claim, checked only against the brief and CampaignSpec content it records; it is not independent verification.
|
|
33
31
|
Only matching fingerprints carry `matching`; absent or unverifiable binding is
|
|
34
32
|
`unconfirmed`. A current local output hash never proves a deployment is current.
|
|
35
33
|
|
|
@@ -67,7 +65,9 @@ rename; a live process is never evicted. A lock directory with no owner record
|
|
|
67
65
|
a second. If one is found, or if recovery itself is interrupted, capture fails
|
|
68
66
|
closed and the warning names the affected `.allocation-lock` directory: stop
|
|
69
67
|
all Campaigns OS writers for that target, then remove that directory before
|
|
70
|
-
retrying `next`.
|
|
68
|
+
retrying `next`. A file or other non-lock entry at the lock path is named in
|
|
69
|
+
the warning and never removed: move it out of the way. Do not remove a lock
|
|
70
|
+
while a writer is active. Do not run an
|
|
71
71
|
older Campaigns OS release against the same target at the same time.
|
|
72
72
|
|
|
73
73
|
An unchanged projection reuses its ID, timestamp and sequence. Identity
|
|
@@ -59,8 +59,10 @@ the toolkit runs that loop in a fixed order:
|
|
|
59
59
|
2. **Serve and prove.** Serve `_site/` on localhost (the `next deploy` handoff
|
|
60
60
|
names the directory and any root-route rewrite) and run `polish capture`
|
|
61
61
|
against it. Once polish is recorded, `record deploy --packet <p> --base-url
|
|
62
|
-
<url>` records the URL on `deploy.preview_url` and the deploy stage
|
|
63
|
-
|
|
62
|
+
<url>` records the URL on `deploy.preview_url` and the deploy stage, bound
|
|
63
|
+
to the build it probed (a `record build` of different output makes it owed
|
|
64
|
+
again and keeps the old record in the deploy stage's history); then run `qa run --browser` and the typed-card order paths against
|
|
65
|
+
it.
|
|
64
66
|
3. **Prove the pin on the production output.** Before committing, run
|
|
65
67
|
|
|
66
68
|
```bash
|
|
@@ -726,6 +728,16 @@ spec resolved from the portal for the run — keep publish-by-default regardless
|
|
|
726
728
|
of consent: those verdicts are the QA tab's product surface, not telemetry.
|
|
727
729
|
Explicit flags always win in both directions for saved-Map QA.
|
|
728
730
|
|
|
731
|
+
A run whose base URL is a local address (`localhost` or any `*.localhost`
|
|
732
|
+
name, any `127.x.x.x` address, `0.0.0.0`, `[::1]` or an IPv4-mapped loopback,
|
|
733
|
+
from `--base-url` or the packet's `deploy.preview_url`) keeps its verdict local
|
|
734
|
+
by default, whatever the spec source. Those runs are checks on your own
|
|
735
|
+
machine, and people watching the Map's QA tab should not see them. The output
|
|
736
|
+
names the destination and how to publish: `qa publish` for the stored verdict,
|
|
737
|
+
or `--post-verdict` on the next run. `--post-verdict` publishes a local-address
|
|
738
|
+
run, and `publish_decision.reason` reads `loopback_base_url` when the default
|
|
739
|
+
kept it local.
|
|
740
|
+
|
|
729
741
|
A packet with `local_spec_id` always keeps its verdict and progress local.
|
|
730
742
|
`qa run` suppresses portal publication even with `--post-verdict`; the flag
|
|
731
743
|
does not turn a local ID into a Map destination. `qa publish` refuses such a
|
|
@@ -1810,7 +1822,12 @@ The browser driver intentionally behaves like a user:
|
|
|
1810
1822
|
proven against the persisted-order voucher read-back
|
|
1811
1823
|
- checkout is advanced through the visible cart/checkout button
|
|
1812
1824
|
- address autocomplete is settled or closed before submit
|
|
1813
|
-
-
|
|
1825
|
+
- the card and CVV iframes are filled with sequential keystrokes: Spreedly's
|
|
1826
|
+
iFrame fields on SDK 0.4.40 and earlier, and on 0.4.41 and later the
|
|
1827
|
+
Spreedly-hosted fields that NEXT's `payments.29next.com` script mounts.
|
|
1828
|
+
Typing starts once the checkout form drops `next-loading-spreedly` (the
|
|
1829
|
+
fields are ready), and both fields are read back and retyped, up to three
|
|
1830
|
+
attempts in all, before `card_fields_filled` passes
|
|
1814
1831
|
- the real submit button is clicked without fabricating SDK state
|
|
1815
1832
|
|
|
1816
1833
|
The intended QA order matrix is:
|
|
@@ -1893,6 +1910,8 @@ A redirect to the right page is fine. Missing links, links whose query differs,
|
|
|
1893
1910
|
|
|
1894
1911
|
**Accepting a warning.** `campaigns-os checkpoint accept` records an operator's decision about a warning that a Campaigns OS check measured. It does not change readiness, does not remove the warning, does not change the status `next` reports, and cannot be used on review, unexercised, or excluded results. The warning must already be on record (in the doctor snapshot `next` writes, the Polish capture, or the full QA verdict) before it can be accepted. The accept is bound to the exact measured state and lapses when that state changes, or when its evidence is stale or cannot be reproduced. An optional expiry or review condition adds a second bound. Run it only with the operator's explicit decision, made after the warning was shown to them. A plausible name is not authorization. The command refuses placeholder and automation identities. Results are re-derived from the package's raw captures on every read, and a result whose capture is missing or does not match is listed as unexercised. These checks are tamper evidence, not proof of authorship: a hand-written record that copies every value and recomputes the checksum, or a hand-written result written together with a matching capture, is not detected.
|
|
1895
1912
|
|
|
1913
|
+
A readability warning is keyed by its colour pair and text size class on each page and width. The QC handoff lists each pair once with every page and width that uses it. Accepting it, with one `checkpoint accept` naming every listed ref, records the operator's decision for those pages. The accept states whether it covers normal text, large text, or both, by which size-class refs are named. When either colour changes, the pair becomes a new warning, and the old accept no longer matches any result (it is listed as orphaned). An accept lapses when the exact colours, ratio, font size or font weight of any text using the pair on that page change, when its capture is stale — including built output that changed since the build was recorded — or when it cannot be reproduced, including a missing crop. A page that starts using the same pair later is a new warning. An accept never hides review, unexercised or excluded results.
|
|
1914
|
+
|
|
1896
1915
|
## Launch Readiness Note
|
|
1897
1916
|
|
|
1898
1917
|
Campaigns OS can prove the campaign build, SDK wiring, browser behavior, and
|
|
@@ -1960,6 +1979,14 @@ placeholder-text blocker and the other residue gates still run. The emitted
|
|
|
1960
1979
|
minimal packet is marked `_synthesized` — it points doctor/QA at the built
|
|
1961
1980
|
output and family, and is not a substitute for a real Build Packet.
|
|
1962
1981
|
|
|
1982
|
+
`doctor --built` reads the local built files only. `--base-url` is not
|
|
1983
|
+
fetched: it fills `deploy.preview_url` in the minimal packet, and the output
|
|
1984
|
+
says so (`derived.base_url.fetched: false` in JSON). A clean `doctor --built`
|
|
1985
|
+
run with a preview URL says nothing about the preview. To check the served
|
|
1986
|
+
pages, run `qa run --site <repo> --base-url <url> --family <family> --browser`.
|
|
1987
|
+
The emitted minimal packet does not drive packet QA: `qa resolve` and
|
|
1988
|
+
`qa run --packet` block on it, because it carries no local CampaignSpec.
|
|
1989
|
+
|
|
1963
1990
|
**Trade-off — non-packet QA is narrower than packet-driven QA.** It runs the
|
|
1964
1991
|
built-output gates (residue, placeholder text, demo-asset, pricing-CSS, brand
|
|
1965
1992
|
contract) but **skips the CampaignSpec/source-HTML-driven checks** a packet
|
package/docs/readback.md
CHANGED
|
@@ -6,7 +6,9 @@ Packet, the doctor output sidecar, the build context, the assembly report, a QA
|
|
|
6
6
|
verdict, and a findings export when present. It reads each of those files at
|
|
7
7
|
most once, plus two fixed Git metadata files (the nearest `.git` entry at the
|
|
8
8
|
target or an ancestor, and that Git directory's `logs/HEAD` reflog) for the
|
|
9
|
-
freshness comparison
|
|
9
|
+
freshness comparison, and the CampaignSpec and normalized Campaign Build Brief
|
|
10
|
+
the Build Packet names, whose current content decides each stage's effective
|
|
11
|
+
status.
|
|
10
12
|
|
|
11
13
|
The safety contract is the point of the command. The readback **writes nothing**
|
|
12
14
|
under the target — not even a lifecycle journal entry, which every other command
|
|
@@ -107,7 +109,7 @@ RUN IDENTITY
|
|
|
107
109
|
template_family = olympus [build packet]
|
|
108
110
|
qa run_id = MSRBUNDLEFIXTURE000000000000 [QA verdict]
|
|
109
111
|
|
|
110
|
-
STAGES [assembly report; report status: prepared]
|
|
112
|
+
STAGES [assembly report; report status: prepared; effective: prepared]
|
|
111
113
|
prepare_build completed
|
|
112
114
|
doctor pending
|
|
113
115
|
setup pending
|
|
@@ -129,6 +131,21 @@ QA VERDICT [QA verdict; disposition: ready — Campaigns OS is the verdict auth
|
|
|
129
131
|
pass http:checkout (family funnel-flow)
|
|
130
132
|
```
|
|
131
133
|
|
|
134
|
+
### STAGES
|
|
135
|
+
|
|
136
|
+
The STAGES header prints the status the Assembly Report records and the status recomputed from each stage's effective status. A stage's effective status is the recorded one, except that a stage made owed again by a brief or CampaignSpec change reads `required`, and a stage whose inputs cannot be confirmed reads `unknown`. When the two differ, the stage line adds the recorded status and the reason. Superseded records appear only under "history (not current proof)".
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
STAGES [assembly report; report status: completed; effective: prepared]
|
|
140
|
+
prepare_build completed
|
|
141
|
+
doctor pending
|
|
142
|
+
setup completed
|
|
143
|
+
assembly required (recorded: completed; spec_material_changed)
|
|
144
|
+
polish required (recorded: completed; spec_material_changed)
|
|
145
|
+
deploy completed
|
|
146
|
+
qa required (recorded: completed; spec_material_changed)
|
|
147
|
+
```
|
|
148
|
+
|
|
132
149
|
## The JSON contract
|
|
133
150
|
|
|
134
151
|
```json
|
|
@@ -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.54.0`.
|
|
12
12
|
|
|
13
13
|
## What this is
|
|
14
14
|
|