@wix/agent-skills 1.35.1 → 1.36.1
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/package.json +8 -3
- package/skills/wix-app/references/CODE_QUALITY.md +1 -1
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +27 -29
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +1 -1
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +26 -25
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +12 -42
- package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +9 -20
- package/skills/wix-app/references/editor-react-component/MANIFEST-ERRORS.md +5 -4
- package/skills/wix-app/references/editor-react-component/PARTS.md +1 -1
- package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +1 -1
- package/skills/wix-app/references/editor-react-component/VECTOR-ART.md +95 -0
- package/skills/wix-app/references/editor-react-component/VISUAL-QUALITY.md +39 -0
- package/skills/wix-docs/references/CALLING.md +1 -1
- package/skills/wix-headless-kit/guides/api-run.md +1 -1
- package/skills/wix-headless-kit/install/pins.json +1 -1
- package/skills/wix-headless-templates/blog/seed/seed-blog.mjs +1 -1
- package/skills/wix-headless-templates/donations/app/wix/donations/campaigns.ts +0 -1
- package/skills/wix-headless-templates/donations/app/wix/donations/donate.ts +1 -1
- package/skills/wix-headless-templates/donations/project/src/wix/donations/campaigns.ts +0 -1
- package/skills/wix-headless-templates/donations/project/src/wix/donations/donate.ts +1 -1
- package/skills/wix-headless-templates/donations/rest/campaigns.ts +0 -1
- package/skills/wix-headless-templates/donations/rest/donate.ts +1 -1
- package/skills/wix-headless-templates/events/seed/seed-events.mjs +1 -1
- package/skills/wix-headless-templates/faq/INSTRUCTIONS.md +1 -1
- package/skills/wix-headless-templates/faq/app/wix/faq/faq.ts +2 -2
- package/skills/wix-headless-templates/faq/project/src/wix/faq/faq.ts +2 -2
- package/skills/wix-headless-templates/faq/rest/faq.ts +2 -2
- package/skills/wix-headless-templates/faq/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/faq/seed/read-site.mjs +1 -1
- package/skills/wix-headless-templates/faq/seed/seed-faq.mjs +5 -5
- package/skills/wix-headless-templates/forms/app/wix/forms/forms.ts +2 -2
- package/skills/wix-headless-templates/forms/app/wix/forms/submissions.ts +2 -2
- package/skills/wix-headless-templates/forms/project/src/wix/forms/forms.ts +2 -2
- package/skills/wix-headless-templates/forms/project/src/wix/forms/submissions.ts +2 -2
- package/skills/wix-headless-templates/members/seed/read-site.mjs +1 -1
- package/skills/wix-headless-templates/pricing-plans/app/wix/pricing-plans/plan-access.ts +1 -1
- package/skills/wix-headless-templates/pricing-plans/project/src/wix/pricing-plans/plan-access.ts +1 -1
- package/skills/wix-headless-templates/pricing-plans/rest/plan-access.ts +1 -1
- package/skills/wix-headless-templates/restaurants/app/wix/restaurants/menu.ts +0 -1
- package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/menu.ts +0 -1
- package/skills/wix-headless-templates/restaurants/rest/menu.ts +0 -1
- package/skills/wix-headless-templates/shared/rest/client.ts +1 -1
- package/skills/wix-headless-templates/shared/seed/site.mjs +2 -1
- package/skills/wix-manage/SKILL.md +3 -3
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +78 -1
- package/skills/wix-manage/references/google-ads/create-performance-max-campaign.md +11 -11
- package/skills/wix-manage/references/google-ads/get-campaign-suggestions.md +0 -1
- package/skills/wix-manage/references/google-ads/install-and-create-account.md +0 -1
- package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +20 -51
- package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +4 -4
- package/skills/wix-manage/references/rich-content/ricos-converter-service.md +1 -1
- package/skills/wix-base44-connector/SKILL.md +0 -437
- package/skills/wix-base44-connector/references/CAPABILITY_MAP.md +0 -78
- package/skills/wix-base44-connector/scripts/utils.cjs +0 -466
- package/skills/wix-export/SKILL.md +0 -83
- package/skills/wix-harmony-editor/.publish-manifest.json +0 -8
- package/skills/wix-harmony-editor/COPY-SITE.md +0 -494
- package/skills/wix-harmony-editor/SKILL.md +0 -317
- package/skills/wix-harmony-editor/scripts/copy/build-description.mjs +0 -249
- package/skills/wix-harmony-editor/scripts/copy/check-description.mjs +0 -225
- package/skills/wix-harmony-editor/scripts/copy/check-mirror.mjs +0 -89
- package/skills/wix-harmony-editor/scripts/copy/compare-preview.mjs +0 -76
- package/skills/wix-harmony-editor/scripts/copy/deploy-description.mjs +0 -212
- package/skills/wix-harmony-editor/scripts/copy/desc-hash.mjs +0 -44
- package/skills/wix-harmony-editor/scripts/copy/editor-run.mjs +0 -45
- package/skills/wix-harmony-editor/scripts/copy/export-page.mjs +0 -53
- package/skills/wix-harmony-editor/scripts/copy/find-interactions.mjs +0 -76
- package/skills/wix-harmony-editor/scripts/copy/hide-scrollbars.mjs +0 -16
- package/skills/wix-harmony-editor/scripts/copy/import-media.mjs +0 -150
- package/skills/wix-harmony-editor/scripts/copy/link-url.mjs +0 -27
- package/skills/wix-harmony-editor/scripts/copy/lint-api.mjs +0 -56
- package/skills/wix-harmony-editor/scripts/copy/measure-source.mjs +0 -211
- package/skills/wix-harmony-editor/scripts/copy/merge-mirrors.mjs +0 -29
- package/skills/wix-harmony-editor/scripts/copy/mirror-site.mjs +0 -92
- package/skills/wix-harmony-editor/scripts/copy/mobile-stage.mjs +0 -56
- package/skills/wix-harmony-editor/scripts/copy/preview-widths.mjs +0 -73
- package/skills/wix-harmony-editor/scripts/copy/render-plate.mjs +0 -67
- package/skills/wix-harmony-editor/scripts/copy/serve-mirror.mjs +0 -102
- package/skills/wix-harmony-editor/scripts/copy/snapshot-capabilities.mjs +0 -40
- package/skills/wix-harmony-editor/scripts/copy/split-sections.mjs +0 -191
- package/skills/wix-harmony-editor/scripts/copy/upload-media-manager.mjs +0 -61
- package/skills/wix-harmony-editor/scripts/launch.sh +0 -359
- package/skills/wix-headless/SKILL.md +0 -151
- package/skills/wix-headless/references/AI_FEATURES.md +0 -178
- package/skills/wix-headless/references/CAPABILITIES.md +0 -117
- package/skills/wix-headless/references/CONTENT.md +0 -36
- package/skills/wix-headless/references/DESIGN.md +0 -55
- package/skills/wix-headless/references/DISCOVERY.md +0 -80
- package/skills/wix-headless/references/DOC_DISCOVERY.md +0 -43
- package/skills/wix-headless/references/FEEDBACK.md +0 -129
- package/skills/wix-headless/references/IMAGE_GENERATION.md +0 -80
- package/skills/wix-headless/references/SDK_HANDOFF.md +0 -108
- package/skills/wix-headless/references/SEED.md +0 -74
- package/skills/wix-headless/references/SETUP.md +0 -60
- package/skills/wix-headless/references/astro.md +0 -98
- package/skills/wix-headless/references/inline-recipes/experience-donations.md +0 -102
- package/skills/wix-headless/references/inline-recipes/experience-store.md +0 -49
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +0 -196
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +0 -590
- package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +0 -279
- package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +0 -163
- package/skills/wix-headless/references/inline-recipes/how-to-code-donations.md +0 -146
- package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +0 -203
- package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +0 -696
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +0 -124
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +0 -160
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +0 -118
- package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +0 -132
- package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +0 -126
- package/skills/wix-headless/references/inline-recipes/how-to-code-rentals.md +0 -254
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +0 -191
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +0 -213
- package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +0 -161
- package/skills/wix-headless/references/inline-recipes/setup-blog.md +0 -175
- package/skills/wix-headless/references/inline-recipes/setup-bookings.md +0 -232
- package/skills/wix-headless/references/inline-recipes/setup-cms.md +0 -200
- package/skills/wix-headless/references/inline-recipes/setup-donations.md +0 -15
- package/skills/wix-headless/references/inline-recipes/setup-events.md +0 -196
- package/skills/wix-headless/references/inline-recipes/setup-forms.md +0 -288
- package/skills/wix-headless/references/inline-recipes/setup-online-store.md +0 -214
- package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +0 -155
- package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +0 -229
- package/skills/wix-headless/references/inline-recipes/setup-rentals.md +0 -254
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +0 -73
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +0 -197
- package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +0 -136
- package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +0 -152
- package/skills/wix-headless/references/managed/AUTHENTICATION.md +0 -71
- package/skills/wix-headless/references/managed/CONNECT.md +0 -74
- package/skills/wix-headless/references/managed/CREATE.md +0 -149
- package/skills/wix-headless/references/managed/DEPLOYMENT.md +0 -104
- package/skills/wix-headless/references/non-astro.md +0 -59
- package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +0 -22
- package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +0 -15
- package/skills/wix-headless/references/stripe/AUTHENTICATION.md +0 -55
- package/skills/wix-headless/references/stripe/DEPLOYMENT.md +0 -71
- package/skills/wix-headless/scripts/bootstrap.mjs +0 -156
- package/skills/wix-headless-replatform/.publish-manifest.json +0 -8
- package/skills/wix-headless-replatform/SKILL.md +0 -116
- package/skills/wix-headless-replatform/agents/openai.yaml +0 -6
- package/skills/wix-headless-replatform/references/control-and-visual-fidelity-contract.md +0 -43
- package/skills/wix-headless-replatform/references/extraction-schema.md +0 -187
- package/skills/wix-headless-replatform/references/interaction-runtime-contract.md +0 -137
- package/skills/wix-headless-replatform/references/layout-blueprint-contract.md +0 -41
- package/skills/wix-headless-replatform/references/output-contract.md +0 -93
- package/skills/wix-headless-replatform/references/post-build-gap-contract.md +0 -125
- package/skills/wix-headless-replatform/references/qa-checklist.md +0 -83
- package/skills/wix-headless-replatform/references/safety.md +0 -19
- package/skills/wix-headless-replatform/references/ui-normalization-contract.md +0 -70
- package/skills/wix-headless-replatform/references/workflow.md +0 -157
- package/skills/wix-headless-replatform/registry/README.md +0 -20
- package/skills/wix-headless-replatform/registry/approvals/button/shadcn-default-ac60ef5.json +0 -28
- package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/button.tsx +0 -54
- package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/capabilities.json +0 -60
- package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/contract.md +0 -48
- package/skills/wix-headless-replatform/registry/licenses/shadcn-ui-ac60ef5-MIT.txt +0 -21
- package/skills/wix-headless-replatform/registry/registry.json +0 -36
- package/skills/wix-headless-replatform/resources/rp-facelift/SKILL.md +0 -32
- package/skills/wix-headless-replatform/resources/rp-project-context/SKILL.md +0 -62
- package/skills/wix-headless-replatform/resources/rp-qa-gap-loop/SKILL.md +0 -64
- package/skills/wix-headless-replatform/resources/rp-source-evidence/SKILL.md +0 -42
- package/skills/wix-headless-replatform/resources/rp-ui-implementation/SKILL.md +0 -73
- package/skills/wix-headless-replatform/schemas/build-plan.schema.json +0 -10
- package/skills/wix-headless-replatform/schemas/candidate.schema.json +0 -19
- package/skills/wix-headless-replatform/schemas/common.schema.json +0 -33
- package/skills/wix-headless-replatform/schemas/decision-patch.schema.json +0 -10
- package/skills/wix-headless-replatform/schemas/decision-request.schema.json +0 -10
- package/skills/wix-headless-replatform/schemas/extraction-gap.schema.json +0 -28
- package/skills/wix-headless-replatform/schemas/extraction-manifest.schema.json +0 -34
- package/skills/wix-headless-replatform/schemas/final-report.schema.json +0 -10
- package/skills/wix-headless-replatform/schemas/foundation.schema.json +0 -11
- package/skills/wix-headless-replatform/schemas/metadata.schema.json +0 -11
- package/skills/wix-headless-replatform/schemas/observation-packet.schema.json +0 -17
- package/skills/wix-headless-replatform/schemas/page-capture.schema.json +0 -11
- package/skills/wix-headless-replatform/schemas/page-resolution.schema.json +0 -33
- package/skills/wix-headless-replatform/schemas/shared-chrome.schema.json +0 -11
- package/skills/wix-headless-replatform/schemas/unit.schema.json +0 -40
- package/skills/wix-headless-replatform/scripts/assemble-extraction.mjs +0 -844
- package/skills/wix-headless-replatform/scripts/benchmark-extraction.mjs +0 -163
- package/skills/wix-headless-replatform/scripts/browser-extraction-preflight.mjs +0 -42
- package/skills/wix-headless-replatform/scripts/build-extracted-homepage.mjs +0 -228
- package/skills/wix-headless-replatform/scripts/clear-wix-starter.mjs +0 -87
- package/skills/wix-headless-replatform/scripts/create-wix-project.mjs +0 -505
- package/skills/wix-headless-replatform/scripts/discover-urls.mjs +0 -142
- package/skills/wix-headless-replatform/scripts/extract-assets.mjs +0 -140
- package/skills/wix-headless-replatform/scripts/extract-design-system.mjs +0 -637
- package/skills/wix-headless-replatform/scripts/extract-interactions.mjs +0 -1903
- package/skills/wix-headless-replatform/scripts/extract-page.mjs +0 -2461
- package/skills/wix-headless-replatform/scripts/extract-seo.mjs +0 -25
- package/skills/wix-headless-replatform/scripts/extract-tokens.mjs +0 -63
- package/skills/wix-headless-replatform/scripts/finalize-extraction-report.mjs +0 -126
- package/skills/wix-headless-replatform/scripts/finalize-gap-review.mjs +0 -180
- package/skills/wix-headless-replatform/scripts/finalize-gap-review.test.mjs +0 -98
- package/skills/wix-headless-replatform/scripts/finalize-migration-website.mjs +0 -103
- package/skills/wix-headless-replatform/scripts/generate-control-state-contract.mjs +0 -176
- package/skills/wix-headless-replatform/scripts/generate-design-md.mjs +0 -261
- package/skills/wix-headless-replatform/scripts/generate-extraction-validators.mjs +0 -25
- package/skills/wix-headless-replatform/scripts/generate-invoice-download-backend.mjs +0 -342
- package/skills/wix-headless-replatform/scripts/generate-layout-blueprint.mjs +0 -140
- package/skills/wix-headless-replatform/scripts/generate-repeater-cms.mjs +0 -22
- package/skills/wix-headless-replatform/scripts/generate-route-plan.mjs +0 -143
- package/skills/wix-headless-replatform/scripts/generate-scene-contract.mjs +0 -288
- package/skills/wix-headless-replatform/scripts/generate-ui-normalization.mjs +0 -266
- package/skills/wix-headless-replatform/scripts/generate-visual-assets.mjs +0 -111
- package/skills/wix-headless-replatform/scripts/lib/browser-tooling.mjs +0 -570
- package/skills/wix-headless-replatform/scripts/lib/common.mjs +0 -329
- package/skills/wix-headless-replatform/scripts/lib/component-capabilities.mjs +0 -121
- package/skills/wix-headless-replatform/scripts/lib/component-registry.mjs +0 -282
- package/skills/wix-headless-replatform/scripts/lib/content-boundary.mjs +0 -61
- package/skills/wix-headless-replatform/scripts/lib/extraction-contract.mjs +0 -374
- package/skills/wix-headless-replatform/scripts/lib/extraction-detectors.mjs +0 -243
- package/skills/wix-headless-replatform/scripts/lib/extraction-validators.generated.mjs +0 -1139
- package/skills/wix-headless-replatform/scripts/lib/font-contract.mjs +0 -326
- package/skills/wix-headless-replatform/scripts/lib/frontend-automation-state.mjs +0 -184
- package/skills/wix-headless-replatform/scripts/lib/gap-analysis.mjs +0 -396
- package/skills/wix-headless-replatform/scripts/lib/html-extract.mjs +0 -168
- package/skills/wix-headless-replatform/scripts/lib/interaction-bootstrap.mjs +0 -10
- package/skills/wix-headless-replatform/scripts/lib/interaction-runtime.mjs +0 -194
- package/skills/wix-headless-replatform/scripts/lib/interaction-timeline.mjs +0 -348
- package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/classifiers.mjs +0 -141
- package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/geometry.mjs +0 -43
- package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/vocabulary.mjs +0 -18
- package/skills/wix-headless-replatform/scripts/lib/ui-normalization.css +0 -64
- package/skills/wix-headless-replatform/scripts/optimize-svg.mjs +0 -31
- package/skills/wix-headless-replatform/scripts/post-build-gap.mjs +0 -358
- package/skills/wix-headless-replatform/scripts/provision-repeater-cms.mjs +0 -25
- package/skills/wix-headless-replatform/scripts/record-gap-decision.mjs +0 -39
- package/skills/wix-headless-replatform/scripts/replay-extraction.mjs +0 -107
- package/skills/wix-headless-replatform/scripts/resolve-home-page.mjs +0 -96
- package/skills/wix-headless-replatform/scripts/site-clone.mjs +0 -658
- package/skills/wix-headless-replatform/scripts/validate-font-contract.mjs +0 -29
- package/skills/wix-headless-replatform/scripts/verify-interactions.mjs +0 -669
- package/skills/wix-headless-replatform/scripts/visual-qa.mjs +0 -370
- package/skills/wix-import/SKILL.md +0 -90
- package/skills/wix-replatform/.publish-manifest.json +0 -8
- package/skills/wix-replatform/CLI.md +0 -162
- package/skills/wix-replatform/README.md +0 -52
- package/skills/wix-replatform/SKILL.md +0 -238
- package/skills/wix-replatform/console/account.css +0 -1
- package/skills/wix-replatform/console/app.css +0 -1
- package/skills/wix-replatform/console/app.js +0 -117
- package/skills/wix-replatform/console/index.html +0 -4
- package/skills/wix-replatform/console/json.css +0 -11
- package/skills/wix-replatform/lib/agent-adapters/index.js +0 -71
- package/skills/wix-replatform/lib/app-page-routing.js +0 -75
- package/skills/wix-replatform/lib/artifact-freshness.js +0 -225
- package/skills/wix-replatform/lib/blocked-data-requests.js +0 -427
- package/skills/wix-replatform/lib/census-reconciliation.js +0 -426
- package/skills/wix-replatform/lib/completion-report.js +0 -1580
- package/skills/wix-replatform/lib/config-env.js +0 -133
- package/skills/wix-replatform/lib/confirmed-pictures.js +0 -212
- package/skills/wix-replatform/lib/dependency-readiness.js +0 -423
- package/skills/wix-replatform/lib/dependency-recovery.js +0 -21
- package/skills/wix-replatform/lib/destination-choice.js +0 -163
- package/skills/wix-replatform/lib/destination-handoff.js +0 -62
- package/skills/wix-replatform/lib/destination-http.js +0 -81
- package/skills/wix-replatform/lib/destination-route.js +0 -98
- package/skills/wix-replatform/lib/destination.js +0 -919
- package/skills/wix-replatform/lib/discovered-census.js +0 -231
- package/skills/wix-replatform/lib/discovered-set-status.js +0 -255
- package/skills/wix-replatform/lib/document-url-guard.js +0 -203
- package/skills/wix-replatform/lib/entity-qa-attestation.js +0 -339
- package/skills/wix-replatform/lib/entity-verification.js +0 -18
- package/skills/wix-replatform/lib/execution-capability.js +0 -120
- package/skills/wix-replatform/lib/execution-log.js +0 -170
- package/skills/wix-replatform/lib/execution-manifest.js +0 -203
- package/skills/wix-replatform/lib/execution-state.js +0 -195
- package/skills/wix-replatform/lib/expectations-ledger.js +0 -96
- package/skills/wix-replatform/lib/generated-plugin-handoff.js +0 -140
- package/skills/wix-replatform/lib/import-readiness-contract.js +0 -23
- package/skills/wix-replatform/lib/import-recovery.js +0 -1107
- package/skills/wix-replatform/lib/imported-redirect-targets.js +0 -608
- package/skills/wix-replatform/lib/internal-link-reconciliation.js +0 -166
- package/skills/wix-replatform/lib/link-audit.js +0 -173
- package/skills/wix-replatform/lib/link-census.js +0 -194
- package/skills/wix-replatform/lib/link-plan.js +0 -148
- package/skills/wix-replatform/lib/link-verification.js +0 -274
- package/skills/wix-replatform/lib/link-write-journal.js +0 -42
- package/skills/wix-replatform/lib/local-state.js +0 -1481
- package/skills/wix-replatform/lib/member-completion.js +0 -658
- package/skills/wix-replatform/lib/member-crosswalk.js +0 -69
- package/skills/wix-replatform/lib/member-outcome-registry.js +0 -265
- package/skills/wix-replatform/lib/migration-completion.js +0 -311
- package/skills/wix-replatform/lib/operator-identity.js +0 -47
- package/skills/wix-replatform/lib/orchestration-decisions.js +0 -248
- package/skills/wix-replatform/lib/orchestration-preflight.js +0 -525
- package/skills/wix-replatform/lib/orchestration-router.js +0 -698
- package/skills/wix-replatform/lib/orchestration-state.js +0 -631
- package/skills/wix-replatform/lib/order-reference-readiness.js +0 -30
- package/skills/wix-replatform/lib/payments-outcome-registry.js +0 -200
- package/skills/wix-replatform/lib/plugin-cms-fallback.js +0 -618
- package/skills/wix-replatform/lib/preservation-approval-stamp.js +0 -54
- package/skills/wix-replatform/lib/product-media-deferral.js +0 -96
- package/skills/wix-replatform/lib/progress-log.js +0 -240
- package/skills/wix-replatform/lib/progress-log.sh +0 -140
- package/skills/wix-replatform/lib/read-back-approval.js +0 -184
- package/skills/wix-replatform/lib/read-back-comparison.js +0 -136
- package/skills/wix-replatform/lib/read-back-contract.js +0 -243
- package/skills/wix-replatform/lib/read-back-extract.js +0 -118
- package/skills/wix-replatform/lib/read-back-gate.js +0 -734
- package/skills/wix-replatform/lib/read-back-reads.js +0 -94
- package/skills/wix-replatform/lib/read-back-state.js +0 -107
- package/skills/wix-replatform/lib/read-back-summary.js +0 -81
- package/skills/wix-replatform/lib/rerun-conflicts.js +0 -334
- package/skills/wix-replatform/lib/run-controller.js +0 -401
- package/skills/wix-replatform/lib/seo-outcome-registry.js +0 -45
- package/skills/wix-replatform/lib/setup-verification.js +0 -148
- package/skills/wix-replatform/lib/source-access.js +0 -231
- package/skills/wix-replatform/lib/source-coverage.js +0 -1130
- package/skills/wix-replatform/lib/source-secrets.js +0 -277
- package/skills/wix-replatform/lib/structured-link-patch.js +0 -59
- package/skills/wix-replatform/lib/task-state.js +0 -110
- package/skills/wix-replatform/lib/url-preservation-report.js +0 -319
- package/skills/wix-replatform/lib/url-preservation-state.js +0 -739
- package/skills/wix-replatform/lib/url-redirect-plan.js +0 -728
- package/skills/wix-replatform/lib/url-verification.js +0 -315
- package/skills/wix-replatform/lib/user-action-effects.js +0 -42
- package/skills/wix-replatform/lib/user-actions.js +0 -264
- package/skills/wix-replatform/lib/website-handoff.js +0 -1220
- package/skills/wix-replatform/lib/wix-client-callers.js +0 -54
- package/skills/wix-replatform/lib/wix-dashboard-url.js +0 -35
- package/skills/wix-replatform/lib/write-verification-state.js +0 -92
- package/skills/wix-replatform/lib/write-verification.js +0 -274
- package/skills/wix-replatform/resources/rp-destination/SKILL.md +0 -137
- package/skills/wix-replatform/resources/rp-discovery/SKILL.md +0 -357
- package/skills/wix-replatform/resources/rp-discovery/scripts/promote-source-coverage.js +0 -47
- package/skills/wix-replatform/resources/rp-execute-import/SKILL.md +0 -1369
- package/skills/wix-replatform/resources/rp-execute-import/scripts/shipping-estimate-audit.js +0 -70
- package/skills/wix-replatform/resources/rp-execute-setup/SKILL.md +0 -482
- package/skills/wix-replatform/resources/rp-execute-setup/scripts/execute-setup.js +0 -442
- package/skills/wix-replatform/resources/rp-execute-setup/scripts/mint-token.sh +0 -64
- package/skills/wix-replatform/resources/rp-execution-policy/SKILL.md +0 -112
- package/skills/wix-replatform/resources/rp-execution-policy/lib/coupon-currency-plan-report.js +0 -143
- package/skills/wix-replatform/resources/rp-execution-policy/lib/payments-report.js +0 -244
- package/skills/wix-replatform/resources/rp-import-codegen/SKILL.md +0 -3158
- package/skills/wix-replatform/resources/rp-mapper/SKILL.md +0 -947
- package/skills/wix-replatform/resources/rp-mapper/lib/mapping-resolve.js +0 -217
- package/skills/wix-replatform/resources/rp-mapper/lib/structure-bridge-planning.js +0 -303
- package/skills/wix-replatform/resources/rp-mapper/scripts/coverage-reconcile.js +0 -88
- package/skills/wix-replatform/resources/rp-mapper/scripts/resolve-mapping.js +0 -148
- package/skills/wix-replatform/resources/rp-mapper/wix-business-solutions-taxonomy.md +0 -378
- package/skills/wix-replatform/resources/rp-preflight/SKILL.md +0 -40
- package/skills/wix-replatform/resources/rp-project-intake/SKILL.md +0 -55
- package/skills/wix-replatform/resources/rp-quick-runtime/SKILL.md +0 -60
- package/skills/wix-replatform/resources/rp-quick-runtime/lib/import-progress.js +0 -89
- package/skills/wix-replatform/resources/rp-quick-runtime/lib/limit-recovery.js +0 -36
- package/skills/wix-replatform/resources/rp-quick-shopify/SKILL.md +0 -123
- package/skills/wix-replatform/resources/rp-quick-shopify/quick-mode.json +0 -124
- package/skills/wix-replatform/resources/rp-quick-shopify/scripts/quick-mode.js +0 -522
- package/skills/wix-replatform/resources/rp-quick-woocommerce/SKILL.md +0 -56
- package/skills/wix-replatform/resources/rp-quick-woocommerce/quick-mode.json +0 -91
- package/skills/wix-replatform/resources/rp-quick-woocommerce/scripts/quick-mode.js +0 -255
- package/skills/wix-replatform/resources/rp-setup-discovery/SKILL.md +0 -253
- package/skills/wix-replatform/resources/rp-setup-discovery/scripts/quick-setup-plan.js +0 -44
- package/skills/wix-replatform/resources/rp-source-csv/SKILL.md +0 -481
- package/skills/wix-replatform/resources/rp-source-csv/lib/canonical-record.js +0 -136
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fileset.js +0 -0
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fingerprint.js +0 -310
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-layout.js +0 -1081
- package/skills/wix-replatform/resources/rp-source-csv/lib/csv-parse.js +0 -618
- package/skills/wix-replatform/resources/rp-source-csv/scripts/csv-discovery.js +0 -969
- package/skills/wix-replatform/resources/rp-source-csv/source-schema.example.json +0 -196
- package/skills/wix-replatform/resources/rp-source-csv/vendors/bigcommerce.json +0 -66
- package/skills/wix-replatform/resources/rp-source-csv/vendors/magento.json +0 -60
- package/skills/wix-replatform/resources/rp-source-csv/vendors/shopify.json +0 -95
- package/skills/wix-replatform/resources/rp-source-csv/vendors/woocommerce.json +0 -82
- package/skills/wix-replatform/resources/rp-source-inputs/SKILL.md +0 -66
- package/skills/wix-replatform/resources/rp-source-sfcc/SKILL.md +0 -153
- package/skills/wix-replatform/resources/rp-source-shopify/SKILL.md +0 -394
- package/skills/wix-replatform/resources/rp-source-shopify/lib/discovery-reads.js +0 -200
- package/skills/wix-replatform/resources/rp-source-shopify/lib/import-findings.js +0 -34
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-bulk-sqlite-worker.js +0 -44
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-bulk.js +0 -1470
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-cli.js +0 -446
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-gift-cards.js +0 -474
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-protected-fields.js +0 -879
- package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-session.js +0 -426
- package/skills/wix-replatform/resources/rp-source-shopify/queries/articles.graphql +0 -24
- package/skills/wix-replatform/resources/rp-source-shopify/queries/blogs.graphql +0 -18
- package/skills/wix-replatform/resources/rp-source-shopify/queries/collections.graphql +0 -40
- package/skills/wix-replatform/resources/rp-source-shopify/queries/customers.graphql +0 -76
- package/skills/wix-replatform/resources/rp-source-shopify/queries/delivery-profiles.graphql +0 -51
- package/skills/wix-replatform/resources/rp-source-shopify/queries/discounts.graphql +0 -35
- package/skills/wix-replatform/resources/rp-source-shopify/queries/files.graphql +0 -20
- package/skills/wix-replatform/resources/rp-source-shopify/queries/gift-cards.graphql +0 -18
- package/skills/wix-replatform/resources/rp-source-shopify/queries/inventory.graphql +0 -31
- package/skills/wix-replatform/resources/rp-source-shopify/queries/locations.graphql +0 -21
- package/skills/wix-replatform/resources/rp-source-shopify/queries/menus.graphql +0 -21
- package/skills/wix-replatform/resources/rp-source-shopify/queries/metaobjects.graphql +0 -30
- package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-core.graphql +0 -150
- package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-fulfillments.graphql +0 -36
- package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-refunds.graphql +0 -48
- package/skills/wix-replatform/resources/rp-source-shopify/queries/pages.graphql +0 -20
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-address.graphql +0 -8
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-email.graphql +0 -6
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-name.graphql +0 -8
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-phone.graphql +0 -8
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-address.graphql +0 -12
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-email.graphql +0 -6
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-name.graphql +0 -13
- package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-phone.graphql +0 -13
- package/skills/wix-replatform/resources/rp-source-shopify/queries/products.graphql +0 -73
- package/skills/wix-replatform/resources/rp-source-shopify/queries/url-redirects.graphql +0 -13
- package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-discovery.js +0 -693
- package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-extract.js +0 -283
- package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-gift-card-remedy.js +0 -191
- package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-session-check.js +0 -115
- package/skills/wix-replatform/resources/rp-source-shopify/source-schema.example.json +0 -201
- package/skills/wix-replatform/resources/rp-source-wordpress/PLUGIN-READ-PLANS.md +0 -146
- package/skills/wix-replatform/resources/rp-source-wordpress/SKILL.md +0 -970
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/blocked-data-handlers.js +0 -73
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/checkout-field-csv-parser.js +0 -115
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/checkout-field-csv-parser.test-fixture.js +0 -91
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/coupon-discovery.js +0 -440
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/currency-gate.js +0 -125
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-source-keys.js +0 -252
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-source-keys.json +0 -183
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-summary.mjs +0 -333
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/invoice-discovery.js +0 -426
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-disposition.js +0 -400
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-entity-read-plan.js +0 -633
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-knowledge.js +0 -1127
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/refund-corroboration.js +0 -194
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/refund-corroboration.json +0 -100
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/sampled-ids-batch.js +0 -157
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/secret-value-guard.js +0 -234
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/store-currency.js +0 -101
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/structure-bridge-reader.js +0 -755
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wix-wp-plugin-v2-client.js +0 -1074
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wix-wp-plugin-v2-client.test-fixture.js +0 -149
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/woo-inventory.js +0 -25
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-http.js +0 -444
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-image-url.js +0 -186
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-plugin-detect.js +0 -1422
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-redirect-sources.js +0 -343
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-route-classifier.js +0 -680
- package/skills/wix-replatform/resources/rp-source-wordpress/lib/yoast-product-seo-evidence.js +0 -272
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/README.md +0 -373
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/advanced-custom-fields.json +0 -101
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/back-in-stock-notifier-for-woocommerce.json +0 -81
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/capabilities-pending-decision.json +0 -128
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/cms-fallback-eligible.json +0 -18
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/contact-form-7.json +0 -104
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/facebook-for-woocommerce.json +0 -44
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/fingerprint-aliases.json +0 -49
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/free-gifts-for-woocommerce.json +0 -94
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/index.json +0 -863
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/mailpoet.json +0 -52
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/membership-for-woocommerce.json +0 -161
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/mwb-bookings-for-woocommerce.json +0 -180
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/no-migration-needed.json +0 -286
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/pricing-deals-for-woocommerce.json +0 -79
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/product-extras-for-woocommerce.json +0 -120
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/pw-woocommerce-gift-cards.json +0 -128
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/requires-development.json +0 -88
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/seo-by-rank-math.json +0 -80
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/seriously-simple-podcasting.json +0 -46
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/strong-testimonials.json +0 -55
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/subscriptions-for-woocommerce.json +0 -99
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/tablepress.json +0 -55
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/the-events-calendar.json +0 -160
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/ti-woocommerce-wishlist.json +0 -81
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-checkout-field-editor-pro.json +0 -60
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-discount-rules.json +0 -74
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-product-bundle.json +0 -75
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-gift-cards.json +0 -60
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-other-payment-gateway.json +0 -44
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-pdf-invoices-packing-slips.json +0 -57
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-product-addon.json +0 -141
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-progressive-discounts.json +0 -54
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wordpress-seo.json +0 -158
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wpc-composite-products.json +0 -97
- package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wpforms-lite.json +0 -86
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/capture-store-settings.js +0 -90
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/plugin-knowledge-validate.js +0 -51
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/plugin-knowledge.js +0 -103
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-discovery.js +0 -2409
- package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-plugin-inventory.js +0 -273
- package/skills/wix-replatform/resources/rp-source-wordpress/secret-value-patterns.v1.json +0 -11
- package/skills/wix-replatform/resources/rp-source-wordpress/source-schema.example.json +0 -71
- package/skills/wix-replatform/resources/rp-source-wordpress/sub-parts.json +0 -13
- package/skills/wix-replatform/resources/rp-target-wix/SKILL.md +0 -1046
- package/skills/wix-replatform/resources/rp-target-wix/app-page-routing-capabilities.json +0 -36
- package/skills/wix-replatform/resources/rp-target-wix/domains/README.md +0 -57
- package/skills/wix-replatform/resources/rp-target-wix/domains/SERVICE-PLUGINS.md +0 -322
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/docs-survey.json +0 -70
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/domain.json +0 -29
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/category.json +0 -71
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/comment.json +0 -83
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/post.json +0 -95
- package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/tag.json +0 -66
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/docs-survey.json +0 -332
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/domain.json +0 -75
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/booking.json +0 -181
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/service-add-on.json +0 -130
- package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/service.json +0 -180
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/docs-survey.json +0 -161
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/domain.json +0 -25
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/collection.json +0 -67
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/data-item.json +0 -75
- package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/reference.json +0 -41
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/docs-survey.json +0 -204
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/domain.json +0 -27
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/contact.json +0 -275
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/email-subscription.json +0 -105
- package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/label.json +0 -77
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/docs-survey.json +0 -306
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/domain.json +0 -44
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/delivery-profile.json +0 -171
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/discount-rule.json +0 -182
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-applied-discount.json +0 -129
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-invoice-document.json +0 -192
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item-options.json +0 -123
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item.json +0 -85
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-merchant-note.json +0 -112
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-payment.json +0 -191
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-refund.json +0 -164
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order.json +0 -462
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/pickup-location.json +0 -122
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/shipping-option.json +0 -98
- package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/wishlist-item.json +0 -86
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/docs-survey.json +0 -128
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/domain.json +0 -38
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/category.json +0 -73
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/event.json +0 -107
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/registration-form.json +0 -89
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/rsvp.json +0 -118
- package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/ticket-definition.json +0 -93
- package/skills/wix-replatform/resources/rp-target-wix/domains/forms/docs-survey.json +0 -86
- package/skills/wix-replatform/resources/rp-target-wix/domains/forms/domain.json +0 -56
- package/skills/wix-replatform/resources/rp-target-wix/domains/forms/entities/form-submission.json +0 -121
- package/skills/wix-replatform/resources/rp-target-wix/domains/forms/entities/form.json +0 -134
- package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/docs-survey.json +0 -41
- package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/domain.json +0 -50
- package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/entities/gift-card-product.json +0 -87
- package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/entities/gift-card.json +0 -130
- package/skills/wix-replatform/resources/rp-target-wix/domains/index.json +0 -1418
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/docs-survey.json +0 -41
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/domain.json +0 -24
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-file.json +0 -75
- package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-folder.json +0 -71
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/docs-survey.json +0 -119
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/domain.json +0 -23
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/badge.json +0 -71
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/custom-field.json +0 -82
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member-about.json +0 -77
- package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member.json +0 -205
- package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/docs-survey.json +0 -41
- package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/domain.json +0 -46
- package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/entities/plan-order.json +0 -137
- package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/entities/plan.json +0 -133
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/docs-survey.json +0 -7
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/domain.json +0 -36
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/amazon-shop-connect.json +0 -71
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/ebay-shop-connect.json +0 -67
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/google-youtube-connect.json +0 -72
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/meta-catalog-connect.json +0 -74
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/pinterest-catalog-connect.json +0 -69
- package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/tiktok-catalog-connect.json +0 -61
- package/skills/wix-replatform/resources/rp-target-wix/domains/schema.json +0 -111
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/docs-survey.json +0 -109
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/domain.json +0 -73
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/item-seo-tags.json +0 -148
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/seo-pattern.json +0 -91
- package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/site-seo-tags.json +0 -79
- package/skills/wix-replatform/resources/rp-target-wix/domains/site/docs-survey.json +0 -32
- package/skills/wix-replatform/resources/rp-target-wix/domains/site/domain.json +0 -49
- package/skills/wix-replatform/resources/rp-target-wix/domains/site/entities/business-contact.json +0 -103
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/docs-survey.json +0 -203
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/domain.json +0 -42
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/back-in-stock-request.json +0 -119
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/brand.json +0 -111
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/category.json +0 -81
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/coupon.json +0 -241
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/inventory-item.json +0 -118
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-customization.json +0 -111
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-tag.json +0 -79
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product.json +0 -221
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/promotion.json +0 -84
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/review.json +0 -178
- package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/subscription-product.json +0 -87
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/docs-survey.json +0 -106
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/domain.json +0 -64
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/manual-tax-mapping.json +0 -119
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-group.json +0 -94
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-region.json +0 -113
- package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-settings.json +0 -65
- package/skills/wix-replatform/resources/rp-target-wix/lib/audit-summary.js +0 -64
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-author-attribution.js +0 -165
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-client.js +0 -60
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-duplicates.js +0 -201
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-ledger.js +0 -306
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-report.js +0 -229
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-stage.js +0 -55
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build.js +0 -715
- package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-media.js +0 -567
- package/skills/wix-replatform/resources/rp-target-wix/lib/bulk-results.js +0 -73
- package/skills/wix-replatform/resources/rp-target-wix/lib/contact-extended-fields.js +0 -194
- package/skills/wix-replatform/resources/rp-target-wix/lib/contact-import.js +0 -133
- package/skills/wix-replatform/resources/rp-target-wix/lib/contract-ledger.js +0 -150
- package/skills/wix-replatform/resources/rp-target-wix/lib/coupon-redemption-drift.js +0 -491
- package/skills/wix-replatform/resources/rp-target-wix/lib/customer-member.js +0 -104
- package/skills/wix-replatform/resources/rp-target-wix/lib/data-extension-schema.js +0 -673
- package/skills/wix-replatform/resources/rp-target-wix/lib/domain-knowledge.js +0 -835
- package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-balance-reconciliation.js +0 -119
- package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-build.js +0 -566
- package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-redemption-build.js +0 -249
- package/skills/wix-replatform/resources/rp-target-wix/lib/ndjson.js +0 -260
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-applied-discount-build.js +0 -63
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-buyer.js +0 -187
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-channel-build.js +0 -64
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-history.js +0 -1027
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-invoice-contract.js +0 -90
- package/skills/wix-replatform/resources/rp-target-wix/lib/order-payment-mapping.js +0 -420
- package/skills/wix-replatform/resources/rp-target-wix/lib/pricing-plan-definition.js +0 -177
- package/skills/wix-replatform/resources/rp-target-wix/lib/product-import.js +0 -151
- package/skills/wix-replatform/resources/rp-target-wix/lib/product-seo.js +0 -723
- package/skills/wix-replatform/resources/rp-target-wix/lib/seo-redirects.js +0 -353
- package/skills/wix-replatform/resources/rp-target-wix/lib/shipping-build.js +0 -1437
- package/skills/wix-replatform/resources/rp-target-wix/lib/shipping-option-stage.js +0 -354
- package/skills/wix-replatform/resources/rp-target-wix/lib/store-config-build.js +0 -134
- package/skills/wix-replatform/resources/rp-target-wix/lib/stores-category-attach.js +0 -221
- package/skills/wix-replatform/resources/rp-target-wix/lib/stores-inventory.js +0 -248
- package/skills/wix-replatform/resources/rp-target-wix/lib/stores-verification.js +0 -321
- package/skills/wix-replatform/resources/rp-target-wix/lib/tax-build.js +0 -231
- package/skills/wix-replatform/resources/rp-target-wix/lib/url-mapper-transport.js +0 -28
- package/skills/wix-replatform/resources/rp-target-wix/lib/value-utils.js +0 -14
- package/skills/wix-replatform/resources/rp-target-wix/lib/variant-correlation.js +0 -34
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-build.js +0 -388
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-target-spec.js +0 -255
- package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +0 -6041
- package/skills/wix-replatform/resources/rp-target-wix/lib/write-verification.js +0 -3
- package/skills/wix-replatform/resources/rp-target-wix/scripts/docs-survey-sync.js +0 -150
- package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge-validate.js +0 -69
- package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge.js +0 -100
- package/skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js +0 -112
- package/skills/wix-replatform/resources/rp-telemetry/SKILL.md +0 -484
- package/skills/wix-replatform/resources/rp-telemetry/lib/bi-sink.js +0 -318
- package/skills/wix-replatform/resources/rp-telemetry/lib/telemetry-recorder.js +0 -2831
- package/skills/wix-replatform/resources/rp-telemetry/lib/transcript-digest.js +0 -318
- package/skills/wix-replatform/resources/rp-telemetry/scripts/meter-from-audit.js +0 -83
- package/skills/wix-replatform/resources/rp-telemetry/scripts/rp-telemetry.js +0 -243
- package/skills/wix-replatform/resources/rp-website-continuation/SKILL.md +0 -94
- package/skills/wix-replatform/scripts/artifact-freshness.js +0 -76
- package/skills/wix-replatform/scripts/census-reconciliation.js +0 -25
- package/skills/wix-replatform/scripts/cms-fallback.js +0 -89
- package/skills/wix-replatform/scripts/discovered-census.js +0 -26
- package/skills/wix-replatform/scripts/discovered-set-status.js +0 -33
- package/skills/wix-replatform/scripts/entity-qa-attestation.js +0 -130
- package/skills/wix-replatform/scripts/execution-health-check.js +0 -415
- package/skills/wix-replatform/scripts/execution-manifest-validate.js +0 -43
- package/skills/wix-replatform/scripts/execution-state-prepare.js +0 -128
- package/skills/wix-replatform/scripts/expectations-ledger.js +0 -44
- package/skills/wix-replatform/scripts/import-contract-verify.js +0 -183
- package/skills/wix-replatform/scripts/internal-link-audit.js +0 -90
- package/skills/wix-replatform/scripts/item-seo-tags-verify.js +0 -205
- package/skills/wix-replatform/scripts/migration-completion.js +0 -76
- package/skills/wix-replatform/scripts/orchestration-preflight.js +0 -67
- package/skills/wix-replatform/scripts/orchestration-route.js +0 -77
- package/skills/wix-replatform/scripts/orchestration-state.js +0 -381
- package/skills/wix-replatform/scripts/read-back-gate.js +0 -175
- package/skills/wix-replatform/scripts/replatform-console.js +0 -146
- package/skills/wix-replatform/scripts/replatform.js +0 -436
- package/skills/wix-replatform/scripts/source-secrets.js +0 -77
- package/skills/wix-replatform/scripts/url-redirects.js +0 -286
- package/skills/wix-replatform/scripts/user-action.js +0 -58
- package/skills/wix-replatform/scripts/website-handoff-generate.js +0 -64
- package/skills/wix-replatform/scripts/wix-dashboard-url.js +0 -54
- package/skills/wix-replatform/scripts/wix-destination.js +0 -143
- package/skills/wix-replatform/scripts/wix-headless-scaffold.js +0 -762
- package/skills/wix-vibe-headless/SKILL.md +0 -183
- package/skills/wix-vibe-headless/install/deploy.cjs +0 -221
- package/skills/wix-vibe-headless/install/pin-agents-md.cjs +0 -35
- package/skills/wix-vibe-headless/platforms/README.md +0 -26
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +0 -127
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +0 -121
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +0 -87
- package/skills/wix-vibe-headless/platforms/base44.md +0 -148
- package/skills/wix-vibe-headless/platforms/generic.md +0 -132
- package/skills/wix-vibe-headless/references/_shared/nav/nav.tanstack.js +0 -30
- package/skills/wix-vibe-headless/references/_shared/routing.md +0 -86
- package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +0 -270
- package/skills/wix-vibe-headless/references/blog/app/components/PostCard.jsx +0 -43
- package/skills/wix-vibe-headless/references/blog/app/components/PostChips.jsx +0 -25
- package/skills/wix-vibe-headless/references/blog/app/components/PostGrid.jsx +0 -15
- package/skills/wix-vibe-headless/references/blog/app/context/TaxonomyContext.jsx +0 -35
- package/skills/wix-vibe-headless/references/blog/app/hooks/usePostDetail.js +0 -38
- package/skills/wix-vibe-headless/references/blog/app/pages/Blog.jsx +0 -40
- package/skills/wix-vibe-headless/references/blog/app/pages/CategoryPage.jsx +0 -54
- package/skills/wix-vibe-headless/references/blog/app/pages/PostDetail.jsx +0 -65
- package/skills/wix-vibe-headless/references/blog/app/pages/TagPage.jsx +0 -50
- package/skills/wix-vibe-headless/references/blog/app/rest/wix-blog.js +0 -217
- package/skills/wix-vibe-headless/references/blog/seed/SEED.md +0 -99
- package/skills/wix-vibe-headless/references/blog/seed/seed-blog.cjs +0 -269
- package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +0 -342
- package/skills/wix-vibe-headless/references/bookings/app/components/BookingForm.jsx +0 -65
- package/skills/wix-vibe-headless/references/bookings/app/components/RentalDurationPicker.jsx +0 -67
- package/skills/wix-vibe-headless/references/bookings/app/components/ServiceCard.jsx +0 -47
- package/skills/wix-vibe-headless/references/bookings/app/components/ServiceGrid.jsx +0 -13
- package/skills/wix-vibe-headless/references/bookings/app/components/SlotPicker.jsx +0 -126
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useRentalDuration.js +0 -128
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useServiceDetail.js +0 -110
- package/skills/wix-vibe-headless/references/bookings/app/hooks/useServices.js +0 -58
- package/skills/wix-vibe-headless/references/bookings/app/lib/serviceFacts.js +0 -95
- package/skills/wix-vibe-headless/references/bookings/app/pages/ServiceDetail.jsx +0 -94
- package/skills/wix-vibe-headless/references/bookings/app/pages/Services.jsx +0 -89
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-checkout.js +0 -148
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-rentals.js +0 -250
- package/skills/wix-vibe-headless/references/bookings/app/rest/wix-bookings-services.js +0 -300
- package/skills/wix-vibe-headless/references/bookings/seed/SEED.md +0 -154
- package/skills/wix-vibe-headless/references/bookings/seed/seed-bookings.cjs +0 -333
- package/skills/wix-vibe-headless/references/bookings/seed/seed-rentals.cjs +0 -267
- package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +0 -178
- package/skills/wix-vibe-headless/references/cms/app/collection.config.js +0 -31
- package/skills/wix-vibe-headless/references/cms/app/lib/wixImage.js +0 -18
- package/skills/wix-vibe-headless/references/cms/app/rest/wix-cms.js +0 -231
- package/skills/wix-vibe-headless/references/cms/seed/SEED.md +0 -145
- package/skills/wix-vibe-headless/references/donations/INSTRUCTIONS.md +0 -88
- package/skills/wix-vibe-headless/references/donations/app/rest/wix-donations.js +0 -112
- package/skills/wix-vibe-headless/references/donations/seed/SEED.md +0 -11
- package/skills/wix-vibe-headless/references/donations/tests/donations.test.mjs +0 -258
- package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +0 -267
- package/skills/wix-vibe-headless/references/events/app/components/CategoryFilter.jsx +0 -23
- package/skills/wix-vibe-headless/references/events/app/components/EventCard.jsx +0 -43
- package/skills/wix-vibe-headless/references/events/app/components/EventGrid.jsx +0 -13
- package/skills/wix-vibe-headless/references/events/app/components/EventRegistration.jsx +0 -29
- package/skills/wix-vibe-headless/references/events/app/components/RsvpForm.jsx +0 -57
- package/skills/wix-vibe-headless/references/events/app/components/TicketPicker.jsx +0 -78
- package/skills/wix-vibe-headless/references/events/app/hooks/useEventDetail.js +0 -27
- package/skills/wix-vibe-headless/references/events/app/hooks/useEventsList.js +0 -54
- package/skills/wix-vibe-headless/references/events/app/hooks/useRsvpForm.js +0 -40
- package/skills/wix-vibe-headless/references/events/app/hooks/useTicketing.js +0 -81
- package/skills/wix-vibe-headless/references/events/app/pages/EventDetail.jsx +0 -48
- package/skills/wix-vibe-headless/references/events/app/pages/Events.jsx +0 -36
- package/skills/wix-vibe-headless/references/events/app/rest/wix-events-browse.js +0 -136
- package/skills/wix-vibe-headless/references/events/app/rest/wix-events-registration.js +0 -187
- package/skills/wix-vibe-headless/references/events/seed/SEED.md +0 -103
- package/skills/wix-vibe-headless/references/events/seed/seed-events.cjs +0 -251
- package/skills/wix-vibe-headless/references/forms/INSTRUCTIONS.md +0 -487
- package/skills/wix-vibe-headless/references/forms/app/hooks/useWixForm.js +0 -425
- package/skills/wix-vibe-headless/references/forms/app/lib/wix-form-schema-utils.js +0 -187
- package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms-submissions.js +0 -238
- package/skills/wix-vibe-headless/references/forms/app/rest/wix-forms.js +0 -108
- package/skills/wix-vibe-headless/references/forms/seed/SEED.md +0 -1836
- package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +0 -328
- package/skills/wix-vibe-headless/references/members/app/components/LoginForm.jsx +0 -101
- package/skills/wix-vibe-headless/references/members/app/components/MemberMenu.jsx +0 -33
- package/skills/wix-vibe-headless/references/members/app/components/RequireAuth.jsx +0 -21
- package/skills/wix-vibe-headless/references/members/app/components/SocialButtons.jsx +0 -29
- package/skills/wix-vibe-headless/references/members/app/context/MemberContext.jsx +0 -54
- package/skills/wix-vibe-headless/references/members/app/hooks/useLoginForm.js +0 -77
- package/skills/wix-vibe-headless/references/members/app/pages/Account.jsx +0 -36
- package/skills/wix-vibe-headless/references/members/app/pages/Callback.jsx +0 -32
- package/skills/wix-vibe-headless/references/members/app/pages/Login.jsx +0 -35
- package/skills/wix-vibe-headless/references/members/app/rest/wix-members-auth.js +0 -390
- package/skills/wix-vibe-headless/references/members/install/Register.jsx +0 -22
- package/skills/wix-vibe-headless/references/members/seed/SEED.md +0 -21
- package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +0 -270
- package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionCard.jsx +0 -31
- package/skills/wix-vibe-headless/references/portfolio/app/components/CollectionGrid.jsx +0 -15
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectCard.jsx +0 -39
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectGrid.jsx +0 -15
- package/skills/wix-vibe-headless/references/portfolio/app/components/ProjectMedia.jsx +0 -29
- package/skills/wix-vibe-headless/references/portfolio/app/hooks/useCollectionProjects.js +0 -35
- package/skills/wix-vibe-headless/references/portfolio/app/hooks/usePortfolioGallery.js +0 -27
- package/skills/wix-vibe-headless/references/portfolio/app/hooks/useProjectDetail.js +0 -25
- package/skills/wix-vibe-headless/references/portfolio/app/pages/CollectionPage.jsx +0 -39
- package/skills/wix-vibe-headless/references/portfolio/app/pages/Portfolio.jsx +0 -27
- package/skills/wix-vibe-headless/references/portfolio/app/pages/ProjectDetail.jsx +0 -60
- package/skills/wix-vibe-headless/references/portfolio/app/rest/wix-portfolio.js +0 -223
- package/skills/wix-vibe-headless/references/portfolio/seed/SEED.md +0 -90
- package/skills/wix-vibe-headless/references/portfolio/seed/seed-portfolio.cjs +0 -236
- package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +0 -280
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/OrderCard.jsx +0 -35
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanCard.jsx +0 -48
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanGrid.jsx +0 -16
- package/skills/wix-vibe-headless/references/pricing-plans/app/components/PlanPrice.jsx +0 -35
- package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/useMyPlans.js +0 -25
- package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlanDetail.js +0 -28
- package/skills/wix-vibe-headless/references/pricing-plans/app/hooks/usePlans.js +0 -30
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/MyPlans.jsx +0 -30
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/PlanDetail.jsx +0 -53
- package/skills/wix-vibe-headless/references/pricing-plans/app/pages/Plans.jsx +0 -28
- package/skills/wix-vibe-headless/references/pricing-plans/app/rest/wix-pricing-plans.js +0 -157
- package/skills/wix-vibe-headless/references/pricing-plans/seed/SEED.md +0 -82
- package/skills/wix-vibe-headless/references/pricing-plans/seed/seed-pricing-plans.cjs +0 -251
- package/skills/wix-vibe-headless/references/rentals/INSTRUCTIONS.md +0 -13
- package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +0 -290
- package/skills/wix-vibe-headless/references/restaurants/app/components/ItemDialog.jsx +0 -81
- package/skills/wix-vibe-headless/references/restaurants/app/components/MenuItemCard.jsx +0 -71
- package/skills/wix-vibe-headless/references/restaurants/app/components/MenuList.jsx +0 -43
- package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartButton.jsx +0 -21
- package/skills/wix-vibe-headless/references/restaurants/app/components/OrderCartDrawer.jsx +0 -72
- package/skills/wix-vibe-headless/references/restaurants/app/context/OrderCartContext.jsx +0 -66
- package/skills/wix-vibe-headless/references/restaurants/app/hooks/useItemOrder.js +0 -32
- package/skills/wix-vibe-headless/references/restaurants/app/hooks/useReservation.js +0 -73
- package/skills/wix-vibe-headless/references/restaurants/app/pages/Menu.jsx +0 -28
- package/skills/wix-vibe-headless/references/restaurants/app/pages/Reservations.jsx +0 -102
- package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-menu.js +0 -224
- package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-ordering.js +0 -218
- package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-reservations.js +0 -107
- package/skills/wix-vibe-headless/references/restaurants/seed/SEED.md +0 -176
- package/skills/wix-vibe-headless/references/restaurants/seed/seed-restaurants.cjs +0 -452
- package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +0 -56
- package/skills/wix-vibe-headless/references/shared/app/lib/nav.js +0 -9
- package/skills/wix-vibe-headless/references/shared/app/rest/wix-client.js +0 -185
- package/skills/wix-vibe-headless/references/shared/app/rest/wix-config.js +0 -8
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +0 -471
- package/skills/wix-vibe-headless/references/storefront/app/components/CartButton.jsx +0 -20
- package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +0 -167
- package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +0 -83
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +0 -104
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +0 -163
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useShop.js +0 -83
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useVariantOptions.js +0 -76
- package/skills/wix-vibe-headless/references/storefront/app/lib/storeImage.js +0 -64
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +0 -176
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +0 -218
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +0 -174
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.cjs +0 -546
|
@@ -1,1580 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs/promises');
|
|
4
|
-
const path = require('node:path');
|
|
5
|
-
const { verificationKind } = require('./entity-verification');
|
|
6
|
-
const { appendExecutionLog } = require('./execution-log.js');
|
|
7
|
-
const { validReceipt } = require('./write-verification');
|
|
8
|
-
const { OUTCOME_RANK, aggregateOutcome } = require('./blocked-data-requests.js');
|
|
9
|
-
const { PAYMENT_OUTCOMES, WRITER_OUTCOMES } = require('./payments-outcome-registry.js');
|
|
10
|
-
const memberRegistry = require('./member-outcome-registry.js');
|
|
11
|
-
const { SEO_OUTCOMES, SEO_ACCEPTED_OUTCOMES, SEO_OUTCOME_ACTIONS } = require('./seo-outcome-registry.js');
|
|
12
|
-
const { coverageDisagreements, describeCoverageMismatch } = require('./import-recovery.js');
|
|
13
|
-
const { cmsFallbackOperatorActions } = require('./plugin-cms-fallback.js');
|
|
14
|
-
const { summarizeProjectCoverage } = require('./source-coverage.js');
|
|
15
|
-
const { readLinkAudit, linkOperatorActions, RELATIVE_PATH: LINK_AUDIT_PATH } = require('./link-audit.js');
|
|
16
|
-
const { projectDestinationOperatorActions } = require('./destination-handoff.js');
|
|
17
|
-
const { sourceAccessOperatorActions, withheldDetailsOperatorActions } = require('./source-access.js');
|
|
18
|
-
const { evaluateGiftCardCutover, GIFT_CODES_COVERAGE_ID, GIFT_ACTION_CODE } = require('../resources/rp-source-shopify/lib/shopify-gift-cards.js');
|
|
19
|
-
|
|
20
|
-
const COMPLETION_STATUS = [
|
|
21
|
-
'aborted',
|
|
22
|
-
'incomplete_with_mismatches',
|
|
23
|
-
'incomplete_with_failures',
|
|
24
|
-
'complete_with_deferred_records',
|
|
25
|
-
'complete_with_recovered_records',
|
|
26
|
-
'complete_with_warnings',
|
|
27
|
-
'complete',
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
const STATUS_RANK = new Map(COMPLETION_STATUS.map((status, index) => [status, index]));
|
|
31
|
-
|
|
32
|
-
function count(value, field) {
|
|
33
|
-
if (value === undefined || value === null) {
|
|
34
|
-
return 0;
|
|
35
|
-
}
|
|
36
|
-
if (!Number.isInteger(value) || value < 0) {
|
|
37
|
-
throw new Error(`${field} must be a non-negative integer`);
|
|
38
|
-
}
|
|
39
|
-
return value;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function requireString(value, field) {
|
|
43
|
-
if (!value || typeof value !== 'string') {
|
|
44
|
-
throw new Error(`${field} must be a non-empty string`);
|
|
45
|
-
}
|
|
46
|
-
return value;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function asArray(value, field) {
|
|
50
|
-
if (value === undefined || value === null) {
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
if (!Array.isArray(value)) {
|
|
54
|
-
throw new Error(`${field} must be an array`);
|
|
55
|
-
}
|
|
56
|
-
return value;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function hasCount(rows, field) {
|
|
60
|
-
return rows.some((row) => row[field] > 0);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function worseStatus(left, right) {
|
|
64
|
-
const leftRank = STATUS_RANK.has(left) ? STATUS_RANK.get(left) : STATUS_RANK.get('complete');
|
|
65
|
-
const rightRank = STATUS_RANK.has(right) ? STATUS_RANK.get(right) : STATUS_RANK.get('complete');
|
|
66
|
-
return leftRank <= rightRank ? left : right;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function chooseCompletionStatus(input = {}) {
|
|
70
|
-
const statuses = asArray(input.statuses, 'statuses');
|
|
71
|
-
let selected = input.defaultStatus || 'complete';
|
|
72
|
-
if (!STATUS_RANK.has(selected)) {
|
|
73
|
-
throw new Error(`unknown completion status: ${selected}`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
for (const status of statuses) {
|
|
77
|
-
if (!STATUS_RANK.has(status)) {
|
|
78
|
-
throw new Error(`unknown completion status: ${status}`);
|
|
79
|
-
}
|
|
80
|
-
selected = worseStatus(selected, status);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (input.aborted) {
|
|
84
|
-
selected = worseStatus(selected, 'aborted');
|
|
85
|
-
}
|
|
86
|
-
if (input.hasMismatches) {
|
|
87
|
-
selected = worseStatus(selected, 'incomplete_with_mismatches');
|
|
88
|
-
}
|
|
89
|
-
if (input.hasFailures) {
|
|
90
|
-
selected = worseStatus(selected, 'incomplete_with_failures');
|
|
91
|
-
}
|
|
92
|
-
if (input.hasDeferred) {
|
|
93
|
-
selected = worseStatus(selected, 'complete_with_deferred_records');
|
|
94
|
-
}
|
|
95
|
-
if (input.hasRecoveredRecords) {
|
|
96
|
-
selected = worseStatus(selected, 'complete_with_recovered_records');
|
|
97
|
-
}
|
|
98
|
-
if (input.hasWarnings) {
|
|
99
|
-
selected = worseStatus(selected, 'complete_with_warnings');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return selected;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// A structured reason entry is `{ outcome: 'deferred' | 'failed', code:
|
|
106
|
-
// non-empty string, count: positive integer }`. `code` is taken verbatim and compared
|
|
107
|
-
// case-sensitively -- there is no normalization, so `INTERNAL` and `internal` are distinct and
|
|
108
|
-
// cannot collide. A plain string entry is a legacy free-text note and is returned unchanged.
|
|
109
|
-
function normalizeReasonEntry(entry, label, index) {
|
|
110
|
-
if (typeof entry === 'string') {
|
|
111
|
-
return entry;
|
|
112
|
-
}
|
|
113
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
114
|
-
throw new Error(`${label}.reasons[${index}] must be a string or a structured { outcome, code, count } object`);
|
|
115
|
-
}
|
|
116
|
-
if (entry.outcome !== 'deferred' && entry.outcome !== 'failed') {
|
|
117
|
-
throw new Error(`${label}.reasons[${index}].outcome must be 'deferred' or 'failed'`);
|
|
118
|
-
}
|
|
119
|
-
const code = requireString(entry.code, `${label}.reasons[${index}].code`);
|
|
120
|
-
if (!Number.isInteger(entry.count) || entry.count <= 0) {
|
|
121
|
-
throw new Error(`${label}.reasons[${index}].count must be a positive integer`);
|
|
122
|
-
}
|
|
123
|
-
return { outcome: entry.outcome, code, count: entry.count };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// A structured entry is only ever trustworthy because the journal join can tie it back
|
|
127
|
-
// to the journal; with no `sourceKeys` there is nothing to join against, so a caller cannot hand
|
|
128
|
-
// `createCompletionReport` a fabricated counted tally with no journal backing at all -- the exact
|
|
129
|
-
// defect of "a count with no rows behind it". Also rejects a duplicate
|
|
130
|
-
// `(outcome, code)` pair and a structured sum for an outcome that exceeds that outcome's count.
|
|
131
|
-
function validateReasons(reasons, { entity, subtype, sourceKeys, deferred, failed }) {
|
|
132
|
-
const label = `${entity}/${subtype}`;
|
|
133
|
-
const hasStructured = reasons.some((entry) => typeof entry === 'object');
|
|
134
|
-
if (hasStructured && (!sourceKeys || sourceKeys.length === 0)) {
|
|
135
|
-
throw new Error(`${label}: a structured reason requires sourceKeys`);
|
|
136
|
-
}
|
|
137
|
-
const seen = new Set();
|
|
138
|
-
const sums = { deferred: 0, failed: 0 };
|
|
139
|
-
for (const entry of reasons) {
|
|
140
|
-
if (typeof entry !== 'object') {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
const key = `${entry.outcome}\0${entry.code}`;
|
|
144
|
-
if (seen.has(key)) {
|
|
145
|
-
throw new Error(`${label}: duplicate reason (${entry.outcome}, ${entry.code})`);
|
|
146
|
-
}
|
|
147
|
-
seen.add(key);
|
|
148
|
-
sums[entry.outcome] += entry.count;
|
|
149
|
-
}
|
|
150
|
-
if (sums.deferred > deferred) {
|
|
151
|
-
throw new Error(`${label}: structured deferred reasons sum to ${sums.deferred}, more than deferred count ${deferred}`);
|
|
152
|
-
}
|
|
153
|
-
if (sums.failed > failed) {
|
|
154
|
-
throw new Error(`${label}: structured failed reasons sum to ${sums.failed}, more than failed count ${failed}`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function normalizeCompletenessRow(row, index = 0) {
|
|
159
|
-
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
160
|
-
throw new Error(`entityCompleteness[${index}] must be an object`);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const entity = requireString(row.entity, `entityCompleteness[${index}].entity`);
|
|
164
|
-
const subtype = row.subtype === undefined || row.subtype === null ? 'all' : requireString(row.subtype, `entityCompleteness[${index}].subtype`);
|
|
165
|
-
const extracted = count(row.extracted, `${entity}/${subtype}.extracted`);
|
|
166
|
-
const inScope = count(row.inScope, `${entity}/${subtype}.inScope`);
|
|
167
|
-
const attempted = count(row.attempted, `${entity}/${subtype}.attempted`);
|
|
168
|
-
const imported = count(row.imported, `${entity}/${subtype}.imported`);
|
|
169
|
-
const alreadyPresentByCrosswalk = count(
|
|
170
|
-
row.alreadyPresentByCrosswalk === undefined ? row.alreadyPresent : row.alreadyPresentByCrosswalk,
|
|
171
|
-
`${entity}/${subtype}.alreadyPresentByCrosswalk`
|
|
172
|
-
);
|
|
173
|
-
const deferred = count(row.deferred, `${entity}/${subtype}.deferred`);
|
|
174
|
-
const failed = count(row.failed, `${entity}/${subtype}.failed`);
|
|
175
|
-
// A write of unknown outcome: may or may not be on the merchant's store. Its own bucket, never
|
|
176
|
-
// folded into `failed` and never left as an unexplained shortfall. Existing callers omit it.
|
|
177
|
-
const unresolved = count(row.unresolved, `${entity}/${subtype}.unresolved`);
|
|
178
|
-
const skippedOutOfScope = count(row.skippedOutOfScope, `${entity}/${subtype}.skippedOutOfScope`);
|
|
179
|
-
const unexpectedSkipped = count(
|
|
180
|
-
row.unexpectedSkipped === undefined ? row.skippedInScope : row.unexpectedSkipped,
|
|
181
|
-
`${entity}/${subtype}.unexpectedSkipped`
|
|
182
|
-
);
|
|
183
|
-
const reconciled = imported + alreadyPresentByCrosswalk + deferred + failed + unresolved;
|
|
184
|
-
const expectedReconciled = inScope;
|
|
185
|
-
// The shipped reconciliation contract (rp-import-codegen/SKILL.md) requires
|
|
186
|
-
// `inScope + skippedOutOfScope <= extracted` -- an excluded record must never also be counted
|
|
187
|
-
// inside `inScope`. This is only detectable when the caller actually supplied `extracted`
|
|
188
|
-
// (present, not defaulted by `count()` to 0 for an omitted or explicit `null` field); the raw
|
|
189
|
-
// field is checked here, before `count()` erases that distinction. `scopeOverflow` names only
|
|
190
|
-
// the exact, arithmetic-provable excess -- never a claimed count of overlapping records, since
|
|
191
|
-
// an extracted record that belongs to neither bucket can make the true overlap larger than this
|
|
192
|
-
// number (worked example: 10 extracted, 8 in-scope, 4 skipped, 1 in neither bucket --
|
|
193
|
-
// true overlap could be 3 while counts exceed extracted by only 2).
|
|
194
|
-
const extractedSupplied = row.extracted !== undefined && row.extracted !== null;
|
|
195
|
-
const scopeOverflow = extractedSupplied && inScope + skippedOutOfScope > extracted
|
|
196
|
-
? inScope + skippedOutOfScope - extracted
|
|
197
|
-
: 0;
|
|
198
|
-
const mismatch = reconciled !== expectedReconciled || unexpectedSkipped > 0 || scopeOverflow > 0;
|
|
199
|
-
const sourceKeys = Array.isArray(row.sourceKeys) ? row.sourceKeys : null;
|
|
200
|
-
const reasons = (Array.isArray(row.reasons) ? row.reasons : [])
|
|
201
|
-
.map((entry, entryIndex) => normalizeReasonEntry(entry, `${entity}/${subtype}`, entryIndex));
|
|
202
|
-
validateReasons(reasons, { entity, subtype, sourceKeys, deferred, failed });
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
entity,
|
|
206
|
-
subtype,
|
|
207
|
-
extracted,
|
|
208
|
-
inScope,
|
|
209
|
-
attempted,
|
|
210
|
-
imported,
|
|
211
|
-
alreadyPresentByCrosswalk,
|
|
212
|
-
deferred,
|
|
213
|
-
failed,
|
|
214
|
-
unresolved,
|
|
215
|
-
skippedOutOfScope,
|
|
216
|
-
unexpectedSkipped,
|
|
217
|
-
reconciled,
|
|
218
|
-
expectedReconciled,
|
|
219
|
-
scopeOverflow,
|
|
220
|
-
mismatch,
|
|
221
|
-
reasons,
|
|
222
|
-
verificationKind: verificationKind(row),
|
|
223
|
-
sourceKeys,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// The post-import voucher reconciliation answer. Closed set, no default: a run that
|
|
228
|
-
// imported usage-limited coupons cannot report itself complete while staying silent about whether
|
|
229
|
-
// the reconciliation happened. The whole defect this guards is that "we never did this" and
|
|
230
|
-
// "there was nothing to do" used to be the same shape — an absent field.
|
|
231
|
-
const VOUCHER_RECONCILIATION = [
|
|
232
|
-
'not-applicable',
|
|
233
|
-
'reconciled',
|
|
234
|
-
'plan-pending-owner',
|
|
235
|
-
'remedied',
|
|
236
|
-
'not-run',
|
|
237
|
-
];
|
|
238
|
-
// Both of these are money still live on the merchant's store, or possibly so. Neither is allowed
|
|
239
|
-
// to be a quiet line item: they reach the headline and they raise the final status.
|
|
240
|
-
const VOUCHER_RECONCILIATION_WARNING = new Set(['not-run', 'plan-pending-owner']);
|
|
241
|
-
|
|
242
|
-
const VOUCHER_RECONCILIATION_MESSAGE = {
|
|
243
|
-
'not-run': 'vouchers: the post-import reconciliation was due and did not run. Run it BEFORE cutover — afterwards there is no fresh source read to compare against and the target exposes no redemption count, so which codes were already spent becomes unknowable',
|
|
244
|
-
'plan-pending-owner': 'vouchers: a reconciliation plan is waiting on the owner\'s decision per code. Until each is decided and confirmed by a post-settle read-back, money spent at source is still redeemable on the target',
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
// The same two states also owe an urgent operator action, so the warning reaches the
|
|
248
|
-
// "Urgent Operator Actions" section of both the summary and the merchant handoff instead of only
|
|
249
|
-
// the headline. Fixed per state -- nothing is left for a renderer to invent.
|
|
250
|
-
const VOUCHER_RECONCILIATION_ACTION = {
|
|
251
|
-
'not-run': {
|
|
252
|
-
code: 'voucher-reconciliation-not-run',
|
|
253
|
-
severity: 'critical',
|
|
254
|
-
action: 'Run the post-import voucher reconciliation against a fresh source read before cutover.',
|
|
255
|
-
deadlineReason: 'once the old platform stops being the system of record there is no fresh source read left to compare against, and which codes were already spent becomes permanently unknowable',
|
|
256
|
-
},
|
|
257
|
-
'plan-pending-owner': {
|
|
258
|
-
code: 'voucher-reconciliation-plan-pending',
|
|
259
|
-
severity: 'critical',
|
|
260
|
-
action: 'Decide the remedy for each coupon on the pending reconciliation plan, then confirm each decision with a post-settle read-back.',
|
|
261
|
-
deadlineReason: 'until every code is decided and confirmed, money already spent at source remains redeemable on the target',
|
|
262
|
-
},
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
// Anchored at the end of the entity name, not a bare
|
|
266
|
-
// substring test. A bare `/coupon/i` also matches an entity whose name merely CONTAINS "coupon"
|
|
267
|
-
// as a qualifier for something else entirely -- for example
|
|
268
|
-
// `smart-coupons-extra-fields` (a per-coupon CMS-shadow metadata facet of the SAME coupon
|
|
269
|
-
// records, recordCount tied 1:1 to `coupon`, not a second population of coupons) matched the old
|
|
270
|
-
// regex and got summed into `affectedCount` alongside the real `coupon` row, inflating the
|
|
271
|
-
// voucher-reconciliation operator action to roughly double the real exposure. Anchoring to "coupon(s)" at the end of the name keeps matching a
|
|
272
|
-
// genuinely separate coupon population named with a suffix (`giftCoupons`, tested below) while
|
|
273
|
-
// excluding a differently-suffixed entity that merely mentions coupons in passing
|
|
274
|
-
// (`*-extra-fields`, `*-rule-definition`, etc.).
|
|
275
|
-
const isCouponRow = (row) => /coupons?$/i.test(row.entity);
|
|
276
|
-
|
|
277
|
-
// Confidentiality: the merchant handoff may carry the voucher urgency signal, but
|
|
278
|
-
// never a caller's free-text warning, which can hold names, emails or order contents. Only the
|
|
279
|
-
// fixed voucher messages above pass, rebuilt from constants so no caller-supplied field rides along.
|
|
280
|
-
const VOUCHER_MESSAGES = new Set(Object.values(VOUCHER_RECONCILIATION_MESSAGE));
|
|
281
|
-
|
|
282
|
-
function handoffVoucherSignal(report = {}) {
|
|
283
|
-
const state = VOUCHER_RECONCILIATION.includes(report.voucherReconciliation) ? report.voucherReconciliation : null;
|
|
284
|
-
const headline = (Array.isArray(report.headline) ? report.headline : [])
|
|
285
|
-
.filter((row) => row && row.subtype === 'voucher-reconciliation' && VOUCHER_MESSAGES.has(row.message))
|
|
286
|
-
.map((row) => ({ entity: 'coupons', subtype: 'voucher-reconciliation', message: row.message }));
|
|
287
|
-
const warnings = (Array.isArray(report.warnings) ? report.warnings : [])
|
|
288
|
-
.filter((message) => VOUCHER_MESSAGES.has(message));
|
|
289
|
-
return { voucherReconciliation: state, headline, warnings };
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// `affectedCount` counts from the same rows that decide whether the step is owed. With no coupon
|
|
293
|
-
// completeness row (only `usageLimitedCouponsInSource` triggered it) it is 0, meaning "no row to
|
|
294
|
-
// count from" -- never "no coupons affected", and never less urgent.
|
|
295
|
-
function voucherOperatorAction(voucherReconciliation, entityCompleteness) {
|
|
296
|
-
const template = VOUCHER_RECONCILIATION_ACTION[voucherReconciliation];
|
|
297
|
-
if (!template) return null;
|
|
298
|
-
const affectedCount = entityCompleteness.filter(isCouponRow).reduce((sum, row) => sum + row.inScope, 0);
|
|
299
|
-
return { ...template, entity: 'coupons', affectedCount };
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Is this run subject to the step at all? Either the caller says the source carried
|
|
303
|
-
// usage-limited coupons, or the run has an in-scope coupon entity row. Silence from a run that
|
|
304
|
-
// never touched coupons is not the defect; silence from a run that imported them is.
|
|
305
|
-
function voucherReconciliationRequired(input, entityCompleteness) {
|
|
306
|
-
if (input.usageLimitedCouponsInSource === true) return true;
|
|
307
|
-
return entityCompleteness.some((row) => isCouponRow(row) && row.inScope > 0);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function normalizeVoucherReconciliation(input, entityCompleteness) {
|
|
311
|
-
const raw = input.voucherReconciliation;
|
|
312
|
-
if (raw !== undefined && raw !== null) {
|
|
313
|
-
if (typeof raw !== 'string' || !VOUCHER_RECONCILIATION.includes(raw)) {
|
|
314
|
-
throw new Error(`voucherReconciliation must be one of ${VOUCHER_RECONCILIATION.join(', ')}`);
|
|
315
|
-
}
|
|
316
|
-
return raw;
|
|
317
|
-
}
|
|
318
|
-
// A missing answer is read as `not-run`, NEVER as `not-applicable`: defaulting an absent
|
|
319
|
-
// answer to the benign value is exactly how a skipped step reported itself complete.
|
|
320
|
-
return voucherReconciliationRequired(input, entityCompleteness) ? 'not-run' : null;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function headlineForRow(row) {
|
|
324
|
-
const parts = [];
|
|
325
|
-
// This fragment must name an actual reconciliation
|
|
326
|
-
// shortfall, not merely `row.mismatch` -- `mismatch` can now also be forced by `scopeOverflow`
|
|
327
|
-
// (a counting error, not a lost record) or by `unexpectedSkipped` alone, either of which can
|
|
328
|
-
// leave `reconciled === expectedReconciled`. Gating on the real inequality avoids a
|
|
329
|
-
// self-contradictory "mismatch N/N" once concatenated with the scopeOverflow headline below.
|
|
330
|
-
if (row.reconciled !== row.expectedReconciled) {
|
|
331
|
-
parts.push(`mismatch ${row.reconciled}/${row.expectedReconciled}`);
|
|
332
|
-
}
|
|
333
|
-
if (row.scopeOverflow > 0) {
|
|
334
|
-
parts.push(`inScope + skippedOutOfScope exceeds extracted by ${row.scopeOverflow}; count exclusions outside inScope`);
|
|
335
|
-
}
|
|
336
|
-
if (row.deferred > 0) {
|
|
337
|
-
parts.push(`${row.deferred} deferred`);
|
|
338
|
-
}
|
|
339
|
-
if (row.failed > 0) {
|
|
340
|
-
parts.push(`${row.failed} failed`);
|
|
341
|
-
}
|
|
342
|
-
if (row.unresolved > 0) {
|
|
343
|
-
parts.push(`${row.unresolved} unresolved`);
|
|
344
|
-
}
|
|
345
|
-
if (row.unexpectedSkipped > 0) {
|
|
346
|
-
parts.push(`${row.unexpectedSkipped} unexpected skipped`);
|
|
347
|
-
}
|
|
348
|
-
if (!parts.length) {
|
|
349
|
-
return null;
|
|
350
|
-
}
|
|
351
|
-
return {
|
|
352
|
-
entity: row.entity,
|
|
353
|
-
subtype: row.subtype,
|
|
354
|
-
message: `${row.entity}/${row.subtype}: ${parts.join(', ')}`,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function nextStepForBlockedRequest(request, snapshot, requestAggregateOutcome) {
|
|
359
|
-
// A `fulfilled` request only means the source-side snapshot was captured cleanly — it says
|
|
360
|
-
// nothing about whether any target field actually got written using it. Never let this
|
|
361
|
-
// branch claim "no action is required" while a dependent's own write accounting says the
|
|
362
|
-
// value was never applied (deferred/failed) or applied only via a degraded default
|
|
363
|
-
// (warning); check the real per-dependent outcome, not the snapshot status alone.
|
|
364
|
-
if (request.status === 'fulfilled' && (requestAggregateOutcome === 'deferred' || requestAggregateOutcome === 'failed')) {
|
|
365
|
-
return 'The source data was captured successfully, but no target field has been written using it yet — capturing the definitions is not the same as completing the migration. See dependentOutcomes for which field(s) remain unattempted.';
|
|
366
|
-
}
|
|
367
|
-
if (request.status === 'fulfilled' && requestAggregateOutcome === 'warning') {
|
|
368
|
-
return 'The source data was captured successfully, but at least one target field was written using its existing degraded default rather than this data — see dependentOutcomes for which field(s).';
|
|
369
|
-
}
|
|
370
|
-
if (request.status === 'fulfilled' && request.stale) {
|
|
371
|
-
return `The import used data pulled ${snapshot.extractedAt} (snapshot v${snapshot.version}); verify no relevant source activity occurred after that date, or restore the ${request.fulfillment.kind} path and re-run to refresh.`;
|
|
372
|
-
}
|
|
373
|
-
if (request.status === 'fulfilled') return 'No action is required; the import used the recorded source-data snapshot.';
|
|
374
|
-
if (request.status === 'invalid') {
|
|
375
|
-
if (request.fulfillmentErrorCode === 'handler-not-registered') {
|
|
376
|
-
return `Fulfillment handler ${request.fulfillment.handlerId} is not registered in this runner. This is a wiring bug in the import runner, not a data problem — register the handler and re-run.`;
|
|
377
|
-
}
|
|
378
|
-
return request.fulfillment.kind === 'csv-upload'
|
|
379
|
-
? `Correct the file at ${request.fulfillment.expectedInputPath} and re-run.`
|
|
380
|
-
: 'The source bridge responded with unusable data; correct or reinstall it and re-run.';
|
|
381
|
-
}
|
|
382
|
-
if (request.status === 'declined') return 'Supply the blocked source data and re-run if you want to replace the degraded default.';
|
|
383
|
-
if (request.lastError) return `The source namespace probe failed: ${request.lastError}. Fix source connectivity or authentication, then re-run.`;
|
|
384
|
-
if (request.askedInteractively !== true) {
|
|
385
|
-
return request.fulfillment.kind === 'csv-upload'
|
|
386
|
-
? `This run did not pause to ask. Supply ${request.fulfillment.expectedInputPath} and re-run.`
|
|
387
|
-
: 'This run did not pause to ask. Install the production-ready source bridge and re-run when that option becomes available.';
|
|
388
|
-
}
|
|
389
|
-
return request.fulfillment.kind === 'csv-upload'
|
|
390
|
-
? `Supply ${request.fulfillment.expectedInputPath} and re-run.`
|
|
391
|
-
: 'Install the production-ready source bridge and re-run when that option becomes available.';
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function normalizeBlockedDataRequest(request, index = 0) {
|
|
395
|
-
if (!request || typeof request !== 'object' || Array.isArray(request)) throw new Error(`blockedDataRequests[${index}] must be an object`);
|
|
396
|
-
const sourceEntityRef = requireString(request.sourceEntityRef, `blockedDataRequests[${index}].sourceEntityRef`);
|
|
397
|
-
const status = requireString(request.status, `blockedDataRequests[${index}].status`);
|
|
398
|
-
if (!['offered', 'missing', 'declined', 'invalid', 'fulfilled'].includes(status)) throw new Error(`${sourceEntityRef}.status is invalid`);
|
|
399
|
-
if (status === 'declined' && request.askedInteractively !== true) throw new Error(`${sourceEntityRef}: declined requires askedInteractively: true`);
|
|
400
|
-
const snapshot = request.snapshot || {};
|
|
401
|
-
if (status === 'fulfilled' && (!Number.isInteger(snapshot.version) || !snapshot.extractedAt || !snapshot.checksum)) {
|
|
402
|
-
throw new Error(`${sourceEntityRef}: fulfilled requires snapshot version, extractedAt, and checksum`);
|
|
403
|
-
}
|
|
404
|
-
const dependentOutcomes = asArray(request.dependentOutcomes, `${sourceEntityRef}.dependentOutcomes`).map((dependent, dependentIndex) => {
|
|
405
|
-
let recordOutcome = dependent.recordOutcome;
|
|
406
|
-
if (recordOutcome === null || recordOutcome === undefined) {
|
|
407
|
-
throw new Error(`${sourceEntityRef}.dependentOutcomes[${dependentIndex}].recordOutcome must be filled from write accounting`);
|
|
408
|
-
}
|
|
409
|
-
if (!OUTCOME_RANK.has(recordOutcome)) throw new Error(`${sourceEntityRef}.dependentOutcomes[${dependentIndex}].recordOutcome is invalid`);
|
|
410
|
-
if (status !== 'fulfilled' && recordOutcome === 'fulfilled') {
|
|
411
|
-
throw new Error(`${sourceEntityRef}.dependentOutcomes[${dependentIndex}] cannot be fulfilled without a fulfilled request`);
|
|
412
|
-
}
|
|
413
|
-
if (status === 'fulfilled' && request.stale && recordOutcome === 'fulfilled') recordOutcome = 'warning';
|
|
414
|
-
const row = {
|
|
415
|
-
targetEntity: requireString(dependent.targetEntity, `${sourceEntityRef}.dependentOutcomes[${dependentIndex}].targetEntity`),
|
|
416
|
-
degradedField: requireString(dependent.degradedField, `${sourceEntityRef}.dependentOutcomes[${dependentIndex}].degradedField`),
|
|
417
|
-
pitfallCode: dependent.pitfallCode || null,
|
|
418
|
-
recordOutcome,
|
|
419
|
-
};
|
|
420
|
-
if (status === 'fulfilled') {
|
|
421
|
-
row.snapshotVersion = snapshot.version;
|
|
422
|
-
row.extractedAt = snapshot.extractedAt;
|
|
423
|
-
row.checksum = snapshot.checksum;
|
|
424
|
-
}
|
|
425
|
-
return row;
|
|
426
|
-
});
|
|
427
|
-
const requestAggregateOutcome = aggregateOutcome(dependentOutcomes);
|
|
428
|
-
return {
|
|
429
|
-
sourceEntityRef,
|
|
430
|
-
status,
|
|
431
|
-
stale: Boolean(request.stale),
|
|
432
|
-
snapshotVersion: status === 'fulfilled' ? snapshot.version : null,
|
|
433
|
-
snapshotExtractedAt: status === 'fulfilled' ? snapshot.extractedAt : null,
|
|
434
|
-
snapshotChecksum: status === 'fulfilled' ? snapshot.checksum : null,
|
|
435
|
-
dependentOutcomes,
|
|
436
|
-
aggregateOutcome: requestAggregateOutcome,
|
|
437
|
-
consequenceIfMissing: request.consequenceIfMissing || null,
|
|
438
|
-
whatUserCanDoNext: nextStepForBlockedRequest({ ...request, fulfillment: request.fulfillment || {} }, snapshot, requestAggregateOutcome),
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// A per-stage failure against a record the entity row already counts as `imported`.
|
|
443
|
-
//
|
|
444
|
-
// An order whose import succeeded and whose payment failed is a live order sitting unpaid on the
|
|
445
|
-
// merchant's store, and the completeness arithmetic cannot see it: that arithmetic partitions the
|
|
446
|
-
// POPULATION, and the order did import. So the report printed the failed payment and counted the
|
|
447
|
-
// run complete. This is the orthogonal axis, and it is never folded into `failed` -- doing that
|
|
448
|
-
// would break the `imported + alreadyPresentByCrosswalk + deferred + failed == inScope` identity
|
|
449
|
-
// the partition exists to protect.
|
|
450
|
-
//
|
|
451
|
-
// The outcome vocabulary comes from the registry both sides already import, so a stage cannot
|
|
452
|
-
// report a code the report has no line for.
|
|
453
|
-
const STAGE_RESIDUAL_OUTCOMES = new Set([...WRITER_OUTCOMES, ...PAYMENT_OUTCOMES, ...SEO_OUTCOMES]);
|
|
454
|
-
// Owner-dispositioned SEO rows stay visible as counts but are decisions, not open
|
|
455
|
-
// work, so they never block completion.
|
|
456
|
-
const ACCEPTED_STAGE_OUTCOMES = new Set(SEO_ACCEPTED_OUTCOMES);
|
|
457
|
-
|
|
458
|
-
function normalizeStageResidual(row, index = 0) {
|
|
459
|
-
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
460
|
-
throw new Error(`stageResiduals[${index}] must be an object`);
|
|
461
|
-
}
|
|
462
|
-
const entity = requireString(row.entity, `stageResiduals[${index}].entity`);
|
|
463
|
-
const stage = requireString(row.stage, `stageResiduals[${index}].stage`);
|
|
464
|
-
const outcome = requireString(row.outcome, `stageResiduals[${index}].outcome`);
|
|
465
|
-
if (!STAGE_RESIDUAL_OUTCOMES.has(outcome)) {
|
|
466
|
-
throw new Error(`stageResiduals[${index}].outcome is invalid: ${outcome}`);
|
|
467
|
-
}
|
|
468
|
-
const rowCount = count(row.count, `stageResiduals[${index}].count`);
|
|
469
|
-
const sourceKeys = asArray(row.sourceKeys, `stageResiduals[${index}].sourceKeys`)
|
|
470
|
-
.map((key, keyIndex) => requireString(key, `stageResiduals[${index}].sourceKeys[${keyIndex}]`));
|
|
471
|
-
return { entity, stage, outcome, count: rowCount, sourceKeys };
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
function stageResidualMessage(row) {
|
|
475
|
-
const named = row.sourceKeys.length ? ` (${row.sourceKeys.join(', ')})` : '';
|
|
476
|
-
if (SEO_OUTCOME_ACTIONS[row.outcome]) return `${row.entity}/${row.stage}: ${row.count} ${row.entity} ${row.outcome}${named}`;
|
|
477
|
-
return `${row.entity}/${row.stage}: ${row.count} imported ${row.entity} still ${row.outcome}${named}`;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function stageResidualAction(row) {
|
|
481
|
-
const named = row.sourceKeys.length ? ` (${row.sourceKeys.join(', ')})` : '';
|
|
482
|
-
if (SEO_OUTCOME_ACTIONS[row.outcome]) return `${SEO_OUTCOME_ACTIONS[row.outcome]} Affected: ${row.count}${named}.`;
|
|
483
|
-
return `Re-run the ${row.stage} stage for ${row.entity}${row.sourceKeys.length ? ` ${row.sourceKeys.join(', ')}` : ''}: ${row.count} imported ${row.entity} ${row.count === 1 ? 'is' : 'are'} ${row.outcome}.`;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Consume the runner's final per-order outcomes, never recount API attempts or read a log.
|
|
487
|
-
// Keep only the last result for a source key so a recovery pass clears a resolved failure.
|
|
488
|
-
function collectOrderStageResiduals(results, { entity = 'order' } = {}) {
|
|
489
|
-
if (!Array.isArray(results)) throw new Error('order history results must be an array');
|
|
490
|
-
requireString(entity, 'order history entity');
|
|
491
|
-
const latest = new Map();
|
|
492
|
-
for (const row of results) {
|
|
493
|
-
const sourceKey = requireString(row && row.sourceKey, 'order history sourceKey');
|
|
494
|
-
if (!row.outcomes || !['order-imported', 'order-already-imported', 'order-import-failed', 'order-target-read-failed'].includes(row.outcomes.order)) {
|
|
495
|
-
throw new Error(`order history ${sourceKey}: missing or invalid order outcome`);
|
|
496
|
-
}
|
|
497
|
-
latest.set(sourceKey, row.outcomes);
|
|
498
|
-
}
|
|
499
|
-
const groups = new Map();
|
|
500
|
-
for (const [sourceKey, outcomes] of latest) {
|
|
501
|
-
if (!['order-imported', 'order-already-imported'].includes(outcomes.order)) continue;
|
|
502
|
-
const stages = [
|
|
503
|
-
['payment', outcomes.payment],
|
|
504
|
-
['invoice', outcomes.invoice],
|
|
505
|
-
...(outcomes.refunds || []).map((refund) => ['refund', refund.outcome]),
|
|
506
|
-
];
|
|
507
|
-
for (const [stage, outcome] of stages) {
|
|
508
|
-
if (!STAGE_RESIDUAL_OUTCOMES.has(outcome)) {
|
|
509
|
-
throw new Error(`order history ${sourceKey}: missing or invalid ${stage} outcome`);
|
|
510
|
-
}
|
|
511
|
-
if (!outcome.endsWith('-failed') && outcome !== 'payment-reconciliation-ambiguous') continue;
|
|
512
|
-
const key = `${stage}:${outcome}`;
|
|
513
|
-
if (!groups.has(key)) groups.set(key, { entity, stage, outcome, sourceKeys: new Set() });
|
|
514
|
-
groups.get(key).sourceKeys.add(sourceKey);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
return [...groups.values()].map((row) => ({
|
|
518
|
-
...row, count: row.sourceKeys.size, sourceKeys: [...row.sourceKeys],
|
|
519
|
-
}));
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
const COMPLETION_REPORT_FILE = 'execution/completion-report.json';
|
|
523
|
-
const COMPLETION_SUMMARY_FILE = 'execution/review/completion-summary.md';
|
|
524
|
-
|
|
525
|
-
// There is no fourth shape and no bare free-text cause.
|
|
526
|
-
const CAUSE_CONFIDENCE = ['verified-source', 'verified-target', 'unverified'];
|
|
527
|
-
|
|
528
|
-
// `unverified` must not carry a cause string: a hypothesis rendered next to the
|
|
529
|
-
// word "unverified" is read as a finding by every reader who is skimming. In practice that
|
|
530
|
-
// turned "three products failed" into "the merchant's source data has duplicate slugs", which
|
|
531
|
-
// nobody had checked and which was false. Reject it at build time rather than dropping it
|
|
532
|
-
// quietly -- a row that meant to name a cause needs the check that establishes it, not silence.
|
|
533
|
-
function normalizeCauseAttribution(row, label) {
|
|
534
|
-
const confidence = row.causeConfidence === undefined || row.causeConfidence === null
|
|
535
|
-
? 'unverified'
|
|
536
|
-
: requireString(row.causeConfidence, `${label}.causeConfidence`);
|
|
537
|
-
if (!CAUSE_CONFIDENCE.includes(confidence)) {
|
|
538
|
-
throw new Error(`${label}.causeConfidence must be one of ${CAUSE_CONFIDENCE.join(', ')}`);
|
|
539
|
-
}
|
|
540
|
-
if (confidence === 'unverified') {
|
|
541
|
-
if (row.cause !== undefined && row.cause !== null && row.cause !== '') {
|
|
542
|
-
throw new Error(`${label}: causeConfidence 'unverified' must not carry a cause; name the destination-side check or leave the cause out`);
|
|
543
|
-
}
|
|
544
|
-
if (row.check !== undefined && row.check !== null) {
|
|
545
|
-
throw new Error(`${label}: causeConfidence 'unverified' must not carry a check`);
|
|
546
|
-
}
|
|
547
|
-
return { causeConfidence: 'unverified', cause: null, check: null };
|
|
548
|
-
}
|
|
549
|
-
const cause = requireString(row.cause, `${label}.cause`);
|
|
550
|
-
const check = row.check;
|
|
551
|
-
if (!check || typeof check !== 'object' || Array.isArray(check)) {
|
|
552
|
-
throw new Error(`${label}: causeConfidence '${confidence}' requires a recorded check { kind, target, result }`);
|
|
553
|
-
}
|
|
554
|
-
return {
|
|
555
|
-
causeConfidence: confidence,
|
|
556
|
-
cause,
|
|
557
|
-
check: {
|
|
558
|
-
kind: requireString(check.kind, `${label}.check.kind`),
|
|
559
|
-
target: requireString(check.target, `${label}.check.target`),
|
|
560
|
-
result: requireString(check.result, `${label}.check.result`),
|
|
561
|
-
},
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
const OPERATOR_ACTION_SEVERITIES = ['critical', 'high', 'medium', 'low'];
|
|
566
|
-
|
|
567
|
-
// Anything the operator has to do by hand goes above the counts, with the
|
|
568
|
-
// deadline reason attached. The worked example is a large batch of discount codes
|
|
569
|
-
// with no redemption cap -- correctable only while the source is still the system of record.
|
|
570
|
-
function normalizeOperatorAction(row, index) {
|
|
571
|
-
if (!row || typeof row !== 'object' || Array.isArray(row)) {
|
|
572
|
-
throw new Error(`operatorActions[${index}] must be an object`);
|
|
573
|
-
}
|
|
574
|
-
const label = `operatorActions[${index}]`;
|
|
575
|
-
const severity = requireString(row.severity, `${label}.severity`);
|
|
576
|
-
if (!OPERATOR_ACTION_SEVERITIES.includes(severity)) {
|
|
577
|
-
throw new Error(`${label}.severity must be one of ${OPERATOR_ACTION_SEVERITIES.join(', ')}`);
|
|
578
|
-
}
|
|
579
|
-
return {
|
|
580
|
-
code: requireString(row.code, `${label}.code`),
|
|
581
|
-
severity,
|
|
582
|
-
action: requireString(row.action, `${label}.action`),
|
|
583
|
-
deadlineReason: requireString(row.deadlineReason, `${label}.deadlineReason`),
|
|
584
|
-
entity: row.entity || null,
|
|
585
|
-
affectedCount: row.affectedCount === null ? null : count(row.affectedCount, `${label}.affectedCount`),
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// `createCompletionReport` never recomputes the check itself -- it only binds the
|
|
590
|
-
// already-computed block (from `live-import-summary.json`, via `writeLiveImportSummary`) to the
|
|
591
|
-
// run it is completing, and decides what that binding means for the final status and operator
|
|
592
|
-
// actions.
|
|
593
|
-
//
|
|
594
|
-
// `live-import-summary.json` is one file, fully overwritten on every flush, and interleaved
|
|
595
|
-
// run ids are allowed in the same project directory -- so the most recent `coverageCheck`
|
|
596
|
-
// read off disk is not necessarily about the run being completed. A supplied check is honored only
|
|
597
|
-
// when its own `runId` strictly equals `input.runId`; anything else (a different run's check, or
|
|
598
|
-
// one with no `runId` at all) is treated exactly like an omitted check.
|
|
599
|
-
function normalizeCoverageCheckInput(input) {
|
|
600
|
-
const raw = input.coverageCheck;
|
|
601
|
-
if (raw === undefined || raw === null) {
|
|
602
|
-
return { coverageCheck: { supplied: false }, raw: null };
|
|
603
|
-
}
|
|
604
|
-
if (typeof raw !== 'object' || Array.isArray(raw)) {
|
|
605
|
-
throw new Error('coverageCheck must be an object');
|
|
606
|
-
}
|
|
607
|
-
requireString(input.runId, 'runId must be a non-empty string when coverageCheck is supplied');
|
|
608
|
-
const matches = typeof raw.runId === 'string' && raw.runId === input.runId;
|
|
609
|
-
return { coverageCheck: { ...raw, supplied: matches }, raw };
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
function coverageFirstDisagreeingEntity(coverageCheck) {
|
|
613
|
-
const items = coverageDisagreements(coverageCheck);
|
|
614
|
-
if (items.length) {
|
|
615
|
-
return items[0].key;
|
|
616
|
-
}
|
|
617
|
-
return (coverageCheck.unmatched && coverageCheck.unmatched[0]) || null;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
function coverageAffectedCount(coverageCheck) {
|
|
621
|
-
return coverageDisagreements(coverageCheck).length
|
|
622
|
-
+ ((coverageCheck.unmatched && coverageCheck.unmatched.length) || 0)
|
|
623
|
-
+ ((coverageCheck.unmatchedJournalEntities && coverageCheck.unmatchedJournalEntities.length) || 0);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// The reason text for a `coverage_unchecked` operator action: whatever reason the writer itself
|
|
627
|
-
// recorded (or `has_unresolved`) when a run-matched check came back `unchecked`, or a
|
|
628
|
-
// reason naming why no run-matched check was honored at all, when one wasn't.
|
|
629
|
-
function coverageUncheckedReason(coverageCheck, raw, runId) {
|
|
630
|
-
if (coverageCheck.supplied) {
|
|
631
|
-
return coverageCheck.reason || 'unchecked';
|
|
632
|
-
}
|
|
633
|
-
if (!raw) {
|
|
634
|
-
return 'omitted';
|
|
635
|
-
}
|
|
636
|
-
if (typeof raw.runId !== 'string' || !raw.runId) {
|
|
637
|
-
return 'missing_run_id';
|
|
638
|
-
}
|
|
639
|
-
return `run_id_mismatch (coverageCheck.runId: ${raw.runId}, completing run: ${runId})`;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
// The operator-action shape, with concrete values so `normalizeOperatorAction`'s required
|
|
643
|
-
// `code`/`action` never throw: both are built here, never left for a caller to invent.
|
|
644
|
-
function coverageOperatorActionRow(kind, { coverageCheck, raw, runId }) {
|
|
645
|
-
if (kind === 'mismatch') {
|
|
646
|
-
return {
|
|
647
|
-
code: 'coverage_mismatch',
|
|
648
|
-
severity: 'high',
|
|
649
|
-
action: `Investigate the live-import count cross-check for run ${runId}: ${describeCoverageMismatch(coverageCheck)}. See the Import Operator prompt in the migration handoff.`,
|
|
650
|
-
deadlineReason: 'before DNS change',
|
|
651
|
-
entity: coverageFirstDisagreeingEntity(coverageCheck),
|
|
652
|
-
affectedCount: coverageAffectedCount(coverageCheck),
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
const reason = coverageUncheckedReason(coverageCheck, raw, runId);
|
|
656
|
-
return {
|
|
657
|
-
code: 'coverage_unchecked',
|
|
658
|
-
severity: 'medium',
|
|
659
|
-
action: `Confirm the live-import counts for run ${runId} before DNS: the completion report has no run-matched coverageCheck (reason: ${reason}). See the Import Operator prompt in the migration handoff.`,
|
|
660
|
-
deadlineReason: 'before DNS change',
|
|
661
|
-
entity: null,
|
|
662
|
-
affectedCount: 0,
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// The omitted/rejected-run default escalation is bounded
|
|
667
|
-
// to runs that declare participation (`coverageCheckExpected: true` -- this run called
|
|
668
|
-
// `writeLiveImportSummary` with a `runId` at least once). A run that never adopted this contract
|
|
669
|
-
// never sets that flag, so a missing check there changes nothing, exactly as before.
|
|
670
|
-
function resolveCoverageCheckEffect(input) {
|
|
671
|
-
const { coverageCheck, raw } = normalizeCoverageCheckInput(input);
|
|
672
|
-
const coverageCheckExpected = input.coverageCheckExpected === true;
|
|
673
|
-
let hasMismatch = false;
|
|
674
|
-
let hasWarnings = false;
|
|
675
|
-
let operatorActionRow = null;
|
|
676
|
-
|
|
677
|
-
if (coverageCheck.supplied) {
|
|
678
|
-
// Supplying a run-matched block at all is itself evidence of participation: its normal effect
|
|
679
|
-
// applies regardless of `coverageCheckExpected`.
|
|
680
|
-
if (coverageCheck.status === 'mismatch') {
|
|
681
|
-
hasMismatch = true;
|
|
682
|
-
operatorActionRow = coverageOperatorActionRow('mismatch', { coverageCheck, raw, runId: input.runId });
|
|
683
|
-
} else if (coverageCheck.status === 'unchecked') {
|
|
684
|
-
hasWarnings = true;
|
|
685
|
-
operatorActionRow = coverageOperatorActionRow('unchecked', { coverageCheck, raw, runId: input.runId });
|
|
686
|
-
}
|
|
687
|
-
} else if (coverageCheckExpected) {
|
|
688
|
-
hasWarnings = true;
|
|
689
|
-
operatorActionRow = coverageOperatorActionRow('unchecked', { coverageCheck, raw, runId: input.runId });
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
return { coverageCheck, coverageCheckExpected, hasMismatch, hasWarnings, operatorActionRow };
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// Four different things needing four different responses:
|
|
696
|
-
// a loss is not an exclusion, an orphan is not a loss, and a dropped field is the one that
|
|
697
|
-
// vanishes most easily because the record count reconciles perfectly. Rendered from these
|
|
698
|
-
// durable rows, never recounted (the rows themselves are the journal contract).
|
|
699
|
-
function normalizeCreditSubtype(row, label) {
|
|
700
|
-
return row.subtype === undefined || row.subtype === null ? null : requireString(row.subtype, `${label}.subtype`);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
function normalizeLoss(row, index) {
|
|
704
|
-
if (!row || typeof row !== 'object' || Array.isArray(row)) throw new Error(`losses[${index}] must be an object`);
|
|
705
|
-
const label = `losses[${index}]`;
|
|
706
|
-
return {
|
|
707
|
-
entity: requireString(row.entity, `${label}.entity`),
|
|
708
|
-
// With a subtype, this credit covers only that entity/subtype's failed
|
|
709
|
-
// shortfall; without one, it joins the entity's shared pool across its subtypes.
|
|
710
|
-
subtype: normalizeCreditSubtype(row, label),
|
|
711
|
-
reasonCode: requireString(row.reasonCode, `${label}.reasonCode`),
|
|
712
|
-
count: count(row.count, `${label}.count`),
|
|
713
|
-
whyNoRetry: requireString(row.whyNoRetry, `${label}.whyNoRetry`),
|
|
714
|
-
sourceIds: Array.isArray(row.sourceIds) ? row.sourceIds : [],
|
|
715
|
-
...normalizeCauseAttribution(row, label),
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
function normalizeExclusion(row, index) {
|
|
720
|
-
if (!row || typeof row !== 'object' || Array.isArray(row)) throw new Error(`exclusions[${index}] must be an object`);
|
|
721
|
-
const label = `exclusions[${index}]`;
|
|
722
|
-
return {
|
|
723
|
-
entity: requireString(row.entity, `${label}.entity`),
|
|
724
|
-
...(row.censusId !== undefined ? { censusId: requireString(row.censusId, `${label}.censusId`) } : {}),
|
|
725
|
-
// With a subtype, this credit covers only that entity/subtype's deferred
|
|
726
|
-
// shortfall; without one, it joins the entity's shared pool across its subtypes.
|
|
727
|
-
subtype: normalizeCreditSubtype(row, label),
|
|
728
|
-
reasonCode: requireString(row.reasonCode, `${label}.reasonCode`),
|
|
729
|
-
count: count(row.count, `${label}.count`),
|
|
730
|
-
rationale: requireString(row.rationale, `${label}.rationale`),
|
|
731
|
-
// A choice is not a loss, and an undecided choice is not a decision: say whether a human
|
|
732
|
-
// actually gated it rather than letting an unreviewed filter read as an approved one.
|
|
733
|
-
operatorGateRecorded: Boolean(row.operatorGateRecorded),
|
|
734
|
-
};
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
function normalizeUnresolved(row, index) {
|
|
738
|
-
if (!row || typeof row !== 'object' || Array.isArray(row)) throw new Error(`unresolved[${index}] must be an object`);
|
|
739
|
-
const label = `unresolved[${index}]`;
|
|
740
|
-
const kind = requireString(row.kind, `${label}.kind`);
|
|
741
|
-
if (!['orphaned_target', 'unmapped_source', 'mapping_without_record'].includes(kind)) {
|
|
742
|
-
throw new Error(`${label}.kind must be orphaned_target, unmapped_source or mapping_without_record`);
|
|
743
|
-
}
|
|
744
|
-
return {
|
|
745
|
-
entity: requireString(row.entity, `${label}.entity`),
|
|
746
|
-
kind,
|
|
747
|
-
count: count(row.count, `${label}.count`),
|
|
748
|
-
targetIds: Array.isArray(row.targetIds) ? row.targetIds : [],
|
|
749
|
-
sourceIds: Array.isArray(row.sourceIds) ? row.sourceIds : [],
|
|
750
|
-
...normalizeCauseAttribution(row, label),
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
function normalizeDroppedField(row, index) {
|
|
755
|
-
if (!row || typeof row !== 'object' || Array.isArray(row)) throw new Error(`droppedFields[${index}] must be an object`);
|
|
756
|
-
const label = `droppedFields[${index}]`;
|
|
757
|
-
return {
|
|
758
|
-
entity: requireString(row.entity, `${label}.entity`),
|
|
759
|
-
...(row.subtype !== undefined ? { subtype: requireString(row.subtype, `${label}.subtype`) } : {}),
|
|
760
|
-
field: requireString(row.field, `${label}.field`),
|
|
761
|
-
recordCount: count(row.recordCount, `${label}.recordCount`),
|
|
762
|
-
reasonCode: requireString(row.reasonCode, `${label}.reasonCode`),
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
// The member pass's per-reason breakdown. Every registry code carries a line,
|
|
767
|
-
// zeros included: an absent line is indistinguishable from a code nobody counted.
|
|
768
|
-
function normalizeMemberOutcomes(value) {
|
|
769
|
-
if (value === undefined || value === null) return null;
|
|
770
|
-
if (typeof value !== 'object' || Array.isArray(value)) throw new Error('memberOutcomes must be an object');
|
|
771
|
-
requireString(value.populationBasis, 'memberOutcomes.populationBasis');
|
|
772
|
-
const lines = (field, codes) => {
|
|
773
|
-
const source = value[field];
|
|
774
|
-
if (!source || typeof source !== 'object') throw new Error(`memberOutcomes.${field} must be an object`);
|
|
775
|
-
for (const key of Object.keys(source)) {
|
|
776
|
-
if (!codes.includes(key)) throw new Error(`memberOutcomes.${field} has unknown code ${key}`);
|
|
777
|
-
}
|
|
778
|
-
return Object.fromEntries(codes.map((code) => {
|
|
779
|
-
if (!(code in source)) throw new Error(`memberOutcomes.${field}.${code} is missing; every registry code carries a line`);
|
|
780
|
-
return [code, count(source[code], `memberOutcomes.${field}.${code}`)];
|
|
781
|
-
}));
|
|
782
|
-
};
|
|
783
|
-
const defect = value.populationDefect || {};
|
|
784
|
-
return {
|
|
785
|
-
...value,
|
|
786
|
-
entity: value.entity || memberRegistry.MEMBER_ENTITY,
|
|
787
|
-
byOutcome: lines('byOutcome', memberRegistry.MEMBER_OUTCOMES),
|
|
788
|
-
byFinding: lines('byFinding', memberRegistry.MEMBER_FINDINGS),
|
|
789
|
-
populationDefect: {
|
|
790
|
-
missingSourceKey: count(defect.missingSourceKey, 'memberOutcomes.populationDefect.missingSourceKey'),
|
|
791
|
-
duplicateSourceKey: count(defect.duplicateSourceKey, 'memberOutcomes.populationDefect.duplicateSourceKey'),
|
|
792
|
-
unknownSourceKey: count(defect.unknownSourceKey, 'memberOutcomes.populationDefect.unknownSourceKey'),
|
|
793
|
-
},
|
|
794
|
-
projectionErrors: asArray(value.projectionErrors, 'memberOutcomes.projectionErrors'),
|
|
795
|
-
};
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
function memberHeadline(memberOutcomes) {
|
|
799
|
-
if (!memberOutcomes) return [];
|
|
800
|
-
const out = [];
|
|
801
|
-
const d = memberOutcomes.populationDefect;
|
|
802
|
-
if (d.missingSourceKey || d.duplicateSourceKey || d.unknownSourceKey) {
|
|
803
|
-
out.push({ entity: memberOutcomes.entity, message: `${memberOutcomes.entity}: population gate refused the member pass (${d.missingSourceKey} missing, ${d.duplicateSourceKey} duplicate, ${d.unknownSourceKey} unknown source keys); no member was written` });
|
|
804
|
-
}
|
|
805
|
-
if (memberOutcomes.projectionErrors.length) {
|
|
806
|
-
out.push({ entity: memberOutcomes.entity, message: `${memberOutcomes.entity}: ${memberOutcomes.projectionErrors.length} journal pairing error(s); member counts are not trustworthy until the journal is repaired` });
|
|
807
|
-
}
|
|
808
|
-
return out;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
// `own(r, o)` -- the sum of a row's own structured entries for outcome `o`.
|
|
812
|
-
function ownTally(row, outcome) {
|
|
813
|
-
return row.reasons
|
|
814
|
-
.filter((entry) => typeof entry === 'object' && entry.outcome === outcome)
|
|
815
|
-
.reduce((sum, entry) => sum + entry.count, 0);
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
// Exclusions credit only deferred; losses credit only failed. A record with a
|
|
819
|
-
// `subtype` covers only that entity/subtype; without one it joins the entity's shared pool.
|
|
820
|
-
function buildCreditPools(records) {
|
|
821
|
-
const scoped = new Map();
|
|
822
|
-
const pool = new Map();
|
|
823
|
-
for (const record of records) {
|
|
824
|
-
if (record.subtype) {
|
|
825
|
-
const key = `${record.entity}\0${record.subtype}`;
|
|
826
|
-
scoped.set(key, (scoped.get(key) || 0) + record.count);
|
|
827
|
-
} else {
|
|
828
|
-
pool.set(record.entity, (pool.get(record.entity) || 0) + record.count);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
return { scoped, pool };
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
function spendCredit(map, key, amount) {
|
|
835
|
-
if (amount <= 0) return 0;
|
|
836
|
-
const available = map.get(key) || 0;
|
|
837
|
-
const spent = Math.min(available, amount);
|
|
838
|
-
if (spent > 0) map.set(key, available - spent);
|
|
839
|
-
return spent;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
// For each row, apply its subtype-scoped credit first, then draw from the
|
|
843
|
-
// entity's shared pool -- but ONLY when the journal already attributes at least one reason of
|
|
844
|
-
// this outcome to this specific row (`own(r, o) > 0`), or this row is its entity's only row. A
|
|
845
|
-
// row whose own tally is exactly zero, in a multi-row entity, can be cleared only by a credit
|
|
846
|
-
// explicitly scoped to its own subtype: count alone cannot show the pool's records are really
|
|
847
|
-
// about this row, which is the "genuinely reason-less row" that must fail loudly.
|
|
848
|
-
// Returns a parallel array of { unexplainedDeferred, unexplainedFailed }, same order/length as
|
|
849
|
-
// `entityCompleteness`. Mutates nothing it is not given.
|
|
850
|
-
function allocateUnexplainedCounts(entityCompleteness, exclusions, losses, memberOutcomes) {
|
|
851
|
-
const deferredCredit = buildCreditPools(exclusions);
|
|
852
|
-
const failedCredit = buildCreditPools(losses);
|
|
853
|
-
const countByEntity = new Map();
|
|
854
|
-
for (const row of entityCompleteness) {
|
|
855
|
-
countByEntity.set(row.entity, (countByEntity.get(row.entity) || 0) + 1);
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
return entityCompleteness.map((row) => {
|
|
859
|
-
// The member entity's unexplained is 0 for both outcomes, as today -- the
|
|
860
|
-
// member breakdown already names every failed member's reason and recovery path.
|
|
861
|
-
if (memberOutcomes && row.entity === memberOutcomes.entity) {
|
|
862
|
-
return { unexplainedDeferred: 0, unexplainedFailed: 0 };
|
|
863
|
-
}
|
|
864
|
-
const ownDeferred = ownTally(row, 'deferred');
|
|
865
|
-
const ownFailed = ownTally(row, 'failed');
|
|
866
|
-
let residualDeferred = Math.max(0, row.deferred - ownDeferred);
|
|
867
|
-
let residualFailed = Math.max(0, row.failed - ownFailed);
|
|
868
|
-
const scopeKey = `${row.entity}\0${row.subtype}`;
|
|
869
|
-
|
|
870
|
-
residualDeferred -= spendCredit(deferredCredit.scoped, scopeKey, residualDeferred);
|
|
871
|
-
residualFailed -= spendCredit(failedCredit.scoped, scopeKey, residualFailed);
|
|
872
|
-
|
|
873
|
-
const isOnlyRow = countByEntity.get(row.entity) === 1;
|
|
874
|
-
if ((ownDeferred > 0 || isOnlyRow) && residualDeferred > 0) {
|
|
875
|
-
residualDeferred -= spendCredit(deferredCredit.pool, row.entity, residualDeferred);
|
|
876
|
-
}
|
|
877
|
-
if ((ownFailed > 0 || isOnlyRow) && residualFailed > 0) {
|
|
878
|
-
residualFailed -= spendCredit(failedCredit.pool, row.entity, residualFailed);
|
|
879
|
-
}
|
|
880
|
-
return { unexplainedDeferred: residualDeferred, unexplainedFailed: residualFailed };
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
// The join between a completeness row and the journal rows that back its deferred
|
|
885
|
-
// and failed counts: a completeness row cannot be identified by `targetEntityType` alone (two
|
|
886
|
-
// subtypes can share one target type), so the join key is the row's own `sourceKeys` list, which
|
|
887
|
-
// the codegen contract already asks every row to carry. Pure; returns a new rows array.
|
|
888
|
-
//
|
|
889
|
-
// `runIds` is required: a non-empty array of distinct, non-empty run id strings. An ordinary
|
|
890
|
-
// report passes `[runId]`; a cutover repair regeneration lists the original run's id plus every
|
|
891
|
-
// repair pass's own id, because a repair pass through the importer is a fresh process start and
|
|
892
|
-
// its rows land under a new `runId` that the append-only journal never retrofits onto the
|
|
893
|
-
// original run.
|
|
894
|
-
function attachAttemptReasons(entityCompleteness, journalRows, { runIds } = {}) {
|
|
895
|
-
if (!Array.isArray(runIds) || runIds.length === 0 || runIds.some((id) => typeof id !== 'string' || !id)) {
|
|
896
|
-
throw new Error('attachAttemptReasons requires runIds: a non-empty array of non-empty run id strings');
|
|
897
|
-
}
|
|
898
|
-
const runIdSet = new Set(runIds);
|
|
899
|
-
|
|
900
|
-
// One Map(sourceKey -> rowIndex). A key listed by two rows makes attribution ambiguous.
|
|
901
|
-
const keyToIndex = new Map();
|
|
902
|
-
entityCompleteness.forEach((row, index) => {
|
|
903
|
-
for (const key of row.sourceKeys || []) {
|
|
904
|
-
if (keyToIndex.has(key)) {
|
|
905
|
-
throw new Error(`sourceKey ${key} is listed by ${keyToIndex.get(key)} and ${index}`);
|
|
906
|
-
}
|
|
907
|
-
keyToIndex.set(key, index);
|
|
908
|
-
}
|
|
909
|
-
});
|
|
910
|
-
|
|
911
|
-
// One pass over journalRows: skip rows whose runId is not listed or whose sourceStableKey is
|
|
912
|
-
// not in the map; keep the latest remaining row per key, in file order (same rule as
|
|
913
|
-
// `foldAttemptsBySourceKey`, applied after the run filter) -- forward iteration with a Map
|
|
914
|
-
// overwrite means the last (latest) row for a key always wins.
|
|
915
|
-
const latestByKey = new Map();
|
|
916
|
-
for (const row of journalRows || []) {
|
|
917
|
-
if (!row || !runIdSet.has(row.runId)) continue;
|
|
918
|
-
const key = row.sourceStableKey;
|
|
919
|
-
if (!keyToIndex.has(key)) continue;
|
|
920
|
-
latestByKey.set(key, row);
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
const tallies = entityCompleteness.map(() => new Map());
|
|
924
|
-
for (const [key, row] of latestByKey) {
|
|
925
|
-
let outcome = null;
|
|
926
|
-
let code = null;
|
|
927
|
-
if (row.status === 'deferred') {
|
|
928
|
-
outcome = 'deferred';
|
|
929
|
-
// The documented contract
|
|
930
|
-
// (rp-import-codegen/SKILL.md's "Named reason" rule) is a top-level
|
|
931
|
-
// `row.reason` string, but the real generated importer for that run wrote its
|
|
932
|
-
// deferred rows' reason into `failure.errorCode` (the same field used for failed rows),
|
|
933
|
-
// leaving `row.reason` absent on every one of its deferred rows. Preferring
|
|
934
|
-
// `row.reason` keeps the documented contract authoritative when a future importer
|
|
935
|
-
// actually writes it, while this fallback stops a real, already-recorded reason from
|
|
936
|
-
// being silently dropped as "unexplained" just because it landed on the sibling field.
|
|
937
|
-
code = typeof row.reason === 'string' && row.reason ? row.reason : row.failure && row.failure.errorCode;
|
|
938
|
-
} else if (row.status === 'failed_retryable' || row.status === 'failed_terminal') {
|
|
939
|
-
outcome = 'failed';
|
|
940
|
-
code = row.failure && row.failure.errorCode;
|
|
941
|
-
} else {
|
|
942
|
-
continue;
|
|
943
|
-
}
|
|
944
|
-
if (typeof code !== 'string' || code === '') continue;
|
|
945
|
-
const map = tallies[keyToIndex.get(key)];
|
|
946
|
-
const tallyKey = `${outcome}\0${code}`;
|
|
947
|
-
map.set(tallyKey, (map.get(tallyKey) || 0) + 1);
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
return entityCompleteness.map((row, index) => {
|
|
951
|
-
if (!row.sourceKeys || !row.sourceKeys.length) {
|
|
952
|
-
return row;
|
|
953
|
-
}
|
|
954
|
-
const structured = [...tallies[index].entries()]
|
|
955
|
-
.map(([tallyKey, tallyCount]) => {
|
|
956
|
-
const [outcome, code] = tallyKey.split('\0');
|
|
957
|
-
return { outcome, code, count: tallyCount };
|
|
958
|
-
})
|
|
959
|
-
.sort((a, b) => (a.outcome === b.outcome ? (a.code < b.code ? -1 : a.code > b.code ? 1 : 0) : (a.outcome < b.outcome ? -1 : 1)));
|
|
960
|
-
const strings = (row.reasons || []).filter((entry) => typeof entry === 'string');
|
|
961
|
-
return { ...row, reasons: [...structured, ...strings] };
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
// CMS records this run wrote per approved fallback capability: { capability, collections[], count }.
|
|
966
|
-
function normalizeCmsFallbackWrites(value) {
|
|
967
|
-
if (value === undefined || value === null) return [];
|
|
968
|
-
if (!Array.isArray(value)) throw new Error('cmsFallback.writes must be an array');
|
|
969
|
-
return value.map((row, index) => {
|
|
970
|
-
if (!row || typeof row !== 'object' || Array.isArray(row)) throw new Error(`cmsFallback.writes[${index}] must be an object`);
|
|
971
|
-
return {
|
|
972
|
-
capability: requireString(row.capability, `cmsFallback.writes[${index}].capability`),
|
|
973
|
-
collections: Array.isArray(row.collections) ? row.collections.map(String) : [],
|
|
974
|
-
count: count(row.count, `cmsFallback.writes[${index}].count`),
|
|
975
|
-
};
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
function createCompletionReport(input = {}) {
|
|
980
|
-
const entityCompleteness = asArray(input.entityCompleteness, 'entityCompleteness')
|
|
981
|
-
.map((row, index) => normalizeCompletenessRow(row, index));
|
|
982
|
-
const verification = input.verification || {};
|
|
983
|
-
const verificationGaps = [];
|
|
984
|
-
for (const row of entityCompleteness) {
|
|
985
|
-
if (row.verificationKind === 'none') continue;
|
|
986
|
-
const report = verification[row.entity];
|
|
987
|
-
const entries = Array.isArray(report?.rows) ? report.rows : [];
|
|
988
|
-
const unique = new Set(entries.map(r => r.sourceKey));
|
|
989
|
-
const ids = entries.map(r => r.targetId);
|
|
990
|
-
const kind = row.verificationKind;
|
|
991
|
-
const expectedKeys = row.sourceKeys;
|
|
992
|
-
const covered = Array.isArray(expectedKeys) && new Set(expectedKeys).size === row.inScope && expectedKeys.length === row.inScope && expectedKeys.every(k => unique.has(k)) && report?.siteId && report.siteId === (input.siteId || input.destinations?.siteId) && entries.every(r => typeof r.sourceKey === 'string' && r.sourceKey) && unique.size === entries.length && entries.length === row.inScope;
|
|
993
|
-
const verified = entries.filter(r => validReceipt(r.receipt, { siteId: report?.siteId, sourceKey: r.sourceKey, targetId: r.targetId, kind })).length;
|
|
994
|
-
if ((row.inScope && (!covered || verified !== row.inScope || new Set(ids).size !== ids.length)) || entries.length !== row.inScope) {
|
|
995
|
-
// Absence stays exactly as blocking as a failed check; only the wording differs,
|
|
996
|
-
// so a reader can tell "never supplied" from "supplied and failed".
|
|
997
|
-
const absent = !Array.isArray(report?.rows) || report.rows.length === 0;
|
|
998
|
-
verificationGaps.push({
|
|
999
|
-
entity: row.entity,
|
|
1000
|
-
message: absent
|
|
1001
|
-
? `${row.entity}: verification not recorded; supply verification input before completing`
|
|
1002
|
-
: `${row.entity}: identity/value verification missing or invalid; reconcile source records before completing`,
|
|
1003
|
-
});
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
const mismatches = entityCompleteness.filter((row) => row.mismatch);
|
|
1007
|
-
const voucherReconciliation = normalizeVoucherReconciliation(input, entityCompleteness);
|
|
1008
|
-
const voucherHeadline = VOUCHER_RECONCILIATION_WARNING.has(voucherReconciliation)
|
|
1009
|
-
? [{ entity: 'coupons', subtype: 'voucher-reconciliation', message: VOUCHER_RECONCILIATION_MESSAGE[voucherReconciliation] }]
|
|
1010
|
-
: [];
|
|
1011
|
-
// Missing accounting is not a clean payment history. Known order entities and mapped
|
|
1012
|
-
// order targets must explicitly pass the collected list, including [] when it is empty.
|
|
1013
|
-
const hasOrders = input.orderHistoryInScope === true || (input.entityCompleteness || []).some((row) =>
|
|
1014
|
-
row.inScope > 0 && [row.entity, row.targetEntityType].some((name) => /(^|[/:._-])orders?$/i.test(name || '')));
|
|
1015
|
-
if (hasOrders && !Array.isArray(input.stageResiduals)) {
|
|
1016
|
-
throw new Error('stageResiduals must be an array for order history; collect outcomes before completing');
|
|
1017
|
-
}
|
|
1018
|
-
const stageResiduals = asArray(input.stageResiduals, 'stageResiduals')
|
|
1019
|
-
.map((row, index) => normalizeStageResidual(row, index));
|
|
1020
|
-
// A gate that fires on zero is noise, so only a row that actually has records speaks.
|
|
1021
|
-
const openStageResiduals = stageResiduals.filter((row) => row.count > 0 && !ACCEPTED_STAGE_OUTCOMES.has(row.outcome));
|
|
1022
|
-
const acceptedStageOutcomes = stageResiduals.filter((row) => row.count > 0 && ACCEPTED_STAGE_OUTCOMES.has(row.outcome));
|
|
1023
|
-
const memberOutcomes = normalizeMemberOutcomes(input.memberOutcomes);
|
|
1024
|
-
const headline = [
|
|
1025
|
-
...entityCompleteness.map(headlineForRow).filter(Boolean),
|
|
1026
|
-
...memberHeadline(memberOutcomes),
|
|
1027
|
-
...verificationGaps,
|
|
1028
|
-
...voucherHeadline,
|
|
1029
|
-
...openStageResiduals.map((row) => ({ entity: row.entity, stage: row.stage, message: stageResidualMessage(row) })),
|
|
1030
|
-
];
|
|
1031
|
-
const warnings = [...asArray(input.warnings, 'warnings'), ...voucherHeadline.map((row) => row.message)];
|
|
1032
|
-
const recoveryActions = [
|
|
1033
|
-
...asArray(input.recoveryActions, 'recoveryActions'),
|
|
1034
|
-
...verificationGaps.map(g => ({ entity: g.entity, action: g.message })),
|
|
1035
|
-
...openStageResiduals.map((row) => ({
|
|
1036
|
-
entity: row.entity,
|
|
1037
|
-
stage: row.stage,
|
|
1038
|
-
action: stageResidualAction(row),
|
|
1039
|
-
})),
|
|
1040
|
-
];
|
|
1041
|
-
const blockedDataRequests = asArray(input.blockedDataRequests, 'blockedDataRequests')
|
|
1042
|
-
.map((request, index) => normalizeBlockedDataRequest(request, index));
|
|
1043
|
-
const urlPreservation = normalizeUrlPreservation(input.urlPreservation);
|
|
1044
|
-
const operatorActions = asArray(input.operatorActions, 'operatorActions').map(normalizeOperatorAction);
|
|
1045
|
-
for (const action of urlPreservation ? urlPreservation.operatorActions : []) {
|
|
1046
|
-
if (!operatorActions.some((row) => row.code === action.code)) {
|
|
1047
|
-
operatorActions.push(normalizeOperatorAction(action, operatorActions.length));
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
// Folded once, at the source, so no caller has to remember it. A caller-supplied row with the
|
|
1051
|
-
// same code is authoritative; a derived duplicate would print the same urgent line twice.
|
|
1052
|
-
const voucherAction = voucherOperatorAction(voucherReconciliation, entityCompleteness);
|
|
1053
|
-
if (voucherAction && !operatorActions.some((row) => row.code === voucherAction.code)) {
|
|
1054
|
-
operatorActions.push(normalizeOperatorAction(voucherAction, operatorActions.length));
|
|
1055
|
-
}
|
|
1056
|
-
// Binds a supplied `coverageCheck` to `input.runId` and decides the escalation --
|
|
1057
|
-
// never recomputes the check itself.
|
|
1058
|
-
const coverageEffect = resolveCoverageCheckEffect(input);
|
|
1059
|
-
if (coverageEffect.operatorActionRow && !operatorActions.some((row) => row.code === coverageEffect.operatorActionRow.code)) {
|
|
1060
|
-
operatorActions.push(normalizeOperatorAction(coverageEffect.operatorActionRow, operatorActions.length));
|
|
1061
|
-
}
|
|
1062
|
-
// CMS fallback for recognized plugin capabilities: one action per approved fallback (feature
|
|
1063
|
-
// still absent), per undecided/declined eligible capability (records not migrated), per write
|
|
1064
|
-
// the write-time guard refused, and per capability whose earlier CMS records no longer have a
|
|
1065
|
-
// valid approval. `cmsFallbackWrites` is persisted so the next run can detect withdrawal.
|
|
1066
|
-
const cmsFallbackInput = input.cmsFallback || null;
|
|
1067
|
-
const cmsFallbackWrites = cmsFallbackInput ? normalizeCmsFallbackWrites(cmsFallbackInput.writes) : [];
|
|
1068
|
-
if (cmsFallbackInput) {
|
|
1069
|
-
const derived = cmsFallbackOperatorActions({
|
|
1070
|
-
coverage: cmsFallbackInput.coverage || null,
|
|
1071
|
-
refusals: Array.isArray(cmsFallbackInput.refusals) ? cmsFallbackInput.refusals : [],
|
|
1072
|
-
previousWrites: normalizeCmsFallbackWrites(cmsFallbackInput.previousWrites
|
|
1073
|
-
|| (cmsFallbackInput.previousReport && cmsFallbackInput.previousReport.cmsFallbackWrites)),
|
|
1074
|
-
writes: cmsFallbackWrites,
|
|
1075
|
-
});
|
|
1076
|
-
for (const row of derived) {
|
|
1077
|
-
if (!operatorActions.some((existing) => existing.code === row.code && existing.entity === row.entity)) {
|
|
1078
|
-
operatorActions.push(normalizeOperatorAction(row, operatorActions.length));
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
const losses = asArray(input.losses, 'losses').map(normalizeLoss);
|
|
1083
|
-
const exclusions = asArray(input.exclusions, 'exclusions').map(normalizeExclusion);
|
|
1084
|
-
const unresolved = asArray(input.unresolved, 'unresolved').map(normalizeUnresolved);
|
|
1085
|
-
const droppedFields = asArray(input.droppedFields, 'droppedFields').map(normalizeDroppedField);
|
|
1086
|
-
|
|
1087
|
-
// Where the durable rows are absent for an entity that reports a shortfall, the
|
|
1088
|
-
// section says the shortfall is unexplained and the final status carries the mismatch. An
|
|
1089
|
-
// entity that lost records with nothing on record explaining how is exactly the case this check
|
|
1090
|
-
// exists to stop from rendering as a clean, empty section.
|
|
1091
|
-
//
|
|
1092
|
-
// Dropped fields never explain a missing record -- they are about records that did
|
|
1093
|
-
// land -- so they are deliberately NOT in this set. `failed` is also no longer part of this
|
|
1094
|
-
// predicate: a failed row now gets its own counted guard entry below, never the bare
|
|
1095
|
-
// reconciliation message.
|
|
1096
|
-
const mismatchExplainedEntities = new Set([
|
|
1097
|
-
...losses.map((row) => row.entity),
|
|
1098
|
-
...exclusions.map((row) => row.entity),
|
|
1099
|
-
...unresolved.map((row) => row.entity),
|
|
1100
|
-
// A scopeOverflow row already carries its own proof of why it mismatches
|
|
1101
|
-
// (the headline text plus the scopeOverflow count on its mismatches entry) -- exactly as
|
|
1102
|
-
// self-explaining as a caller-supplied losses/exclusions/unresolved row. Never fabricate a
|
|
1103
|
-
// synthetic entry in one of those arrays for it; that would misrepresent a counting-error
|
|
1104
|
-
// row as a deliberate loss or exclusion.
|
|
1105
|
-
...entityCompleteness.filter((row) => row.scopeOverflow > 0).map((row) => row.entity),
|
|
1106
|
-
// The member breakdown names every failed member's reason and recovery path.
|
|
1107
|
-
...(memberOutcomes ? [memberOutcomes.entity] : []),
|
|
1108
|
-
]);
|
|
1109
|
-
const unexplainedShortfalls = entityCompleteness
|
|
1110
|
-
.filter((row) => (row.mismatch || row.unexpectedSkipped > 0) && !mismatchExplainedEntities.has(row.entity))
|
|
1111
|
-
.map((row) => ({
|
|
1112
|
-
entity: row.entity,
|
|
1113
|
-
subtype: row.subtype,
|
|
1114
|
-
shortfall: row.expectedReconciled - row.reconciled,
|
|
1115
|
-
message: `${row.entity}/${row.subtype}: shortfall unexplained -- no loss, exclusion or unresolved row accounts for it`,
|
|
1116
|
-
}));
|
|
1117
|
-
// Deferred and failed are counted, not just named. A row's deferred records
|
|
1118
|
-
// are explained only by its own journal-backed tally and by exclusions; its failed records only
|
|
1119
|
-
// by its own tally and by losses -- each capped at the row's actual residual, never spilling
|
|
1120
|
-
// into another row (except through the gated, provenance-checked shared pool above).
|
|
1121
|
-
const counts = allocateUnexplainedCounts(entityCompleteness, exclusions, losses, memberOutcomes);
|
|
1122
|
-
entityCompleteness.forEach((row, index) => {
|
|
1123
|
-
const { unexplainedDeferred, unexplainedFailed } = counts[index];
|
|
1124
|
-
if (unexplainedDeferred > 0) {
|
|
1125
|
-
unexplainedShortfalls.push({
|
|
1126
|
-
entity: row.entity,
|
|
1127
|
-
subtype: row.subtype,
|
|
1128
|
-
outcome: 'deferred',
|
|
1129
|
-
shortfall: unexplainedDeferred,
|
|
1130
|
-
message: `${row.entity}/${row.subtype}: ${unexplainedDeferred} deferred with no recorded reason`,
|
|
1131
|
-
});
|
|
1132
|
-
}
|
|
1133
|
-
if (unexplainedFailed > 0) {
|
|
1134
|
-
unexplainedShortfalls.push({
|
|
1135
|
-
entity: row.entity,
|
|
1136
|
-
subtype: row.subtype,
|
|
1137
|
-
outcome: 'failed',
|
|
1138
|
-
shortfall: unexplainedFailed,
|
|
1139
|
-
message: `${row.entity}/${row.subtype}: ${unexplainedFailed} failed with no recorded reason`,
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
});
|
|
1143
|
-
// An `unresolved` counter must be matched by unresolved[] rows for that entity.
|
|
1144
|
-
for (const row of entityCompleteness) {
|
|
1145
|
-
if (!row.unresolved) continue;
|
|
1146
|
-
const listed = unresolved.filter((entry) => entry.entity === row.entity).reduce((sum, entry) => sum + entry.count, 0);
|
|
1147
|
-
if (listed < row.unresolved) {
|
|
1148
|
-
unexplainedShortfalls.push({
|
|
1149
|
-
entity: row.entity,
|
|
1150
|
-
subtype: row.subtype,
|
|
1151
|
-
shortfall: row.unresolved - listed,
|
|
1152
|
-
message: `${row.entity}/${row.subtype}: ${row.unresolved - listed} unresolved record(s) with no unresolved row naming them`,
|
|
1153
|
-
});
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
const lastWriteAt = input.lastWriteAt === undefined || input.lastWriteAt === null
|
|
1158
|
-
? null
|
|
1159
|
-
: requireString(input.lastWriteAt, 'lastWriteAt');
|
|
1160
|
-
|
|
1161
|
-
const status = chooseCompletionStatus({
|
|
1162
|
-
statuses: input.statuses,
|
|
1163
|
-
aborted: input.aborted,
|
|
1164
|
-
hasMismatches: mismatches.length > 0 || verificationGaps.length > 0 || unresolved.length > 0 || unexplainedShortfalls.length > 0
|
|
1165
|
-
|| coverageEffect.hasMismatch,
|
|
1166
|
-
hasFailures: hasCount(entityCompleteness, 'failed') || losses.length > 0
|
|
1167
|
-
|| openStageResiduals.length > 0
|
|
1168
|
-
|| blockedDataRequests.some((request) => request.aggregateOutcome === 'failed'),
|
|
1169
|
-
hasDeferred: hasCount(entityCompleteness, 'deferred') || blockedDataRequests.some((request) => request.aggregateOutcome === 'deferred'),
|
|
1170
|
-
hasRecoveredRecords: recoveryActions.length > 0 || Boolean(input.hasRecoveredRecords),
|
|
1171
|
-
// A deliberate exclusion is a choice, not a loss -- but an exclusion nobody gated is an
|
|
1172
|
-
// unreviewed filter, and that is a warning.
|
|
1173
|
-
hasWarnings: warnings.length > 0 || Boolean(input.hasWarnings) || droppedFields.length > 0
|
|
1174
|
-
|| exclusions.some((row) => !row.operatorGateRecorded)
|
|
1175
|
-
|| blockedDataRequests.some((request) => request.aggregateOutcome === 'warning')
|
|
1176
|
-
|| coverageEffect.hasWarnings,
|
|
1177
|
-
});
|
|
1178
|
-
|
|
1179
|
-
return {
|
|
1180
|
-
schemaVersion: 1,
|
|
1181
|
-
runId: input.runId || null,
|
|
1182
|
-
status,
|
|
1183
|
-
headline,
|
|
1184
|
-
voucherReconciliation,
|
|
1185
|
-
// The bound (or rejected/omitted) coverage check and whether this run declared
|
|
1186
|
-
// participation in the contract at all -- `website-handoff.js` uses both to decide whether its
|
|
1187
|
-
// "Count cross-check" section applies to this run.
|
|
1188
|
-
coverageCheck: coverageEffect.coverageCheck,
|
|
1189
|
-
coverageCheckExpected: coverageEffect.coverageCheckExpected,
|
|
1190
|
-
entityCompleteness,
|
|
1191
|
-
stageResiduals,
|
|
1192
|
-
acceptedStageOutcomes,
|
|
1193
|
-
verificationGaps,
|
|
1194
|
-
verification,
|
|
1195
|
-
mismatches: mismatches.map((row) => ({
|
|
1196
|
-
entity: row.entity,
|
|
1197
|
-
subtype: row.subtype,
|
|
1198
|
-
expectedReconciled: row.expectedReconciled,
|
|
1199
|
-
reconciled: row.reconciled,
|
|
1200
|
-
unexpectedSkipped: row.unexpectedSkipped,
|
|
1201
|
-
scopeOverflow: row.scopeOverflow,
|
|
1202
|
-
})).concat(verificationGaps.map(gap => ({ ...gap, type: 'identity_value_verification' }))),
|
|
1203
|
-
warnings,
|
|
1204
|
-
recoveryActions,
|
|
1205
|
-
blockedDataRequests,
|
|
1206
|
-
operatorActions,
|
|
1207
|
-
cmsFallbackWrites,
|
|
1208
|
-
losses,
|
|
1209
|
-
exclusions,
|
|
1210
|
-
unresolved,
|
|
1211
|
-
droppedFields,
|
|
1212
|
-
unexplainedShortfalls,
|
|
1213
|
-
memberOutcomes,
|
|
1214
|
-
// The handoff proves it postdates the import by comparing its own
|
|
1215
|
-
// `generatedAt` against this value -- never against a file mtime, which a copy refreshes.
|
|
1216
|
-
lastWriteAt,
|
|
1217
|
-
artifacts: input.artifacts || {},
|
|
1218
|
-
destinations: input.destinations || {},
|
|
1219
|
-
urlPreservation,
|
|
1220
|
-
};
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
const URL_PRESERVATION_STATUS = ['verified', 'action_required', 'pending_cutover_verification', 'pending_website_build'];
|
|
1224
|
-
|
|
1225
|
-
function normalizeUrlPreservation(value) {
|
|
1226
|
-
if (value === undefined || value === null) return null;
|
|
1227
|
-
if (typeof value !== 'object' || Array.isArray(value)) throw new Error('urlPreservation must be an object');
|
|
1228
|
-
// Older callers passed the import-time policy object; keep it verbatim.
|
|
1229
|
-
if (value.status === undefined) return { ...value, operatorActions: [] };
|
|
1230
|
-
if (!URL_PRESERVATION_STATUS.includes(value.status)) {
|
|
1231
|
-
throw new Error(`urlPreservation.status must be one of ${URL_PRESERVATION_STATUS.join(', ')}`);
|
|
1232
|
-
}
|
|
1233
|
-
return { ...value, operatorActions: asArray(value.operatorActions, 'urlPreservation.operatorActions') };
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
function causeLine(row) {
|
|
1237
|
-
// `unverified` renders the words and nothing else. No cause text, no hypothesis.
|
|
1238
|
-
if (row.causeConfidence !== 'verified-source' && row.causeConfidence !== 'verified-target') {
|
|
1239
|
-
return 'cause unverified';
|
|
1240
|
-
}
|
|
1241
|
-
return `${row.cause} (${row.causeConfidence}; established by ${row.check.kind} on ${row.check.target} -> ${row.check.result})`;
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
// "Permanent Losses: None" must not read as "nothing permanent happened" while
|
|
1245
|
-
// Deliberate Exclusions lists records that were permanently left out. Only claim the
|
|
1246
|
-
// exclusions were reviewed when every row has an operator gate recorded.
|
|
1247
|
-
function emptyPermanentLossesLine(exclusions = []) {
|
|
1248
|
-
if (!exclusions.length) return '- None.';
|
|
1249
|
-
return exclusions.every((row) => row.operatorGateRecorded)
|
|
1250
|
-
? '- None (see Deliberate Exclusions below for permanent, operator-reviewed gaps).'
|
|
1251
|
-
: '- None (see Deliberate Exclusions below for permanent gaps; some are not operator-reviewed).';
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
// One line per outcome for a row with a nonzero count: the tallied reasons, and
|
|
1255
|
-
// -- only when the tallies do not already cover the whole count -- the remainder with no
|
|
1256
|
-
// recorded reason at all. `count` always comes from the row's own field (`deferred`/`failed`),
|
|
1257
|
-
// never from the allocation/pool step, so this line is independent of whether an exclusion or
|
|
1258
|
-
// loss credit later clears the row in "Unexplained Shortfalls".
|
|
1259
|
-
function reasonLine(row, outcome, totalCount) {
|
|
1260
|
-
const tallies = row.reasons.filter((entry) => typeof entry === 'object' && entry.outcome === outcome);
|
|
1261
|
-
if (!tallies.length) {
|
|
1262
|
-
return `- ${row.entity}/${row.subtype}: ${totalCount} ${outcome} -- no recorded reason`;
|
|
1263
|
-
}
|
|
1264
|
-
const tallySum = tallies.reduce((sum, entry) => sum + entry.count, 0);
|
|
1265
|
-
const remainder = totalCount - tallySum;
|
|
1266
|
-
const tallyText = tallies.map((entry) => `${entry.code}: ${entry.count}`).join(', ');
|
|
1267
|
-
const remainderText = remainder > 0 ? `; ${remainder} with no recorded reason` : '';
|
|
1268
|
-
return `- ${row.entity}/${row.subtype}: ${totalCount} ${outcome} -- ${tallyText}${remainderText}`;
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
// The user-facing summary is RENDERED from the report object, never
|
|
1272
|
-
// composed independently and never derived from `execution-log.md`.
|
|
1273
|
-
function renderCompletionSummary(report) {
|
|
1274
|
-
const lines = [
|
|
1275
|
-
'# Migration Completion Summary',
|
|
1276
|
-
'',
|
|
1277
|
-
`- Status: ${report.status}`,
|
|
1278
|
-
`- Run id: ${report.runId || 'not recorded'}`,
|
|
1279
|
-
`- Last write: ${report.lastWriteAt || 'not recorded'}`,
|
|
1280
|
-
...(report.voucherReconciliation ? [`- Voucher reconciliation: ${report.voucherReconciliation}`] : []),
|
|
1281
|
-
...(report.headline || []).map((row) => `- ${row.message}`),
|
|
1282
|
-
'',
|
|
1283
|
-
'## Urgent Operator Actions',
|
|
1284
|
-
'',
|
|
1285
|
-
];
|
|
1286
|
-
if (!report.operatorActions.length) {
|
|
1287
|
-
lines.push('- None.');
|
|
1288
|
-
} else {
|
|
1289
|
-
for (const action of report.operatorActions) {
|
|
1290
|
-
lines.push(`- [${action.severity}] ${action.code}: ${action.action} (${action.affectedCount === null ? 'affected count unknown' : `${action.affectedCount} affected`}; act now because ${action.deadlineReason})`);
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
if (report.urlPreservation && report.urlPreservation.status) {
|
|
1295
|
-
const u = report.urlPreservation;
|
|
1296
|
-
const c = u.counts || {};
|
|
1297
|
-
lines.push('', '## Old Links', '', `- Status: ${u.status}${u.coverageComplete === false ? ' (source redirect discovery incomplete)' : ''}`);
|
|
1298
|
-
lines.push(`- Verified: ${(c.verified_direct || 0) + (c.verified_redirect || 0)}; not verified: ${c.not_verified || 0}; unresolved: ${c.unresolved || 0}; manual review: ${c.manual_review || 0}; failed: ${c.failed || 0}`);
|
|
1299
|
-
if (u.status !== 'verified') lines.push(`- Old emails and bookmarks using unconfirmed links may fail. Details: ${u.detailPath || 'execution/url-preservation-report.md'}`);
|
|
1300
|
-
}
|
|
1301
|
-
if (report.readBack) lines.push(...require('./read-back-summary').renderReadBackSummary(report.readBack));
|
|
1302
|
-
|
|
1303
|
-
lines.push('', '## Per-Entity Reconciliation', '', '| Entity | Extracted | In scope | Landed | Unreconciled |', '| --- | --- | --- | --- | --- |');
|
|
1304
|
-
if (!report.entityCompleteness.length) {
|
|
1305
|
-
lines.push('| (none) | 0 | 0 | 0 | 0 |');
|
|
1306
|
-
} else {
|
|
1307
|
-
for (const row of report.entityCompleteness) {
|
|
1308
|
-
lines.push(`| ${row.entity}/${row.subtype} | ${row.extracted} | ${row.inScope} | ${row.imported + row.alreadyPresentByCrosswalk} | ${row.expectedReconciled - row.reconciled} |`);
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
lines.push('', '## Permanent Losses', '');
|
|
1313
|
-
if (!report.losses.length) lines.push(emptyPermanentLossesLine(report.exclusions));
|
|
1314
|
-
else for (const row of report.losses) lines.push(`- ${row.entity}: ${row.count} lost (${row.reasonCode}); ${row.whyNoRetry}; ${causeLine(row)}`);
|
|
1315
|
-
|
|
1316
|
-
lines.push('', '## Deliberate Exclusions', '');
|
|
1317
|
-
if (!report.exclusions.length) lines.push('- None.');
|
|
1318
|
-
else for (const row of report.exclusions) lines.push(`- ${row.entity}: ${row.count} excluded (${row.reasonCode}); ${row.rationale}; operator gate recorded: ${row.operatorGateRecorded}`);
|
|
1319
|
-
|
|
1320
|
-
lines.push('', '## Unresolved And Orphaned', '');
|
|
1321
|
-
if (!report.unresolved.length) lines.push('- None.');
|
|
1322
|
-
else for (const row of report.unresolved) lines.push(`- ${row.entity}: ${row.count} ${row.kind}; ${causeLine(row)}`);
|
|
1323
|
-
|
|
1324
|
-
lines.push('', '## Fields Dropped For The Destination', '');
|
|
1325
|
-
if (!report.droppedFields.length) lines.push('- None.');
|
|
1326
|
-
else for (const row of report.droppedFields) lines.push(`- ${row.entity}.${row.field}: ${row.recordCount} records (${row.reasonCode})`);
|
|
1327
|
-
|
|
1328
|
-
if ((report.acceptedStageOutcomes || []).length) {
|
|
1329
|
-
lines.push('', '## Accepted And Materialized Gaps', '');
|
|
1330
|
-
for (const row of report.acceptedStageOutcomes) lines.push(`- ${stageResidualAction(row)}`);
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
if (report.memberOutcomes) {
|
|
1334
|
-
const m = report.memberOutcomes;
|
|
1335
|
-
lines.push('', '## Members', '', `- Population: ${m.populationBasis}`);
|
|
1336
|
-
for (const [cls, entry] of Object.entries(m.byClass || {})) {
|
|
1337
|
-
lines.push(`- ${entry.outcome}${cls !== entry.outcome && /^(failed-|reconciled-)/.test(cls) ? ` (${cls})` : ''}: ${entry.count}${entry.count && entry.nextAction ? ` -- ${entry.nextAction}` : ''}`);
|
|
1338
|
-
}
|
|
1339
|
-
if (m.danglingStarted) lines.push(`- write started, never concluded: ${m.danglingStarted}`);
|
|
1340
|
-
if (m.unexpectedSkipped) lines.push(`- no journal row: ${m.unexpectedSkipped}`);
|
|
1341
|
-
for (const [code, n] of Object.entries(m.byFinding)) lines.push(`- finding ${code}: ${n}`);
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
if (report.recoveryActions.length) {
|
|
1345
|
-
lines.push('', '## Recovery Actions', '');
|
|
1346
|
-
for (const row of report.recoveryActions) lines.push(`- ${row.action}`);
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
// Before "Unexplained Shortfalls": a reader sees why records did not land
|
|
1350
|
-
// without opening JSON, and sees the unexplained remainder on the same line.
|
|
1351
|
-
lines.push('', '## Why Records Did Not Land', '');
|
|
1352
|
-
const reasonRows = report.entityCompleteness.filter((row) => row.deferred > 0 || row.failed > 0);
|
|
1353
|
-
if (!reasonRows.length) {
|
|
1354
|
-
lines.push('- None.');
|
|
1355
|
-
} else {
|
|
1356
|
-
for (const row of reasonRows) {
|
|
1357
|
-
if (row.deferred > 0) lines.push(reasonLine(row, 'deferred', row.deferred));
|
|
1358
|
-
if (row.failed > 0) lines.push(reasonLine(row, 'failed', row.failed));
|
|
1359
|
-
for (const entry of row.reasons.filter((r) => typeof r === 'string')) {
|
|
1360
|
-
lines.push(` - note: ${entry}`);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
if (report.unexplainedShortfalls.length) {
|
|
1366
|
-
lines.push('', '## Unexplained Shortfalls', '');
|
|
1367
|
-
for (const row of report.unexplainedShortfalls) lines.push(`- ${row.message}`);
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
// Only a joined (schema v2) report carries this; an un-joined in-memory report
|
|
1371
|
-
// renders exactly as before.
|
|
1372
|
-
if (Array.isArray(report.sourceCoverage)) {
|
|
1373
|
-
lines.push('', '## Not Migrated From The Source', '');
|
|
1374
|
-
if (report.sourceCoverageRecorded === false) lines.push('- Source coverage was not recorded for this run (discovery predates the coverage contract).');
|
|
1375
|
-
else if (!report.sourceCoverage.length) lines.push('- None.');
|
|
1376
|
-
else for (const row of report.sourceCoverage) lines.push(sourceCoverageLine(row));
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
return `${lines.join('\n')}\n`;
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
function sourceCoverageLine(row) {
|
|
1383
|
-
const label = row.capability || row.sourceEntity;
|
|
1384
|
-
return `- ${label} (${row.reason}; ${row.treatment}): ${row.cutoverAction}`;
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
async function writeFileAtomic(filePath, text) {
|
|
1388
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
1389
|
-
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
1390
|
-
await fs.writeFile(tempPath, text, 'utf8');
|
|
1391
|
-
await fs.rename(tempPath, filePath);
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
// Before this, `createCompletionReport` built the object and nothing
|
|
1395
|
-
// shipped wrote it: the codegen snippet told generated importers to "persist it using the shared
|
|
1396
|
-
// runtime" and the shared runtime had no such function, so no non-quick-mode run ever produced
|
|
1397
|
-
// the artifact its own completion gate keys on. One shared writer, and the summary rendered from
|
|
1398
|
-
// the same object it just wrote.
|
|
1399
|
-
//
|
|
1400
|
-
// The source-coverage join happens HERE, inside the one writer, so status is
|
|
1401
|
-
// computed once from everything. `createCompletionReport` already chose a status before this
|
|
1402
|
-
// function sees the project; folding the coverage floor in with the same rank (`worseStatus`)
|
|
1403
|
-
// equals a one-pass computation. The written artifact is schema v2.
|
|
1404
|
-
async function joinSourceCoverage(projectDir, report) {
|
|
1405
|
-
if (Object.prototype.hasOwnProperty.call(report, 'sourceCoverage') || Object.prototype.hasOwnProperty.call(report, 'sourceCoverageRecorded')) {
|
|
1406
|
-
throw new Error('writeCompletionReport: the report already carries sourceCoverage; the writer owns that join and a caller cannot pre-empt it');
|
|
1407
|
-
}
|
|
1408
|
-
// Throws on a stamped project whose coverage file is missing, malformed or unreconciled.
|
|
1409
|
-
const coverage = await summarizeProjectCoverage(projectDir);
|
|
1410
|
-
const giftCards = await evaluateGiftCardCutover(projectDir);
|
|
1411
|
-
const operatorActions = [...asArray(report.operatorActions, 'operatorActions')].filter((row) => row.code !== GIFT_ACTION_CODE);
|
|
1412
|
-
for (const row of coverage.operatorActions) {
|
|
1413
|
-
operatorActions.push(normalizeOperatorAction(row, operatorActions.length));
|
|
1414
|
-
}
|
|
1415
|
-
const linkAudit = await readLinkAudit(projectDir);
|
|
1416
|
-
for (const row of linkOperatorActions(linkAudit)) {
|
|
1417
|
-
if (!operatorActions.some((existing) => existing.code === row.code)) operatorActions.push(normalizeOperatorAction(row, operatorActions.length));
|
|
1418
|
-
}
|
|
1419
|
-
const linkStatus = linkAudit && linkOperatorActions(linkAudit).length ? 'complete_with_warnings' : null;
|
|
1420
|
-
// Remaining Editor website/publication work. A caller-supplied row with the
|
|
1421
|
-
// same code is authoritative and is never duplicated.
|
|
1422
|
-
for (const row of await projectDestinationOperatorActions(projectDir)) {
|
|
1423
|
-
if (!operatorActions.some((existing) => existing.code === row.code)) {
|
|
1424
|
-
operatorActions.push(normalizeOperatorAction(row, operatorActions.length));
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
// Source access that outlives the migration (the Shopify staff account) is an
|
|
1428
|
-
// operator action beside the coverage rows until its removal is confirmed. It is an action, not
|
|
1429
|
-
// a coverage gap, so status is unchanged.
|
|
1430
|
-
for (const row of sourceAccessOperatorActions(projectDir)) {
|
|
1431
|
-
if (!operatorActions.some((existing) => existing.code === row.code && existing.entity === row.entity)) {
|
|
1432
|
-
operatorActions.push(normalizeOperatorAction(row, operatorActions.length));
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
// Mapping may accept withheld Shopify details, but Shopify remains their only
|
|
1436
|
-
// copy until the merchant's export is confirmed after the latest read. This action is separate
|
|
1437
|
-
// from sourceCoverage's disposition and does not change report status.
|
|
1438
|
-
for (const row of withheldDetailsOperatorActions(projectDir)) {
|
|
1439
|
-
if (!operatorActions.some((existing) => existing.code === row.code && existing.entity === row.entity)) {
|
|
1440
|
-
operatorActions.push(normalizeOperatorAction(row, operatorActions.length));
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
if (giftCards.action) operatorActions.push(normalizeOperatorAction(giftCards.action, operatorActions.length));
|
|
1444
|
-
const sourceCoverage = coverage.sourceCoverage.map((row) => (row.coverageId === GIFT_CODES_COVERAGE_ID
|
|
1445
|
-
? { ...row, cutoverAction: giftCards.cutoverAction || row.cutoverAction }
|
|
1446
|
-
: row));
|
|
1447
|
-
return {
|
|
1448
|
-
...report,
|
|
1449
|
-
schemaVersion: 2,
|
|
1450
|
-
status: linkStatus ? worseStatus(coverage.status ? worseStatus(report.status, coverage.status) : report.status, linkStatus)
|
|
1451
|
-
: coverage.status ? worseStatus(report.status, coverage.status) : report.status,
|
|
1452
|
-
headline: [...asArray(report.headline, 'headline'), ...coverage.headline],
|
|
1453
|
-
operatorActions,
|
|
1454
|
-
...(linkAudit ? { linkAudit: {
|
|
1455
|
-
path: LINK_AUDIT_PATH, stage: linkAudit.stage, counts: linkAudit.counts, estimate: linkAudit.estimate || null,
|
|
1456
|
-
pendingOccurrenceIds: linkAudit.pendingDestinations.flatMap((row) => row.occurrenceIds),
|
|
1457
|
-
unresolvedOccurrenceIds: linkAudit.unresolved.flatMap((row) => row.occurrenceIds),
|
|
1458
|
-
pendingWriterFields: (linkAudit.writerIntegrations || []).filter((row) => row.status === 'pending').map((row) => row.field),
|
|
1459
|
-
missingCutoverUiIntegration: true,
|
|
1460
|
-
} } : {}),
|
|
1461
|
-
sourceCoverageRecorded: coverage.sourceCoverageRecorded,
|
|
1462
|
-
...(coverage.sourceCoverageReason ? { sourceCoverageReason: coverage.sourceCoverageReason } : {}),
|
|
1463
|
-
sourceCoverage,
|
|
1464
|
-
};
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
async function dependencyOperatorActions(projectDir) {
|
|
1468
|
-
let diagnostic;
|
|
1469
|
-
try { diagnostic = JSON.parse(await fs.readFile(path.join(projectDir, 'execution/dependency-readiness.json'), 'utf8')); } catch (error) { if (error.code === 'ENOENT') return []; throw error; }
|
|
1470
|
-
const { rows } = await require('./local-state').loadAttemptJournal(projectDir).catch(error => { if (error.code === 'ENOENT') return { rows: [] }; throw error; });
|
|
1471
|
-
const recovery = require('./dependency-recovery'), indexes = new Map(), result = [];
|
|
1472
|
-
for (const task of Object.values(diagnostic.tasks || {})) if (!indexes.has(task.siteId)) indexes.set(task.siteId, recovery.foldRecoveryAttempts(rows, task.siteId));
|
|
1473
|
-
for (const task of Object.values(diagnostic.tasks || {})) {
|
|
1474
|
-
const waiting = recovery.waitingForTask(indexes.get(task.siteId), task.taskId);
|
|
1475
|
-
const groups = new Map();
|
|
1476
|
-
for (const block of task.blocked || []) groups.set(`${block.ref}:${block.recovery}`, { ref: block.ref, recovery: block.recovery, count: task.affectedCount ?? null });
|
|
1477
|
-
for (const row of waiting) { const ref = row.deferral.ref || row.deferral.reason, key = `${ref}:resume-deferred`; if (!groups.has(key)) groups.set(key, { ref, recovery: 'resume-deferred', count: 0 }); groups.get(key).count++; }
|
|
1478
|
-
for (const group of groups.values()) result.push({ code: 'dependency-readiness-blocked', severity: 'blocker', entity: task.taskId, affectedCount: group.count,
|
|
1479
|
-
deadlineReason: 'Resolve before launch; affected dependent records have not finished importing.',
|
|
1480
|
-
action: `Requires Import Operator. Prerequisites: original migration project, source snapshot and current-site access; retain existing owner approvals. Copy prompt: Inspect execution/dependency-readiness.json task ${task.taskId} and ref ${group.ref}; resolve ${group.recovery}, re-evaluate and resume only eligible deferred records. Read back uncertain writes, never infer completion from old-site evidence and do not resend existing orders. Completion requires terminal outcomes for dependent records as well as repaired prerequisites.` });
|
|
1481
|
-
}
|
|
1482
|
-
return result;
|
|
1483
|
-
}
|
|
1484
|
-
async function writeCompletionReport(projectDir, unjoinedReport, { runId = null, appendLog = true } = {}) {
|
|
1485
|
-
if (!unjoinedReport || typeof unjoinedReport !== 'object' || !unjoinedReport.status) {
|
|
1486
|
-
throw new Error('writeCompletionReport requires a completion report object');
|
|
1487
|
-
}
|
|
1488
|
-
const censusReport = JSON.parse(JSON.stringify(unjoinedReport));
|
|
1489
|
-
if ((censusReport.exclusions || []).some(row => row.censusId)) {
|
|
1490
|
-
const { readReconciliation, ownerApprovedCensusIds } = require('./census-reconciliation.js');
|
|
1491
|
-
const reconciliation = await readReconciliation(projectDir);
|
|
1492
|
-
const approved = await ownerApprovedCensusIds(projectDir);
|
|
1493
|
-
for (const row of censusReport.exclusions) {
|
|
1494
|
-
if (!row.censusId) continue;
|
|
1495
|
-
if (!reconciliation?.rows?.some(entry => entry.censusId === row.censusId && ['excluded','unsupported'].includes(entry.state))) throw Object.assign(new Error('exclusion_census_id_invalid'), { code: 'exclusion_census_id_invalid' });
|
|
1496
|
-
row.operatorGateRecorded = approved.has(row.censusId);
|
|
1497
|
-
if (!row.operatorGateRecorded && censusReport.status === 'complete') censusReport.status = 'complete_with_warnings';
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
await require('./read-back-approval').recoverAcknowledgements(projectDir);
|
|
1501
|
-
const gateState = require('./read-back-state');
|
|
1502
|
-
const entities = await require('./read-back-approval').gatedEntities(projectDir);
|
|
1503
|
-
const gateRun = censusReport.runId || runId || 'unidentified-completion';
|
|
1504
|
-
if (entities.length) await gateState.update(projectDir, gate => {
|
|
1505
|
-
gate.runs[gateRun] ||= { entities: {} };
|
|
1506
|
-
for (const entity of entities) gate.runs[gateRun].entities[entity] ||= { status: 'unknown', stopReason: 'gate-not-run', canaries: [] };
|
|
1507
|
-
});
|
|
1508
|
-
for (const entity of entities) await require('./read-back-approval').createAcknowledgement(projectDir, gateRun, entity);
|
|
1509
|
-
const gate = await gateState.load(projectDir);
|
|
1510
|
-
const readBack = require('./read-back-summary').projectReadBackSummary(gate);
|
|
1511
|
-
const gateActions = require('./read-back-gate').operatorActions(gate);
|
|
1512
|
-
const readinessActions = await dependencyOperatorActions(projectDir);
|
|
1513
|
-
gateActions.push(...readinessActions);
|
|
1514
|
-
const blocked = gateActions.some(a => ['read-back-gate-stopped', 'read-back-suspect-interval'].includes(a.code));
|
|
1515
|
-
let report = await joinSourceCoverage(projectDir, { ...censusReport,
|
|
1516
|
-
readBack,
|
|
1517
|
-
status: blocked ? worseStatus(censusReport.status, 'incomplete_with_failures') : censusReport.status,
|
|
1518
|
-
operatorActions: [...(censusReport.operatorActions || []).filter(a => !a.code?.startsWith('read-back-')), ...gateActions] });
|
|
1519
|
-
let redirectReport = null;
|
|
1520
|
-
try {
|
|
1521
|
-
redirectReport = JSON.parse(await fs.readFile(path.join(projectDir, 'execution', 'url-rewrite-report.json'), 'utf8'));
|
|
1522
|
-
} catch (error) {
|
|
1523
|
-
if (error.code !== 'ENOENT') throw error;
|
|
1524
|
-
}
|
|
1525
|
-
if (redirectReport) {
|
|
1526
|
-
const actions = Array.isArray(report.operatorActions) ? report.operatorActions : [];
|
|
1527
|
-
const known = new Set(actions.map((row) => `${row.code}\t${row.entity}`));
|
|
1528
|
-
const redirectSources = new Map((redirectReport.rules || []).map((row) => [row.sourceStableKey, row.source]));
|
|
1529
|
-
const urlVerificationOwned = new Set((report.urlPreservation?.urls || [])
|
|
1530
|
-
.filter((row) => !String(row.disposition).startsWith('verified_'))
|
|
1531
|
-
.map((row) => row.originalUrl));
|
|
1532
|
-
for (const action of redirectReport.operatorActions || []) {
|
|
1533
|
-
const source = redirectSources.get(action.entity);
|
|
1534
|
-
if (source && urlVerificationOwned.has(source) &&
|
|
1535
|
-
action.code === 'cutover_links_pending_verification') continue;
|
|
1536
|
-
const key = `${action.code}\t${action.entity}`;
|
|
1537
|
-
if (!known.has(key)) actions.push(normalizeOperatorAction(action, actions.length));
|
|
1538
|
-
known.add(key);
|
|
1539
|
-
}
|
|
1540
|
-
report = { ...report, operatorActions: actions };
|
|
1541
|
-
}
|
|
1542
|
-
const reportPath = path.join(projectDir, COMPLETION_REPORT_FILE);
|
|
1543
|
-
const summaryPath = path.join(projectDir, COMPLETION_SUMMARY_FILE);
|
|
1544
|
-
const summary = renderCompletionSummary(report);
|
|
1545
|
-
await writeFileAtomic(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
1546
|
-
await writeFileAtomic(summaryPath, summary);
|
|
1547
|
-
if (appendLog) {
|
|
1548
|
-
await appendExecutionLog(projectDir, {
|
|
1549
|
-
runId: runId || report.runId || 'unknown-run',
|
|
1550
|
-
phase: 'completion',
|
|
1551
|
-
event: 'completion_report_written',
|
|
1552
|
-
detail: `final outcome recorded with status ${report.status}`,
|
|
1553
|
-
artifacts: [COMPLETION_REPORT_FILE, COMPLETION_SUMMARY_FILE],
|
|
1554
|
-
});
|
|
1555
|
-
}
|
|
1556
|
-
return { reportPath, summaryPath, summary, report };
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
module.exports = {
|
|
1560
|
-
dependencyOperatorActions,
|
|
1561
|
-
COMPLETION_STATUS,
|
|
1562
|
-
VOUCHER_RECONCILIATION,
|
|
1563
|
-
handoffVoucherSignal,
|
|
1564
|
-
COMPLETION_REPORT_FILE,
|
|
1565
|
-
COMPLETION_SUMMARY_FILE,
|
|
1566
|
-
CAUSE_CONFIDENCE,
|
|
1567
|
-
normalizeCauseAttribution,
|
|
1568
|
-
renderCompletionSummary,
|
|
1569
|
-
emptyPermanentLossesLine,
|
|
1570
|
-
writeCompletionReport,
|
|
1571
|
-
chooseCompletionStatus,
|
|
1572
|
-
createCompletionReport,
|
|
1573
|
-
normalizeCompletenessRow,
|
|
1574
|
-
normalizeBlockedDataRequest,
|
|
1575
|
-
normalizeOperatorAction,
|
|
1576
|
-
normalizeStageResidual,
|
|
1577
|
-
normalizeMemberOutcomes,
|
|
1578
|
-
collectOrderStageResiduals,
|
|
1579
|
-
attachAttemptReasons,
|
|
1580
|
-
};
|