Generated banner for Rp Execution Policy
Generated logo for Rp Execution Policy

Rp Execution Policy

WixOfficialPublished by Wix

  • Skill
  • Workflow
  • Software development
  • MIT
  • Low risk
  • Has scripts

From the author

Applies cross-stage execution safety policy and writes the aggregate migration completion artifact after all delivery-specific terminal evidence validates.
— the description by Wix

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured process for handling execution approvals and finalizing migration completion artifacts. It enforces strict validation rules for payment reporting, entity quality assurance, and site settings to ensure that migration outcomes are auditable and accurate. It manages the generation of completion reports, verifies the presence of required execution logs and handoffs, and handles the status of discovered sets during the finalization phase.

What it does

  • Enforces execution approval gates for migration plans.
  • Validates payment, coupon, and currency reporting to ensure data integrity.
  • Verifies the presence and freshness of execution logs and post-execution handoffs.
  • Conducts entity-QA attestation checks to confirm import coverage.
  • Generates and updates migration completion artifacts and discovered-set status files.
  • Provides preview functionality for site regional settings like currency and locale.

When to use it

  • When finalizing a migration project to generate the official completion artifact.
  • During the approval gate phase to review execution plans and payment summaries.
  • When verifying that all required QA attestations and execution logs are present and current.
  • When reconciling discovered-set status for backend migration outcomes.

Advantages

  • Ensures auditability by requiring explicit, non-retained credential reporting.
  • Prevents approval of incomplete or mismatched data through strict validation logic.
  • Maintains consistency by verifying timestamps and digests across artifacts.
  • Provides clear, actionable refusal reasons when validation fails.

Usage examples

“Finalize the migration completion for the current project.”
The agent runs the migration-completion script to validate backend accounting and frontend receipts, then generates the migration-completion.json file.
“Preview the regional settings for the site import.”
The agent executes the site-regional preview script to display currency, time zone, and locale settings derived from the source file.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/wix/rp-execution-policy-slaw/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip rp-execution-policy.zip -d .agents/skills

Documentation by Wix

rp-execution-policy

Use this resource for the execution approval gate and aggregate finalization.

For awaiting_execution_approval, present the generated execution plan, wait for the required user acceptance in normal mode, and persist it in orchestration/approvals.json. In explicit user-requested 1-click mode (automationMode=one_click, source=user), use the deterministic agent approval helper only after the plan and any required code-safety review validate. Then route again. A non-user-authored one_click value is normal interactive mode and must wait for user acceptance.

Disclosing what was read from a credential-bearing source surface

If the run read payment-gateway configuration from the source, the plan presented at the approval gate must say so: that gateway configuration was read, which gateways were found, how many are enabled, how many credential fields are configured, and that no credential value was retained. The source adapter produces that line — for WordPress, describeGatewaySummary() in rp-source-wordpress/lib/gateway-summary.mjs.

A run that reports "N gateways, M enabled, K credential fields present, 0 values retained" is auditable. One that read the same thing silently is not, and the user approving the plan has no way to tell the two apart. Never put a credential value, prefix, length or hash into the plan report itself.

Reporting the payments section of the plan

If the run imports payment records, refunds or invoice documents, build that section of the plan with lib/payments-report.js rather than by hand. It enforces three things a hand-written summary has got wrong before, and it BLOCKS the approval gate rather than printing a caveat:

  • The payment outcomes must sum to the discovered source-order population. A report whose numbers do not add up is hiding orders. The builder returns approvable: false with the shortfall named.

  • Zero is a result. Every outcome and every count is emitted even at zero, so "0 refunds found" is reviewable at a glance. An implausible number is cheapest to catch here.

  • Candidate coverage is not writable coverage. Orders carrying a reference-shaped value and orders whose reference can be defensibly attributed are separate lines. Collapsing them overstates fidelity.

The denominator must come from the run's own reconciled reads (from: 'run-reconciled-read'), not from a prior survey. Where a survey counted differently, both numbers are shown and the discrepancy must be reconciled before the pilot.

The provider inventory carries identifier, display name and enabled state only — a credential-shaped field on a provider blocks the gate. The phrase "you will lose your history" is prohibited: it is false for payment records, which are provider-independent.

