@nextcommerce/campaigns-os 1.33.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 +204 -0
- package/CHANGELOG.md +5002 -0
- package/CONTEXT.md +685 -0
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +368 -0
- package/agents/claude/CLAUDE.md +32 -0
- package/agents/codex/AGENTS.md +27 -0
- package/agents/copilot/copilot-instructions.md +14 -0
- package/agents/cursor/campaigns-os.mdc +13 -0
- package/bin/campaigns-os.mjs +38 -0
- package/campaign-spec/README.md +138 -0
- package/campaign-spec/dist/analytics-vocabulary.d.ts +47 -0
- package/campaign-spec/dist/analytics-vocabulary.js +74 -0
- package/campaign-spec/dist/index.d.ts +40 -0
- package/campaign-spec/dist/index.js +77 -0
- package/campaign-spec/dist/normalize.d.ts +22 -0
- package/campaign-spec/dist/normalize.js +41 -0
- package/campaign-spec/dist/routing.d.ts +190 -0
- package/campaign-spec/dist/routing.js +263 -0
- package/campaign-spec/dist/rules/analytics-contract-shape.d.ts +42 -0
- package/campaign-spec/dist/rules/analytics-contract-shape.js +303 -0
- package/campaign-spec/dist/rules/assembly-hints-shape.d.ts +42 -0
- package/campaign-spec/dist/rules/assembly-hints-shape.js +191 -0
- package/campaign-spec/dist/rules/campaign-metadata.d.ts +14 -0
- package/campaign-spec/dist/rules/campaign-metadata.js +40 -0
- package/campaign-spec/dist/rules/checkout-has-success-url.d.ts +31 -0
- package/campaign-spec/dist/rules/checkout-has-success-url.js +66 -0
- package/campaign-spec/dist/rules/cycle-detection.d.ts +12 -0
- package/campaign-spec/dist/rules/cycle-detection.js +141 -0
- package/campaign-spec/dist/rules/design-source-shape.d.ts +29 -0
- package/campaign-spec/dist/rules/design-source-shape.js +142 -0
- package/campaign-spec/dist/rules/downsell-without-upsell.d.ts +11 -0
- package/campaign-spec/dist/rules/downsell-without-upsell.js +40 -0
- package/campaign-spec/dist/rules/exit-intent-validation.d.ts +23 -0
- package/campaign-spec/dist/rules/exit-intent-validation.js +147 -0
- package/campaign-spec/dist/rules/funnel-count.d.ts +9 -0
- package/campaign-spec/dist/rules/funnel-count.js +37 -0
- package/campaign-spec/dist/rules/funnel-hypothesis-length.d.ts +22 -0
- package/campaign-spec/dist/rules/funnel-hypothesis-length.js +60 -0
- package/campaign-spec/dist/rules/funnel-identity.d.ts +15 -0
- package/campaign-spec/dist/rules/funnel-identity.js +57 -0
- package/campaign-spec/dist/rules/funnel-weight-sum.d.ts +20 -0
- package/campaign-spec/dist/rules/funnel-weight-sum.js +66 -0
- package/campaign-spec/dist/rules/index.d.ts +69 -0
- package/campaign-spec/dist/rules/index.js +131 -0
- package/campaign-spec/dist/rules/offer-ref-integrity.d.ts +13 -0
- package/campaign-spec/dist/rules/offer-ref-integrity.js +59 -0
- package/campaign-spec/dist/rules/package-pricing-sanity.d.ts +12 -0
- package/campaign-spec/dist/rules/package-pricing-sanity.js +42 -0
- package/campaign-spec/dist/rules/page-count.d.ts +11 -0
- package/campaign-spec/dist/rules/page-count.js +31 -0
- package/campaign-spec/dist/rules/page-id-uniqueness.d.ts +14 -0
- package/campaign-spec/dist/rules/page-id-uniqueness.js +47 -0
- package/campaign-spec/dist/rules/promo-code-input-validation.d.ts +8 -0
- package/campaign-spec/dist/rules/promo-code-input-validation.js +126 -0
- package/campaign-spec/dist/rules/promo-codes-shape.d.ts +30 -0
- package/campaign-spec/dist/rules/promo-codes-shape.js +187 -0
- package/campaign-spec/dist/rules/route-field-ignored-for-page-type.d.ts +33 -0
- package/campaign-spec/dist/rules/route-field-ignored-for-page-type.js +81 -0
- package/campaign-spec/dist/rules/route-target-resolves.d.ts +32 -0
- package/campaign-spec/dist/rules/route-target-resolves.js +112 -0
- package/campaign-spec/dist/rules/schema-version.d.ts +21 -0
- package/campaign-spec/dist/rules/schema-version.js +53 -0
- package/campaign-spec/dist/rules/sdk-version.d.ts +26 -0
- package/campaign-spec/dist/rules/sdk-version.js +96 -0
- package/campaign-spec/dist/rules/shipping-countries-shape.d.ts +10 -0
- package/campaign-spec/dist/rules/shipping-countries-shape.js +30 -0
- package/campaign-spec/dist/rules/shipping-methods-present.d.ts +10 -0
- package/campaign-spec/dist/rules/shipping-methods-present.js +26 -0
- package/campaign-spec/dist/rules/store-profile-shape.d.ts +30 -0
- package/campaign-spec/dist/rules/store-profile-shape.js +127 -0
- package/campaign-spec/dist/rules/thank-you-requirement.d.ts +16 -0
- package/campaign-spec/dist/rules/thank-you-requirement.js +50 -0
- package/campaign-spec/dist/rules/unknown-top-level-fields.d.ts +28 -0
- package/campaign-spec/dist/rules/unknown-top-level-fields.js +114 -0
- package/campaign-spec/dist/rules/upsell-has-packages.d.ts +9 -0
- package/campaign-spec/dist/rules/upsell-has-packages.js +35 -0
- package/campaign-spec/dist/rules/upsell-routing-complete.d.ts +10 -0
- package/campaign-spec/dist/rules/upsell-routing-complete.js +45 -0
- package/campaign-spec/dist/rules/upsell-without-checkout.d.ts +11 -0
- package/campaign-spec/dist/rules/upsell-without-checkout.js +44 -0
- package/campaign-spec/dist/rules/variant-labels-shape.d.ts +28 -0
- package/campaign-spec/dist/rules/variant-labels-shape.js +86 -0
- package/campaign-spec/dist/sdk-version-parse.d.ts +43 -0
- package/campaign-spec/dist/sdk-version-parse.js +62 -0
- package/campaign-spec/dist/types.d.ts +674 -0
- package/campaign-spec/dist/types.js +40 -0
- package/campaign-spec/package.json +12 -0
- package/compatibility.json +25 -0
- package/contracts/agent-relevant-change-policy.v1.json +111 -0
- package/contracts/brand-theme-source-defaults.figma-sections-export.v0.json +32 -0
- package/contracts/brand-theme-target-tokens.next-core.v0.json +65 -0
- package/contracts/campaign-cart-checkout-field-contract.v0.json +45 -0
- package/contracts/campaign-cart-sdk-support-policy.v0.json +11 -0
- package/contracts/commerce-surface-catalog.json +2452 -0
- package/contracts/fixtures/orientation/canonicalization/v1.json +34 -0
- package/contracts/fixtures/orientation/envelope/current.json +95 -0
- package/contracts/fixtures/orientation/envelope/freshness_unknown.json +97 -0
- package/contracts/fixtures/orientation/envelope/legacy_baseline.json +95 -0
- package/contracts/fixtures/orientation/envelope/orientation_available.json +136 -0
- package/contracts/fixtures/orientation/envelope/recovered_interrupted_update.json +136 -0
- package/contracts/fixtures/orientation/envelope/refused.json +100 -0
- package/contracts/fixtures/orientation/envelope/restart_required.json +137 -0
- package/contracts/fixtures/orientation/envelope/updated.json +135 -0
- package/contracts/fixtures/orientation/hostile-target/README.md +61 -0
- package/contracts/fixtures/orientation/hostile-target/manifest.json +82 -0
- package/contracts/fixtures/orientation/hostile-target/repo/CHANGELOG.md +18 -0
- package/contracts/fixtures/orientation/hostile-target/repo/bin/intended.mjs +12 -0
- package/contracts/fixtures/orientation/hostile-target/repo/bin/tripwire.mjs +15 -0
- package/contracts/fixtures/orientation/hostile-target/repo/contracts/release-ledger.json +48 -0
- package/contracts/fixtures/orientation/hostile-target/repo/contracts/supported-surface.json +17 -0
- package/contracts/fixtures/orientation/hostile-target/repo/docs/example-contract.md +13 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hooks/post-checkout +5 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hooks/post-merge +5 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hooks/pre-commit +5 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hostile-dependency-tripwire/package.json +15 -0
- package/contracts/fixtures/orientation/hostile-target/repo/hostile-dependency-tripwire/tripwire.mjs +9 -0
- package/contracts/fixtures/orientation/hostile-target/repo/package.json +20 -0
- package/contracts/fixtures/orientation/hostile-target/repo/schemas/example.v0.schema.json +15 -0
- package/contracts/fixtures/orientation/release-gate/cases.json +1073 -0
- package/contracts/fixtures/runtime-recipe/accept/current.json +299 -0
- package/contracts/fixtures/runtime-recipe/accept/minimal.json +294 -0
- package/contracts/fixtures/runtime-recipe/dist-states.json +51 -0
- package/contracts/fixtures/runtime-recipe/manifest.json +85 -0
- package/contracts/fixtures/runtime-recipe/reject/advisory-enforcement.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/allowlist-without-hosts.json +297 -0
- package/contracts/fixtures/runtime-recipe/reject/committed-output-claim.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/engines-disagreement-warns.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/lifecycle-scripts-enabled.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/missing-required-field.json +251 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-kind.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-network-policy.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-output-check.json +310 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-revision.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unknown-step-id.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unperformable-check-skipped.json +299 -0
- package/contracts/fixtures/runtime-recipe/reject/unpinned-lockfile.json +299 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/assembly-report.json +180 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/build-context.json +115 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/doctor-output.json +29 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/.campaign-runtime/qa-verdict.json +27 -0
- package/contracts/fixtures/sidecar-bundle/production-shaped/campaign-runtime.build.json +141 -0
- package/contracts/migration-sidecar-bundle.v0.json +149 -0
- package/contracts/orientation-limits.v1.json +41 -0
- package/contracts/orientation-reason-codes.v1.json +196 -0
- package/contracts/private-template-sources.json +8 -0
- package/contracts/release-ledger.json +4598 -0
- package/contracts/reserved-skill-names.json +13 -0
- package/contracts/runtime-recipe.campaigns-os-node-v1.json +299 -0
- package/contracts/supported-surface.json +180 -0
- package/contracts/template-brand-contract.apollo-mv-single-step.v0.json +27 -0
- package/contracts/template-brand-contract.apollo.v0.json +27 -0
- package/contracts/template-brand-contract.demeter.v0.json +27 -0
- package/contracts/template-brand-contract.olympus-mv-single-step.v0.json +27 -0
- package/contracts/template-brand-contract.olympus-mv-two-step.v0.json +28 -0
- package/contracts/template-brand-contract.olympus.v0.json +27 -0
- package/contracts/template-brand-contract.shared-commerce.v0.json +190 -0
- package/contracts/template-brand-contract.shop-single-step.v0.json +27 -0
- package/contracts/template-brand-contract.shop-three-step.v0.json +29 -0
- package/contracts/template-slot-manifest.apollo-mv-single-step.v0.json +14 -0
- package/contracts/template-slot-manifest.apollo.v0.json +12 -0
- package/contracts/template-slot-manifest.demeter.v0.json +30 -0
- package/contracts/template-slot-manifest.olympus-mv-single-step.v0.json +14 -0
- package/contracts/template-slot-manifest.olympus-mv-two-step.v0.json +15 -0
- package/contracts/template-slot-manifest.olympus.v0.json +12 -0
- package/contracts/template-slot-manifest.shared-content-core.v0.json +4254 -0
- package/contracts/template-slot-manifest.shop-single-step.v0.json +47 -0
- package/contracts/template-slot-manifest.shop-three-step.v0.json +33 -0
- package/docs/brand-theme-bridge.md +159 -0
- package/docs/build-packet.md +1300 -0
- package/docs/campaign-build-brief.md +145 -0
- package/docs/campaign-standardization-report.md +329 -0
- package/docs/campaigns-os-build-flow.md +117 -0
- package/docs/design-source-package.md +784 -0
- package/docs/legacy-migration.md +58 -0
- package/docs/migration-sidecar-bundle.md +139 -0
- package/docs/orientation-contract-reference.md +1220 -0
- package/docs/polish-evidence.md +502 -0
- package/docs/qa-and-test-orders.md +1691 -0
- package/docs/release-ledger-authoring-guide.md +274 -0
- package/docs/runtime-readiness.md +211 -0
- package/docs/supported-surface.md +83 -0
- package/docs/versioning.md +55 -0
- package/docs/workflow-findings-sidecar.md +588 -0
- package/package.json +135 -0
- package/prompts/first-build.md +27 -0
- package/prompts/friction-log.md +30 -0
- package/schemas/campaign-build-brief.v1.schema.json +149 -0
- package/schemas/campaign-design-source-package.v0.schema.json +697 -0
- package/schemas/campaign-runtime-assembly-report.v0.schema.json +390 -0
- package/schemas/campaign-runtime-build-context.v0.schema.json +339 -0
- package/schemas/campaign-runtime-build-packet.v0.schema.json +452 -0
- package/schemas/campaign-spec.v4.schema.json +582 -0
- package/schemas/campaigns-os-doctor-output.v0.schema.json +43 -0
- package/schemas/campaigns-os-legacy-migration-inventory.v0.schema.json +112 -0
- package/schemas/campaigns-os-legacy-provisioning-plan.v0.schema.json +38 -0
- package/schemas/campaigns-os-legacy-provisioning-receipt.v0.schema.json +57 -0
- package/schemas/campaigns-os-qa-verdict-sidecar.v0.schema.json +150 -0
- package/schemas/campaigns-os-qa-verdict.v0.schema.json +438 -0
- package/schemas/campaigns-os-release-ledger.v1.schema.json +162 -0
- package/schemas/campaigns-os-run-record.v0.schema.json +343 -0
- package/schemas/campaigns-os-runtime-recipe.v1.schema.json +313 -0
- package/schemas/campaigns-os-sidecar-bundle-conformance.v0.schema.json +78 -0
- package/schemas/campaigns-os-tooling-orientation.v1.schema.json +381 -0
- package/schemas/campaigns-os-workflow-finding.v0.schema.json +134 -0
- package/schemas/source-html-manifest.v0.schema.json +252 -0
- package/skills/next-campaigns-build/SKILL.md +73 -0
- package/skills/next-campaigns-os/SKILL.md +101 -0
- package/skills/next-campaigns-os/references/session-intake.md +160 -0
- package/skills/next-campaigns-os-setup/SKILL.md +22 -0
- package/skills/next-campaigns-polish/SKILL.md +145 -0
- package/skills/next-campaigns-qa/SKILL.md +92 -0
- package/skills.json +56 -0
- package/skills.sh +64 -0
- package/src/adapter-decision-contract.mjs +333 -0
- package/src/brand-theme.mjs +1151 -0
- package/src/browser-launch.mjs +79 -0
- package/src/build-brief.mjs +781 -0
- package/src/built-site-scope.mjs +312 -0
- package/src/campaign-ecosystem.mjs +734 -0
- package/src/campaign-identity.mjs +405 -0
- package/src/campaign-workspace.mjs +127 -0
- package/src/checkpoint-waiver.mjs +302 -0
- package/src/cli.mjs +13442 -0
- package/src/commercial-journey.mjs +1119 -0
- package/src/commercial-parity.mjs +965 -0
- package/src/consent.mjs +347 -0
- package/src/content-residue.mjs +322 -0
- package/src/deadline.mjs +81 -0
- package/src/design-source-package.mjs +2604 -0
- package/src/deviation.mjs +107 -0
- package/src/doctor-check-registry.mjs +49 -0
- package/src/doctor-sidecar.mjs +106 -0
- package/src/finding-cause.mjs +557 -0
- package/src/findings.mjs +326 -0
- package/src/fs-identity.mjs +68 -0
- package/src/gate-actions.mjs +105 -0
- package/src/html-scan.mjs +53 -0
- package/src/install-mode.mjs +272 -0
- package/src/legacy-migration.d.ts +128 -0
- package/src/legacy-migration.mjs +510 -0
- package/src/lifecycle.mjs +338 -0
- package/src/local-proof.mjs +401 -0
- package/src/map-pin-writeback.mjs +210 -0
- package/src/orchestration-stage-contract.mjs +81 -0
- package/src/package-install-fixture.mjs +33 -0
- package/src/page-kit-build-summary.mjs +175 -0
- package/src/page-kit-campaign-config.mjs +57 -0
- package/src/page-kit-sdk-version.mjs +392 -0
- package/src/page-kit-store-profile.mjs +369 -0
- package/src/page-kit-sync.mjs +162 -0
- package/src/polish-browser.mjs +867 -0
- package/src/polish-capture.mjs +1094 -0
- package/src/polish-deadline.mjs +51 -0
- package/src/polish-gate.mjs +739 -0
- package/src/polish-node.mjs +639 -0
- package/src/polish-page-load.mjs +1100 -0
- package/src/private-template-source.mjs +237 -0
- package/src/proof-policy.mjs +82 -0
- package/src/qa-analytics-correctness.mjs +307 -0
- package/src/qa-analytics-errors.mjs +38 -0
- package/src/qa-analytics-parity.mjs +699 -0
- package/src/qa-binding-evidence.mjs +140 -0
- package/src/qa-browser.mjs +6608 -0
- package/src/qa-cart-entry.mjs +406 -0
- package/src/qa-commercial-parity.mjs +641 -0
- package/src/qa-node.mjs +3620 -0
- package/src/qa-order-bump.mjs +381 -0
- package/src/qa-parity-capture.mjs +428 -0
- package/src/qa-parity-fixture.mjs +359 -0
- package/src/qa-publish.mjs +362 -0
- package/src/qa-purchase-data-layer.mjs +263 -0
- package/src/qa-route-probe.mjs +272 -0
- package/src/qa-sidecar.mjs +188 -0
- package/src/qa-test-order-topology.mjs +207 -0
- package/src/qa-url-privacy.mjs +13 -0
- package/src/qa-verdict-discovery.mjs +192 -0
- package/src/qa-verdict-publish.mjs +105 -0
- package/src/qa-verdict.mjs +287 -0
- package/src/remit.mjs +388 -0
- package/src/repo-scan.mjs +83 -0
- package/src/route-identity.mjs +133 -0
- package/src/run-record-closeout.mjs +229 -0
- package/src/run-record.mjs +839 -0
- package/src/run-session.mjs +226 -0
- package/src/runtime-state-ignore.mjs +113 -0
- package/src/sdk-attribute-index.mjs +212 -0
- package/src/sdk-markup.mjs +358 -0
- package/src/sdk-meta-tags.mjs +52 -0
- package/src/shell-token.mjs +7 -0
- package/src/sidecar-bundle.mjs +399 -0
- package/src/source-asset-crawl.mjs +469 -0
- package/src/source-html-intake.mjs +627 -0
- package/src/source-html-manifest.mjs +276 -0
- package/src/source-prep.mjs +284 -0
- package/src/spec-derive-store.mjs +431 -0
- package/src/spec-derive.mjs +514 -0
- package/src/spec-fetch.mjs +66 -0
- package/src/spec-hash.mjs +48 -0
- package/src/spec-identity.mjs +27 -0
- package/src/stage-ledger.mjs +509 -0
- package/src/standardization-report.mjs +1297 -0
- package/src/template-brand-contract.mjs +473 -0
- package/src/template-freshness.mjs +196 -0
- package/src/template-reference.mjs +81 -0
- package/src/template-slot-manifest.mjs +150 -0
- package/src/text-safety.mjs +62 -0
- package/src/theme-gate.mjs +185 -0
- package/src/upsell-selector-scope.mjs +299 -0
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
# Design Source Package v0
|
|
2
|
+
|
|
3
|
+
The Design Source Package is the durable, normalized creative/provenance input
|
|
4
|
+
that Build and Polish consume. It does not replace CampaignSpec commerce truth,
|
|
5
|
+
the Campaign Build Brief, Template Reference runtime proof, or Polish Evidence.
|
|
6
|
+
Those artifacts stay separate and join through stable Surface Identity and
|
|
7
|
+
fingerprints.
|
|
8
|
+
|
|
9
|
+
This guide documents the v0 implementation shipped by this repository. The
|
|
10
|
+
normative JSON shape is
|
|
11
|
+
[`schemas/campaign-design-source-package.v0.schema.json`](../schemas/campaign-design-source-package.v0.schema.json),
|
|
12
|
+
and the runtime validator and producer enforce additional cross-record and
|
|
13
|
+
current-input checks.
|
|
14
|
+
|
|
15
|
+
## Contract identity and compatibility
|
|
16
|
+
|
|
17
|
+
- Schema identity: `campaign-design-source-package/v0`.
|
|
18
|
+
- Schema file: `schemas/campaign-design-source-package.v0.schema.json`.
|
|
19
|
+
- Default target-repo path:
|
|
20
|
+
`.campaign-runtime/input/design-source-package.json`.
|
|
21
|
+
- Current `prepare-build` producer: `source_kind: "html_funnel"`.
|
|
22
|
+
- Current source adapter remains visible in `packet.source_html`. During v0,
|
|
23
|
+
`prepare-build` emits that compatibility block alongside the normalized
|
|
24
|
+
package; the source-html manifest and page mappings seed the package but do
|
|
25
|
+
not become it.
|
|
26
|
+
|
|
27
|
+
The package is strict at its top level. It contains `schema_version`,
|
|
28
|
+
`package_id`, `source_kind`, `generated_at`, `material_fingerprint`,
|
|
29
|
+
`surface_identity`, `contributions`, `source_gaps`, `source_todos`, `waivers`,
|
|
30
|
+
`divergences`, `proposed_exceptions`, `notes`, `readiness`, and `readback`.
|
|
31
|
+
Unknown top-level fields are rejected.
|
|
32
|
+
|
|
33
|
+
## Contributions and coverage
|
|
34
|
+
|
|
35
|
+
Each `contributions[]` record identifies one source contribution and records:
|
|
36
|
+
|
|
37
|
+
- `id`, `kind`, `trust`, and whether it is `renderable`;
|
|
38
|
+
- structured `provenance` and `presentation_intent`;
|
|
39
|
+
- `source_refs`, source-side `screenshot_refs`, and comparison-side
|
|
40
|
+
`reference_refs`;
|
|
41
|
+
- optional `template_reference` proof; and
|
|
42
|
+
- `mappings` that join the contribution to Surface Identity.
|
|
43
|
+
|
|
44
|
+
The v0 contribution kinds are `html_funnel`, `figma_frames`, `figma_sections`,
|
|
45
|
+
`page_kit`, `static_source`, `template_stock`, `agency_source`, and `other`.
|
|
46
|
+
Trust is `native`, `structured`, `rendered`, or `opaque`.
|
|
47
|
+
|
|
48
|
+
Every contribution mapping uses exactly one `coverage_role`:
|
|
49
|
+
|
|
50
|
+
- `primary_design`
|
|
51
|
+
- `partial_design`
|
|
52
|
+
- `brand_tokens`
|
|
53
|
+
- `asset_source`
|
|
54
|
+
- `copy_source`
|
|
55
|
+
- `template_baseline`
|
|
56
|
+
- `reference_only`
|
|
57
|
+
- `fallback_legacy`
|
|
58
|
+
|
|
59
|
+
Mapping `confidence` is `high`, `medium`, `low`, or `unknown`. It describes
|
|
60
|
+
confidence in the source-to-surface relationship, not design quality or
|
|
61
|
+
approval. A page-level `primary_design` claim must be `high` or `medium` to
|
|
62
|
+
qualify for readiness.
|
|
63
|
+
|
|
64
|
+
## Surface Identity
|
|
65
|
+
|
|
66
|
+
`surface_identity[]` is the campaign-facing join catalog. Exactly one entry
|
|
67
|
+
must reserve this identity pair:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"id": "campaign",
|
|
72
|
+
"kind": "campaign"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The `campaign` ID and `campaign` kind are reserved for each other; the other
|
|
77
|
+
fields are not required to be empty. Current synthesis sets `label: "Campaign"`,
|
|
78
|
+
adds the current campaign Map ID and route slug to `aliases` when present, and
|
|
79
|
+
records those values as `mappings.campaign_map_id` and
|
|
80
|
+
`mappings.campaign_slug`. Current-input validation refuses an existing package
|
|
81
|
+
that omits or changes either mapping when the corresponding current value is
|
|
82
|
+
available. Campaign scope is used for legitimate campaign-wide gaps, TODOs,
|
|
83
|
+
waivers, divergences, and exceptions.
|
|
84
|
+
|
|
85
|
+
Current synthesis also creates a `kind: "page"` Surface Identity for every
|
|
86
|
+
active CampaignSpec page or mapped source page. Stable, human-semantic
|
|
87
|
+
CampaignSpec page IDs are preferred; otherwise the producer derives an ID from
|
|
88
|
+
the normalized page role and order. Section and runtime-surface entries are
|
|
89
|
+
allowed, but `prepare-build` does not invent them.
|
|
90
|
+
|
|
91
|
+
Page Surface Identity keeps these namespaces distinct in `mappings`:
|
|
92
|
+
|
|
93
|
+
- `campaign_spec_page_id`
|
|
94
|
+
- `map_builder_label`
|
|
95
|
+
- `map_builder_custom_name`
|
|
96
|
+
- `public_route`
|
|
97
|
+
- `producer_page_type`
|
|
98
|
+
- `source_page_id`
|
|
99
|
+
- `source_path`
|
|
100
|
+
- `page_kit`
|
|
101
|
+
|
|
102
|
+
The `page_kit` projection contains `target_path`, `output_path`,
|
|
103
|
+
`public_route`, `page_type`, and optional `spec_route` and
|
|
104
|
+
`permalink_required`. These fields, public routes, producer IDs, and DOM or
|
|
105
|
+
runtime names are mappings or aliases; none replaces Surface Identity.
|
|
106
|
+
|
|
107
|
+
## Source and visual references
|
|
108
|
+
|
|
109
|
+
Source references have a stable `id`, a `kind`, and at least one of `path` or
|
|
110
|
+
`url`. Their kinds are `html_file`, `manifest`, `asset`, `url`, `export`,
|
|
111
|
+
`document`, and `other`. They may also carry a byte hash, role, media type, and
|
|
112
|
+
notes.
|
|
113
|
+
|
|
114
|
+
Visual references use shared viewport keys: `desktop`, `mobile`, and optional
|
|
115
|
+
`tablet`. Their kinds are:
|
|
116
|
+
|
|
117
|
+
- `source_screenshot`
|
|
118
|
+
- `template_reference_screenshot`
|
|
119
|
+
- `render_reference`
|
|
120
|
+
- `export_reference`
|
|
121
|
+
- `unavailable_render`
|
|
122
|
+
|
|
123
|
+
An available visual requires a path or URL. An unavailable visual requires an
|
|
124
|
+
`unavailable_reason`. Available source and Template Reference screenshots must
|
|
125
|
+
also link to a known source record with `source_ref_id`. Width, height, device
|
|
126
|
+
profile, scale factor, browser, and capture time are metadata; they do not
|
|
127
|
+
create additional viewport names.
|
|
128
|
+
|
|
129
|
+
A coverage mapping cites reference IDs through `source_refs`,
|
|
130
|
+
`screenshot_refs`, and `reference_refs`. Readiness counts only correctly typed,
|
|
131
|
+
linked proof. A render reference, a generic image asset, or an image without an
|
|
132
|
+
explicit viewport is not silently promoted to source-screenshot proof. An
|
|
133
|
+
`unavailable_render` documents an absence but does not satisfy an available
|
|
134
|
+
desktop/mobile proof requirement.
|
|
135
|
+
|
|
136
|
+
### Source screenshot behavior
|
|
137
|
+
|
|
138
|
+
Primary-design coverage is evaluated existentially per page, not independently
|
|
139
|
+
for every claim. To satisfy the page through `primary_design`, at least one
|
|
140
|
+
`high` or `medium` claim must qualify. A non-renderable contribution needs no
|
|
141
|
+
screenshot proof; a renderable one qualifies only with available, linked
|
|
142
|
+
`source_screenshot` proof for both `desktop` and `mobile`. Tablet is optional in
|
|
143
|
+
v0. Once one qualifying claim has the required proof, another incomplete claim
|
|
144
|
+
for the same page does not create a screenshot blocker or TODO.
|
|
145
|
+
|
|
146
|
+
If the page has one or more high/medium claims but none has the required proof,
|
|
147
|
+
synthesis selects the claim with the fewest missing standard viewports (then by
|
|
148
|
+
stable contribution/mapping ID order) and produces one blocking
|
|
149
|
+
`missing_source_screenshot` Source TODO per viewport missing from that claim.
|
|
150
|
+
An accepted screenshot-coverage Source Gap or active approved waiver supersedes
|
|
151
|
+
those TODOs.
|
|
152
|
+
|
|
153
|
+
Low or unknown primary coverage produces a blocking
|
|
154
|
+
`low_confidence_primary_design` TODO only when the page has no high/medium
|
|
155
|
+
primary claim and no accepted primary-coverage Source Gap or active approved
|
|
156
|
+
waiver. If there is no primary claim or coverage exception, an incomplete
|
|
157
|
+
selected Template Reference produces the specific TODO described below; a
|
|
158
|
+
complete proven `template_baseline` satisfies coverage, and otherwise the page
|
|
159
|
+
receives a blocking `missing_primary_design` TODO.
|
|
160
|
+
|
|
161
|
+
### Template Reference behavior
|
|
162
|
+
|
|
163
|
+
A template family name alone is not proof. `template_baseline` coverage is
|
|
164
|
+
emitted only when the contribution has a Template Reference with:
|
|
165
|
+
|
|
166
|
+
- an `id`, `family`, and `version`;
|
|
167
|
+
- a `contract_path` or `artifact_path`; and
|
|
168
|
+
- available, linked `template_reference_screenshot` records for the standard
|
|
169
|
+
`desktop` and `mobile` viewports.
|
|
170
|
+
|
|
171
|
+
If family selection lacks the version/location linkage, synthesis emits a
|
|
172
|
+
blocking `missing_template_reference` TODO. If the Template Reference is linked
|
|
173
|
+
but a standard viewport is absent, it emits a blocking
|
|
174
|
+
`missing_template_viewport` TODO for that viewport. Until proof is complete,
|
|
175
|
+
the template contribution carries no `template_baseline` mapping; the producer
|
|
176
|
+
does not invent a reference or viewport capture.
|
|
177
|
+
|
|
178
|
+
## Gaps, TODOs, waivers, and readiness
|
|
179
|
+
|
|
180
|
+
The detailed records are authoritative. `readiness` and `readback` are generated
|
|
181
|
+
summaries and must agree with them.
|
|
182
|
+
|
|
183
|
+
- Source Gap kinds are `coverage_absence`, `screenshot_absence`,
|
|
184
|
+
`reference_absence`, `source_limitation`, and `other`; statuses are
|
|
185
|
+
`proposed`, `accepted`, and `resolved`. A proposed gap blocks. An accepted
|
|
186
|
+
gap with the matching kind, scope, and `applies_to` target may satisfy the
|
|
187
|
+
relevant absence and yield `ready_with_gaps`.
|
|
188
|
+
- Source TODOs are `pending`, `blocked`, `completed`, or `skipped`. Any
|
|
189
|
+
non-completed TODO blocks unless an active approved waiver matches its record
|
|
190
|
+
or affected surface. A skipped TODO without that waiver is invalid as well
|
|
191
|
+
as blocking.
|
|
192
|
+
- Source TODO kinds are `missing_source_screenshot`,
|
|
193
|
+
`missing_template_reference`, `missing_template_viewport`,
|
|
194
|
+
`missing_primary_design`, `low_confidence_primary_design`,
|
|
195
|
+
`unreadable_reference`, and `other`.
|
|
196
|
+
- Waivers are `proposed`, `approved`, `revoked`, or `expired`. An effective
|
|
197
|
+
waiver must be approved, attributed, and bounded by a future `expires_at` or
|
|
198
|
+
a non-empty `review_condition`.
|
|
199
|
+
- A proposed readiness-affecting divergence or proposed exception blocks.
|
|
200
|
+
Non-readiness-affecting records remain visible without creating that blocker.
|
|
201
|
+
|
|
202
|
+
Design Source Package readiness uses only:
|
|
203
|
+
|
|
204
|
+
- `pending`
|
|
205
|
+
- `blocked`
|
|
206
|
+
- `ready`
|
|
207
|
+
- `ready_with_gaps`
|
|
208
|
+
- `ready_with_waivers`
|
|
209
|
+
|
|
210
|
+
There is no `ready_with_warnings` source-readiness state. Warning-like source
|
|
211
|
+
conditions must be represented as a concrete gap, TODO, divergence, proposed
|
|
212
|
+
exception, note, or waiver. Notes never clear a blocker.
|
|
213
|
+
|
|
214
|
+
The shared checkpoint vocabulary additionally includes `completed`,
|
|
215
|
+
`completed_with_warnings`, and `skipped`, but those are not valid values for
|
|
216
|
+
`design_source_package.readiness.status`.
|
|
217
|
+
|
|
218
|
+
The generated `readiness` record also carries `blocking_reasons`, total
|
|
219
|
+
`gap_count`, `todo_count`, and `waiver_count`, plus `generated_at`. The generated
|
|
220
|
+
`readback` has the fixed buckets `summary`, `included_sources`, `handled`,
|
|
221
|
+
`blockers`, `gaps`, `todos`, `waivers`, and `next_actions`. Validation rejects a
|
|
222
|
+
summary or readback that contradicts the authoritative records.
|
|
223
|
+
|
|
224
|
+
## Artifact references and fingerprints
|
|
225
|
+
|
|
226
|
+
The Build Packet, Build Context, and Assembly Report emitted by
|
|
227
|
+
`prepare-build` each carry the same four-field reference, never an embedded
|
|
228
|
+
package:
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"path": ".campaign-runtime/input/design-source-package.json",
|
|
233
|
+
"schema_version": "campaign-design-source-package/v0",
|
|
234
|
+
"sha256": "sha256:<64 lowercase hex>",
|
|
235
|
+
"material_fingerprint": "sha256:<64 lowercase hex>"
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
`path` is relative to the artifact containing the reference. With default
|
|
240
|
+
locations, the packet uses
|
|
241
|
+
`.campaign-runtime/input/design-source-package.json`, while the context and
|
|
242
|
+
report use `input/design-source-package.json`. A custom nested report receives
|
|
243
|
+
the corresponding artifact-relative path. Consumers must resolve each path
|
|
244
|
+
from its owning artifact instead of comparing the path strings directly.
|
|
245
|
+
|
|
246
|
+
The two hashes have different jobs:
|
|
247
|
+
|
|
248
|
+
- `sha256` hashes the exact package bytes on disk. Whitespace, key order,
|
|
249
|
+
timestamps, notes, and every other serialized byte affect it. When an
|
|
250
|
+
existing package is reused, the reference hashes those original bytes rather
|
|
251
|
+
than a reserialization.
|
|
252
|
+
- `material_fingerprint` hashes canonical JSON for the explicit v0 material
|
|
253
|
+
projection. It drives Build/Polish freshness and is not a whole-JSON hash.
|
|
254
|
+
|
|
255
|
+
### Exact v0 material projection
|
|
256
|
+
|
|
257
|
+
The material projection contains:
|
|
258
|
+
|
|
259
|
+
- top-level `schema_version` and `source_kind`;
|
|
260
|
+
- every Surface Identity's `id`, `kind`, `label`, aliases, and mappings,
|
|
261
|
+
including the Page Kit projection;
|
|
262
|
+
- every contribution's identity, kind, trust, renderability, provenance,
|
|
263
|
+
presentation intent, source references, source screenshot/comparison
|
|
264
|
+
references, Template Reference, and coverage mappings;
|
|
265
|
+
- all Source Gaps and Source TODOs;
|
|
266
|
+
- approved waivers only; and
|
|
267
|
+
- divergences and proposed exceptions whose `readiness_affecting` value is
|
|
268
|
+
`true`.
|
|
269
|
+
|
|
270
|
+
The projected fields are explicit:
|
|
271
|
+
|
|
272
|
+
| Record | Fields in the v0 projection |
|
|
273
|
+
| --- | --- |
|
|
274
|
+
| Surface Identity | `id`, `kind`, `label`, `aliases`, and `mappings`: `campaign_map_id`, `campaign_slug`, `campaign_spec_page_id`, `map_builder_label`, `map_builder_custom_name`, `public_route`, `producer_page_type`, `source_page_id`, `source_path`, `page_kit`, `parent_surface_id`, `dom_selector`, `runtime_surface`. |
|
|
275
|
+
| Contribution core | `id`, `kind`, `trust`, `renderable`. |
|
|
276
|
+
| Provenance | `source_type`, `adapter`, `producer`, `generator`, `generator_version`, `source_root`, `manifest_schema_version`, `manifest_path`, `manifest_sha256`, `producer_material_fingerprint`, `asset_crawl_schema_version`. |
|
|
277
|
+
| Presentation intent | `summary`, `composition`, `content_hierarchy`, `imagery`, `copy`, `brand`, `responsive_behavior`. |
|
|
278
|
+
| Source reference | `id`, `kind`, `path`, `url`, `sha256`, `role`, `media_type`. |
|
|
279
|
+
| Visual reference | `id`, `kind`, `viewport`, `availability`, `url`, `path`, `sha256`, `width`, `height`, `device_profile`, `scale_factor`, `browser`, `source_ref_id`, `unavailable_reason`. |
|
|
280
|
+
| Template Reference | `id`, `family`, `version`, `contract_path`, `artifact_path`, `sha256`, `standard_viewport_refs` using the visual projection above. |
|
|
281
|
+
| Coverage mapping | `id`, `surface_id`, `coverage_role`, `confidence`, `source_refs`, `screenshot_refs`, `reference_refs`, `template_reference_id`. |
|
|
282
|
+
| Source Gap | `id`, `kind`, `scope`, `applies_to`, `reason`, `status`, `attributed_by`, `attributed_at`, `evidence_refs`. |
|
|
283
|
+
| Source TODO | `id`, `kind`, `scope`, `applies_to`, `description`, `status`, `owner`, `required_viewports`, `source_ref_ids`. |
|
|
284
|
+
| Approved waiver | `id`, `scope`, `applies_to`, `reason`, `status`, `waived_by`, `waived_at`, `expires_at`, `review_condition`, `evidence_refs`. |
|
|
285
|
+
| Readiness-affecting divergence | `id`, `scope`, `applies_to`, `summary`, `status`, `recorded_stage`, `readiness_affecting`, `attributed_by`, `evidence_refs`. |
|
|
286
|
+
| Readiness-affecting exception | `id`, `scope`, `applies_to`, `reason`, `status`, `readiness_affecting`, `proposed_by`, `evidence_refs`. |
|
|
287
|
+
|
|
288
|
+
Record collections and set-like ID lists are normalized deterministically
|
|
289
|
+
before hashing, and nested SHA-256 values have one material identity whether
|
|
290
|
+
written bare or with the `sha256:` prefix.
|
|
291
|
+
|
|
292
|
+
Administrative examples that are not projected include:
|
|
293
|
+
|
|
294
|
+
- top-level `package_id` and `generated_at`;
|
|
295
|
+
- generated `readiness` and `readback` fields;
|
|
296
|
+
- top-level, contribution, mapping, source-reference, and visual-reference
|
|
297
|
+
notes;
|
|
298
|
+
- visual `captured_at` alone; and
|
|
299
|
+
- JSON formatting, object-key order, record-collection order, and set-like
|
|
300
|
+
string-list order where the projection sorts by ID/value.
|
|
301
|
+
|
|
302
|
+
Non-approved waivers and non-readiness-affecting divergences/exceptions are also
|
|
303
|
+
outside the v0 projection. Even when a derived field is non-material, stale or
|
|
304
|
+
invented `readiness` or `readback` still fails validation. A changed
|
|
305
|
+
administrative byte therefore requires a refreshed full `sha256` reference but
|
|
306
|
+
does not, by itself, make Build or Polish stale.
|
|
307
|
+
|
|
308
|
+
## `prepare-build`: emit, validate, or refuse
|
|
309
|
+
|
|
310
|
+
`prepare-build` treats the default package path as an ownership boundary.
|
|
311
|
+
|
|
312
|
+
When the package is missing, it synthesizes the current `html_funnel` package,
|
|
313
|
+
validates it, serializes it, and reports mode `emitted`. Synthesis uses current
|
|
314
|
+
active pages and page mappings, campaign Map ID and route slug, current source
|
|
315
|
+
file hashes, the source-html manifest and its exact byte hash when present, the
|
|
316
|
+
source asset crawl, and the source-side template-family input. A coherent but
|
|
317
|
+
blocked package is still emitted so its TODOs and blockers are durable.
|
|
318
|
+
|
|
319
|
+
When the package already exists, `prepare-build`:
|
|
320
|
+
|
|
321
|
+
1. reads and retains its exact bytes;
|
|
322
|
+
2. parses and validates its strict shape, material fingerprint, generated
|
|
323
|
+
readiness, and generated readback;
|
|
324
|
+
3. validates current campaign and page identity/mappings;
|
|
325
|
+
4. validates current HTML source/provenance, required source refs and byte
|
|
326
|
+
hashes, coverage mappings, and current template material; and
|
|
327
|
+
5. requires `source_kind: "html_funnel"`.
|
|
328
|
+
|
|
329
|
+
If all checks pass, the mode is `reused`: the package bytes and modification
|
|
330
|
+
time are untouched, and all three artifact references use the full hash of
|
|
331
|
+
those exact bytes. Harmless reformatting and administrative notes can therefore
|
|
332
|
+
be reused when the material fingerprint and derived summaries remain valid.
|
|
333
|
+
|
|
334
|
+
If any current campaign, active/mapped page, source material, manifest or crawl
|
|
335
|
+
provenance, coverage, template family/reference, material fingerprint,
|
|
336
|
+
readiness, or readback check fails, `prepare-build` refuses. It leaves the
|
|
337
|
+
existing package and packet/context/report/brief sidecars byte-identical. It
|
|
338
|
+
does not silently regenerate or overwrite the package; source preparation must
|
|
339
|
+
reconcile the package and its references explicitly before retrying.
|
|
340
|
+
|
|
341
|
+
Before writing any output, `prepare-build` also requires distinct paths for the
|
|
342
|
+
Build Packet, Build Context, Assembly Report, Doctor output, normalized Build
|
|
343
|
+
Brief, and fixed Design Source Package. Equal paths and filesystem aliases are
|
|
344
|
+
rejected, including symlinks, hard links, dangling leaf symlinks, and symlinked
|
|
345
|
+
parent directories.
|
|
346
|
+
|
|
347
|
+
This behavior is the implemented v0 compatibility boundary. It does not promise
|
|
348
|
+
that a separate future workflow command will generate, repair, approve, or
|
|
349
|
+
silently refresh the package.
|
|
350
|
+
|
|
351
|
+
## Clearing `DESIGN_SOURCE_PACKAGE_NOT_READY`
|
|
352
|
+
|
|
353
|
+
`prepare-build` and `start` block at intake when a renderable page has no
|
|
354
|
+
qualifying primary-design claim with linked `desktop` and `mobile`
|
|
355
|
+
`source_screenshot` proof. Doctor reports one
|
|
356
|
+
`DESIGN_SOURCE_PACKAGE_NOT_READY` error per blocking reason; the blocked
|
|
357
|
+
`capture-<surface>-<viewport>` Source TODOs are themselves blocking reasons, so a
|
|
358
|
+
four-page funnel with no proof reports twelve. `next` routes back to
|
|
359
|
+
`prepare-build`, and doctor's `next` block says the same. Nothing is wrong with the run: the package is coherent and
|
|
360
|
+
durable, and it is telling you that the source material arrived without visual
|
|
361
|
+
proof.
|
|
362
|
+
|
|
363
|
+
The input channel that supplies that proof is the source-html manifest at
|
|
364
|
+
`<source-root>/.campaigns-os/source-html-manifest.json` — or, when the source
|
|
365
|
+
root is not yours to write, a manifest of the same schema anywhere else, named
|
|
366
|
+
with `--design-manifest <path>` on `start`, `prepare-build`, or `build` (see
|
|
367
|
+
[A read-only source root](#a-read-only-source-root)). Each `pages[]` entry may
|
|
368
|
+
carry a `screenshots[]` array; `prepare-build` reads it, alongside the
|
|
369
|
+
equivalent `screenshot_refs` and `source_screenshot_refs` keys, and normalizes
|
|
370
|
+
each record into the html_funnel contribution's `screenshot_refs`. This is the
|
|
371
|
+
only operator-authored channel that seeds source-screenshot proof (a producer's
|
|
372
|
+
`section_exports[].images` is the other, producer-authored path); the package
|
|
373
|
+
itself is not hand-edited, and there is no capture command.
|
|
374
|
+
|
|
375
|
+
The manifest is read only when the whole file is a valid `source-html-manifest/v0`
|
|
376
|
+
document: `schema_version` set to `"source-html-manifest/v0"` and a `pages[]`
|
|
377
|
+
array whose entries carry the active CampaignSpec `page_id` and exactly one of
|
|
378
|
+
the source-root-relative `path` or a `skip_reason` for a page that has no source
|
|
379
|
+
HTML by design (schema: `schemas/source-html-manifest.v0.schema.json`;
|
|
380
|
+
consumer mechanics: [Source HTML Manifest Auto-Population](build-packet.md#source-html-manifest-auto-population)).
|
|
381
|
+
A manifest that fails validation is reported as a doctor *warning*, not an error:
|
|
382
|
+
`prepare-build` falls back to filesystem matching and never reads `screenshots[]`,
|
|
383
|
+
so the run blocks again with nothing else changed. `node scripts/reference-ai-producer.mjs`
|
|
384
|
+
emits a valid envelope (page ids, paths, `source_hash`) from a folder of HTML
|
|
385
|
+
files; add the `screenshots[]` records to its output, or start from the complete
|
|
386
|
+
example below. (`context.source.manifest_draft` in the build context is populated
|
|
387
|
+
only when filename matching was ambiguous, and is `null` otherwise.)
|
|
388
|
+
|
|
389
|
+
### The record shape
|
|
390
|
+
|
|
391
|
+
One manifest page entry with its proof, inside the envelope the validator requires
|
|
392
|
+
(the mobile record is elided):
|
|
393
|
+
|
|
394
|
+
```json
|
|
395
|
+
{
|
|
396
|
+
"schema_version": "source-html-manifest/v0",
|
|
397
|
+
"pages": [
|
|
398
|
+
{
|
|
399
|
+
"page_id": "landing",
|
|
400
|
+
"path": "landing.html",
|
|
401
|
+
"source_hash": "<64 lowercase hex>",
|
|
402
|
+
"screenshots": [
|
|
403
|
+
{
|
|
404
|
+
"id": "source-landing-desktop",
|
|
405
|
+
"kind": "source_screenshot",
|
|
406
|
+
"viewport": "desktop",
|
|
407
|
+
"availability": "available",
|
|
408
|
+
"path": ".campaigns-os/screenshots/landing-desktop.png",
|
|
409
|
+
"sha256": "<64 lowercase hex>",
|
|
410
|
+
"width": 1440,
|
|
411
|
+
"height": 900,
|
|
412
|
+
"device_profile": "desktop-1440x900",
|
|
413
|
+
"browser": "playwright-chromium/151.0.7922.34",
|
|
414
|
+
"captured_at": "2026-09-06T01:39:57.250Z"
|
|
415
|
+
},
|
|
416
|
+
{ "viewport": "mobile", "path": ".campaigns-os/screenshots/landing-mobile.png" }
|
|
417
|
+
]
|
|
418
|
+
}
|
|
419
|
+
]
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Three fields decide whether a record counts toward the gate:
|
|
424
|
+
|
|
425
|
+
- `viewport` must be `desktop`, `mobile`, or `tablet`. A record without a
|
|
426
|
+
recognized viewport is an asset, not proof, and is dropped.
|
|
427
|
+
- `availability` must be `available`, which means the record carries a `path` or
|
|
428
|
+
a `url`. An `unavailable` record needs an `unavailable_reason`; it documents
|
|
429
|
+
an absence and never satisfies a viewport.
|
|
430
|
+
- `kind` must be `source_screenshot`, which is the default when the field is
|
|
431
|
+
omitted and the only kind that counts as proof. `unavailable_render` is
|
|
432
|
+
accepted by the channel and retained, but it records an absence and never
|
|
433
|
+
satisfies a viewport. Any other kind — `render_reference`, `export_reference` —
|
|
434
|
+
is dropped for this channel rather than promoted.
|
|
435
|
+
|
|
436
|
+
You need one qualifying `desktop` record and one qualifying `mobile` record per
|
|
437
|
+
renderable page. `tablet` is optional in v0. A record that fails any of the three
|
|
438
|
+
tests is still dropped rather than rejecting the manifest, but it no longer goes
|
|
439
|
+
unreported: `start`/`prepare-build` and `doctor` warn once per bad record, naming
|
|
440
|
+
the record (`pages[i].screenshots[j]`), its `page_id`, and the field that failed,
|
|
441
|
+
so a typo shows up as a warning instead of as a page that silently stays blocked.
|
|
442
|
+
|
|
443
|
+
Everything else in the record is metadata that is retained but not required:
|
|
444
|
+
`id` (otherwise derived from the page surface, viewport, and a content digest),
|
|
445
|
+
`sha256` (64 lowercase hex, bare or `sha256:`-prefixed), `width`/`height` (positive integers, or a nested `dimensions` object),
|
|
446
|
+
`device_profile`, `scale_factor`, `browser`, `captured_at`, and `notes`. The
|
|
447
|
+
`source_ref_id` link that readiness requires is back-filled for you from the
|
|
448
|
+
page's own HTML source reference; you do not write it in the manifest.
|
|
449
|
+
|
|
450
|
+
Paths follow the manifest's own convention: relative to the source root passed
|
|
451
|
+
to `prepare-build`, not to the `.campaigns-os` directory. Keeping the PNGs
|
|
452
|
+
inside the source root — `.campaigns-os/screenshots/` is a good home — keeps the
|
|
453
|
+
manifest portable.
|
|
454
|
+
|
|
455
|
+
Editing the manifest changes its byte hash, which is part of the package's
|
|
456
|
+
provenance. Expect the recovery below to be required whenever you add or change
|
|
457
|
+
`screenshots[]`.
|
|
458
|
+
|
|
459
|
+
### What counts as source proof
|
|
460
|
+
|
|
461
|
+
A `source_screenshot` attests what the merchant's design actually looks like.
|
|
462
|
+
That means a **standalone HTML document**: a complete page that renders on its
|
|
463
|
+
own in a browser — its own `<html>`, its own stylesheets and assets, no build
|
|
464
|
+
step. Render it at a desktop and a mobile viewport and the capture is honest
|
|
465
|
+
evidence of the design you are asked to preserve.
|
|
466
|
+
|
|
467
|
+
A **prepared page-kit fragment** is not that. A file that carries page-kit
|
|
468
|
+
frontmatter and a bare body fragment (the shape of the quick-start fixtures
|
|
469
|
+
under `examples/source-html/`) has no standalone appearance; screenshotting it
|
|
470
|
+
captures frontmatter text and an unstyled fragment. It is a faithful capture of
|
|
471
|
+
a file and it is not proof of a design.
|
|
472
|
+
|
|
473
|
+
The gate cannot tell the two apart — see the negative controls below — so this
|
|
474
|
+
distinction is yours to hold. When the source is fragments, or when the design
|
|
475
|
+
exists only inside a tool you cannot render, the honest record is an
|
|
476
|
+
`unavailable_render` with an `unavailable_reason`, which documents the absence
|
|
477
|
+
in the package but **does not clear the gate**. What would clear it honestly is
|
|
478
|
+
an accepted screenshot-absence Source Gap or an approved waiver, described under
|
|
479
|
+
[Gaps, TODOs, waivers, and readiness](#gaps-todos-waivers-and-readiness); in
|
|
480
|
+
v0 neither has an operator-authored input channel (the manifest carries no gap
|
|
481
|
+
key, `checkpoint waive` registers no design-source gate, and the package is not
|
|
482
|
+
hand-edited). A source that cannot be captured honestly therefore stays blocked
|
|
483
|
+
at intake in v0. Hold there and escalate; do not attest a capture of something
|
|
484
|
+
that was never a page.
|
|
485
|
+
|
|
486
|
+
### Template-stock pages: the family decides
|
|
487
|
+
|
|
488
|
+
A third case is neither a standalone design nor an uncapturable one: the page
|
|
489
|
+
has no bespoke design at all, because the design *is* the starter template
|
|
490
|
+
family. There is nothing of the merchant's to screenshot, so a
|
|
491
|
+
`source_screenshot` would be a capture of stock the toolkit already ships.
|
|
492
|
+
|
|
493
|
+
Declare such a page out of source scope and intake treats it as template
|
|
494
|
+
stock. Two ways to declare it, both first-class:
|
|
495
|
+
|
|
496
|
+
- a per-page `skip_reason` entry in the source-html manifest — a `pages[]` entry
|
|
497
|
+
carrying `page_id` and `skip_reason` and no `path` (a page entry takes exactly
|
|
498
|
+
one of the two);
|
|
499
|
+
- `build_scope.mode: "partial"` on the CampaignSpec, when the whole scope is
|
|
500
|
+
partial rather than a few named pages.
|
|
501
|
+
|
|
502
|
+
Either declaration records the page on the assembly report's
|
|
503
|
+
`dec_page_scope_<page>` decision with `template_stock: true` and
|
|
504
|
+
`template_family` set to the family the packet locks, lists it under
|
|
505
|
+
`stages.prepare_build.declared_out_of_scope`, and reaches
|
|
506
|
+
`stages.prepare_build.status: "completed_partial"`. Intake demands no design
|
|
507
|
+
source for the page: no `capture-*` TODO, no `link-*` TODO. The build stage
|
|
508
|
+
materialises it from the locked family's own page of that role — the `next
|
|
509
|
+
build` prompt names every template-stock page and the family to copy it from —
|
|
510
|
+
and the family decides only *how* the package records its coverage:
|
|
511
|
+
|
|
512
|
+
- **A family that publishes complete Template Reference proof — today `apollo`
|
|
513
|
+
alone** — covers the page with synthesized `template_baseline` coverage from
|
|
514
|
+
the catalog's Template Reference. `readiness.status` is `ready`.
|
|
515
|
+
`src/partial-source-build.test.mjs` covers both declarations end to end
|
|
516
|
+
against `apollo`, including a clean re-run.
|
|
517
|
+
- **Every other family** has no Template Reference to link, so the package
|
|
518
|
+
records an accepted `coverage_absence` Source Gap for the page instead
|
|
519
|
+
(`template-stock-<surface>`, scope `primary_design_coverage`,
|
|
520
|
+
`attributed_by: "prepare-build"`, reason naming the locked family).
|
|
521
|
+
`readiness.status` is `ready_with_gaps` — ready, and honest that the page's
|
|
522
|
+
design is stock. `src/template-stock-intake.test.mjs` covers this on the
|
|
523
|
+
two-step fixture family, whose `select` step is template stock by
|
|
524
|
+
construction.
|
|
525
|
+
|
|
526
|
+
Until the build stage has materialised the stock pages, that is a partial
|
|
527
|
+
build and it carries partial-build limits: the declared pages appear under
|
|
528
|
+
`declared_out_of_scope` (with `declared_by` recording which mechanism declared
|
|
529
|
+
them) and under `derived.scope.out_of_scope_pages` (each carrying
|
|
530
|
+
`template_stock: true` and `template_family`); doctor labels only the mapped
|
|
531
|
+
routes as previewable, warns `CampaignSpec page "<id>" is template stock and
|
|
532
|
+
not built yet`, and keeps checkout launch and test-order proof blocked while a
|
|
533
|
+
runtime page (`select`, `checkout`, `upsell`, `receipt`) is among them. You get
|
|
534
|
+
a terminal, honest intake — not a fully proven campaign.
|
|
535
|
+
|
|
536
|
+
The build stage lifts those limits page by page. `next-campaigns-build`
|
|
537
|
+
materialises each template-stock page from the locked family's own page of
|
|
538
|
+
that role (the pre-checkout `select` step first, because it seeds the cart the
|
|
539
|
+
runtime pages read). Once the page's built HTML exists at its route under
|
|
540
|
+
`_site/<slug>/`, doctor reads the `template_stock` marker on the scope decision
|
|
541
|
+
and counts the page as built: it moves into `derived.scope.built_pages` (with
|
|
542
|
+
`template_stock: true`, `template_family`, and no `source_path`), joins the
|
|
543
|
+
previewable routes, no longer blocks runtime QA, and the ready list says
|
|
544
|
+
`Template-stock page(s) materialised by the build stage: <id> (<family>)`. A
|
|
545
|
+
declared page the build leaves unbuilt stays out of scope exactly as before.
|
|
546
|
+
|
|
547
|
+
One limit stands in this version: `polish capture` plans its routes from the
|
|
548
|
+
packet's mapped pages (`source_html.pages[].page_kit`), and a template-stock
|
|
549
|
+
mapping carries no `page_kit`, so a materialised stock page is outside the
|
|
550
|
+
polish capture plan (`route_scope: "selected"`) and its hidden-eager-media
|
|
551
|
+
checkpoint — the checkpoint does not demand a capture it cannot plan. Browser
|
|
552
|
+
QA resolves its routes from the CampaignSpec topology and covers the page.
|
|
553
|
+
|
|
554
|
+
Do not attest a screenshot of stock template output as a design source to get
|
|
555
|
+
past intake. It is no longer the only route through for a non-apollo family,
|
|
556
|
+
and it never was honest: the toolkit records such a page as a high-confidence
|
|
557
|
+
design-source match with a `source_hash`, and nothing downstream knows the
|
|
558
|
+
page is stock. Declare it instead.
|
|
559
|
+
|
|
560
|
+
The template-stock TODO that still fires — `link-<page>-template-reference`,
|
|
561
|
+
for a page that is *undeclared* and has no source HTML on a family without
|
|
562
|
+
Template Reference proof — names the family the packet locks (`--template-family`
|
|
563
|
+
first, then the CampaignSpec `preferred_template_family` hint), the same
|
|
564
|
+
family the `template-baseline` contribution's `presentation_intent` names.
|
|
565
|
+
|
|
566
|
+
### Recovery after a blocked first run
|
|
567
|
+
|
|
568
|
+
A blocked run still emits the package, and `prepare-build` never refreshes a
|
|
569
|
+
package it did not just create. So a first run that blocked leaves a package
|
|
570
|
+
whose provenance names the *old* manifest, and simply rerunning with a new
|
|
571
|
+
manifest fails closed:
|
|
572
|
+
|
|
573
|
+
```
|
|
574
|
+
campaigns-os: Design Source Package at <target>/.campaign-runtime/input/design-source-package.json
|
|
575
|
+
is invalid, stale, or contradictory: [design_source_package.current_html_funnel_material_stale] … ;
|
|
576
|
+
[design_source_package.current_source_material_stale] … "…/source-html-manifest.json" is missing or
|
|
577
|
+
has stale kind, role, or byte hash. …
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
That refusal is the explicit reconciliation the ownership boundary requires. The
|
|
581
|
+
sanctioned sequence, from the source-preparation side:
|
|
582
|
+
|
|
583
|
+
```bash
|
|
584
|
+
# 1. capture the proof and write it into the manifest
|
|
585
|
+
# <source-root>/.campaigns-os/source-html-manifest.json → pages[].screenshots[]
|
|
586
|
+
|
|
587
|
+
# 2. remove the package the blocked run emitted, so prepare-build re-synthesizes it
|
|
588
|
+
rm <page-kit-repository>/.campaign-runtime/input/design-source-package.json
|
|
589
|
+
|
|
590
|
+
# 3. rerun intake, from the Campaigns OS checkout (npm run resolves package.json
|
|
591
|
+
# from the current directory; run from the source directory it fails with a
|
|
592
|
+
# bare npm ENOENT)
|
|
593
|
+
npm run campaigns-os -- start \
|
|
594
|
+
--spec <campaign-spec.json> \
|
|
595
|
+
--source <prepared-html-directory> \
|
|
596
|
+
--target <page-kit-repository> \
|
|
597
|
+
--template-family <family>
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Step 2 is only ever correct for a package emitted by a blocked run that no
|
|
601
|
+
downstream stage has consumed. Once Build or Polish has bound its evidence to a
|
|
602
|
+
package fingerprint, deleting it invalidates that evidence; reconcile through
|
|
603
|
+
the stage-freshness lanes instead.
|
|
604
|
+
|
|
605
|
+
After the rerun, `readiness.status` is `ready` (or `ready_with_gaps` /
|
|
606
|
+
`ready_with_waivers` when accepted gaps or active waivers exist), `blocking_reasons`
|
|
607
|
+
is empty, the `capture-*` TODOs are gone, and doctor advances to assembly.
|
|
608
|
+
|
|
609
|
+
### Negative controls: what the gate does not check
|
|
610
|
+
|
|
611
|
+
The package producer reads no files. It never opens the PNG at `path` and never
|
|
612
|
+
recomputes `sha256`; `sha256` is only checked for its spelling (64 lowercase hex,
|
|
613
|
+
bare or `sha256:`-prefixed) when present. A record pointing at a file that does not exist, and a record whose
|
|
614
|
+
`sha256` does not match its file, both clear the gate exactly as a real capture
|
|
615
|
+
does.
|
|
616
|
+
|
|
617
|
+
`screenshots[]` is therefore an **attestation**, not a verified artifact. The
|
|
618
|
+
toolkit takes your word that the capture is real, current, and of the thing it
|
|
619
|
+
names. Treat a wrong or absent file as what it is — a false claim about the
|
|
620
|
+
merchant's design that will surface as a mismatch during Polish, when there is
|
|
621
|
+
no honest evidence to compare against.
|
|
622
|
+
|
|
623
|
+
### A read-only source root
|
|
624
|
+
|
|
625
|
+
Intake needs the source root to be readable, not writable. Every access
|
|
626
|
+
`prepare-build`, `start`, and doctor make under the source root is a read — the
|
|
627
|
+
HTML files, the manifest, the asset crawl — and every artifact the run produces
|
|
628
|
+
is written under the target repository: the Design Source Package at
|
|
629
|
+
`.campaign-runtime/input/design-source-package.json`, plus the Build Packet,
|
|
630
|
+
Build Context, Assembly Report, and normalized Build Brief. So the gate clears
|
|
631
|
+
from a writable target repo against a source root nobody can write to, and an
|
|
632
|
+
operator does not need a work copy of the source in order to run intake.
|
|
633
|
+
|
|
634
|
+
The proof itself can also stay outside the source tree. A visual reference is
|
|
635
|
+
available when it carries either a `path` or a `url`, and the producer opens no
|
|
636
|
+
file (see the negative controls above), so `screenshots[]` records that give a
|
|
637
|
+
`url` for their desktop and mobile captures satisfy the requirement with no PNG
|
|
638
|
+
bytes anywhere under the source root. An accepted screenshot-absence Source Gap
|
|
639
|
+
or an active approved `source_screenshot`-scope waiver clears it the same way,
|
|
640
|
+
where one exists — v0 has no operator channel for authoring either.
|
|
641
|
+
|
|
642
|
+
Two mechanics to plan around. The manifest is read from
|
|
643
|
+
`<source-root>/.campaigns-os/source-html-manifest.json` by default; when nobody
|
|
644
|
+
can write there, pass `--design-manifest <path>` to `start`, `prepare-build`,
|
|
645
|
+
or `build` and the same `source-html-manifest/v0` document is read from that
|
|
646
|
+
file instead — `pages[].path` and `files[].path` stay relative to `--source`,
|
|
647
|
+
never to the manifest. The Design Source Package records the file it read
|
|
648
|
+
(`contributions[html-funnel].provenance.manifest_path`, relative to the
|
|
649
|
+
package), and doctor validates that same file on every later run. A bare flag,
|
|
650
|
+
a missing file, or a manifest that fails validation is an error before
|
|
651
|
+
anything is written, not the warning-and-filesystem-fallback the default path
|
|
652
|
+
gets: you named the file. And the packet stores `source_html.root` relative to
|
|
653
|
+
the packet file, so a packet resolves its source from the location it was
|
|
654
|
+
written at: replay a run from the same place, or expect doctor to report
|
|
655
|
+
`source_html.root` as missing.
|
|
656
|
+
|
|
657
|
+
## Lifecycle ownership and freshness
|
|
658
|
+
|
|
659
|
+
Prepare owns source normalization and the three package references. It records
|
|
660
|
+
Design Source Package readiness blockers in both `report.blockers` and
|
|
661
|
+
`report.stages.prepare_build.blockers`, and sets the Prepare stage to
|
|
662
|
+
`completed` or `blocked`. Prepare leaves Assembly pending and does **not** write
|
|
663
|
+
`stages.assembly.source_package_material_fingerprint`.
|
|
664
|
+
|
|
665
|
+
Build owns consumption. Before Assembly is marked complete, Build records its
|
|
666
|
+
build fingerprint and copies the current
|
|
667
|
+
`report.design_source_package.material_fingerprint` to
|
|
668
|
+
`stages.assembly.source_package_material_fingerprint`.
|
|
669
|
+
|
|
670
|
+
Polish owns its distinct evidence. A current Polish record must match:
|
|
671
|
+
|
|
672
|
+
- the current Assembly build fingerprint through
|
|
673
|
+
`stages.polish.source_build_fingerprint`; and
|
|
674
|
+
- the current Design Source Package material fingerprint through
|
|
675
|
+
`stages.polish.source_package_material_fingerprint`.
|
|
676
|
+
|
|
677
|
+
If a current package exists but Assembly's source-package fingerprint is
|
|
678
|
+
missing or different, the Polish gate returns
|
|
679
|
+
`polish.assembly_source_package_fingerprint_missing` or
|
|
680
|
+
`polish.assembly_source_package_stale`, and `campaigns-os next` routes back to
|
|
681
|
+
Build. `campaigns-os validate-assembly-report` applies the same missing-
|
|
682
|
+
fingerprint condition and fails with
|
|
683
|
+
`stages.assembly.source_package_material_fingerprint`, and reports a waiver
|
|
684
|
+
whose `expires_at` does not parse as
|
|
685
|
+
`stages.assembly.waiver_expires_at_invalid`, so a hand-authored report cannot
|
|
686
|
+
pass the standalone validator and then block the ladder. If Assembly is current but Polish's build or source-package fingerprint
|
|
687
|
+
is missing or stale, the route is back to Polish. A legacy report with no
|
|
688
|
+
current Design Source Package material fingerprint keeps build-only Polish
|
|
689
|
+
freshness and emits a warning.
|
|
690
|
+
|
|
691
|
+
The exceptional Assembly Source Freshness waiver lane remains explicit and
|
|
692
|
+
attributed. It can let Polish proceed despite missing/stale Assembly source
|
|
693
|
+
consumption, but Polish must still bind its own evidence to the current package;
|
|
694
|
+
the waiver is not a substitute for Polish Evidence.
|
|
695
|
+
|
|
696
|
+
Prepare blockers are coherent across the report. A terminal-looking
|
|
697
|
+
`stages.prepare_build.status` does not override `report.status: "blocked"`,
|
|
698
|
+
retained stage blockers, or retained top-level
|
|
699
|
+
`DESIGN_SOURCE_PACKAGE_NOT_READY` blockers.
|
|
700
|
+
|
|
701
|
+
For custom report locations, Build Context's `report_path` is the durable
|
|
702
|
+
pointer that packet-only `next` follows. Before any downstream stage is
|
|
703
|
+
selected, `next` verifies the context-to-packet pointer, report-to-packet and
|
|
704
|
+
report-to-context pointers, campaign Map ID and route slug, and the DSP
|
|
705
|
+
schema/hash/material/path binding across packet, context, and report. A missing
|
|
706
|
+
or foreign report, or any mismatched binding, fails closed at Prepare instead
|
|
707
|
+
of bypassing the earliest gate.
|
|
708
|
+
|
|
709
|
+
See [Polish evidence](./polish-evidence.md) for the full stage-evidence and
|
|
710
|
+
freshness gate.
|
|
711
|
+
|
|
712
|
+
## Inspect, validate, and test
|
|
713
|
+
|
|
714
|
+
This repository is a private package checkout. Invoke its CLI through the npm
|
|
715
|
+
script form: `npm run campaigns-os -- <cmd>`.
|
|
716
|
+
|
|
717
|
+
From the target repo root, inspect the default artifact and recompute its
|
|
718
|
+
exact-byte audit hash with Node:
|
|
719
|
+
|
|
720
|
+
```bash
|
|
721
|
+
node --input-type=module -e '
|
|
722
|
+
import { createHash } from "node:crypto";
|
|
723
|
+
import { readFileSync } from "node:fs";
|
|
724
|
+
const path = ".campaign-runtime/input/design-source-package.json";
|
|
725
|
+
const bytes = readFileSync(path);
|
|
726
|
+
const value = JSON.parse(bytes);
|
|
727
|
+
console.log(JSON.stringify({
|
|
728
|
+
path,
|
|
729
|
+
schema_version: value.schema_version,
|
|
730
|
+
readiness: value.readiness,
|
|
731
|
+
sha256: `sha256:${createHash("sha256").update(bytes).digest("hex")}`,
|
|
732
|
+
material_fingerprint: value.material_fingerprint
|
|
733
|
+
}, null, 2));
|
|
734
|
+
'
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
From this repository root, validate a target package's standalone runtime
|
|
738
|
+
contract:
|
|
739
|
+
|
|
740
|
+
```bash
|
|
741
|
+
node --input-type=module -e '
|
|
742
|
+
import { readFileSync } from "node:fs";
|
|
743
|
+
import { validateDesignSourcePackage } from "./src/design-source-package.mjs";
|
|
744
|
+
const value = JSON.parse(readFileSync(process.argv[1], "utf8"));
|
|
745
|
+
const result = validateDesignSourcePackage(value);
|
|
746
|
+
console.log(JSON.stringify(result, null, 2));
|
|
747
|
+
if (!result.ok) process.exitCode = 1;
|
|
748
|
+
' <page-kit-repository>/.campaign-runtime/input/design-source-package.json
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
The standalone validator does not know the current source directory or
|
|
752
|
+
CampaignSpec. Before downstream stage evidence exists, rerun the real producer
|
|
753
|
+
with the same current inputs to exercise current campaign/page/source/template
|
|
754
|
+
validation and exact-byte reuse:
|
|
755
|
+
|
|
756
|
+
```bash
|
|
757
|
+
npm run campaigns-os -- prepare-build \
|
|
758
|
+
--spec <campaign-spec.json> \
|
|
759
|
+
--source <prepared-html-directory> \
|
|
760
|
+
--target <page-kit-repository> \
|
|
761
|
+
--template-family <family> \
|
|
762
|
+
--no-run-session \
|
|
763
|
+
--json
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
`prepare-build` protects an Assembly Report that already contains downstream
|
|
767
|
+
stage evidence; do not use a destructive override merely to perform a check.
|
|
768
|
+
Use Doctor and `next` to inspect the cross-artifact and lifecycle gates:
|
|
769
|
+
|
|
770
|
+
```bash
|
|
771
|
+
npm run campaigns-os -- doctor --packet <page-kit-repository>/campaign-runtime.build.json
|
|
772
|
+
npm run campaigns-os -- next --packet <page-kit-repository>/campaign-runtime.build.json --json
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
Run the focused contract and negative-control suite from this repository:
|
|
776
|
+
|
|
777
|
+
```bash
|
|
778
|
+
node --test \
|
|
779
|
+
src/design-source-package.test.mjs \
|
|
780
|
+
src/design-source-package-prepare-build.test.mjs \
|
|
781
|
+
src/design-source-package-polish.integration.test.mjs \
|
|
782
|
+
src/polish-gate.test.mjs
|
|
783
|
+
npm run check
|
|
784
|
+
```
|