From 337249411dac9d4e65578c8ae7442e09947b2d3d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 19:58:19 +0000 Subject: [PATCH] Spec audit, first pass: pen-compat for included pens, swapped body sizes - Every tablet's included pen is now in that pen's pen-compat row (16 were missing), and data-quality checks it from now on. - Cintiq 24 / 24 touch (2025) and Cintiq 16 (2025) had body width and height swapped, so the body read narrower than the active area. - Intuos Small Bluetooth: digitizer type PASSIVE_EMR, as the wired model. Part of #54. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01JKHYepZSQw6ek2WLhWP4jf --- data/pen-compat/HUION-pen-compat.json | 10 +++++- data/pen-compat/UGEE-pen-compat.json | 3 +- data/pen-compat/WACOM-pen-compat.json | 12 +++++++ data/pen-compat/XPPEN-pen-compat.json | 17 ++++++--- data/tablets/WACOM-tablets.json | 23 ++++++------ data/version.json | 4 +-- lib/data-quality-included-pen.test.ts | 33 +++++++++++++++++ lib/data-quality.ts | 35 +++++++++++++++++++ .../tablets/wacom/wacom.tablet.ctl4100wl.json | 5 +-- source/tablets/wacom/wacom.tablet.dth246.json | 6 ++-- source/tablets/wacom/wacom.tablet.dtk168.json | 6 ++-- source/tablets/wacom/wacom.tablet.dtk246.json | 6 ++-- 12 files changed, 129 insertions(+), 31 deletions(-) diff --git a/data/pen-compat/HUION-pen-compat.json b/data/pen-compat/HUION-pen-compat.json index c2aff00..01d7eac 100644 --- a/data/pen-compat/HUION-pen-compat.json +++ b/data/pen-compat/HUION-pen-compat.json @@ -171,7 +171,8 @@ "GT1902", "GT2701", "GT2402", - "GT2702" + "GT2702", + "Q640M" ] }, { @@ -181,6 +182,13 @@ "GT156V2" ] }, + { + "Brand": "HUION", + "PenId": "PW600C", + "TabletIds": [ + "KP1202" + ] + }, { "Brand": "HUION", "PenId": "PW600L", diff --git a/data/pen-compat/UGEE-pen-compat.json b/data/pen-compat/UGEE-pen-compat.json index aecfa59..1c6f691 100644 --- a/data/pen-compat/UGEE-pen-compat.json +++ b/data/pen-compat/UGEE-pen-compat.json @@ -26,7 +26,8 @@ "Brand": "UGEE", "PenId": "PH12", "TabletIds": [ - "U1200" + "U1200", + "U1600" ] } ] diff --git a/data/pen-compat/WACOM-pen-compat.json b/data/pen-compat/WACOM-pen-compat.json index d7e7852..617b032 100644 --- a/data/pen-compat/WACOM-pen-compat.json +++ b/data/pen-compat/WACOM-pen-compat.json @@ -76,6 +76,13 @@ "DTH-A140" ] }, + { + "Brand": "WACOM", + "PenId": "FP-320", + "TabletIds": [ + "DTF-720" + ] + }, { "Brand": "WACOM", "PenId": "GP-110", @@ -252,7 +259,9 @@ "DTH-135", "DTH-167", "DTH-172", + "DTH-2200", "DTH-227", + "DTH-2400", "DTH-246", "DTH-271", "DTK-1300", @@ -269,6 +278,7 @@ "PTH-460", "PTH-650", "PTH-651", + "PTH-651SE", "PTH-660", "PTH-850", "PTH-851", @@ -336,6 +346,7 @@ "DTH-246", "DTH-271", "DTH-3220", + "DTH-W1320", "DTH-W1321", "DTH-W1620", "DTH-W1621", @@ -456,6 +467,7 @@ "CTH-461", "CTH-470", "CTH-480", + "CTH-660", "CTH-661", "CTH-670", "CTH-680", diff --git a/data/pen-compat/XPPEN-pen-compat.json b/data/pen-compat/XPPEN-pen-compat.json index 50bd253..646bd0a 100644 --- a/data/pen-compat/XPPEN-pen-compat.json +++ b/data/pen-compat/XPPEN-pen-compat.json @@ -7,7 +7,9 @@ "CT1060", "CT430", "CT640", - "G640" + "G430S", + "G640", + "STAR03V2" ] }, { @@ -29,7 +31,8 @@ "Brand": "XPPEN", "PenId": "P03", "TabletIds": [ - "DECO01" + "DECO01", + "STAR05" ] }, { @@ -39,7 +42,8 @@ "ARTIST15.6", "DECO01V2", "DECO01V3", - "DECO03" + "DECO03", + "G640SV2" ] }, { @@ -47,7 +51,8 @@ "PenId": "P05D", "TabletIds": [ "DECOMINI4", - "DECOMINI7" + "DECOMINI7", + "DECOMINI7V2" ] }, { @@ -62,7 +67,9 @@ "PenId": "P06", "TabletIds": [ "ARTIST12", - "DECO02" + "DECO02", + "STAR06", + "STAR06C" ] }, { diff --git a/data/tablets/WACOM-tablets.json b/data/tablets/WACOM-tablets.json index a4e77f0..49b1f6a 100644 --- a/data/tablets/WACOM-tablets.json +++ b/data/tablets/WACOM-tablets.json @@ -1752,7 +1752,7 @@ "EntityId": "wacom.tablet.ctl4100wl", "_id": "8e17d9b8-7a38-4c37-9add-6bdc82091525", "_CreateDate": "2025-12-12T12:33:46.983Z", - "_ModifiedDate": "2026-10-02T16:41:18.000Z" + "_ModifiedDate": "2026-10-02T19:57:22.862Z" }, "Model": { "Brand": "WACOM", @@ -1840,7 +1840,8 @@ }, "Density": "100", "ReportRate": "133", - "Tilt": "0" + "Tilt": "0", + "Type": "PASSIVE_EMR" }, "Physical": { "Dimensions": { @@ -4733,7 +4734,7 @@ "EntityId": "wacom.tablet.dth246", "_id": "62cd9b73-c996-422b-93c4-4c128acdaf6e", "_CreateDate": "2025-12-12T12:33:46.983Z", - "_ModifiedDate": "2026-10-02T17:46:31.000Z" + "_ModifiedDate": "2026-10-02T19:57:18.221Z" }, "Model": { "Brand": "WACOM", @@ -4857,8 +4858,8 @@ }, "Physical": { "Dimensions": { - "Width": 340, - "Height": 568, + "Width": 568, + "Height": 340, "Depth": 21 }, "Weight": "5000", @@ -6655,7 +6656,7 @@ "EntityId": "wacom.tablet.dtk168", "_id": "fae638fa-e305-434a-a2a6-f6852ec8c02e", "_CreateDate": "2025-12-12T12:33:46.983Z", - "_ModifiedDate": "2026-10-02T18:08:36.000Z" + "_ModifiedDate": "2026-10-02T19:57:21.120Z" }, "Model": { "Brand": "WACOM", @@ -6767,8 +6768,8 @@ }, "Physical": { "Dimensions": { - "Width": 259, - "Height": 384, + "Width": 384, + "Height": 259, "Depth": 15 }, "Weight": "2000", @@ -7381,7 +7382,7 @@ "EntityId": "wacom.tablet.dtk246", "_id": "98f59bc7-4ba6-49f0-b3b2-4acafcffe64c", "_CreateDate": "2025-12-12T12:33:46.983Z", - "_ModifiedDate": "2026-06-18T18:13:44.058Z" + "_ModifiedDate": "2026-10-02T19:57:19.680Z" }, "Model": { "Brand": "WACOM", @@ -7454,8 +7455,8 @@ }, "Physical": { "Dimensions": { - "Width": 340, - "Height": 568, + "Width": 568, + "Height": 340, "Depth": 21 }, "Weight": "4800" diff --git a/data/version.json b/data/version.json index 1e6bcdb..8184ebf 100644 --- a/data/version.json +++ b/data/version.json @@ -119,7 +119,7 @@ "xppen.pen.pd04b": 2 } }, - "sourceDigest": "9a0850225955e89f0cd9325a54bd4198f3600d6223231e5793cb66e9dba83b6d", + "sourceDigest": "e914caa30753ecd0fdfcadcd6bb6d0ec6237b934b567a68e04d234b85e8e1352", "bundles": [ { "path": "tablets/APPLE-tablets.json", @@ -163,7 +163,7 @@ }, { "path": "tablets/WACOM-tablets.json", - "sha256": "0fb81a90cd349ca91ec8f221ad538888da45d5270c929da1b463337a79ea8a43", + "sha256": "21de51f4e044ec2e39ecf19b54a90b2e1a4901508678b688c8a58ad6b07eb406", "count": 197 }, { diff --git a/lib/data-quality-included-pen.test.ts b/lib/data-quality-included-pen.test.ts index 6517c3c..e9f4f57 100644 --- a/lib/data-quality-included-pen.test.ts +++ b/lib/data-quality-included-pen.test.ts @@ -63,3 +63,36 @@ describe("IncludedPen checks", () => { ]); }); }); + +describe("IncludedPen vs pen-compat", () => { + function compatIssues(tabletIds: string[] | null) { + put("tablets/WACOM-tablets.json", { + DrawingTablets: [tablet({ ReleaseYear: "2016", IncludedPen: ["wacom.pen.kp504e"] })], + }); + put("pens/WACOM-pens.json", { + Pens: [{ EntityId: "wacom.pen.kp504e", Brand: "WACOM", PenId: "KP-504E", ReleaseYear: "2016" }], + }); + put("pen-compat/WACOM-pen-compat.json", { + PenCompat: tabletIds ? [{ Brand: "WACOM", PenId: "KP-504E", TabletIds: tabletIds }] : [], + }); + return runDataQuality(dataDir) + .filter((i) => i.field === "PenCompat") + .map((i) => `${i.issue}: ${i.value}`); + } + + it("accepts an included pen whose compat row lists the tablet", () => { + expect(compatIssues(["T0", "T1"])).toEqual([]); + }); + + it("reports an included pen whose compat row leaves the tablet out", () => { + expect(compatIssues(["T0"])).toEqual([ + "ships a pen its pen-compat row doesn't list: wacom.pen.kp504e (add T1 to WACOM KP-504E)", + ]); + }); + + it("reports an included pen with no compat row at all", () => { + expect(compatIssues(null)).toEqual([ + "ships a pen its pen-compat row doesn't list: wacom.pen.kp504e (add T1 to WACOM KP-504E)", + ]); + }); +}); diff --git a/lib/data-quality.ts b/lib/data-quality.ts index ade4e5a..023717f 100644 --- a/lib/data-quality.ts +++ b/lib/data-quality.ts @@ -565,6 +565,41 @@ function runCrossEntityChecks(dataDir: string): Issue[] { } } + // A pen a tablet ships with works with that tablet, so pen-compat must say + // so. Sixteen tablets shipped a pen their pen-compat row didn't list, and + // the pen pages showed those tablets as incompatible (DrawTabData #54). + const compatKeys = new Set(); + for (const { record } of penCompat) { + const brand = getString(record, "Brand"); + const penId = getString(record, "PenId"); + const tabletIds = (record as { TabletIds?: unknown }).TabletIds; + if (!brand || !penId || !Array.isArray(tabletIds)) continue; + for (const tid of tabletIds) compatKeys.add(`${brand}|${penId}|${tid}`); + } + const penById = new Map(); + for (const { record } of pens) { + const id = getString(record, "EntityId"); + if (id) penById.set(id, { brand: getString(record, "Brand"), penId: getString(record, "PenId") }); + } + for (const { file, record } of tablets) { + const included = (record.Model as { IncludedPen?: unknown } | undefined)?.IncludedPen; + const tabletId = getNestedString(record, "Model", "Id"); + if (!Array.isArray(included) || !tabletId) continue; + for (const penEntityId of included) { + const pen = typeof penEntityId === "string" ? penById.get(penEntityId) : undefined; + if (!pen?.brand || !pen.penId) continue; // unknown pens are reported above + if (!compatKeys.has(`${pen.brand}|${pen.penId}|${tabletId}`)) { + issues.push({ + file, + entityId: getEntityId(record), + field: "PenCompat", + issue: "ships a pen its pen-compat row doesn't list", + value: `${penEntityId} (add ${tabletId} to ${pen.brand} ${pen.penId})`, + }); + } + } + } + // Pressure response references const pressureResponse = readAllInDir( dataDir, diff --git a/source/tablets/wacom/wacom.tablet.ctl4100wl.json b/source/tablets/wacom/wacom.tablet.ctl4100wl.json index ea82eae..68bc8f7 100644 --- a/source/tablets/wacom/wacom.tablet.ctl4100wl.json +++ b/source/tablets/wacom/wacom.tablet.ctl4100wl.json @@ -3,7 +3,7 @@ "EntityId": "wacom.tablet.ctl4100wl", "_id": "8e17d9b8-7a38-4c37-9add-6bdc82091525", "_CreateDate": "2025-12-12T12:33:46.983Z", - "_ModifiedDate": "2026-10-02T16:41:18.000Z" + "_ModifiedDate": "2026-10-02T19:57:22.862Z" }, "Model": { "Brand": "WACOM", @@ -91,7 +91,8 @@ }, "Density": "100", "ReportRate": "133", - "Tilt": "0" + "Tilt": "0", + "Type": "PASSIVE_EMR" }, "Physical": { "Dimensions": { diff --git a/source/tablets/wacom/wacom.tablet.dth246.json b/source/tablets/wacom/wacom.tablet.dth246.json index ded6e14..5e69df7 100644 --- a/source/tablets/wacom/wacom.tablet.dth246.json +++ b/source/tablets/wacom/wacom.tablet.dth246.json @@ -3,7 +3,7 @@ "EntityId": "wacom.tablet.dth246", "_id": "62cd9b73-c996-422b-93c4-4c128acdaf6e", "_CreateDate": "2025-12-12T12:33:46.983Z", - "_ModifiedDate": "2026-10-02T17:46:31.000Z" + "_ModifiedDate": "2026-10-02T19:57:18.221Z" }, "Model": { "Brand": "WACOM", @@ -127,8 +127,8 @@ }, "Physical": { "Dimensions": { - "Width": 340, - "Height": 568, + "Width": 568, + "Height": 340, "Depth": 21 }, "Weight": "5000", diff --git a/source/tablets/wacom/wacom.tablet.dtk168.json b/source/tablets/wacom/wacom.tablet.dtk168.json index bc9e6f9..5fdf5e7 100644 --- a/source/tablets/wacom/wacom.tablet.dtk168.json +++ b/source/tablets/wacom/wacom.tablet.dtk168.json @@ -3,7 +3,7 @@ "EntityId": "wacom.tablet.dtk168", "_id": "fae638fa-e305-434a-a2a6-f6852ec8c02e", "_CreateDate": "2025-12-12T12:33:46.983Z", - "_ModifiedDate": "2026-10-02T18:08:36.000Z" + "_ModifiedDate": "2026-10-02T19:57:21.120Z" }, "Model": { "Brand": "WACOM", @@ -115,8 +115,8 @@ }, "Physical": { "Dimensions": { - "Width": 259, - "Height": 384, + "Width": 384, + "Height": 259, "Depth": 15 }, "Weight": "2000", diff --git a/source/tablets/wacom/wacom.tablet.dtk246.json b/source/tablets/wacom/wacom.tablet.dtk246.json index cfb6cbe..1252e88 100644 --- a/source/tablets/wacom/wacom.tablet.dtk246.json +++ b/source/tablets/wacom/wacom.tablet.dtk246.json @@ -3,7 +3,7 @@ "EntityId": "wacom.tablet.dtk246", "_id": "98f59bc7-4ba6-49f0-b3b2-4acafcffe64c", "_CreateDate": "2025-12-12T12:33:46.983Z", - "_ModifiedDate": "2026-06-18T18:13:44.058Z" + "_ModifiedDate": "2026-10-02T19:57:19.680Z" }, "Model": { "Brand": "WACOM", @@ -76,8 +76,8 @@ }, "Physical": { "Dimensions": { - "Width": 340, - "Height": 568, + "Width": 568, + "Height": 340, "Depth": 21 }, "Weight": "4800"