← back to Dw Gemini Skip TK11321

lib/owner-review-policy.js

66 lines

'use strict';

// Steve's TK-11321 instruction skips Gemini for a fixed vendor-product cohort.
// This is authorization to skip a review, not a settlement clearance.
// This module performs no network requests and cannot edit binding rules.
const MODE = 'owner-authorized-cohort-no-gemini';
const OUTAGE_REASONS = new Set(['vision-http-429', 'vision-vision-http-429', 'vision-rate-limit-cooldown']);
const OWNER_REASON = 'owner-authorized-no-gemini:TK-11321';
const held = reason => ({ verdict: 'HELD', tier: 'owner-review-policy', reason, cost: 0, review_performed: false });

class OwnerReviewPolicy {
  constructor(policy) {
    if (policy?.schema_version !== 1 || policy.mode !== MODE || policy.ticket !== 'TK-11321' ||
        policy.approved_by !== 'Steve' || policy.authorization !== 'skip gemeni and go' ||
        !Array.isArray(policy.entries) || !Array.isArray(policy.historical_blocked_ids))
      throw new Error('Invalid owner review policy; refusing activation');
    this.entries = new Map();
    for (const entry of policy.entries) {
      const fields = ['shopify_id', 'dw_sku', 'queue_vendor', 'live_vendor', 'title', 'manufacturer_code', 'primary_image_url'];
      if (fields.some(key => typeof entry[key] !== 'string' || !entry[key].trim()) || this.entries.has(entry.shopify_id))
        throw new Error('Incomplete or duplicate owner authorization entry');
      this.entries.set(entry.shopify_id, entry);
    }
    this.blocked = new Set(policy.historical_blocked_ids);
    this.ticket = policy.ticket;
  }

  evaluate({ product, dwSku, vendor, manufacturerCode, plainTexture, lockOk }) {
    if (this.blocked.has(product.id)) return { ...held('historical-settlement-block'), verdict: 'BLOCK' };
    const raw = (product.metafields?.nodes || []).find(m => m.namespace === 'internal' && m.key === 'settlement_hold')?.value;
    if (raw) {
      let previous;
      try { previous = JSON.parse(raw); } catch { return held('unreadable-existing-settlement-status'); }
      if (previous.verdict === 'BLOCK') return { ...held('existing-settlement-block'), verdict: 'BLOCK' };
      const outage = previous.by === 'dw-rotation-activator' && previous.verdict === 'HELD' && OUTAGE_REASONS.has(previous.reason);
      const sameOwner = previous.by === 'dw-rotation-activator' && previous.verdict === 'SKIPPED_BY_OWNER' && previous.reason === OWNER_REASON;
      if (!outage && !sameOwner) return held('existing-review-hold-requires-resolution');
    }
    if (plainTexture) return { verdict: 'PASS', tier: 'auto-pass-texture', reason: 'solid-texture-no-motif', cost: 0, review_performed: false };
    if (!lockOk) return held('settlement-lock-failed');
    const approved = this.entries.get(product.id);
    if (!approved) return held('outside-owner-authorized-cohort');
    const observed = { dw_sku: dwSku, queue_vendor: vendor, live_vendor: product.vendor,
      title: product.title, manufacturer_code: manufacturerCode,
      primary_image_url: product.images?.nodes?.[0]?.url || '' };
    const changed = Object.keys(observed).filter(key => observed[key] !== approved[key]);
    if (changed.length) return held(`owner-authorization-drift:${changed.join(',')}`);
    if (!(product.variants?.nodes || []).some(v => v.sku === dwSku)) return held('owner-authorization-live-sku-mismatch');
    return { verdict: 'SKIPPED_BY_OWNER', tier: 'owner-authorized-vendor', reason: OWNER_REASON,
      cost: 0, review_performed: false, authorization: { ticket: this.ticket, approved_by: 'Steve', instruction: 'skip gemeni and go' } };
  }
}

function permitsActivation(result) {
  return result?.verdict === 'PASS' || (result?.verdict === 'SKIPPED_BY_OWNER' &&
    result.reason === OWNER_REASON && result.authorization?.ticket === 'TK-11321' &&
    result.authorization?.approved_by === 'Steve' && result.review_performed === false);
}

function confirmedReviewRecord(response) {
  return response?.status === 200 && !response.json?.errors?.length &&
    Array.isArray(response.json?.data?.metafieldsSet?.userErrors) && response.json.data.metafieldsSet.userErrors.length === 0;
}

module.exports = { OwnerReviewPolicy, permitsActivation, confirmedReviewRecord, OWNER_REASON };