@nextcommerce/campaigns-os 1.43.2 → 1.46.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/AGENTS.md +5 -0
- package/CHANGELOG.md +648 -5103
- package/README.md +32 -11
- package/agents/claude/CLAUDE.md +1 -1
- package/agents/codex/AGENTS.md +1 -1
- package/agents/copilot/copilot-instructions.md +1 -1
- package/agents/cursor/campaigns-os.mdc +1 -1
- package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +2 -2
- package/campaign-spec/dist/rules/analytics-contract-shape.js +2 -2
- package/campaign-spec/dist/rules/store-profile-shape.d.ts +5 -1
- package/campaign-spec/dist/rules/store-profile-shape.js +8 -10
- package/campaign-spec/dist/types.d.ts +2 -2
- package/contracts/archive/CHANGELOG.2026-09-30.md +5111 -0
- package/contracts/archive/release-ledger.2026-09-30.json +5068 -0
- package/contracts/effects.v1.json +1176 -113
- package/contracts/orientation-reason-codes.v1.json +7 -0
- package/contracts/release-ledger.json +2345 -6087
- package/contracts/supported-surface.json +7 -4
- package/contracts/template-slot-manifest.shared-content-core.v0.json +403 -0
- package/docs/brand-theme-bridge.md +81 -0
- package/docs/build-packet.md +158 -21
- package/docs/campaigns-os-build-flow.md +3 -3
- package/docs/design-source-package.md +73 -0
- package/docs/effects.md +50 -8
- package/docs/gateway-login.md +3 -0
- package/docs/local-setup.md +1 -1
- package/docs/orientation-contract-reference.md +42 -2
- package/docs/polish-evidence.md +74 -0
- package/docs/qa-and-test-orders.md +99 -13
- package/docs/release-ledger-authoring-guide.md +64 -4
- package/docs/runtime-readiness.md +1 -1
- package/docs/sdk-storage-compatibility.md +1 -1
- package/docs/skills-revision.md +10 -10
- package/docs/supported-surface.md +2 -2
- package/docs/versioning.md +4 -1
- package/package.json +1 -1
- package/schemas/campaigns-os-release-ledger.v1.schema.json +32 -2
- package/schemas/campaigns-os-tooling-orientation.v1.schema.json +1 -0
- package/skills/campaign-lifecycle-orientation/SKILL.md +16 -5
- package/skills/campaign-readback-classification/SKILL.md +3 -3
- package/skills/campaign-run-evidence/SKILL.md +7 -6
- package/skills/contribution-intake/SKILL.md +3 -3
- package/skills/next-campaigns-build/SKILL.md +7 -6
- package/skills/next-campaigns-os/SKILL.md +7 -7
- package/skills/next-campaigns-os/references/session-intake.md +9 -3
- package/skills/next-campaigns-os-setup/SKILL.md +5 -5
- package/skills/next-campaigns-polish/SKILL.md +28 -9
- package/skills/next-campaigns-qa/SKILL.md +7 -4
- package/skills.json +10 -10
- package/src/brand-theme.mjs +320 -20
- package/src/built-site-scope.mjs +16 -4
- package/src/cli.mjs +280 -46
- package/src/commercial-parity.mjs +48 -2
- package/src/deviation.mjs +13 -1
- package/src/diagnostic.mjs +5 -2
- package/src/doctor/checks.mjs +320 -81
- package/src/doctor/inspect.mjs +55 -13
- package/src/doctor/source-provenance.mjs +184 -0
- package/src/invocation.mjs +4 -0
- package/src/live-campaign-refs.mjs +466 -0
- package/src/login.mjs +2 -2
- package/src/page-kit-store-profile.mjs +69 -12
- package/src/page-kit-sync.mjs +31 -12
- package/src/progress-node.mjs +3 -1
- package/src/qa-browser.mjs +538 -28
- package/src/qa-commercial-parity.mjs +48 -5
- package/src/qa-node.mjs +122 -7
- package/src/qa-test-order-topology.mjs +148 -0
- package/src/sdk-markup.mjs +72 -8
- package/src/source-html-intake.mjs +116 -0
- package/src/stage-record.mjs +551 -0
- package/src/upsell-selector-scope.mjs +112 -2
package/docs/polish-evidence.md
CHANGED
|
@@ -21,6 +21,11 @@ Three layers must all be satisfied:
|
|
|
21
21
|
`campaigns-os polish capture` from the current packet, report, served build,
|
|
22
22
|
mapped routes, and fixed desktop/mobile viewports.
|
|
23
23
|
|
|
24
|
+
Record all three with `campaigns-os record polish` (§7) rather than editing
|
|
25
|
+
the report by hand: it stamps the stage-record fields from doctor's current
|
|
26
|
+
state, keeps the captured `page_load`, and writes nothing unless this gate would
|
|
27
|
+
pass on the result.
|
|
28
|
+
|
|
24
29
|
## 1. Stage-record requirements
|
|
25
30
|
|
|
26
31
|
The gate only applies once assembly is complete
|
|
@@ -500,3 +505,72 @@ current value.
|
|
|
500
505
|
Polish evidence certifies the polish pass only — it is not QA and does not
|
|
501
506
|
certify launch readiness (`docs/qa-and-test-orders.md` owns the QA proof
|
|
502
507
|
stack).
|
|
508
|
+
|
|
509
|
+
## 7. Recording with `record polish`
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
campaigns-os record polish --packet <campaign-runtime.build.json> --evidence <polish-evidence.json> [--report <json>] [--dry-run] [--json]
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Run it after `campaigns-os polish capture`, against the build `campaigns-os
|
|
516
|
+
record build` recorded. The `--evidence` file is a JSON object with these keys;
|
|
517
|
+
any other key is refused:
|
|
518
|
+
|
|
519
|
+
| Key | Required | Written to |
|
|
520
|
+
|---|---|---|
|
|
521
|
+
| `status` | no (default `completed`) | `stages.polish.status`; `completed`, `completed_with_warnings`, `blocked` or `skipped` |
|
|
522
|
+
| `evidence` | for `completed*` | `stages.polish.evidence`, the seven categories of §2. Leave out `visual_review.page_load`: the value `polish capture` recorded is kept, and a file that carries one is refused. Without it, a `blocked` or `skipped` record keeps the evidence already on the report. |
|
|
523
|
+
| `blockers` | for `blocked` only | `stages.polish.blockers`: a non-empty array of `{"code", "message"}` objects. |
|
|
524
|
+
| `skip_reason` | for `skipped` only | `stages.polish.skip_reason`: a non-empty string. |
|
|
525
|
+
| `repair_loop_defect` | no | `report.theme.repair_loop_defect`: `null`, or the first brand-layer repair-loop defect as an object (`code`, `message`, `path`, `detail`). A non-null defect needs a recorded `report.theme`. |
|
|
526
|
+
|
|
527
|
+
The command itself stamps `performed_by: "next-campaigns-polish"`,
|
|
528
|
+
`source_build_fingerprint` (doctor's `derived.build_output_fingerprint.value`,
|
|
529
|
+
which must equal the recorded `stages.assembly.build_fingerprint`),
|
|
530
|
+
`source_package_material_fingerprint` when the report fingerprints a Design
|
|
531
|
+
Source Package, and `completed_at` (not for `blocked`). It then validates the
|
|
532
|
+
report it would write against
|
|
533
|
+
`schemas/campaign-runtime-assembly-report.v0.schema.json` and doctor's report
|
|
534
|
+
checks and, for a `completed*` status, evaluates this gate and the hidden
|
|
535
|
+
eager-media checkpoint over it exactly as doctor does. A `blocked` or `skipped`
|
|
536
|
+
record keeps this gate blocked, so `next` stays at Polish and QA stays blocked. Any failure is printed by field or gate code
|
|
537
|
+
(for example `repair_loop_defect must be null or an object ... (got string)`, or
|
|
538
|
+
`polish.evidence_incomplete` with its per-field problems), the command exits
|
|
539
|
+
non-zero, and nothing is written. `--dry-run` runs every check and writes
|
|
540
|
+
nothing. Like `record setup` and `record build`, it refuses a report that is not
|
|
541
|
+
bound to the packet (`docs/build-packet.md`, "Recording stage completion") and
|
|
542
|
+
output that changes while it records.
|
|
543
|
+
|
|
544
|
+
A complete file:
|
|
545
|
+
|
|
546
|
+
```json
|
|
547
|
+
{
|
|
548
|
+
"status": "completed",
|
|
549
|
+
"evidence": {
|
|
550
|
+
"visual_review": {
|
|
551
|
+
"screenshots": ["qa-output/polish/landing-desktop.png", "qa-output/polish/landing-mobile.png"]
|
|
552
|
+
},
|
|
553
|
+
"brand_review": {
|
|
554
|
+
"logo_checked": true,
|
|
555
|
+
"favicon": { "status": "confirmed_non_template" },
|
|
556
|
+
"colors": ["#1f4d3a"],
|
|
557
|
+
"brand_bleed": { "cleared": true }
|
|
558
|
+
},
|
|
559
|
+
"checkout_review": {
|
|
560
|
+
"field_labels": "initial field labels and placeholders are legible on desktop and mobile",
|
|
561
|
+
"phone_alignment": "checked",
|
|
562
|
+
"payment_display": "checked",
|
|
563
|
+
"bump_compare_price_rule": "no equal or no-discount compare price renders"
|
|
564
|
+
},
|
|
565
|
+
"template_residue_review": {
|
|
566
|
+
"next_blue": "not found",
|
|
567
|
+
"starter_favicon": { "status": "confirmed_non_template" },
|
|
568
|
+
"lorem": "not found"
|
|
569
|
+
},
|
|
570
|
+
"commerce_flow_review": "direct-entry package selection reviewed",
|
|
571
|
+
"issues": [],
|
|
572
|
+
"commands": ["next-campaigns-polish", "campaigns-os polish capture"]
|
|
573
|
+
},
|
|
574
|
+
"repair_loop_defect": null
|
|
575
|
+
}
|
|
576
|
+
```
|
|
@@ -246,12 +246,18 @@ before QA with the relevant gate ID:
|
|
|
246
246
|
```bash
|
|
247
247
|
campaigns-os checkpoint waive \
|
|
248
248
|
--packet campaign-runtime.build.json \
|
|
249
|
-
--gate <page_kit.store_profile|page_kit.sdk_version|polish.hidden_eager_media|built_output.upsell_selector_scope> \
|
|
249
|
+
--gate <page_kit.store_profile|page_kit.sdk_version|polish.hidden_eager_media|built_output.upsell_selector_scope|source_html.producer_provenance> \
|
|
250
|
+
[--page <page_id>] \
|
|
250
251
|
--reason "<why>" \
|
|
251
252
|
--waived-by "<named human>" \
|
|
252
253
|
--review-condition "<specific re-evaluation trigger>"
|
|
253
254
|
```
|
|
254
255
|
|
|
256
|
+
`--page` is required for `source_html.producer_provenance`, which is waived one
|
|
257
|
+
Figma-typed page at a time (see the
|
|
258
|
+
[Design Source Package](./design-source-package.md) hand-written HTML route),
|
|
259
|
+
and refused for the other gates.
|
|
260
|
+
|
|
255
261
|
Legacy source/theme/QA waiver commands and artifact lanes remain in place until
|
|
256
262
|
those gates are registered. Store Profile evidence includes only the governed
|
|
257
263
|
nine-field matrix plus status, normalized slug, relative target path,
|
|
@@ -432,7 +438,8 @@ work that lands with the receiver's connection contract. This section
|
|
|
432
438
|
documents the semantics of the stamps the receiver already applies.
|
|
433
439
|
|
|
434
440
|
Add `--browser --test-order common` for the normal proof pass: first-party
|
|
435
|
-
Playwright browser checks plus the default typed-card
|
|
441
|
+
Playwright browser checks plus the default `common` typed-card orders
|
|
442
|
+
(described under "Test Orders" below). If the
|
|
436
443
|
browser binary is missing, the CLI will prompt you to run
|
|
437
444
|
`npm run qa:install-browser`:
|
|
438
445
|
|
|
@@ -452,6 +459,30 @@ machine-checkable `agentContract.qaStructure` selectors in the commerce surface
|
|
|
452
459
|
catalog. If the family contract is silent, the assertion returns
|
|
453
460
|
`manual_review`, not `pass`; if declared required structure is missing, it
|
|
454
461
|
soft-fails with warning severity so the verdict becomes `ready_with_exceptions`.
|
|
462
|
+
The checkout wrapper and page composition are source-owned, so QA checks what
|
|
463
|
+
the checkout does rather than family class names. Family shell is a fixed list:
|
|
464
|
+
`.checkout-wrapper`, `.checkout-layout__left`, `.checkout-layout__right`,
|
|
465
|
+
`.checkout__layout`, `.checkout__column--left`, `.checkout__column--right` and
|
|
466
|
+
the family include's `[data-next-component="shipping-field-row"]` marker. When
|
|
467
|
+
family shell is all that is missing, the row is `warn` if the checkout also
|
|
468
|
+
passes three behaviour checks: a `<form data-next-checkout="form">` exists;
|
|
469
|
+
`email`, `fname`, `lname`, `country`, `address1`, `city`, `province` and
|
|
470
|
+
`postal` are each an input, select or textarea carrying that
|
|
471
|
+
`data-next-checkout-field` inside the form, not a `type="hidden"` input or a
|
|
472
|
+
disabled control (visibility is not required, so a field hidden until a country
|
|
473
|
+
is chosen still counts); and a cart-summary total
|
|
474
|
+
(`[data-next-display="cart.total"]` or
|
|
475
|
+
`[data-next-cart-summary] .order-totals__value--total`) is visible with text.
|
|
476
|
+
If any behaviour check fails, or no checkout form is found, the row stays
|
|
477
|
+
`fail`. Any other missing selector is always `fail`: the SDK selectors
|
|
478
|
+
(`[data-next-checkout="form"]`, `[os-checkout-payment]`,
|
|
479
|
+
`[data-next-cart-summary]`, `[data-next-bundle-slots-for]`) and any class the
|
|
480
|
+
list does not name, such as a hosted payment field class. `evidence.behaviour` records
|
|
481
|
+
the three checks and each `evidence.checks[]` entry carries `kind`
|
|
482
|
+
(`family_shell` or `sdk_wiring`).
|
|
483
|
+
The upsell and checkout bundle price checks count the SDK's
|
|
484
|
+
`[data-next-bundle-display*='price']` alongside the contract's price rows; a
|
|
485
|
+
hidden, zero-size or empty bundle-display node does not count.
|
|
455
486
|
Promoted template families must also have
|
|
456
487
|
`contracts/template-brand-contract.<family>.v0.json`; QA emits a blocker if the
|
|
457
488
|
selected family is missing its brand/residue/pricing contract instead of
|
|
@@ -1107,13 +1138,64 @@ npm run campaigns-os -- qa run \
|
|
|
1107
1138
|
--test-order common
|
|
1108
1139
|
```
|
|
1109
1140
|
|
|
1110
|
-
The default mode is **`common`** (also what bare `--test-order` runs)
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1141
|
+
The default mode is **`common`** (also what bare `--test-order` runs). It
|
|
1142
|
+
starts from the selected checkout's declared topology:
|
|
1143
|
+
|
|
1144
|
+
- When every actual terminal path (the `full` set, checkout baseline included)
|
|
1145
|
+
fits under the flood cap (`--max-test-orders`, default `6`), `common` runs
|
|
1146
|
+
them all. The run says so on stderr, and on a funnel with offer pages the
|
|
1147
|
+
coverage row below records
|
|
1148
|
+
`order_path_depth_effective: "full"` with `order_path_depth_reason:
|
|
1149
|
+
"under_cap"`.
|
|
1150
|
+
- Above the cap, or when the topology cannot be walked exhaustively, `common`
|
|
1151
|
+
runs the sample: the checkout baseline, first-offer `accept` and `decline`
|
|
1152
|
+
when `expected_next_url` reaches an upsell/downsell, and the shortest
|
|
1153
|
+
declared path that actually reaches a receipt/thank-you page (deduplicated
|
|
1154
|
+
when it is already `accept` or `decline`; Campaigns OS never invents a
|
|
1155
|
+
receipt path from offer count alone). It then adds, for each offer or
|
|
1156
|
+
downsell page whose decline no planned path clicks yet, the shortest actual
|
|
1157
|
+
terminal path that clicks it, until the plan reaches the cap. Pages still
|
|
1158
|
+
left out are named on stderr and in the coverage row. The sample itself is
|
|
1159
|
+
never trimmed, so a `--max-test-orders` below it is refused before launch as
|
|
1160
|
+
before.
|
|
1161
|
+
|
|
1162
|
+
A page counts as covered only when a path clicks its **decline** control.
|
|
1163
|
+
Reaching a page, or clicking only its accept, does not count: a broken decline
|
|
1164
|
+
link strands the shopper even when accept works.
|
|
1165
|
+
|
|
1166
|
+
Every browser test-order run whose funnels include offer pages records
|
|
1167
|
+
`browser-test-order:upsell-action-coverage`, read from the clicks the runner
|
|
1168
|
+
actually made in orders it placed, not from the plan. It lists the offer pages
|
|
1169
|
+
of every funnel in the run, including funnels the orders do not drive, and a
|
|
1170
|
+
click credits only the funnel whose order made it, never another funnel's page
|
|
1171
|
+
at the same URL. The row is conservative: it is `pass` or `warn` only when
|
|
1172
|
+
coverage is certain, and `manual_review` in every other case.
|
|
1173
|
+
|
|
1174
|
+
Coverage is certain when all of these hold: at least one order was placed;
|
|
1175
|
+
every funnel lists its pages; every page is either a known non-offer type
|
|
1176
|
+
(checkout, landing, thank-you and the like) or an upsell/downsell page with its
|
|
1177
|
+
own absolute http(s) URL that no other page shares; every planned order belongs
|
|
1178
|
+
to exactly one funnel (its checkout is that funnel's checkout and it was
|
|
1179
|
+
planned over that funnel's page list); and every click an order recorded lands
|
|
1180
|
+
on a declared offer page of that order's own funnel. Then the row is `warn`
|
|
1181
|
+
(severity `warn`) naming each page whose decline no order of its funnel
|
|
1182
|
+
clicked, and each funnel no order ran through, or `pass` when every page's
|
|
1183
|
+
decline was clicked.
|
|
1184
|
+
|
|
1185
|
+
Otherwise the row is `manual_review` (severity `warn`) naming the pages not
|
|
1186
|
+
proved clicked through, with `evidence.reason`: `test_orders_off` for a
|
|
1187
|
+
`--test-order off` run, `no_order_recorded` when no order was placed (an
|
|
1188
|
+
attempt that failed before an order reference counts as none), `no_topology`
|
|
1189
|
+
when no funnel topology reached the check, or `not_assessable` for the rest.
|
|
1190
|
+
`evidence.not_assessable[]` names each page that cannot be matched to a click,
|
|
1191
|
+
with a reason: `no_url` or `unresolvable_url`, `shared_url` (the URL belongs to
|
|
1192
|
+
another page too), `unknown_page_type` (neither an offer type nor a known
|
|
1193
|
+
non-offer type), or `no_pages` (a funnel with no page list).
|
|
1194
|
+
`evidence.uncertainty[]` lists the run-level reasons: `unattributed_plan` (a
|
|
1195
|
+
plan that does not match exactly one funnel's checkout and page list),
|
|
1196
|
+
`unattributed_click` (a click with no usable URL, or from such a plan's order)
|
|
1197
|
+
and `undeclared_click` (an order clicked a page its funnel does not declare).
|
|
1198
|
+
`evidence.pages[]` gives `accept_clicked` / `decline_clicked` per page.
|
|
1117
1199
|
|
|
1118
1200
|
Other modes: `checkout` (base order redirect only), `accept`/`decline` (click the
|
|
1119
1201
|
rendered control on the first upsell page), `both` (two fresh orders for those
|
|
@@ -1547,8 +1629,11 @@ npm run campaigns-os -- qa run \
|
|
|
1547
1629
|
```
|
|
1548
1630
|
|
|
1549
1631
|
`--max-test-orders` (default `6`) is an **accidental-flood guard, not a permission
|
|
1550
|
-
gate**. A single checkout's `common`
|
|
1551
|
-
|
|
1632
|
+
gate**. A single checkout's `common` plan always keeps its checkout,
|
|
1633
|
+
first-offer accept/decline and shortest-receipt sample. If that sample alone
|
|
1634
|
+
exceeds the cap (a `--max-test-orders` below it), the run is refused before
|
|
1635
|
+
browser launch, as before. Otherwise the added decline paths stop at the cap.
|
|
1636
|
+
Tier expansion can exceed it. If `full` expands past the cap, the command stops before
|
|
1552
1637
|
browser launch, prints the planned count, lists the planned paths (up to 40 ids;
|
|
1553
1638
|
past that the remainder is counted, never cut silently, and `--select-package
|
|
1554
1639
|
<ref[:qty]>` lists one tier's paths), and names the exact `--max-test-orders <count>` raise. For example, a linear three-offer graph has
|
|
@@ -1729,8 +1814,9 @@ use the declared topology instead of a single happy path:
|
|
|
1729
1814
|
|
|
1730
1815
|
1. Checkout-only with the base cart.
|
|
1731
1816
|
2. Checkout-only with the base cart plus bump when the bump is in scope.
|
|
1732
|
-
3. Base cart through
|
|
1733
|
-
|
|
1817
|
+
3. Base cart through `--test-order common`: every actual terminal path when
|
|
1818
|
+
they fit under the cap, otherwise the checkout/first-action sample, the
|
|
1819
|
+
shortest real receipt path and one decline path per uncovered offer page.
|
|
1734
1820
|
4. Base plus bump cart through the same sample matrix when bump behavior is
|
|
1735
1821
|
launch-relevant.
|
|
1736
1822
|
5. Use `full` when you want every actual terminal path, raising the flood cap to
|
|
@@ -146,7 +146,7 @@ incomplete, append a correction:
|
|
|
146
146
|
"amends": "RL-0007",
|
|
147
147
|
"amendment_reason": "RL-0007 was recorded as compatible; it removed a documented guarantee.",
|
|
148
148
|
"surface_version": null,
|
|
149
|
-
"changelog_section": "1.
|
|
149
|
+
"changelog_section": "1.16.0+agent.1",
|
|
150
150
|
"compatibility": "breaking",
|
|
151
151
|
"migration": "Stop relying on the removed guarantee; see docs/build-packet.md.",
|
|
152
152
|
"agent_impact": "Treat the 1.15.0 packet doc change as breaking, not compatible.",
|
|
@@ -154,6 +154,10 @@ incomplete, append a correction:
|
|
|
154
154
|
}
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
+
The amendment corrects a 1.15.0 entry, but its own section is a new one at the
|
|
158
|
+
very top of `CHANGELOG.md`, numbered on the release on top when it is written
|
|
159
|
+
(`1.16.0` here). It is never inserted under the older release it corrects.
|
|
160
|
+
|
|
157
161
|
An amendment is the only entry kind whose change items may map to no changed
|
|
158
162
|
path in its own range, because it corrects meaning rather than moving bytes.
|
|
159
163
|
|
|
@@ -171,13 +175,25 @@ entry currently holding the link, may re-link a section; any other second link
|
|
|
171
175
|
still fails the one-to-one rule. Say in `amendment_reason` what changed in the
|
|
172
176
|
section and why.
|
|
173
177
|
|
|
178
|
+
This works only for a section still in the live `CHANGELOG.md`. An archived
|
|
179
|
+
section cannot be corrected in place: archive files are never edited, and each
|
|
180
|
+
is pinned by its SHA-256 in `baseline_floor.archives`, so any change to one
|
|
181
|
+
fails the gate. Correct archived history with a new live amendment entry that
|
|
182
|
+
links its own new section at the top of `CHANGELOG.md`, and say in
|
|
183
|
+
`amendment_reason` what is wrong in the archived section.
|
|
184
|
+
|
|
174
185
|
`scripts/check-changelog-structure.mjs` (part of `npm run check`) refuses the
|
|
175
186
|
marker lines outright, in `CHANGELOG.md` and under `docs/`, and also holds the
|
|
176
187
|
section layout: identifiers unique, `+agent.N` sections in one run directly
|
|
177
188
|
above their release with N descending (newest first), and every ledger
|
|
178
|
-
`changelog_section` naming a section that exists.
|
|
179
|
-
|
|
180
|
-
|
|
189
|
+
`changelog_section` naming a section that exists.
|
|
190
|
+
|
|
191
|
+
A new section always goes at the very top of `CHANGELOG.md`: either a new
|
|
192
|
+
release, or a `+agent.N` section numbered on the current top release (one above
|
|
193
|
+
its highest N, or `+agent.1` if it has none). Never add a `+agent.N` section
|
|
194
|
+
under an older release. Given `--base`, both `check-changelog-structure.mjs` and
|
|
195
|
+
the release-ledger gate refuse any new section that sits below a section base
|
|
196
|
+
already had.
|
|
181
197
|
|
|
182
198
|
## Running the gate
|
|
183
199
|
|
|
@@ -274,3 +290,47 @@ a quiet raise.
|
|
|
274
290
|
|
|
275
291
|
Raising a limit is a reviewed policy change: advance `limits_version`, and the
|
|
276
292
|
change owes its own ledger entry like anything else.
|
|
293
|
+
|
|
294
|
+
## Rotating the baseline
|
|
295
|
+
|
|
296
|
+
When the live ledger and changelog approach a bound, rotate them at a reviewed
|
|
297
|
+
cut instead of raising a limit. The first rotation (2026-09-30, `RL-0190`)
|
|
298
|
+
moved `RL-0001` through `RL-0124`; the ledger's `baseline_floor` records it.
|
|
299
|
+
|
|
300
|
+
1. Pick the cut: the last entry to archive. Everything up to it moves, and so
|
|
301
|
+
does the changelog from the first section those entries link down to the end
|
|
302
|
+
of the file, including sections no entry links. If a kept entry amends an
|
|
303
|
+
archived one, or links a section in that tail, move the cut earlier until the
|
|
304
|
+
pair is on one side. Never later.
|
|
305
|
+
2. Run the rotation through `rotateLedger` in `scripts/orientation-contract.mjs`
|
|
306
|
+
with a new dated pair under `contracts/archive/`, e.g.
|
|
307
|
+
`release-ledger.<date>.json` and `CHANGELOG.<date>.md`. It writes the entries
|
|
308
|
+
byte-for-byte (original `sequence` and hashes kept) and the changelog tail
|
|
309
|
+
verbatim, appends the pair and its SHA-256 to `baseline_floor.archives`, and
|
|
310
|
+
moves `last_archived_id`, `last_archived_sequence` and `first_kept_id`. It
|
|
311
|
+
refuses a cut that splits a pair.
|
|
312
|
+
3. Never edit an existing archive file, and never reuse a date: each rotation
|
|
313
|
+
adds a new pair. Add both new files to `named` in
|
|
314
|
+
`contracts/supported-surface.json`.
|
|
315
|
+
4. Record the rotation as its own entry, named in `baseline_floor.rotation_entry`,
|
|
316
|
+
with a change item for each new archive file and a CHANGELOG section of its
|
|
317
|
+
own. Its `agent_impact` tells consumers which baseline is now too old.
|
|
318
|
+
|
|
319
|
+
The gate accepts a base entry missing from the live ledger only when the head
|
|
320
|
+
floor covers it, the new archive holds it canonical-JSON-identical, and the floor
|
|
321
|
+
moved with a new rotation entry in the same range. Any other deletion, an
|
|
322
|
+
archive copy that differs from base, a floor that moves without a rotation
|
|
323
|
+
entry, a floor that moves back or is rewritten, an edited archive file, and a
|
|
324
|
+
cut that archives an entry while an entry that was live at base stays live and
|
|
325
|
+
amends it (the same refusal `rotateLedger` gives) all fail. A new entry added
|
|
326
|
+
after the rotation may still amend an archived entry, linking its own section
|
|
327
|
+
in the live changelog: that is how archived history is corrected. The live changelog and each archive changelog must each be well-formed on
|
|
328
|
+
their own, a section id may appear in only one of them, a section present
|
|
329
|
+
at base must still be in one of them, and the live changelog followed by the
|
|
330
|
+
archives, newest rotation first, must read as the base's sections in order
|
|
331
|
+
with new sections only at the top of the live file. So every live section is
|
|
332
|
+
newer than every archived one, and an archived section never moves back
|
|
333
|
+
(`check-changelog-structure.mjs --base` and the `--base` release-ledger gate).
|
|
334
|
+
The archive files are not mandatory
|
|
335
|
+
orientation reads and are not measured; a consumer whose reviewed baseline is older than the floor refuses
|
|
336
|
+
with `baseline_below_floor` and adopts a newer reviewed baseline.
|
|
@@ -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.46.0`.
|
|
12
12
|
|
|
13
13
|
## What this is
|
|
14
14
|
|
|
@@ -8,7 +8,7 @@ npx --no-install campaigns-os sdk storage-check --target . --target-sdk 0.4.38 -
|
|
|
8
8
|
|
|
9
9
|
Omit `--json` for the concise human report. Exit 0 means source-compatible; exit 2 means incompatible or unknown; invalid arguments or manifests exit 1. No merchant files or pins are rewritten. This is independent of doctor's built HTML markup check.
|
|
10
10
|
|
|
11
|
-
The manifest is generated and owned by Campaign Cart, from its storage registry. The scanner has no independent SDK key list and fetches no remote code. Supply
|
|
11
|
+
The manifest is generated and owned by Campaign Cart, from its storage registry. The scanner has no independent SDK key list and fetches no remote code. Supply `docs/compatibility/storage-migrations.v1.json` from an unmodified checkout of a Campaign Cart release tag, v0.4.40 or later. The report records the full file SHA-256, SDK version, supported target range, registry/extractor/input digest, and Git provenance. When the supplied bytes equal their repository HEAD blob, provenance is `verified-git-blob` with commit and repository path. Proposed, modified, or copied manifests are labeled `unverified-local-file`; the label never certifies a release. Review/pin SDK provenance separately before acting on findings. Targets outside the manifest's supported SDK range report unknown.
|
|
12
12
|
|
|
13
13
|
`--target` must name the Git root. `--scope` is mandatory: comma-separated literal repository-relative directory/file paths; use `.` only when the complete repository is intended. Add shared JavaScript directories explicitly. `--exclude` uses the same syntax and records explicit exclusions. Archives receive no implicit exemption. Only Git-tracked `.html`, `.htm`, `.js`, `.mjs`, and `.cjs` files in the selected scope are scanned, using current working-tree bytes and per-file digests; untracked files and built dependencies are outside this evidence. A selected HTML page's local script outside the selected files reports unknown. Relative scripts affected by an HTML `<base href>` also report unknown; review their actual dependency paths. Remote SDK and third-party scripts are not fetched or analyzed.
|
|
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.46.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.46.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.46.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.46.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.46.0+skills.1",
|
|
94
94
|
"spelling": "bundle",
|
|
95
|
-
"on_disk": "1.
|
|
95
|
+
"on_disk": "1.46.0+skills.1",
|
|
96
96
|
"on_disk_skill": null,
|
|
97
|
-
"message": "match (1.
|
|
97
|
+
"message": "match (1.46.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.46.0+skills.1)
|
|
105
|
+
Skills revision: mismatch: loaded 1.39.0+skills.1, on disk 1.46.0+skills.1 — start a fresh session
|
|
106
|
+
Skills revision: unchecked (on disk 1.46.0+skills.1)
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
`unchecked` is the state when the flag is absent. It is not an error — an
|
|
@@ -14,7 +14,7 @@ implementation detail, however stable it looks.
|
|
|
14
14
|
| Surface | Contract | Change discipline |
|
|
15
15
|
|---|---|---|
|
|
16
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). |
|
|
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
|
|
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
|
| `bin/campaigns-os.mjs` (`campaigns-os`) | The CLI entry itself. | Declared in `package.json` `bin`; the gate fails if it disappears. |
|
|
20
20
|
| Package export `./campaign-spec` | The versioned campaign-spec rule registry, consumed as `@nextcommerce/campaigns-os` (pinned by consumers' lockfiles; lockstep policy — ADR-003 in the ops repo). | Behavior-guarded from the consumer side by their contract tests; the export path itself is gated here. |
|
|
@@ -24,7 +24,7 @@ implementation detail, however stable it looks.
|
|
|
24
24
|
| Contract docs: `CONTEXT.md`, `docs/campaigns-os-build-flow.md`, `docs/build-packet.md`, `docs/migration-sidecar-bundle.md`, `docs/design-source-package.md`, `docs/campaign-build-brief.md`, `docs/campaign-standardization-report.md`, `docs/brand-theme-bridge.md`, `docs/qa-and-test-orders.md`, `docs/legacy-migration.md`, `docs/versioning.md`, `docs/workflow-findings-sidecar.md`, this file | Named entry points consumers pin for context. | Content evolves freely; the path must keep existing. |
|
|
25
25
|
| `skills.json` + `skills/` + `skills.sh` | Versioned skill packages and their installer. | Governed by `check-skill-versions.mjs` (parity + bump gate + reserved external names). `skills.json` ships in the npm pack as of surface 1.0.0. As of 1.40.0 the manifest also carries `bundle_revision` (`<package version>+skills.<n>`), one identity for the bundle that every `SKILL.md` states on its first body line and that `campaigns-os tooling status --skills-revision <value>` checks against the bundle on disk; it must advance whenever any bundled skill changes (`--base` mode), and `docs/skills-revision.md` is its prose. |
|
|
26
26
|
| `compatibility.json` | The published compatibility statement. | Named; must keep existing. |
|
|
27
|
-
| Orientation contract: `AGENTS.md`, `CHANGELOG.md`, `contracts/release-ledger.json`, `contracts/agent-relevant-change-policy.v1.json`, `contracts/orientation-limits.v1.json`, `contracts/orientation-reason-codes.v1.json`, `docs/orientation-contract-reference.md`, `docs/release-ledger-authoring-guide.md`, `contracts/fixtures/orientation/canonicalization/v1.json` | The declarative data a consumer reads from Git objects to decide whether a commit is safe to work against, without executing anything from this repository. Schema `campaigns-os-tooling-orientation/v1`; ledger schema `campaigns-os-release-ledger/v1`. The canonicalization fixture gives independent implementations exact ledger and changelog bytes plus their expected SHA-256 digests. Entry point: [`AGENTS.md`](../AGENTS.md). | Named. The ledger is append-only: a correction ships as a new amendment entry, never an edit. `scripts/check-release-ledger.mjs` enforces the two-way gate; `docs/orientation-contract-reference.md` and the canonicalization fixture are generated together, and CI fails if either is stale. |
|
|
27
|
+
| Orientation contract: `AGENTS.md`, `CHANGELOG.md`, `contracts/release-ledger.json`, `contracts/archive/release-ledger.2026-09-30.json`, `contracts/archive/CHANGELOG.2026-09-30.md`, `contracts/agent-relevant-change-policy.v1.json`, `contracts/orientation-limits.v1.json`, `contracts/orientation-reason-codes.v1.json`, `docs/orientation-contract-reference.md`, `docs/release-ledger-authoring-guide.md`, `contracts/fixtures/orientation/canonicalization/v1.json` | The declarative data a consumer reads from Git objects to decide whether a commit is safe to work against, without executing anything from this repository. Schema `campaigns-os-tooling-orientation/v1`; ledger schema `campaigns-os-release-ledger/v1`. The canonicalization fixture gives independent implementations exact ledger and changelog bytes plus their expected SHA-256 digests. Entry point: [`AGENTS.md`](../AGENTS.md). | Named. The ledger is append-only: a correction ships as a new amendment entry, never an edit. A baseline rotation moves older entries and changelog sections unchanged into dated files under `contracts/archive/`, which never change once merged and are not mandatory orientation reads. `scripts/check-release-ledger.mjs` enforces the two-way gate; `docs/orientation-contract-reference.md` and the canonicalization fixture are generated together, and CI fails if either is stale. |
|
|
28
28
|
| Orientation fixtures: `contracts/fixtures/orientation/envelope/*.json`, `contracts/fixtures/orientation/hostile-target/**` (as named) | The bytes a consumer's parser validates against: one envelope per terminal outcome, plus a hostile target carrying Git hooks, an executable file, and npm lifecycle scripts for proving a reader executes nothing. The hostile target carries a second invariant for the runtime recipe: preparing it must run the recipe's own two steps and no lifecycle script reachable from them. | Named. Regenerate the envelopes with `npm run generate:orientation-docs`. Fixtures under `contracts/fixtures/` that are **not** named here — including the legacy-migration conformance corpus — are this repo's own test data and are not supported. |
|
|
29
29
|
| Runtime recipe: `contracts/runtime-recipe.campaigns-os-node-v1.json` | The declarative description of how a checkout at one commit becomes a usable installed runtime: exact commands, accepted tool ranges, per-step network policy, the enumerated input set, the mandatory output checks, and the enforced bounds. This repository publishes it; the consumer bootstrap executes it. Guide: [`docs/runtime-readiness.md`](runtime-readiness.md). | **Hashed**, deliberately unlike the orientation policy contracts beside it. Any change to commands, network policy, tool versions, inputs, or output verification is an agent-relevant release event, and only a hashed entry makes such a change require `surface_version` to advance in the same PR. A recipe whose commands can change without a version bump is not a contract. |
|
|
30
30
|
| Runtime-recipe fixtures: `contracts/fixtures/runtime-recipe/**` (as named), `docs/runtime-readiness.md` | Accept and single-mutation reject documents a consumer's parser validates against, the prepared-runtime states its output checks must distinguish, and the generated guide. | Named. All of it is generated — regenerate with `npm run generate:runtime-docs`; CI fails on a stale copy. |
|
package/docs/versioning.md
CHANGED
|
@@ -42,7 +42,10 @@ consumer needs to know about an agent-relevant change even when
|
|
|
42
42
|
`CHANGELOG.md` narrates them, and the two are checked against each other in both
|
|
43
43
|
directions. Reason codes and semantic classes are append-only vocabularies —
|
|
44
44
|
renaming or removing one is a breaking change. Raising a limit advances
|
|
45
|
-
`limits_version` and owes its own ledger entry.
|
|
45
|
+
`limits_version` and owes its own ledger entry. Rotating the baseline (moving
|
|
46
|
+
old entries and their sections into a dated `contracts/archive/` pair under the
|
|
47
|
+
ledger's `baseline_floor`) changes no limit and no schema id; it is recorded as
|
|
48
|
+
its own ledger entry.
|
|
46
49
|
|
|
47
50
|
The runtime recipe carries two version identifiers because they gate different
|
|
48
51
|
things. The **kind** names what an installed consumer must already understand in
|
package/package.json
CHANGED
|
@@ -16,11 +16,41 @@
|
|
|
16
16
|
},
|
|
17
17
|
"entries": {
|
|
18
18
|
"type": "array",
|
|
19
|
-
"description": "Ordered oldest to newest. sequence is strictly increasing by 1 from 1, and date is non-decreasing.",
|
|
19
|
+
"description": "Ordered oldest to newest. sequence is strictly increasing by 1 from 1, and date is non-decreasing. When baseline_floor is present, the entries up to the floor live in the archive it names and this array starts at baseline_floor.last_archived_sequence + 1.",
|
|
20
20
|
"items": { "$ref": "#/$defs/entry" }
|
|
21
|
-
}
|
|
21
|
+
},
|
|
22
|
+
"baseline_floor": { "$ref": "#/$defs/baseline_floor" }
|
|
22
23
|
},
|
|
23
24
|
"$defs": {
|
|
25
|
+
"baseline_floor": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": true,
|
|
28
|
+
"description": "Present after a baseline rotation. Entries up to and including last_archived_id moved byte-for-byte, and the changelog from the first section they link to the end of the file moved verbatim, into the dated archive files listed in archives; they keep their original sequence and hashes, so each archived entry's changelog_sha256 verifies against the archive changelog. The archive files are not mandatory orientation reads and count against no orientation limit. A consumer whose reviewed baseline's newest entry is older than last_archived_id refuses with refusal_reason_code and adopts a newer reviewed baseline. The floor only moves forward, only with a new rotation entry, and an archive file never changes once merged; a later rotation appends a new archive pair.",
|
|
29
|
+
"required": ["archives", "last_archived_id", "last_archived_sequence", "first_kept_id", "rotation_entry", "refusal_reason_code"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"archives": {
|
|
32
|
+
"type": "array",
|
|
33
|
+
"minItems": 1,
|
|
34
|
+
"description": "Every archive pair, oldest rotation first. Append-only.",
|
|
35
|
+
"items": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"additionalProperties": true,
|
|
38
|
+
"required": ["ledger_path", "ledger_sha256", "changelog_path", "changelog_sha256"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"ledger_path": { "type": "string", "pattern": "^contracts/archive/[^/]+\\.json$", "description": "Archived entries, same schema_version and shape as this file." },
|
|
41
|
+
"ledger_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$", "description": "SHA-256 over the exact bytes of ledger_path." },
|
|
42
|
+
"changelog_path": { "type": "string", "pattern": "^contracts/archive/[^/]+\\.md$", "description": "The changelog tail that starts at the first section the archived entries link, verbatim and in order, same heading format." },
|
|
43
|
+
"changelog_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$", "description": "SHA-256 over the exact bytes of changelog_path." }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"last_archived_id": { "type": "string", "pattern": "^RL-[0-9]{4}$", "description": "The newest archived entry." },
|
|
48
|
+
"last_archived_sequence": { "type": "integer", "minimum": 1, "description": "Its sequence. The first live entry's sequence is this plus one." },
|
|
49
|
+
"first_kept_id": { "type": "string", "pattern": "^RL-[0-9]{4}$", "description": "The first entry still in this file." },
|
|
50
|
+
"rotation_entry": { "type": "string", "pattern": "^RL-[0-9]{4}$", "description": "The live entry that recorded the rotation which set this floor." },
|
|
51
|
+
"refusal_reason_code": { "type": "string", "minLength": 1, "description": "The reason code, from contracts/orientation-reason-codes.v1.json, a consumer refuses with when its reviewed baseline is below the floor." }
|
|
52
|
+
}
|
|
53
|
+
},
|
|
24
54
|
"entry": {
|
|
25
55
|
"type": "object",
|
|
26
56
|
"additionalProperties": true,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-lifecycle-orientation
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.17
|
|
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.46.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.46.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
|
|
@@ -34,6 +34,15 @@ cite `src/` or `scripts/`: those are implementation and may change without a
|
|
|
34
34
|
supported-surface bump, so a reader cannot check them and a rename would not
|
|
35
35
|
reach this text. `docs/supported-surface.md` is the prose twin of that list.
|
|
36
36
|
|
|
37
|
+
`CHANGELOG.md` and `contracts/release-ledger.json` hold recent history. When
|
|
38
|
+
the ledger declares a `baseline_floor`, the older entries and the sections they
|
|
39
|
+
link sit in the dated `contracts/archive/` files it names: cite those for
|
|
40
|
+
history, but they are not part of an orientation read. An orientation whose
|
|
41
|
+
reviewed baseline is older than the floor is refused with
|
|
42
|
+
`baseline_below_floor`, and the remedy is to adopt a newer reviewed baseline.
|
|
43
|
+
That is a reviewed change for an authorized human, not something to do in a
|
|
44
|
+
session.
|
|
45
|
+
|
|
37
46
|
Identity comes from the tool, not from a file beside the session.
|
|
38
47
|
`campaigns-os tooling status --json` (tier `B`: its only write is the
|
|
39
48
|
command-lifecycle journal) reports the install mode, the version, and a source
|
|
@@ -90,8 +99,10 @@ proceeds. It checks the packet, its CampaignSpec, the artifacts and the built
|
|
|
90
99
|
output, and its ordered check registry records what ran. When blocking errors
|
|
91
100
|
exist the result is not OK, names the errors, and points the next step at
|
|
92
101
|
collecting or correcting input. `campaigns-os doctor --packet <packet> --json`
|
|
93
|
-
(tier `
|
|
94
|
-
target byte-identical
|
|
102
|
+
(tier `A`: inspection is the default, and without `--write` it leaves the
|
|
103
|
+
target byte-identical, but once the site is built and a public campaign key
|
|
104
|
+
resolves it makes one read-only request for the live campaign) is the
|
|
105
|
+
inspection form.
|
|
95
106
|
|
|
96
107
|
Read doctor's recorded result, not a process exit code. Exit codes are knowable
|
|
97
108
|
only from implementation files this skill may not cite, so do not branch on one.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: campaign-readback-classification
|
|
3
|
-
version: 1.0.
|
|
3
|
+
version: 1.0.17
|
|
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.46.0+skills.1
|
|
8
|
+
Run `npx --no-install campaigns-os tooling status --skills-revision 1.46.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
|