[object Object]

← back to Adsense Fleet Viewer

Verify AdSense monitor parsing and stage regression fixes

db060c33644ac90c3c586650edc62b77624afb34 · 2026-09-10 01:02:02 -0700 · Steve Abrams

Files touched

Diff

commit db060c33644ac90c3c586650edc62b77624afb34
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 01:02:02 2026 -0700

    Verify AdSense monitor parsing and stage regression fixes
---
 verification/TK-11352-health/README.md             |  11 ++
 verification/TK-11352-health/baseline-results.json |  55 +++++++
 .../TK-11352-health/candidate-results.json         | 165 +++++++++++++++++++++
 verification/TK-11352-health/console-page1.json    |   9 ++
 verification/TK-11352-health/e2e-proof.json        |  20 +++
 verification/TK-11352-health/fixture-check.cjs     |  47 ++++++
 verification/TK-11352-health/status-before.mjs     |  61 ++++++++
 verification/TK-11352-health/status-candidate.mjs  |  65 ++++++++
 .../TK-11352-health/status-candidate.patch         |  49 ++++++
 9 files changed, 482 insertions(+)

diff --git a/verification/TK-11352-health/README.md b/verification/TK-11352-health/README.md
new file mode 100644
index 0000000..72df836
--- /dev/null
+++ b/verification/TK-11352-health/README.md
@@ -0,0 +1,11 @@
+# TK-11352 supporting monitor verification
+
+Status: candidate patch verified in isolation; shared-source adoption, complete live console inventory, and serving verification remain open.
+
+The captured 50-row Google console response reproduced two defects in the current manager: the evaluator response envelope was not unwrapped, and the total-count regex consumed the three-page count instead of the 124-site count. A control also proved completeness was computed before UNKNOWN inventory rows were appended.
+
+The candidate unwraps responses, checks response target/URL, reads the row-range total, exposes observed_count separately, and computes completeness after inventory reconciliation. Missing totals remain unknown. Six negative/partial scenarios and a synthetic 124-site complete scenario pass. The synthetic scenario is test input, not real account evidence. No pagination implementation is included.
+
+Run: `node verification/TK-11352-health/fixture-check.cjs` from this project. The harness uses the local monetize inventory and saved console fixture, intercepting ALL subprocess execution and shared-state writes in memory. It writes only a result file under /tmp. Compare baseline-results.json and candidate-results.json. The candidate patch is for the source owner's review and has not been applied by this session.
+
+Browser-owner/full-console requests: M-02551, M-02553. Candidate handoffs: M-02555 (codex), M-02556 (adsense-manager). Ticket TK-11352 retains overall remediation ownership. No production/account changes, ad clicks, notifications, browser navigation, or deployments were performed.
diff --git a/verification/TK-11352-health/baseline-results.json b/verification/TK-11352-health/baseline-results.json
new file mode 100644
index 0000000..f9f19e7
--- /dev/null
+++ b/verification/TK-11352-health/baseline-results.json
@@ -0,0 +1,55 @@
+{
+  "timestamp": "2026-09-10T07:59:12.574Z",
+  "source_path": "/Users/macstudio3/.claude/skills/adsense-manager/scripts/status.mjs",
+  "source_sha256": "5a4429a47fa20ed70a38d0c854242afac55749593ebd5d37178dc76f04e0e295",
+  "fixture": "/tmp/TK-11350-manager-console-page1.json",
+  "mode": "in-memory subprocess and write interception; no browser/network/shared-state writes",
+  "results": [
+    {
+      "scenario": "actual-openclaw-envelope",
+      "extracted_rows": 50,
+      "extracted_total": 3,
+      "expected_total": 124,
+      "output_source": "unavailable",
+      "output_complete": false,
+      "output_count": 35,
+      "output_expected_count": 35,
+      "output_tally": {
+        "UNKNOWN": 35
+      },
+      "calls": [
+        "openclaw browser status --json 2>/dev/null",
+        "openclaw browser navigate \"https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list\" --target-id 43668A82713374FB7A21DB79B043A367 >/dev/null 2>&1",
+        "sleep 3",
+        "openclaw browser evaluate --target-id 43668A82713374FB7A21DB79B043A367"
+      ],
+      "writes_intercepted": [
+        "/Users/macstudio3/.claude/skills/adsense-manager/data/status-latest.json"
+      ]
+    },
+    {
+      "scenario": "unwrapped-control",
+      "extracted_rows": 50,
+      "extracted_total": 3,
+      "expected_total": 124,
+      "output_source": "openclaw-console",
+      "output_complete": true,
+      "output_count": 73,
+      "output_expected_count": 3,
+      "output_tally": {
+        "Getting ready": 47,
+        "UNKNOWN": 23,
+        "Requires review": 3
+      },
+      "calls": [
+        "openclaw browser status --json 2>/dev/null",
+        "openclaw browser navigate \"https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list\" --target-id 43668A82713374FB7A21DB79B043A367 >/dev/null 2>&1",
+        "sleep 3",
+        "openclaw browser evaluate --target-id 43668A82713374FB7A21DB79B043A367"
+      ],
+      "writes_intercepted": [
+        "/Users/macstudio3/.claude/skills/adsense-manager/data/status-latest.json"
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/verification/TK-11352-health/candidate-results.json b/verification/TK-11352-health/candidate-results.json
new file mode 100644
index 0000000..7ffab3e
--- /dev/null
+++ b/verification/TK-11352-health/candidate-results.json
@@ -0,0 +1,165 @@
+{
+  "assertions": "PASS: six partial/invalid scenarios plus one synthetic complete inventory",
+  "timestamp": "2026-09-10T08:01:40.669Z",
+  "source_path": "/tmp/TK-11352-status-candidate.mjs",
+  "source_sha256": "f48bf95fa6812c5e9e570edb956dfcdc7eca7d6545086e67784248eea9133df7",
+  "fixture": "/tmp/TK-11350-manager-console-page1.json",
+  "mode": "in-memory subprocess and write interception; no browser/network/shared-state writes",
+  "results": [
+    {
+      "scenario": "actual-openclaw-envelope",
+      "extracted_rows": 50,
+      "extracted_total": 124,
+      "expected_total": 124,
+      "output_source": "openclaw-console",
+      "output_complete": false,
+      "output_count": 73,
+      "output_expected_count": 124,
+      "output_tally": {
+        "Getting ready": 47,
+        "UNKNOWN": 23,
+        "Requires review": 3
+      },
+      "calls": [
+        "openclaw browser status --json 2>/dev/null",
+        "openclaw browser navigate \"https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list\" --target-id 43668A82713374FB7A21DB79B043A367 >/dev/null 2>&1",
+        "sleep 3",
+        "openclaw browser evaluate --target-id 43668A82713374FB7A21DB79B043A367"
+      ],
+      "writes_intercepted": [
+        "/Users/macstudio3/.claude/skills/adsense-manager/data/status-latest.json"
+      ]
+    },
+    {
+      "scenario": "unwrapped-control",
+      "extracted_rows": 50,
+      "extracted_total": 124,
+      "expected_total": 124,
+      "output_source": "openclaw-console",
+      "output_complete": false,
+      "output_count": 73,
+      "output_expected_count": 124,
+      "output_tally": {
+        "Getting ready": 47,
+        "UNKNOWN": 23,
+        "Requires review": 3
+      },
+      "calls": [
+        "openclaw browser status --json 2>/dev/null",
+        "openclaw browser navigate \"https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list\" --target-id 43668A82713374FB7A21DB79B043A367 >/dev/null 2>&1",
+        "sleep 3",
+        "openclaw browser evaluate --target-id 43668A82713374FB7A21DB79B043A367"
+      ],
+      "writes_intercepted": [
+        "/Users/macstudio3/.claude/skills/adsense-manager/data/status-latest.json"
+      ]
+    },
+    {
+      "scenario": "wrong-target",
+      "extracted_rows": 50,
+      "extracted_total": 124,
+      "expected_total": 124,
+      "output_source": "unavailable",
+      "output_complete": false,
+      "output_count": 35,
+      "output_expected_count": null,
+      "output_tally": {
+        "UNKNOWN": 35
+      },
+      "calls": [
+        "openclaw browser status --json 2>/dev/null",
+        "openclaw browser navigate \"https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list\" --target-id 43668A82713374FB7A21DB79B043A367 >/dev/null 2>&1",
+        "sleep 3",
+        "openclaw browser evaluate --target-id 43668A82713374FB7A21DB79B043A367"
+      ],
+      "writes_intercepted": [
+        "/Users/macstudio3/.claude/skills/adsense-manager/data/status-latest.json"
+      ]
+    },
+    {
+      "scenario": "wrong-url",
+      "extracted_rows": 50,
+      "extracted_total": 124,
+      "expected_total": 124,
+      "output_source": "unavailable",
+      "output_complete": false,
+      "output_count": 35,
+      "output_expected_count": null,
+      "output_tally": {
+        "UNKNOWN": 35
+      },
+      "calls": [
+        "openclaw browser status --json 2>/dev/null",
+        "openclaw browser navigate \"https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list\" --target-id 43668A82713374FB7A21DB79B043A367 >/dev/null 2>&1",
+        "sleep 3",
+        "openclaw browser evaluate --target-id 43668A82713374FB7A21DB79B043A367"
+      ],
+      "writes_intercepted": [
+        "/Users/macstudio3/.claude/skills/adsense-manager/data/status-latest.json"
+      ]
+    },
+    {
+      "scenario": "missing-total",
+      "extracted_rows": 50,
+      "extracted_total": null,
+      "expected_total": 124,
+      "output_source": "openclaw-console",
+      "output_complete": false,
+      "output_count": 73,
+      "output_expected_count": null,
+      "output_tally": {
+        "Getting ready": 47,
+        "UNKNOWN": 23,
+        "Requires review": 3
+      },
+      "calls": [
+        "openclaw browser status --json 2>/dev/null",
+        "openclaw browser navigate \"https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list\" --target-id 43668A82713374FB7A21DB79B043A367 >/dev/null 2>&1",
+        "sleep 3",
+        "openclaw browser evaluate --target-id 43668A82713374FB7A21DB79B043A367"
+      ],
+      "writes_intercepted": [
+        "/Users/macstudio3/.claude/skills/adsense-manager/data/status-latest.json"
+      ]
+    },
+    {
+      "scenario": "missing-target",
+      "extracted_rows": 0,
+      "extracted_total": null,
+      "expected_total": 124,
+      "output_source": "unavailable",
+      "output_complete": false,
+      "output_count": 35,
+      "output_expected_count": null,
+      "output_tally": {
+        "UNKNOWN": 35
+      },
+      "calls": [],
+      "writes_intercepted": [
+        "/Users/macstudio3/.claude/skills/adsense-manager/data/status-latest.json"
+      ]
+    },
+    {
+      "scenario": "synthetic-complete",
+      "extracted_rows": 124,
+      "extracted_total": 124,
+      "expected_total": 124,
+      "output_source": "openclaw-console",
+      "output_complete": true,
+      "output_count": 124,
+      "output_expected_count": 124,
+      "output_tally": {
+        "Ready": 124
+      },
+      "calls": [
+        "openclaw browser status --json 2>/dev/null",
+        "openclaw browser navigate \"https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list\" --target-id 43668A82713374FB7A21DB79B043A367 >/dev/null 2>&1",
+        "sleep 3",
+        "openclaw browser evaluate --target-id 43668A82713374FB7A21DB79B043A367"
+      ],
+      "writes_intercepted": [
+        "/Users/macstudio3/.claude/skills/adsense-manager/data/status-latest.json"
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/verification/TK-11352-health/console-page1.json b/verification/TK-11352-health/console-page1.json
new file mode 100644
index 0000000..aaa32d4
--- /dev/null
+++ b/verification/TK-11352-health/console-page1.json
@@ -0,0 +1,9 @@
+{
+  "ok": true,
+  "targetId": "43668A82713374FB7A21DB79B043A367",
+  "url": "https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list",
+  "result": {
+    "url": "https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list",
+    "text": "Skip to content\nmenu\nSites\nhelp\nvideo_youtube\nAdSense for YouTube\nhome\nHome\npicture_in_picture\nAds\ndrive_site\nSites\nPrivacy & messaging\narrow_right\nnot_interested\nBrand safety\nassessment\nReports\narrow_right\ntrending_up\nOptimization\npolicy\nPolicy center\narrow_right\nPayments\narrow_right\nsettings\nAccount\nfeedback\nFeedback\nPrivacy\nTerms\nManage your sites\n\nAdd sites you want to monetize with AdSense. Learn more about monetizing your site\n\nadd\nNew site\nsearch\nSearch\nfilter_list\nFilters:\nReady\nGetting ready\nRequires review\nNeeds attention\nSite URL\nApproval status\nhelp_outline\nStatus details\nAds.txt status\nhelp_outline\nLast updated\nhelp_outline\nAction\ncarmelwallpapers.com\nRequires review\n—\nNot found\nSep 9, 2026\n11:17 PM | PDT\ndelete\niwascute.com\nRequires review\n—\nNot found\nSep 9, 2026\n11:32 PM | PDT\ndelete\nhospitalitydesignreps.com\nRequires review\n—\nNot found\nSep 9, 2026\n10:54 PM | PDT\ndelete\nfireratedwallcovering.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:51 PM | PDT\ndelete\n1950swallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:10 PM | PDT\ndelete\n1960swallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:11 PM | PDT\ndelete\n1970swallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:12 PM | PDT\ndelete\n1980swallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:13 PM | PDT\ndelete\ncontractwallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:19 PM | PDT\ndelete\ncustomdigitalmurals.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:21 PM | PDT\ndelete\nflockedwallpaper.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:02 AM | PDT\ndelete\ngoldleafwallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:25 PM | PDT\ndelete\nhotelwallcoverings.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:31 PM | PDT\ndelete\nmylarwallpaper.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:26 AM | PDT\ndelete\nnaturaltextilewallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:38 PM | PDT\ndelete\nnaturalwallcoverings.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:39 PM | PDT\ndelete\nnaturalwalltextures.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:52 PM | PDT\ndelete\nsuedewallpaper.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:05 AM | PDT\ndelete\nraffiawallcoverings.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:18 AM | PDT\ndelete\nretrowalls.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:14 AM | PDT\ndelete\nsheltermagazines.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:53 PM | PDT\ndelete\nsilkwallpaper.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:22 AM | PDT\ndelete\nstarsofdesign.com\nGetting ready\n—\nNot found\nSep 9, 2026\n10:57 PM | PDT\ndelete\n1800swallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:05 PM | PDT\ndelete\ninteriordesignershowroom.com\nGetting ready\n—\nNot found\nSep 9, 2026\n10:56 PM | PDT\ndelete\nsilverleafwallpaper.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:04 AM | PDT\ndelete\ntextilewallpaper.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:07 AM | PDT\ndelete\nmuseumwallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:59 PM | PDT\ndelete\nsaloonwallpaper.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:15 AM | PDT\ndelete\nbubbesblock.com\nGetting ready\n—\nNot found\nSep 9, 2026\n10:48 PM | PDT\ndelete\nrestorationwallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:43 PM | PDT\ndelete\nrestaurantwallpaper.com\nGetting ready\n—\nNot found\nSep 10, 2026\n12:18 AM | PDT\ndelete\npastelwallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:40 PM | PDT\ndelete\nmetallicwallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:36 PM | PDT\ndelete\nwc01wallcoverings.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:56 PM | PDT\ndelete\ncarmelwallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:50 PM | PDT\ndelete\ndesignermagnetics.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:50 PM | PDT\ndelete\nfabricfridays.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:36 PM | PDT\ndelete\nhandmadewallcovering.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:51 PM | PDT\ndelete\nrestaurantmurals.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:52 PM | PDT\ndelete\nwallpaperweekly.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:55 PM | PDT\ndelete\nlinenwallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:34 PM | PDT\ndelete\nwallpaperny.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:55 PM | PDT\ndelete\ncommercialsalesreps.com\nGetting ready\n—\nNot found\nSep 9, 2026\n4:50 PM | PDT\ndelete\nglitterwalls.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:24 PM | PDT\ndelete\ncommercialdesignreps.com\nGetting ready\n—\nNot found\nSep 9, 2026\n10:50 PM | PDT\ndelete\nwallpaperdefinitions.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:00 PM | PDT\ndelete\n1920swallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:07 PM | PDT\ndelete\njutewallpaper.com\nGetting ready\n—\nNot found\nSep 9, 2026\n11:33 PM | PDT\ndelete\ndesigntradelive.com\nGetting ready\n—\nNot found\nSep 9, 2026\n10:53 PM | PDT\ndelete\nGo to page:\nof 3\nShow rows:\n50\narrow_drop_down\n1 - 50 of 124\nfirst_page\nchevron_left\nchevron_right\nlast_page"
+  }
+}
diff --git a/verification/TK-11352-health/e2e-proof.json b/verification/TK-11352-health/e2e-proof.json
new file mode 100644
index 0000000..eb2e334
--- /dev/null
+++ b/verification/TK-11352-health/e2e-proof.json
@@ -0,0 +1,20 @@
+{
+  "ticket": "TK-11352",
+  "intent": "Independently reproduce monitor parse/completeness regressions and verify isolated candidate",
+  "risk_tier": "R1",
+  "environment": "Node VM; real saved console capture and synthetic complete-inventory test",
+  "baseline": "baseline-results.json",
+  "candidate": "candidate-results.json",
+  "assertions": {
+    "response_envelope": "PASS",
+    "correct_site_total": "PASS",
+    "partial_read_not_complete": "PASS",
+    "wrong_target_and_url_rejected": "PASS",
+    "missing_target_no_subprocess": "PASS",
+    "synthetic_complete_read": "PASS",
+    "live_console_pagination": "SKIP: shared browser ownership unresolved",
+    "production_and_actual_ad_serving": "SKIP: not exercised"
+  },
+  "side_effects": "Subprocesses and manager output writes intercepted; only review artifacts written",
+  "verdict": "PARTIAL: isolated candidate validated, operational monitor not certified"
+}
\ No newline at end of file
diff --git a/verification/TK-11352-health/fixture-check.cjs b/verification/TK-11352-health/fixture-check.cjs
new file mode 100644
index 0000000..c93df0a
--- /dev/null
+++ b/verification/TK-11352-health/fixture-check.cjs
@@ -0,0 +1,47 @@
+const fs = require('node:fs');
+const path = require('node:path');
+const vm = require('node:vm');
+const crypto = require('node:crypto');
+const assert = require('node:assert/strict');
+const sourcePath = process.argv[2] || path.join(__dirname,'status-candidate.mjs');
+const originalPath = '/Users/macstudio3/.claude/skills/adsense-manager/scripts/status.mjs';
+const source = fs.readFileSync(sourcePath, 'utf8');
+const capture = JSON.parse(fs.readFileSync(path.join(__dirname,'console-page1.json'), 'utf8'));
+const fleetDir = '/Users/macstudio3/Projects/dw-domain-fleet/sites';
+const domains = new Set(fs.readdirSync(fleetDir).filter(f=>f.endsWith('.json')).map(f=>{try{return JSON.parse(fs.readFileSync(path.join(fleetDir,f),'utf8'));}catch{return null;}}).filter(s=>s?.monetize===true).map(s=>s.domain));
+for(let i=0;domains.size<124;i++) domains.add('fixture-'+i+'.example.com');
+const syntheticFullText = [...domains].map(d=>[d,'Ready','—','Authorized'].join('\n')).join('\n')+'\n1 - 124 of 124';
+const results = [];
+for (const envelope of ['actual-openclaw-envelope', 'unwrapped-control', 'wrong-target', 'wrong-url', 'missing-total', 'missing-target', 'synthetic-complete']) {
+ const writes = [], calls = [];
+ let extracted;
+ const fakeFS = { ...fs, mkdirSync(){}, writeFileSync(p, data){writes.push({path:p, data:JSON.parse(data)});} };
+ const execSync = cmd => {
+  calls.push(cmd.split(' --fn ')[0]);
+  if (cmd.startsWith('openclaw browser status')) return JSON.stringify({running:true});
+  if (cmd.startsWith('openclaw browser navigate') || cmd === 'sleep 3') return '';
+  if (cmd.startsWith('openclaw browser evaluate')) {
+   const fn = JSON.parse(cmd.split(' --fn ')[1].replace(/ 2>\/dev\/null$/, ''));
+   extracted = vm.runInNewContext('(' + fn + ')()', {document:{body:{innerText:envelope === 'synthetic-complete' ? syntheticFullText : envelope === 'missing-total' ? capture.result.text.replace('1 - 50 of 124','rows unavailable') : capture.result.text}}});
+   return JSON.stringify(envelope === 'unwrapped-control' ? extracted : {ok:true,targetId:envelope === 'wrong-target' ? 'ANOTHER_TARGET' : capture.targetId,url:envelope === 'wrong-url' ? 'https://example.com/' : capture.url,result:extracted});
+  }
+  throw Error('Unexpected subprocess request: '+cmd);
+ };
+ const executable = source.replace(/^import .*;\n/gm,'').replaceAll('import.meta.url', JSON.stringify(sourcePath));
+ vm.runInNewContext(executable, {execSync,fs:fakeFS,path,fileURLToPath:x=>originalPath,process:{env:{ADSENSE_BROWSER_TARGET:envelope === 'missing-target' ? '' : capture.targetId}},console:{log(){}},Date,Set,JSON}, {timeout:5000});
+ const out = writes.at(-1).data;
+ results.push({scenario:envelope, extracted_rows:extracted?.rows.length || 0, extracted_total:extracted?.total ?? null, expected_total:124, output_source:out.source, output_complete:out.complete, output_count:out.count, output_expected_count:out.expected_count, output_tally:out.tally, calls, writes_intercepted:writes.map(w=>w.path)});
+}
+for (const r of results) {
+ if(r.scenario === 'synthetic-complete') {assert.equal(r.output_complete,true);assert.equal(r.output_count,124);assert.equal(r.output_expected_count,124);assert.equal(r.output_tally.Ready,124);continue;}
+ assert.equal(r.output_complete,false,r.scenario+' must fail closed');
+ if (['actual-openclaw-envelope','unwrapped-control'].includes(r.scenario)) {
+  assert.equal(r.output_source,'openclaw-console'); assert.equal(r.output_expected_count,124);
+  assert.equal(r.output_tally['Getting ready'],47); assert.equal(r.output_tally['Requires review'],3);
+ } else if (r.scenario === 'missing-total') { assert.equal(r.output_expected_count,null); }
+ else { assert.equal(r.output_source,'unavailable'); }
+ if (r.scenario === 'missing-target') assert.equal(r.calls.length,0);
+}
+const report={assertions:'PASS: six partial/invalid scenarios plus one synthetic complete inventory',timestamp:new Date().toISOString(),source_path:sourcePath,source_sha256:crypto.createHash('sha256').update(source).digest('hex'),fixture:path.join(__dirname,'console-page1.json'),mode:'in-memory subprocess and write interception; no browser/network/shared-state writes',results};
+fs.writeFileSync(process.argv[3] || '/tmp/TK-11352-recheck-results.json', JSON.stringify(report,null,2));
+console.log(JSON.stringify(report,null,2));
diff --git a/verification/TK-11352-health/status-before.mjs b/verification/TK-11352-health/status-before.mjs
new file mode 100644
index 0000000..4ea97e7
--- /dev/null
+++ b/verification/TK-11352-health/status-before.mjs
@@ -0,0 +1,61 @@
+#!/usr/bin/env node
+// AdSense approval-status poller — reads the live console sites list via the
+// openclaw (Steve's real Chrome, already signed in) and extracts each of our
+// domains' approval + ads.txt status. Best-effort: if openclaw/session is
+// unavailable it writes UNKNOWN rather than a false state. Output: data/status-latest.json
+import { execSync } from 'child_process';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const SKILL = path.join(__dirname, '..');
+const FLEET = '/Users/macstudio3/Projects/dw-domain-fleet/sites';
+const LIST = 'https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list';
+const TARGET = process.env.ADSENSE_BROWSER_TARGET || '';
+
+const ours = new Set(fs.readdirSync(FLEET).filter(f => f.endsWith('.json'))
+  .map(f => { try { return JSON.parse(fs.readFileSync(path.join(FLEET, f), 'utf8')); } catch { return null; } })
+  .filter(s => s && s.monetize === true).map(s => s.domain));
+
+function oc(args) { return execSync(`openclaw ${args}`, { encoding: 'utf8', timeout: 30000, stdio: ['ignore', 'pipe', 'ignore'] }); }
+
+let rows = [], ok = false, complete = false, expectedConsoleCount = null;
+try {
+  if (!/^[A-Za-z0-9_-]+$/.test(TARGET)) throw new Error('ADSENSE_BROWSER_TARGET is required for isolated console reads');
+  const st = JSON.parse(oc('browser status --json 2>/dev/null') || '{}');
+  if (st.running) {
+    oc(`browser navigate ${JSON.stringify(LIST)} --target-id ${TARGET} >/dev/null 2>&1`);
+    execSync('sleep 3');
+    // Parse the sites table innerText into {domain, status, adstxt}
+    const fn = "() => { const t=document.body.innerText; const lines=t.split('\\n').map(x=>x.trim()).filter(Boolean); const out=[]; for(let i=0;i<lines.length;i++){ if(/^[a-z0-9.-]+\\.(com|net|org|ai)$/i.test(lines[i])){ const dom=lines[i]; const win=lines.slice(i+1,i+5).join(' | '); out.push({dom, win}); } } const m=t.match(/\\bof\\s+(\\d+)\\b/i); return {rows:out,total:m?Number(m[1]):null}; }";
+    const raw = oc(`browser evaluate --target-id ${TARGET} --fn ${JSON.stringify(fn)} 2>/dev/null`);
+    const parsedObj = JSON.parse(raw.slice(raw.indexOf('{')));
+    const parsed = parsedObj.rows || [];
+    const consoleTotal = Number.isFinite(parsedObj.total) ? parsedObj.total : null;
+    const statusRe = /\b(Ready|Getting ready|Requires review|Needs attention)\b/i;
+    // "Getting ready" is an approval state, never an ads.txt state. The
+    // previous combined-window parser reported it as ads.txt and hid Not found.
+    const adsRe = /\b(Authorized|Not found|Unauthorized)\b/i;
+    const seen = new Set();
+    for (const p of parsed) {
+      if (seen.has(p.dom)) continue;
+      seen.add(p.dom);
+      rows.push({ domain: p.dom, approval: (p.win.match(statusRe) || [,'UNKNOWN'])[1], ads_txt: (p.win.match(adsRe) || [,'UNKNOWN'])[1] });
+    }
+    complete = rows.length >= (consoleTotal || ours.size) && !rows.some(r => r.approval === 'UNKNOWN' || r.ads_txt === 'UNKNOWN');
+    if (consoleTotal) expectedConsoleCount = consoleTotal;
+    ok = rows.length > 0;
+  }
+} catch (e) { /* fall through to UNKNOWN */ }
+
+// fill any of ours we didn't see as UNKNOWN
+for (const d of ours) if (!rows.find(r => r.domain === d)) rows.push({ domain: d, approval: 'UNKNOWN', ads_txt: 'UNKNOWN' });
+rows.sort((a, b) => a.domain.localeCompare(b.domain));
+
+const tally = rows.reduce((m, r) => (m[r.approval] = (m[r.approval] || 0) + 1, m), {});
+const out = { ts: new Date().toISOString(), source: ok ? 'openclaw-console' : 'unavailable', complete, expected_count: expectedConsoleCount || ours.size, count: rows.length, tally, rows };
+fs.mkdirSync(path.join(SKILL, 'data'), { recursive: true });
+fs.writeFileSync(path.join(SKILL, 'data', 'status-latest.json'), JSON.stringify(out, null, 2));
+console.log(`AdSense status (${out.source}) — ${JSON.stringify(tally)}`);
+if (rows.some(r => r.approval === 'Ready')) console.log('APPROVED:', rows.filter(r => r.approval === 'Ready').map(r => r.domain).join(', '));
diff --git a/verification/TK-11352-health/status-candidate.mjs b/verification/TK-11352-health/status-candidate.mjs
new file mode 100644
index 0000000..553d7ab
--- /dev/null
+++ b/verification/TK-11352-health/status-candidate.mjs
@@ -0,0 +1,65 @@
+#!/usr/bin/env node
+// AdSense approval-status poller — reads the live console sites list via the
+// openclaw (Steve's real Chrome, already signed in) and extracts each of our
+// domains' approval + ads.txt status. Best-effort: if openclaw/session is
+// unavailable it writes UNKNOWN rather than a false state. Output: data/status-latest.json
+import { execSync } from 'child_process';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const SKILL = path.join(__dirname, '..');
+const FLEET = '/Users/macstudio3/Projects/dw-domain-fleet/sites';
+const LIST = 'https://adsense.google.com/adsense/u/0/pub-5278231299883833/sites/list';
+const TARGET = process.env.ADSENSE_BROWSER_TARGET || '';
+
+const ours = new Set(fs.readdirSync(FLEET).filter(f => f.endsWith('.json'))
+  .map(f => { try { return JSON.parse(fs.readFileSync(path.join(FLEET, f), 'utf8')); } catch { return null; } })
+  .filter(s => s && s.monetize === true).map(s => s.domain));
+
+function oc(args) { return execSync(`openclaw ${args}`, { encoding: 'utf8', timeout: 30000, stdio: ['ignore', 'pipe', 'ignore'] }); }
+
+let rows = [], ok = false, complete = false, expectedConsoleCount = null, observedCount = 0;
+try {
+  if (!/^[A-Za-z0-9_-]+$/.test(TARGET)) throw new Error('ADSENSE_BROWSER_TARGET is required for isolated console reads');
+  const st = JSON.parse(oc('browser status --json 2>/dev/null') || '{}');
+  if (st.running) {
+    oc(`browser navigate ${JSON.stringify(LIST)} --target-id ${TARGET} >/dev/null 2>&1`);
+    execSync('sleep 3');
+    // Parse the sites table innerText into {domain, status, adstxt}
+    const fn = "() => { const t=document.body.innerText; const lines=t.split('\\n').map(x=>x.trim()).filter(Boolean); const out=[]; for(let i=0;i<lines.length;i++){ if(/^[a-z0-9.-]+\\.(com|net|org|ai)$/i.test(lines[i])){ const dom=lines[i]; const win=lines.slice(i+1,i+5).join(' | '); out.push({dom, win}); } } const m=t.match(/\\b\\d+\\s*[-–]\\s*\\d+\\s+of\\s+(\\d+)\\b/i); return {rows:out,total:m?Number(m[1]):null}; }";
+    const raw = oc(`browser evaluate --target-id ${TARGET} --fn ${JSON.stringify(fn)} 2>/dev/null`);
+    const envelope = JSON.parse(raw.slice(raw.indexOf('{')));
+    if (envelope.ok === false || (envelope.targetId && envelope.targetId !== TARGET) || (envelope.url && envelope.url !== LIST)) throw new Error('Console response ownership or URL mismatch');
+    const parsedObj = envelope.result || envelope;
+    if (parsedObj.url && parsedObj.url !== LIST) throw new Error('Console navigated away');
+    const parsed = parsedObj.rows || [];
+    const consoleTotal = Number.isFinite(parsedObj.total) ? parsedObj.total : null;
+    const statusRe = /\b(Ready|Getting ready|Requires review|Needs attention)\b/i;
+    // "Getting ready" is an approval state, never an ads.txt state. The
+    // previous combined-window parser reported it as ads.txt and hid Not found.
+    const adsRe = /\b(Authorized|Not found|Unauthorized)\b/i;
+    const seen = new Set();
+    for (const p of parsed) {
+      if (seen.has(p.dom)) continue;
+      seen.add(p.dom);
+      rows.push({ domain: p.dom, approval: (p.win.match(statusRe) || [,'UNKNOWN'])[1], ads_txt: (p.win.match(adsRe) || [,'UNKNOWN'])[1] });
+    }
+    observedCount = rows.length;
+    if (consoleTotal) expectedConsoleCount = consoleTotal;
+    ok = rows.length > 0;
+  }
+} catch (e) { /* fall through to UNKNOWN */ }
+
+// fill any of ours we didn't see as UNKNOWN
+for (const d of ours) if (!rows.find(r => r.domain === d)) rows.push({ domain: d, approval: 'UNKNOWN', ads_txt: 'UNKNOWN' });
+rows.sort((a, b) => a.domain.localeCompare(b.domain));
+complete = ok && Number.isInteger(expectedConsoleCount) && expectedConsoleCount > 0 && observedCount === expectedConsoleCount && !rows.some(r => r.approval === 'UNKNOWN' || r.ads_txt === 'UNKNOWN');
+
+const tally = rows.reduce((m, r) => (m[r.approval] = (m[r.approval] || 0) + 1, m), {});
+const out = { ts: new Date().toISOString(), source: ok ? 'openclaw-console' : 'unavailable', complete, observed_count: observedCount, expected_count: expectedConsoleCount, count: rows.length, tally, rows };
+fs.mkdirSync(path.join(SKILL, 'data'), { recursive: true });
+fs.writeFileSync(path.join(SKILL, 'data', 'status-latest.json'), JSON.stringify(out, null, 2));
+console.log(`AdSense status (${out.source}) — ${JSON.stringify(tally)}`);
+if (rows.some(r => r.approval === 'Ready')) console.log('APPROVED:', rows.filter(r => r.approval === 'Ready').map(r => r.domain).join(', '));
diff --git a/verification/TK-11352-health/status-candidate.patch b/verification/TK-11352-health/status-candidate.patch
new file mode 100644
index 0000000..cf7f7ee
--- /dev/null
+++ b/verification/TK-11352-health/status-candidate.patch
@@ -0,0 +1,49 @@
+diff --git a/tmp/TK-11352-status-before.mjs b/tmp/TK-11352-status-candidate.mjs
+index 4ea97e7..553d7ab 100644
+--- a/tmp/TK-11352-status-before.mjs
++++ b/tmp/TK-11352-status-candidate.mjs
+@@ -20,7 +20,7 @@ const ours = new Set(fs.readdirSync(FLEET).filter(f => f.endsWith('.json'))
+ 
+ function oc(args) { return execSync(`openclaw ${args}`, { encoding: 'utf8', timeout: 30000, stdio: ['ignore', 'pipe', 'ignore'] }); }
+ 
+-let rows = [], ok = false, complete = false, expectedConsoleCount = null;
++let rows = [], ok = false, complete = false, expectedConsoleCount = null, observedCount = 0;
+ try {
+   if (!/^[A-Za-z0-9_-]+$/.test(TARGET)) throw new Error('ADSENSE_BROWSER_TARGET is required for isolated console reads');
+   const st = JSON.parse(oc('browser status --json 2>/dev/null') || '{}');
+@@ -28,9 +28,12 @@ try {
+     oc(`browser navigate ${JSON.stringify(LIST)} --target-id ${TARGET} >/dev/null 2>&1`);
+     execSync('sleep 3');
+     // Parse the sites table innerText into {domain, status, adstxt}
+-    const fn = "() => { const t=document.body.innerText; const lines=t.split('\\n').map(x=>x.trim()).filter(Boolean); const out=[]; for(let i=0;i<lines.length;i++){ if(/^[a-z0-9.-]+\\.(com|net|org|ai)$/i.test(lines[i])){ const dom=lines[i]; const win=lines.slice(i+1,i+5).join(' | '); out.push({dom, win}); } } const m=t.match(/\\bof\\s+(\\d+)\\b/i); return {rows:out,total:m?Number(m[1]):null}; }";
++    const fn = "() => { const t=document.body.innerText; const lines=t.split('\\n').map(x=>x.trim()).filter(Boolean); const out=[]; for(let i=0;i<lines.length;i++){ if(/^[a-z0-9.-]+\\.(com|net|org|ai)$/i.test(lines[i])){ const dom=lines[i]; const win=lines.slice(i+1,i+5).join(' | '); out.push({dom, win}); } } const m=t.match(/\\b\\d+\\s*[-–]\\s*\\d+\\s+of\\s+(\\d+)\\b/i); return {rows:out,total:m?Number(m[1]):null}; }";
+     const raw = oc(`browser evaluate --target-id ${TARGET} --fn ${JSON.stringify(fn)} 2>/dev/null`);
+-    const parsedObj = JSON.parse(raw.slice(raw.indexOf('{')));
++    const envelope = JSON.parse(raw.slice(raw.indexOf('{')));
++    if (envelope.ok === false || (envelope.targetId && envelope.targetId !== TARGET) || (envelope.url && envelope.url !== LIST)) throw new Error('Console response ownership or URL mismatch');
++    const parsedObj = envelope.result || envelope;
++    if (parsedObj.url && parsedObj.url !== LIST) throw new Error('Console navigated away');
+     const parsed = parsedObj.rows || [];
+     const consoleTotal = Number.isFinite(parsedObj.total) ? parsedObj.total : null;
+     const statusRe = /\b(Ready|Getting ready|Requires review|Needs attention)\b/i;
+@@ -43,7 +46,7 @@ try {
+       seen.add(p.dom);
+       rows.push({ domain: p.dom, approval: (p.win.match(statusRe) || [,'UNKNOWN'])[1], ads_txt: (p.win.match(adsRe) || [,'UNKNOWN'])[1] });
+     }
+-    complete = rows.length >= (consoleTotal || ours.size) && !rows.some(r => r.approval === 'UNKNOWN' || r.ads_txt === 'UNKNOWN');
++    observedCount = rows.length;
+     if (consoleTotal) expectedConsoleCount = consoleTotal;
+     ok = rows.length > 0;
+   }
+@@ -52,9 +55,10 @@ try {
+ // fill any of ours we didn't see as UNKNOWN
+ for (const d of ours) if (!rows.find(r => r.domain === d)) rows.push({ domain: d, approval: 'UNKNOWN', ads_txt: 'UNKNOWN' });
+ rows.sort((a, b) => a.domain.localeCompare(b.domain));
++complete = ok && Number.isInteger(expectedConsoleCount) && expectedConsoleCount > 0 && observedCount === expectedConsoleCount && !rows.some(r => r.approval === 'UNKNOWN' || r.ads_txt === 'UNKNOWN');
+ 
+ const tally = rows.reduce((m, r) => (m[r.approval] = (m[r.approval] || 0) + 1, m), {});
+-const out = { ts: new Date().toISOString(), source: ok ? 'openclaw-console' : 'unavailable', complete, expected_count: expectedConsoleCount || ours.size, count: rows.length, tally, rows };
++const out = { ts: new Date().toISOString(), source: ok ? 'openclaw-console' : 'unavailable', complete, observed_count: observedCount, expected_count: expectedConsoleCount, count: rows.length, tally, rows };
+ fs.mkdirSync(path.join(SKILL, 'data'), { recursive: true });
+ fs.writeFileSync(path.join(SKILL, 'data', 'status-latest.json'), JSON.stringify(out, null, 2));
+ console.log(`AdSense status (${out.source}) — ${JSON.stringify(tally)}`);

← ec269c8 Normalize review CSV and preserve captured evidence whitespa  ·  back to Adsense Fleet Viewer  ·  (newest)