Run:

node skills/wix-replatform/scripts/migration-completion.js <projectDir>

This command validates backend completion accounting and, for website delivery, the frontend completion receipt. It writes completion/migration-completion.json atomically.

It also refuses, on both delivery modes, unless the run produced its post-execution handoff and its execution log. A management run produces no website and used to require no handoff at all, which is how a backend-only migration could finalize with no account of itself anywhere in the project. The typed refusal reasons are:

ReasonMeaning
missing_execution_logno execution-log.md; route back to the import step that owes the chronology
missing_post_execution_handoffno execution/review/migration-handoff.json; generate it from the final outcome record
completion_report_missing_last_writethe completion report records no lastWriteAt, so nothing can prove the handoff postdates the import
post_execution_handoff_predates_last_writethe handoff was generated before the run's last write — it describes the plan, not the run

The last two compare against the completion report's own recorded last-write timestamp, never a file mtime: an mtime is refreshed by a copy and proves nothing. If validation fails, do not manufacture completion; route again to the module that owns the missing or stale artifact.

Entity-QA attestation gate

The same finalizer also refuses without a fresh, complete execution/qa/entity-qa-attestation.json that matches the current execution/completion-report.json digest and covers every mapped entity/subtype with matching counts (rp-execute-import/SKILL.md's "Entity-by-entity QA" section), including each mapped entity's archive.<entity> source-archive row when the report carries one. The refusal carries entityQa: { qaStatus, entities, recheck }:

qaStatusMeaningAction
missingno attestation file, it doesn't parse as JSON, its schema is invalid, there is no usable completion report to compare against, or the independent mapped-entity coverage source (mapping/mapping-plan.json or, for quick mode, quick-mode/plan.json) is unavailablerun the full entity-by-entity QA pass from rp-execute-import, then scripts/entity-qa-attestation.js
stalethe attestation's completionReportDigest does not match the report that is shipping — a later pass rewrote itrun the full entity-by-entity QA pass again, then the writer; the shipped prose treats any report change as reopening the whole review
failedthe attestation disagrees with the independent mapped-entity list or the report's own counts for one or more entities, named in entityQa.entitiesdispatch a fresh Developer for the listed entities, then run the full QA pass and the writer

A valid attestation proves import-row freshness and coverage only (scope: 'import-entity-coverage' in migration-completion.json) — never business setup or launch readiness, and never proof the QA pass itself happened rather than a copy of the report's own counts.

Site settings in the execution plan

When the setup plan has import-site-locale, show the owner what the first imported entity will do:

node skills/wix-replatform/scripts/site-regional.js preview <project>

It prints, from the source file alone (the site is read when setup runs), "Currency: set to … (from the store)", the same for time zone, language and locale, and the rule: a site created by this migration, or an existing site with no orders and no products, is set directly; one that holds an order or any product asks the owner first (one question, change or keep, raised by setup — never answered for the owner, one-click included). No sample-product classifier ships today. Nothing regional needs separate approval or a file bound into the approval.

Reporting coupons, currency and plan definitions

lib/coupon-currency-plan-report.js buildCouponCurrencyPlanReport renders the section and decides approvability the same way payments-report.js does: every line with its count including zero; per area written + excluded + skipped = discovered; exactly one currency outcome, and a mismatch or multi-currency history BLOCKS as a halt; the coupon-usage surface must be named. Two phrases are refused wherever they appear — "imported N coupons" (the honest headline is the redeemability breakdown) and "not supported" (zero plan rows is "none found", a measured fact).

Discovered-set status and safe refinalization

For every standard-mode finalization, build execution/discovered-set-status.json with node scripts/discovered-set-status.js build <projectDir> after the completion report, then regenerate the post-execution handoff, retain the current entity-QA attestation, and run node scripts/migration-completion.js <projectDir>. Missing or stale census approval, reconciliation or status is a hold; rebuild the owning artifact and obtain a fresh mapping review where required. The final receipt is version 2. Open discovered-set rows keep a complete backend at complete_with_warnings, and the handoff and receipt must agree.

On reviewOnly: true, refinalization uses the existing import outcomes. Make no Wix client, setup, codegen, import or website dispatch. Rebuild only the discovered-set status and post-execution handoff, then the aggregate completion; the writer archives the original version-1 receipt once. Any other route is held for the Import Operator. Manual work can be recorded only for a manual-open row, by the merchant or Cutover Operator through node scripts/discovered-set-status.js resolve <projectDir> --census-id <id> --by merchant|cutover-operator --evidence <short non-record evidence>. All other open outcomes require their existing mapping/import recovery path. Quick mode requires no discovered-set status.

Files

lib/payments-report.js

'use strict';

// The payments section of the plan report, and the accounting rules
// that decide whether it can be approved at all.
//
// Three properties this module exists to guarantee, each of which a hand-written report has got
// wrong before:
//
//   1. THE OUTCOMES PARTITION THE POPULATION. Every source order lands in exactly one bucket, and
//      the buckets sum to the denominator. A report whose numbers do not add up is not a report
//      with a rounding problem, it is a report that is hiding orders -- so a mismatch BLOCKS the
//      approval gate rather than printing a caveat.
//   2. ZERO IS A RESULT. Every outcome and every count is emitted even when it is zero. "0 refunds
//      found" is reviewable at a glance and is the cheapest place to catch a mis-mapped source
//      field; a missing line is indistinguishable from a line nobody computed.
//   3. CANDIDATE COVERAGE IS NOT WRITABLE COVERAGE. Many orders carry some
//      reference-shaped value while only a smaller share is currently defensible. Reporting the
//      first number as though it were the second overstates fidelity, so the two are separate
//      lines and the report refuses to collapse them.
//
// The denominator gets its own guard. A prior survey's narrower "card order" count must never
// stand in for the run's own reconciled read: the two can differ on a trading
// store, and the completeness claim is only as good as what it counted.

// The codes come from one frozen registry shared with the writers, not from a list kept here and
// not from scraping the writer source. A hand-maintained list drifted four times; the scraper that
// replaced it missed an outcome produced by a ternary, which is a green test over a real gap.
const registry = require('../../../lib/payments-outcome-registry.js');

const PAYMENT_OUTCOMES = registry.PAYMENT_OUTCOMES;
const ADDITIONAL_COUNTS = registry.REPORTABLE;

// False for records, and therefore prohibited: changing payment provider does not lose payment
// history, because these records are provider-independent.
// Mirrors invoice-discovery's STRATEGIES. A store-wide strategy, never a per-document action.
const INVOICE_STRATEGIES = ['none-found', 'number-only', 'wordpress-hosted', 'third-party-hosted', 'mixed'];

const PROHIBITED_PHRASES = ['lose your history', 'lose their history', 'loses your history'];

// A count is a non-negative integer. Coercing anything else let a report with `sourceOrders: -1`
// and `payment-written: -1` come back approvable -- the sum check passes and the numbers are
// nonsense, which is worse than a missing line.
function asCount(value, code, label, blocking) {
  if (value === undefined || value === null) return 0;
  const numeric = Number(value);
  if (!Number.isInteger(numeric) || numeric < 0) {
    if (blocking) {
      blocking.push({ code: code || 'invalid-count', detail: `${label} must be a non-negative integer, got ${JSON.stringify(value)}` });
    }
    return 0;
  }
  return numeric;
}

function buildPaymentsReport({
  denominator = {},
  outcomes = {},
  counts = {},
  referenceCoverage = {},
  providers = null,
  providerParity = {},
  invoiceStrategy = null,
  notes = [],
} = {}) {
  const blocking = [];
  const lines = [];

  const sourceOrders = asCount(denominator.sourceOrders, 'invalid-denominator', 'denominator.sourceOrders', blocking);
  const priorSurvey = denominator.priorSurveyCount === undefined || denominator.priorSurveyCount === null
    ? null
    : asCount(denominator.priorSurveyCount, 'invalid-count', 'denominator.priorSurveyCount', blocking);

  if (denominator.from !== 'run-reconciled-read') {
    blocking.push({
      code: 'denominator-not-from-reconciled-read',
      detail: 'the denominator must come from this run\'s own reconciled source reads, not from a prior survey or a remembered constant',
    });
  }
  // A stored count differing from a fresh one is NOT a defect. A store that is still trading
  // takes new orders continuously, so two counts taken hours apart legitimately differ
  // and no amount of reconciling makes them agree. Blocking on it would block every run on any
  // live store. It is reported as drift, for context, and the run's own read is authoritative.
  if (priorSurvey !== null && priorSurvey !== sourceOrders) {
    lines.push({
      code: 'denominator-drift',
      label: 'Change since the prior survey (expected on a trading store)',
      value: sourceOrders - priorSurvey,
    });
  }

  lines.push({ code: 'source-orders', label: 'Source orders discovered', value: sourceOrders });
  if (priorSurvey !== null) {
    lines.push({ code: 'prior-survey-orders', label: 'Prior survey count (not the denominator)', value: priorSurvey });
  }

  let outcomeTotal = 0;
  for (const code of PAYMENT_OUTCOMES) {
    const value = asCount(outcomes[code], 'invalid-count', `outcome ${code}`, blocking);
    outcomeTotal += value;
    lines.push({ code, label: code, value, partitioning: true });
  }

  // The buyer outcomes are a second partition. Optional as a whole (older runs report
  // none), but once any is present they must account for every source order, or the report is
  // hiding orders the same way a short payment partition would.
  const buyerCodes = Object.keys(outcomes).filter((code) => registry.BUYER_OUTCOMES.includes(code));
  if (buyerCodes.length > 0) {
    let buyerTotal = 0;
    for (const code of registry.BUYER_OUTCOMES) buyerTotal += asCount(outcomes[code], 'invalid-count', `outcome ${code}`, blocking);
    if (buyerTotal !== sourceOrders) {
      blocking.push({ code: 'buyer-outcome-sum-mismatch', detail: `the buyer outcomes sum to ${buyerTotal} but the run discovered ${sourceOrders} source orders` });
    }
  }
  const unknownOutcomes = Object.keys(outcomes).filter((code) => !PAYMENT_OUTCOMES.includes(code) && !registry.BUYER_OUTCOMES.includes(code));
  if (unknownOutcomes.length > 0) {
    blocking.push({
      code: 'unknown-payment-outcome',
      detail: `these outcomes are not declared as partitioning buckets, so the sum cannot be trusted: ${unknownOutcomes.join(', ')}`,
    });
  }
  if (outcomeTotal !== sourceOrders) {
    blocking.push({
      code: 'payment-outcome-sum-mismatch',
      detail: `the payment outcomes sum to ${outcomeTotal} but the run discovered ${sourceOrders} source orders; ${Math.abs(sourceOrders - outcomeTotal)} order(s) have no reported outcome`,
    });
  }
  lines.push({ code: 'payment-outcome-total', label: 'Payment outcomes total', value: outcomeTotal });

  // Both coverage numbers, always, and never one in place of the other.
  const candidates = asCount(referenceCoverage.candidates, 'invalid-count', 'referenceCoverage.candidates', blocking);
  const defensible = asCount(referenceCoverage.defensible, 'invalid-count', 'referenceCoverage.defensible', blocking);
  lines.push({ code: 'reference-candidates', label: 'Orders carrying a reference-shaped candidate', value: candidates });
  lines.push({ code: 'reference-defensible', label: 'Orders with a defensible provider reference', value: defensible });
  lines.push({ code: 'reference-not-defensible', label: 'Orders whose candidate could not be attributed', value: Math.max(candidates - defensible, 0) });
  if (defensible > candidates) {
    blocking.push({
      code: 'reference-coverage-impossible',
      detail: `defensible references (${defensible}) exceed candidates (${candidates}), so one of the two was miscomputed`,
    });
  }

  for (const code of ADDITIONAL_COUNTS) {
    // Buyer outcomes arrive under `outcomes` (they partition); everything else under `counts`.
    const source = registry.BUYER_OUTCOMES.includes(code) ? outcomes : counts;
    lines.push({ code, label: code, value: asCount(source[code], 'invalid-count', `count ${code}`, blocking) });
  }

  // Same provenance rule as the invoice strategy: an absent inventory is missing discovery, not a
  // store with no providers. Rendering "none found" for something nobody looked for is the exact
  // confusion this report exists to prevent.
  if (providers === null || providers === undefined) {
    blocking.push({
      code: 'provider-discovery-missing',
      detail: 'no provider inventory was supplied; a store with genuinely no providers must pass an empty array',
    });
  }
  const providerLines = (providers || []).map((provider) => ({
    id: provider.id,
    displayName: provider.displayName,
    enabled: Boolean(provider.enabled),
  }));
  for (const provider of providers || []) {
    for (const forbidden of ['secret', 'key', 'token', 'password', 'credential']) {
      for (const [field, value] of Object.entries(provider)) {
        if (field.toLowerCase().includes(forbidden) && value !== undefined && value !== null && value !== '') {
          blocking.push({
            code: 'provider-inventory-carries-credential-shaped-field',
            detail: `provider ${provider.id} exposes "${field}" in the report; the inventory carries identifier, display name and enabled state only`,
          });
        }
      }
    }
  }

  const parity = {
    country: providerParity.country || null,
    currency: providerParity.currency || null,
    supported: providerParity.supported || [],
    confidence: providerParity.providerIdentityExposed === true ? 'verified-provider-parity' : 'unverified-provider-parity',
  };

  // A missing strategy is missing DISCOVERY, not an empty result -- reporting it as "none-found"
  // makes "we looked and there is nothing" indistinguishable from "we never looked". And the
  // value must be one of the strategies, not an action: `reference-only` is what happens to one
  // document, and it passed silently as a store-wide strategy.
  const strategy = invoiceStrategy === null || invoiceStrategy === undefined ? null : String(invoiceStrategy);
  if (strategy === null) {
    blocking.push({ code: 'invoice-discovery-missing', detail: 'no invoice strategy was reported; discovery must run before the plan is approved' });
  } else if (!INVOICE_STRATEGIES.includes(strategy)) {
    blocking.push({ code: 'invalid-invoice-strategy', detail: `"${strategy}" is not an invoice strategy; expected one of ${INVOICE_STRATEGIES.join(', ')}` });
  }
  lines.push({ code: 'invoice-strategy', label: 'Invoice strategy', value: strategy === null ? 'not-reported' : strategy });

  const rendered = renderPaymentsReport({ lines, providers: providerLines, parity, notes });
  const lowered = rendered.toLowerCase();
  for (const phrase of PROHIBITED_PHRASES) {
    if (lowered.includes(phrase)) {
      blocking.push({
        code: 'prohibited-phrase',
        detail: `the report contains "${phrase}", which is false for payment records: they are provider-independent and survive a provider change`,
      });
    }
  }

  return { lines, providers: providerLines, parity, blocking, rendered, approvable: blocking.length === 0 };
}

function renderPaymentsReport({ lines, providers, parity, notes = [] }) {
  const out = ['Payments, refunds and invoice documents', ''];

  out.push('Source payment providers found:');
  if (providers.length === 0) {
    out.push('  none found');
  } else {
    for (const provider of providers) {
      out.push(`  ${provider.id} — ${provider.displayName} — ${provider.enabled ? 'enabled' : 'disabled'}`);
    }
  }
  out.push('  no credential value was read or retained');
  out.push('');

  out.push(`Provider availability for ${parity.country || 'unknown country'} / ${parity.currency || 'unknown currency'}: ${parity.confidence}`);
  if (parity.supported.length > 0) out.push(`  supported: ${parity.supported.join(', ')}`);
  out.push('');

  for (const line of lines) {
    out.push(`  ${line.label}: ${line.value}`);
  }

  if (notes.length > 0) {
    out.push('');
    for (const note of notes) out.push(`  note: ${note}`);
  }
  return out.join('\n');
}

module.exports = {
  PAYMENT_OUTCOMES,
  ADDITIONAL_COUNTS,
  PROHIBITED_PHRASES,
  INVOICE_STRATEGIES,
  buildPaymentsReport,
  renderPaymentsReport,
};

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

What our scanner noticed· Low risk

  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Wix
Publisher
Wix (Official)
Source
wix/skills · skills/wix-replatform/resources/rp-execution-policy · commit d168bc2
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Wix.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download rp-execution-policy.zip