[object Object]

← back to Dw Rotation Activator

Enable verified one-day activation catch-up within daily allowance

9720a3e91e6e77e6059eaa39bbcd747ff174a0c2 · 2026-09-09 11:35:52 -0700 · Steve Abrams

Files touched

Diff

commit 9720a3e91e6e77e6059eaa39bbcd747ff174a0c2
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 11:35:52 2026 -0700

    Enable verified one-day activation catch-up within daily allowance
---
 config/daily-catchup-2026-09-09.json       | 11 ++++
 lib/activation-allowance.js                | 30 +++++++++
 rotate-activate.js                         | 23 +++++--
 test/activation-allowance.test.js          | 45 ++++++++++++++
 verification/e2e-proof-before-TK11326.json | 82 +++++++++++++++++++++++++
 verification/e2e-proof.json                | 98 +++++++++++-------------------
 6 files changed, 220 insertions(+), 69 deletions(-)

diff --git a/config/daily-catchup-2026-09-09.json b/config/daily-catchup-2026-09-09.json
new file mode 100644
index 0000000..e4a3033
--- /dev/null
+++ b/config/daily-catchup-2026-09-09.json
@@ -0,0 +1,11 @@
+{
+  "schema_version": 1,
+  "ticket": "TK-11326",
+  "date": "2026-09-09",
+  "approved_by": "Steve",
+  "authorization": "start loading for the day now",
+  "starting_daily_used": 37,
+  "max_additional": 462,
+  "max_daily": 500,
+  "scope": "One-time catch-up for this date. Skip hourly pacing only when explicitly invoked; preserve the fixed vendor authorization, all other gates, zero Gemini and daily500."
+}
diff --git a/lib/activation-allowance.js b/lib/activation-allowance.js
new file mode 100644
index 0000000..e8ef87b
--- /dev/null
+++ b/lib/activation-allowance.js
@@ -0,0 +1,30 @@
+'use strict';
+
+function assertDailyCatchup(date, authorization, now = new Date()) {
+  if (!/^\d{4}-\d{2}-\d{2}$/.test(date || '') || date !== now.toISOString().slice(0, 10) ||
+      authorization?.schema_version !== 1 || authorization.ticket !== 'TK-11326' ||
+      authorization.date !== date || authorization.approved_by !== 'Steve' ||
+      authorization.authorization !== 'start loading for the day now' ||
+      authorization.starting_daily_used !== 37 || authorization.max_additional !== 462 || authorization.max_daily !== 500)
+    throw new Error('Daily catch-up authorization is invalid or expired');
+}
+
+function activationAllowance({ dailyUsed, hourlyUsed, requested = null, dailyCap = 500, hourlyCap = 21,
+  catchupDate = null, authorization = null, now = new Date() }) {
+  if (![dailyUsed, hourlyUsed].every(n => Number.isSafeInteger(n) && n >= 0) ||
+      !Number.isSafeInteger(dailyCap) || dailyCap < 1 || dailyCap > 500 ||
+      !Number.isSafeInteger(hourlyCap) || hourlyCap < 1 || hourlyCap > 21 ||
+      (requested !== null && (!Number.isSafeInteger(requested) || requested < 1)))
+    throw new Error('Invalid activation limits; refusing an unbounded run');
+  const dailyRemaining = Math.max(0, dailyCap - dailyUsed);
+  const hourlyRemaining = Math.max(0, hourlyCap - hourlyUsed);
+  if (catchupDate !== null) {
+    assertDailyCatchup(catchupDate, authorization, now);
+    if (dailyUsed < authorization.starting_daily_used) throw new Error('Daily ledger is below approved catch-up baseline');
+    const authorizedRemaining = Math.max(0, authorization.starting_daily_used + authorization.max_additional - dailyUsed);
+    return { cap: Math.min(requested ?? dailyRemaining, dailyRemaining, authorizedRemaining), mode: 'daily-catchup', dailyRemaining, hourlyRemaining };
+  }
+  return { cap: Math.min(requested ?? dailyRemaining, dailyRemaining, hourlyRemaining), mode: 'hourly', dailyRemaining, hourlyRemaining };
+}
+
+module.exports = { activationAllowance, assertDailyCatchup };
diff --git a/rotate-activate.js b/rotate-activate.js
index 5d3d33f..a3a4443 100644
--- a/rotate-activate.js
+++ b/rotate-activate.js
@@ -51,6 +51,7 @@ const { canonicalProduct, widthFromLive, completeLiveProduct } = require('./lib/
 const { buildRepairReport } = require('./lib/readiness-repairs.js');
 const { confirmedActivation } = require('./lib/activation-result.js');
 const { countHourlyActivations } = require('./lib/hourly-activation-count.js');
+const { activationAllowance, assertDailyCatchup } = require('./lib/activation-allowance.js');
 const { reusedMfrSet, stagingColorFor } = require('./lib/mfr-gate-resolve.js');
 // Canonical showroom-vendor primitive (list + logic live in fix-live-board/config). Showroom-only
 // vendors are addressable-not-discoverable: never rotate them into the New Arrivals discoverability
@@ -66,6 +67,9 @@ const val = (n, d) => { const i = args.indexOf(n); return i >= 0 ? args[i + 1] :
 const COMMIT = flag('--commit');
 const DRY = flag('--dry-run') || !COMMIT;   // safe-by-default: no --commit ⇒ dry-run
 const CLI_MAX = val('--max', null);
+const CATCHUP_DATE = flag('--daily-catchup') ? val('--daily-catchup', '') : null;
+const CATCHUP_AUTHORIZATION = CATCHUP_DATE !== null ? require('./config/daily-catchup-2026-09-09.json') : null;
+if (CATCHUP_DATE !== null) assertDailyCatchup(CATCHUP_DATE, CATCHUP_AUTHORIZATION);
 
 const STORE = 'designer-laboratory-sandbox.myshopify.com';
 const API = '2024-10';
@@ -75,6 +79,7 @@ const AUDIT = path.join(OUTDIR, `rotation-activations-${TODAY}.jsonl`);
 const REPAIR_REPORT = path.join(OUTDIR, 'current-readiness-repairs.json');
 const runDecisions = [];
 function recordDecision(record) {
+  if (CATCHUP_DATE !== null) record.pacing_authorization = { mode: 'daily-catchup', ticket: 'TK-11326', date: CATCHUP_DATE };
   fs.appendFileSync(AUDIT, JSON.stringify(record) + '\n');
   runDecisions.push(record);
 }
@@ -279,16 +284,18 @@ function leakGuard(title, vendor) {
   if (DRY) {
     cap = CLI_MAX != null ? parseInt(CLI_MAX, 10) : 50;
   } else {
-    const remaining = Math.max(0, DAILY_ACTIVATION_CAP - used);
-    const want = CLI_MAX != null ? parseInt(CLI_MAX, 10) : remaining;
     const hourlyUsed = countHourlyActivations(fs.existsSync(AUDIT) ? fs.readFileSync(AUDIT, 'utf8') : '');
-    const hourlyRemaining = Math.max(0, HOURLY_ACTIVATION_CAP - hourlyUsed);
-    cap = Math.min(want, remaining, hourlyRemaining);
+    const allowance = activationAllowance({ dailyUsed: used, hourlyUsed,
+      requested: CLI_MAX !== null ? Number(CLI_MAX) : null, dailyCap: DAILY_ACTIVATION_CAP,
+      hourlyCap: HOURLY_ACTIVATION_CAP, catchupDate: CATCHUP_DATE, authorization: CATCHUP_AUTHORIZATION });
+    cap = allowance.cap;
     if (cap <= 0) {
       console.log(`[budget] activation allowance reached (daily ${used}/${DAILY_ACTIVATION_CAP}, hourly ${hourlyUsed}/${HOURLY_ACTIVATION_CAP}). Nothing to do this run.`);
       return;
     }
-    console.log(`[budget] hourly activation lane: used ${hourlyUsed}/${HOURLY_ACTIVATION_CAP}; manual and scheduled runs share this allowance.`);
+    console.log(allowance.mode === 'daily-catchup'
+      ? `[budget] one-time daily catch-up: ${CATCHUP_DATE} TK-11326; hourly pacing skipped, daily cap remains ${DAILY_ACTIVATION_CAP}.`
+      : `[budget] hourly activation lane: used ${hourlyUsed}/${HOURLY_ACTIVATION_CAP}; manual and scheduled runs share this allowance.`);
     console.log(`[budget] activation lane: used ${used}/${DAILY_ACTIVATION_CAP} today → activating up to ${cap} this run.`);
   }
   console.log(`mode=${DRY ? 'DRY-RUN' : 'COMMIT'}  cap_this_run=${cap}\n`);
@@ -306,6 +313,7 @@ function leakGuard(title, vendor) {
     if (r.status !== 200 || r.json?.errors?.length || !Array.isArray(r.json?.data?.nodes))
       throw new Error('Shopify product batch read failed; activation stopped');
     for (const initialNode of r.json.data.nodes) {
+      if (CATCHUP_DATE !== null) assertDailyCatchup(CATCHUP_DATE, CATCHUP_AUTHORIZATION);
       if (activated >= cap) break;
       if (!initialNode) continue;
       let n = initialNode;
@@ -407,6 +415,11 @@ function leakGuard(title, vendor) {
       }
 
       // COMMIT: flip → ACTIVE, add 'New Arrival', publish to channels (ex-Google).
+      if (CATCHUP_DATE !== null) assertDailyCatchup(CATCHUP_DATE, CATCHUP_AUTHORIZATION);
+      const activeCeiling = CATCHUP_DATE !== null
+        ? Math.min(DAILY_ACTIVATION_CAP, CATCHUP_AUTHORIZATION.starting_daily_used + CATCHUP_AUTHORIZATION.max_additional)
+        : DAILY_ACTIVATION_CAP;
+      if (ledgerUsed() >= activeCeiling) throw new Error('Daily activation allowance reached during run');
       const ar = await gqlRetry(ACTIVATE, { id: n.id });
       const aue = ar.json?.data?.productUpdate?.userErrors;
       if (!confirmedActivation(ar, n.id)) {
diff --git a/test/activation-allowance.test.js b/test/activation-allowance.test.js
new file mode 100644
index 0000000..98d7489
--- /dev/null
+++ b/test/activation-allowance.test.js
@@ -0,0 +1,45 @@
+'use strict';
+const test = require('node:test');
+const assert = require('node:assert/strict');
+const { activationAllowance, assertDailyCatchup } = require('../lib/activation-allowance');
+const authorization = require('../config/daily-catchup-2026-09-09.json');
+const base = { dailyUsed: 37, hourlyUsed: 21, requested: 463, now: new Date('2026-09-09T18:20:00Z') };
+const catchup = extra => ({ ...base, catchupDate: '2026-09-09', authorization, ...extra });
+test('normal scheduled runs retain the21/hour cap', () => {
+  assert.equal(activationAllowance(base).cap, 0);
+  assert.equal(activationAllowance({ ...base, hourlyUsed: 0 }).cap, 21);
+  assert.equal(activationAllowance({ ...base, hourlyUsed: 20 }).cap, 1);
+});
+test('explicit current-day catch-up can use remaining daily allowance', () => {
+  const result = activationAllowance(catchup());
+  assert.equal(result.cap, 462); assert.equal(result.mode, 'daily-catchup');
+  assert.equal(activationAllowance(catchup({ requested: 1 })).cap, 1);
+});
+test('retries cannot exceed500 total activations', () => {
+  assert.equal(activationAllowance(catchup({ dailyUsed: 498 })).cap, 1);
+  assert.equal(activationAllowance(catchup({ dailyUsed: 499 })).cap, 0);
+  assert.equal(activationAllowance(catchup({ dailyUsed: 500 })).cap, 0);
+  assert.equal(activationAllowance(catchup({ dailyUsed: 501 })).cap, 0);
+});
+test('having an authorization file does not enable catch-up for a scheduled invocation', () => {
+  assert.equal(activationAllowance({ ...base, authorization }).cap, 0);
+});
+test('missing, altered, future and expired authorizations fail closed', () => {
+  for (const change of [{ authorization: null }, { authorization: { ...authorization, max_daily: 501 } },
+    { authorization: { ...authorization, max_additional: 463 } },
+    { authorization: { ...authorization, approved_by: 'someone else' } }, { catchupDate: '2026-09-10' },
+    { now: new Date('2026-09-10T00:00:00Z') }, { now: new Date('2026-09-08T23:59:59Z') }])
+    assert.throws(() => activationAllowance(catchup(change)));
+});
+test('the publication-loop date check stops a run crossing midnight', () => {
+  assert.doesNotThrow(() => assertDailyCatchup('2026-09-09', authorization, new Date('2026-09-09T23:59:59Z')));
+  assert.throws(() => assertDailyCatchup('2026-09-09', authorization, new Date('2026-09-10T00:00:00Z')));
+});
+test('a reset ledger cannot grant the original allowance again', () => {
+  assert.throws(() => activationAllowance(catchup({ dailyUsed: 0 })));
+});
+test('malformed and inflated limits cannot disable either cap', () => {
+  for (const change of [{ requested: NaN }, { requested: -1 }, { requested: 0 }, { requested: Infinity },
+    { dailyCap: 501 }, { hourlyCap: 22 }, { dailyUsed: NaN }, { hourlyUsed: -1 }])
+    assert.throws(() => activationAllowance(catchup(change)));
+});
diff --git a/verification/e2e-proof-before-TK11326.json b/verification/e2e-proof-before-TK11326.json
new file mode 100644
index 0000000..b983a3a
--- /dev/null
+++ b/verification/e2e-proof-before-TK11326.json
@@ -0,0 +1,82 @@
+{
+  "ticket": "TK-11321",
+  "timestamp": "2026-09-09T18:15:15.306027+00:00",
+  "risk_tier": "R4",
+  "authorization": "Steve: skip gemeni and go",
+  "verdict": "PASS: owner-authorized no-Gemini rollout, live batch and hourly cap verified",
+  "runtime": "dw-rotation-activator on live designer-laboratory-sandbox Shopify store",
+  "reviewed_commit": "3d2ba05",
+  "scope": {
+    "captured_outage_held": 504,
+    "historical_blocks_preserved_in_cohort": 21,
+    "eligible_before_fresh_checks": 483,
+    "gemini_requests_permitted": 0
+  },
+  "checks": [
+    {
+      "check": "Installed regression tests",
+      "verdict": "PASS",
+      "tests": 46
+    },
+    {
+      "check": "Live dry run",
+      "verdict": "PASS",
+      "scanned": 287,
+      "would_activate": 21,
+      "blocked": 21,
+      "gemini_calls": 0
+    },
+    {
+      "check": "One-product publication and independent verification",
+      "verdict": "PASS",
+      "product": "DWDX-220551",
+      "ledger_before": 16,
+      "ledger_after": 17,
+      "controls_still_draft": 23,
+      "storefront": [
+        {
+          "url": "https://www.designerwallcoverings.com/products/august-lemongrass-dwdx-220551",
+          "status": 200,
+          "product_id_found": true
+        }
+      ]
+    },
+    {
+      "check": "Exact before-file rollback rehearsal",
+      "verdict": "PASS",
+      "evidence": "/Users/macstudio3/Projects/dw-gemini-skip-TK11321/verification/tk11321/rollback-rehearsal.json"
+    },
+    {
+      "check": "Honest skipped-review persistence",
+      "verdict": "PASS",
+      "value": "SKIPPED_BY_OWNER, TK-11321",
+      "review_performed": false
+    },
+    {
+      "check": "Full hourly batch independently verified",
+      "verdict": "PASS",
+      "new_activations": 21,
+      "ledger_before": 16,
+      "ledger_after": 37,
+      "controls_still_draft": 23,
+      "evidence": "/Users/macstudio3/Projects/dw-gemini-skip-TK11321/verification/tk11321/live-verified-21.json"
+    },
+    {
+      "check": "Actual scheduled entrypoint stops at21/hour",
+      "verdict": "PASS",
+      "extra_activations": 0,
+      "exit_code": 0,
+      "evidence": "/Users/macstudio3/Projects/dw-gemini-skip-TK11321/verification/tk11321/hourly-cap-verified.json"
+    }
+  ],
+  "constraints": "21/hour shared by manual and scheduled runs; 500/day; existing Google exclusion and other readiness/vendor/reintroduction guards retained. No binding-text, AI-generator, billing or credentials changes.",
+  "evidence_directory": "/Users/macstudio3/Projects/dw-gemini-skip-TK11321/verification/tk11321",
+  "rollback": "Restore exact prefiles under existing job lock; no automatic product rollback.",
+  "residual": "Owner-authorized products have not received automated image-review clearance. Changed identity/image URL or known blocks remain held.",
+  "production_commit": "b03419a",
+  "newly_activated": 21,
+  "daily_ledger": 37,
+  "gemini_requests": 0,
+  "lock_released_at": "2026-09-09T18:12:42.131826+00:00",
+  "schedule_loaded": true
+}
diff --git a/verification/e2e-proof.json b/verification/e2e-proof.json
index b983a3a..d71aa77 100644
--- a/verification/e2e-proof.json
+++ b/verification/e2e-proof.json
@@ -1,82 +1,52 @@
 {
-  "ticket": "TK-11321",
-  "timestamp": "2026-09-09T18:15:15.306027+00:00",
+  "ticket": "TK-11326",
+  "timestamp": "2026-09-09T18:35:51.996567+00:00",
   "risk_tier": "R4",
-  "authorization": "Steve: skip gemeni and go",
-  "verdict": "PASS: owner-authorized no-Gemini rollout, live batch and hourly cap verified",
-  "runtime": "dw-rotation-activator on live designer-laboratory-sandbox Shopify store",
-  "reviewed_commit": "3d2ba05",
-  "scope": {
-    "captured_outage_held": 504,
-    "historical_blocks_preserved_in_cohort": 21,
-    "eligible_before_fresh_checks": 483,
-    "gemini_requests_permitted": 0
+  "intent": "Load today\u2019s remaining eligible DW drafts now",
+  "authorization": "Steve: start loading for the day now",
+  "verdict": "PASS: installed code and first-product canary; remaining batch pending",
+  "environment": "Production DW Shopify activation pipeline",
+  "reviewed_commit": "7ec14eb",
+  "baseline": {
+    "daily_used": 37,
+    "eligible": 462,
+    "controls": 23
   },
+  "commands": [
+    "node --test",
+    "node rotate-activate.js --daily-catchup 2026-09-09 --max 1 --commit",
+    "node scripts/verify-daily-catchup-independent.js after 1"
+  ],
   "checks": [
     {
       "check": "Installed regression tests",
       "verdict": "PASS",
-      "tests": 46
-    },
-    {
-      "check": "Live dry run",
-      "verdict": "PASS",
-      "scanned": 287,
-      "would_activate": 21,
-      "blocked": 21,
-      "gemini_calls": 0
-    },
-    {
-      "check": "One-product publication and independent verification",
-      "verdict": "PASS",
-      "product": "DWDX-220551",
-      "ledger_before": 16,
-      "ledger_after": 17,
-      "controls_still_draft": 23,
-      "storefront": [
-        {
-          "url": "https://www.designerwallcoverings.com/products/august-lemongrass-dwdx-220551",
-          "status": 200,
-          "product_id_found": true
-        }
-      ]
-    },
-    {
-      "check": "Exact before-file rollback rehearsal",
-      "verdict": "PASS",
-      "evidence": "/Users/macstudio3/Projects/dw-gemini-skip-TK11321/verification/tk11321/rollback-rehearsal.json"
+      "tests": 54
     },
     {
-      "check": "Honest skipped-review persistence",
+      "check": "Independent live canary, persisted state, publication and public PDP",
       "verdict": "PASS",
-      "value": "SKIPPED_BY_OWNER, TK-11321",
-      "review_performed": false
+      "evidence": "/Users/macstudio3/Projects/dw-daily-catchup-20260909/verification/tk11326/live-verified-1.json"
     },
     {
-      "check": "Full hourly batch independently verified",
+      "check": "Exact-file rollback rehearsal",
       "verdict": "PASS",
-      "new_activations": 21,
-      "ledger_before": 16,
-      "ledger_after": 37,
-      "controls_still_draft": 23,
-      "evidence": "/Users/macstudio3/Projects/dw-gemini-skip-TK11321/verification/tk11321/live-verified-21.json"
+      "evidence": "/Users/macstudio3/Projects/dw-daily-catchup-20260909/verification/tk11326/rollback-rehearsal.json"
     },
     {
-      "check": "Actual scheduled entrypoint stops at21/hour",
-      "verdict": "PASS",
-      "extra_activations": 0,
-      "exit_code": 0,
-      "evidence": "/Users/macstudio3/Projects/dw-gemini-skip-TK11321/verification/tk11321/hourly-cap-verified.json"
+      "check": "Remaining daily batch",
+      "verdict": "PENDING"
     }
   ],
-  "constraints": "21/hour shared by manual and scheduled runs; 500/day; existing Google exclusion and other readiness/vendor/reintroduction guards retained. No binding-text, AI-generator, billing or credentials changes.",
-  "evidence_directory": "/Users/macstudio3/Projects/dw-gemini-skip-TK11321/verification/tk11321",
-  "rollback": "Restore exact prefiles under existing job lock; no automatic product rollback.",
-  "residual": "Owner-authorized products have not received automated image-review clearance. Changed identity/image URL or known blocks remain held.",
-  "production_commit": "b03419a",
-  "newly_activated": 21,
-  "daily_ledger": 37,
-  "gemini_requests": 0,
-  "lock_released_at": "2026-09-09T18:12:42.131826+00:00",
-  "schedule_loaded": true
+  "constraints": {
+    "daily_cap": 500,
+    "catchup_ceiling": 499,
+    "additional_max": 462,
+    "date": "2026-09-09",
+    "default_hourly_cap": 21,
+    "gemini_requests": 0
+  },
+  "cleanup": "Owned lock retained until batch verification",
+  "rollback": "Source preimages captured and restoration rehearsed; no automatic product rollback",
+  "evidence_directory": "/Users/macstudio3/Projects/dw-daily-catchup-20260909/verification/tk11326"
 }

← ea60cf7 Keep released runtime worker locks out of repository snapsho  ·  back to Dw Rotation Activator  ·  Require confirmed Online Store availability before counting 0add27a →