{"id":384295,"date":"2026-10-11T11:03:49","date_gmt":"2026-10-11T11:03:49","guid":{"rendered":"https:\/\/cn.wordpress.org\/plugins\/sync-drift-auditor\/"},"modified":"2026-10-11T11:03:41","modified_gmt":"2026-10-11T11:03:41","slug":"driftgauge-sync-auditor","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/driftgauge-sync-auditor\/","author":23583299,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.1.0","stable_tag":"0.1.0","tested":"7.1.3","requires":"6.0","requires_php":"7.4","requires_plugins":null,"header_name":"Driftgauge Sync Auditor","header_author":"dirkdigger","header_description":"Compares two product sources by SKU and shows every difference it can prove \u2014 missing rows, quantity drift, price drift and values that reverted. Read-only: nothing is written back.","assets_banners_color":"192026","last_updated":"2026-10-11 11:03:41","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":39,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.1.0":{"tag":"0.1.0","author":"dirkdigger","date":"2026-10-11 11:03:41","revision":3739372}},"upgrade_notice":{"0.1.0":"<p>Initial release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3739430,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3739430,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3739430,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3739430,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.1.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3739430,"resolution":"1","location":"assets","locale":"","width":1322,"height":1090},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3739430,"resolution":"2","location":"assets","locale":"","width":1418,"height":290}},"screenshots":{"1":"The audit result: summary counts, then one table per kind of difference.","2":"The FAILURES panel, listing rows that could not be compared and why."}},"plugin_section":[],"plugin_tags":[567,6601,12448,1558,286],"plugin_category":[45],"plugin_contributors":[285776],"plugin_business_model":[],"class_list":["post-384295","plugin","type-plugin","status-publish","hentry","plugin_tags-csv","plugin_tags-inventory","plugin_tags-stock","plugin_tags-sync","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_contributors-dirkdigger","plugin_committers-dirkdigger"],"banners":{"banner":"https:\/\/ps.w.org\/driftgauge-sync-auditor\/assets\/banner-772x250.png?rev=3739430","banner_2x":"https:\/\/ps.w.org\/driftgauge-sync-auditor\/assets\/banner-1544x500.png?rev=3739430","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/driftgauge-sync-auditor\/assets\/icon-128x128.png?rev=3739430","icon_2x":"https:\/\/ps.w.org\/driftgauge-sync-auditor\/assets\/icon-256x256.png?rev=3739430","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/driftgauge-sync-auditor\/assets\/screenshot-1.png?rev=3739430","caption":"The audit result: summary counts, then one table per kind of difference."},{"src":"https:\/\/ps.w.org\/driftgauge-sync-auditor\/assets\/screenshot-2.png?rev=3739430","caption":"The FAILURES panel, listing rows that could not be compared and why."}],"raw_content":"<!--section=description-->\n<p><strong>A dry run with a receipt.<\/strong> Point it at a supplier feed, a marketplace export,\nor the CSV you were about to import, and get a line-by-line account of the\ndifference between that file and your WooCommerce catalogue: which SKUs are\nmissing on each side, where the quantity disagrees, where the price disagrees,\nand what the file does not let us decide. Nothing is written to your store.<\/p>\n\n<p>It exists because the usual way to discover a failed inventory sync or a bad CSV\nimport is by accident \u2014 a customer asks why something shows out of stock, a\nproduct quietly disappeared, or a charge turns up on a card statement weeks\nlater. After a sync error or an import error it tells you what actually changed;\nbefore the next one runs, it tells you what would. It is built for checking a\nsupplier feed, a dropship price list or an ERP export before you trust it with\nyour live stock levels.<\/p>\n\n<h4>What it finds<\/h4>\n\n<ul>\n<li><strong>Missing SKUs<\/strong> \u2014 rows that exist in the file but not in your store, and\nproducts in your store that the file never mentions.<\/li>\n<li><strong>Quantity mismatch<\/strong> \u2014 stock levels that disagree between the two sources.<\/li>\n<li><strong>Price mismatch<\/strong> \u2014 prices that disagree, compared as regular price, sale\nprice, or \"effective\" price (sale price where present, otherwise regular).<\/li>\n<li><strong>Silent reversion<\/strong> \u2014 a value that is back to something it held in an earlier\nrun of the same audit, which is what a half-applied or rolled-back sync\nusually looks like afterwards.<\/li>\n<li><strong>Rows that cannot be decided<\/strong> \u2014 reported as undecidable, never guessed.<\/li>\n<\/ul>\n\n<h4>When to use it<\/h4>\n\n<ul>\n<li>Before a product import from a supplier CSV, to see what that import would\nchange.<\/li>\n<li>After a stock sync that looks wrong, to see what actually differs right now.<\/li>\n<li>When a product feed or marketplace export starts overwriting your prices.<\/li>\n<li>When stock levels drift, to get the exact list of SKUs that disagree instead\nof just knowing something is off.<\/li>\n<li>As a recurring stock reconciliation: run the same inventory audit every month\nas part of your inventory management routine, and let the revert detection tell\nyou whether anything moved back.<\/li>\n<\/ul>\n\n<h4>What it refuses to do<\/h4>\n\n<p>These are deliberate, and they are the point:<\/p>\n\n<ol>\n<li><strong>It never silently skips a row it cannot parse.<\/strong> A value that is present but\nunreadable \u2014 <code>abc<\/code> where a number belongs \u2014 is listed under FAILURES and the\nrun is marked as untrustworthy.<\/li>\n<li><strong>It never picks a winner when a SKU is duplicated.<\/strong> Choosing one would hide\nexactly the corruption you went looking for.<\/li>\n<li><strong>It never claims a row is \"missing\" when the other side failed to load\ncompletely.<\/strong> Those rows are reported as <em>cannot be asserted<\/em>.<\/li>\n<li><strong>It never invents a number.<\/strong> An empty quantity or price means unknown: the\nrow is still matched by SKU, it is not compared on that field, and the count\nis stated under Notes. Absent data and damaged data are different things, and\ntreating the first as the second would quietly disable the findings that\nmatter most.<\/li>\n<li><strong>It assigns no score, no rating, no red\/green light, and gives no advice.<\/strong>\nIt states differences; you decide what they mean.<\/li>\n<\/ol>\n\n<h4>Why not just read the sync log?<\/h4>\n\n<p>Because the sync log tells you what the sync <em>tried<\/em> to do. This tells you what\nis true right now on both sides, computed from the data itself.<\/p>\n\n<h3>Development notes<\/h3>\n\n<p>The comparison engine is deliberately free of WordPress dependencies so it can\nbe tested on the command line:<\/p>\n\n<pre><code>php tests\/run-tests.php            # engine, 33 checks\nphp tests\/run-wp-admin-tests.php   # admin layer against function stubs, 23 checks\n<\/code><\/pre>\n\n<p>And inside a real install (from the WordPress root):<\/p>\n\n<pre><code>php wp-content\/plugins\/driftgauge-sync-auditor\/tests\/run-real-wp-tests.php\nphp wp-content\/plugins\/driftgauge-sync-auditor\/tests\/run-real-wp-woo-tests.php\n<\/code><\/pre>\n\n<p>The real-WordPress suite covers plugin loading, the Tools menu (registered for an\nadministrator, absent for a subscriber), the capability gate, a valid nonce being\naccepted and a forged one rejected, the snapshot option, revert detection across\nruns, and a read-only proof that no posts, post meta or other options are\nwritten. Run on <strong>WordPress 7.1.3 \/ PHP 8.4.26: 20\/20<\/strong>.<\/p>\n\n<p>The WooCommerce suite covers the catalogue path: it creates products, then audits\nthe catalogue against a CSV and checks that the matching product is not reported\nas drift, that quantity drift is found, that a CSV-only SKU is reported as\nmissing, that variations are read, that a product with no SKU is never silently\nmatched, and that a catalogue containing untracked-stock or container rows still\nreports the genuinely missing SKU. Run on\n<strong>WordPress 7.1.3 + WooCommerce 11.2.0: 24\/24<\/strong>.<\/p>\n\n<pre><code>tests\/run-wp-admin-tests.php uses stand-in WordPress functions; the real-WP\n<\/code><\/pre>\n\n<p>suites are the ones that prove behaviour inside WordPress itself.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin folder to <code>\/wp-content\/plugins\/<\/code>, or install the ZIP\nthrough Plugins \u2192 Add New \u2192 Upload Plugin.<\/li>\n<li>Activate it through the Plugins menu.<\/li>\n<li>Go to <strong>Tools \u2192 Driftgauge Audit<\/strong>, upload a CSV (or leave the left side\nempty to use your WooCommerce catalogue), and run the audit.<\/li>\n<\/ol>\n\n<p>No API keys, no external service. Uploaded files are parsed in memory from the\ntemporary upload path and are never stored by the plugin.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"how%20do%20i%20check%20whether%20a%20csv%20import%20or%20an%20inventory%20sync%20went%20wrong%3F\"><h3>How do I check whether a CSV import or an inventory sync went wrong?<\/h3><\/dt>\n<dd><p>Run the audit with the file you imported (or a fresh export from the other side)\nas one side, and your catalogue as the other. Every SKU that disagrees on\nquantity or price, every row present on only one side, and every value that\nreverted since the previous run is listed explicitly. Running it changes nothing.<\/p><\/dd>\n<dt id=\"can%20i%20use%20it%20as%20a%20regular%20stock%20reconciliation%20report%3F\"><h3>Can I use it as a regular stock reconciliation report?<\/h3><\/dt>\n<dd><p>Yes. Run the same comparison on a schedule and keep the reports: each run is\ncompared against the previous one, so a value that silently moved back is called\nout as a reversion instead of looking like a fresh difference.<\/p><\/dd>\n<dt id=\"does%20it%20modify%20my%20products%3F\"><h3>Does it modify my products?<\/h3><\/dt>\n<dd><p>No. It reads, compares and reports. There is no write path to products, orders\nor any other store data. The only thing it saves is its own small snapshot\noption, which is what lets it tell you that a value reverted.<\/p><\/dd>\n<dt id=\"what%20if%20my%20file%20has%20different%20column%20names%3F\"><h3>What if my file has different column names?<\/h3><\/dt>\n<dd><p>Common headers are recognised automatically in English and Chinese \u2014 for\nexample <code>SKU<\/code>, <code>Item Code<\/code>, <code>Variant SKU<\/code>, <code>\u8d27\u53f7<\/code>, <code>Qty<\/code>, <code>On Hand<\/code>, <code>\u5e93\u5b58<\/code>,\n    Price, <code>Regular price<\/code>, <code>\u5355\u4ef7<\/code>. For anything else the audit stops rather than\nguessing, and the failure message names the field it could not find.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20shopify%20or%20marketplace%20exports%3F\"><h3>Does it work with Shopify or marketplace exports?<\/h3><\/dt>\n<dd><p>Yes. Shopify's variant export and WooCommerce's own product export are recognised\nas formats, including the parent\/variation rows those exports write without a\nSKU \u2014 those are structure, not errors. Any other CSV is read by its headings.<\/p><\/dd>\n<dt id=\"what%20does%20%22cannot%20be%20asserted%22%20mean%3F\"><h3>What does \"cannot be asserted\" mean?<\/h3><\/dt>\n<dd><p>If one side failed to load some rows, a row that appears only on the other side\nis not proof of anything. The plugin says so instead of reporting a false\ndifference.<\/p><\/dd>\n<dt id=\"how%20large%20a%20file%20can%20it%20handle%3F\"><h3>How large a file can it handle?<\/h3><\/dt>\n<dd><p>The comparison is linear. A 20,000-row pair is processed in well under a second\non a normal machine; the practical limit is your upload and memory limits.<\/p><\/dd>\n<dt id=\"what%20about%20products%20that%20do%20not%20track%20stock%3F\"><h3>What about products that do not track stock?<\/h3><\/dt>\n<dd><p>They are read and matched by SKU, but their quantity is unknown \u2014 there is no\nnumber to compare \u2014 so the row is not compared on quantity and the count of such\nrows is listed under Notes. It is deliberately not treated as zero: that would\ninvent a quantity and then report a confident, false \"quantity disagrees\" against\nevery matching row in the other file. It is also not treated as damaged data,\nbecause that would make the whole side untrustworthy and suppress the\n\"missing on the other side\" findings \u2014 the most useful thing this tool reports.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>First release: SKU-matched quantity and price comparison, WooCommerce and\nShopify export formats, revert detection, explicit failure reporting.<\/li>\n<li>Read-only by design.<\/li>\n<\/ul>","raw_excerpt":"Compare your WooCommerce inventory against any supplier feed, CSV import or marketplace export, and see every difference before you trust it.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/384295","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=384295"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/dirkdigger"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=384295"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=384295"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=384295"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=384295"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=384295"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=384295"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}