[object Object]

← back to Gmc Titlefix

Reject MDC sources that claim both primary and supplemental types

165be730b4b3873ca3605b81f5de69e4763261fe · 2026-09-09 11:53:56 -0700 · Steve Abrams

Files touched

Diff

commit 165be730b4b3873ca3605b81f5de69e4763261fe
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 11:53:56 2026 -0700

    Reject MDC sources that claim both primary and supplemental types
---
 resolve-mdc-sample-source.mjs                    | 1 +
 test-mdc-source-guard.mjs                        | 5 +++++
 verification/tk11307-source-guard-e2e-proof.json | 9 +++++++--
 3 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/resolve-mdc-sample-source.mjs b/resolve-mdc-sample-source.mjs
index b7d280d..257b21f 100644
--- a/resolve-mdc-sample-source.mjs
+++ b/resolve-mdc-sample-source.mjs
@@ -23,6 +23,7 @@ export async function resolveSampleTitleDS(tok, {
       for (const key of ['primaryProductDataSource', 'supplementalProductDataSource']) {
         if (source[key] !== undefined && !isObject(source[key])) throw new Error('source listing has malformed source type');
       }
+      if (source.primaryProductDataSource !== undefined && source.supplementalProductDataSource !== undefined) throw new Error('source listing declares both primary and supplemental types');
       const rule = source.primaryProductDataSource?.defaultRule;
       if (rule !== undefined && !isObject(rule)) throw new Error('source listing has malformed default rule');
       seenNames.add(source.name);
diff --git a/test-mdc-source-guard.mjs b/test-mdc-source-guard.mjs
index 65c1cab..5bcceb0 100644
--- a/test-mdc-source-guard.mjs
+++ b/test-mdc-source-guard.mjs
@@ -52,6 +52,7 @@ globalThis.fetch = async (url, options = {}) => {
 `);
 
 const tests = [
+  ['dual source types with self-link', [{ body: { dataSources: [{ ...supplemental(2), primaryProductDataSource: { defaultRule: { takeFromDataSources: [{ supplementalDataSourceName: source(2) }] } } }] } }]],
   ['missing', [{ body: { dataSources: [primary([])] } }]],
   ['unlinked', [{ body: { dataSources: [primary([]), supplemental(2)] } }]],
   ['ambiguous', [{ body: { dataSources: [primary([2,3]), supplemental(2), supplemental(3)] } }]],
@@ -92,6 +93,7 @@ function cli(label, pages, explicitSource, expectedSuccess = false, builder = fa
     else if (label.startsWith('malformed default rule')) expected = 'source listing has malformed default rule';
     else if (label.startsWith('malformed link')) expected = 'source listing has malformed primary link';
     else expected = {
+      'dual source types with self-link': 'source listing declares both primary and supplemental types',
       missing: 'expected one primary-linked sample title source; found 0',
       unlinked: 'expected one primary-linked sample title source; found 0',
       ambiguous: 'expected one primary-linked sample title source; found 2',
@@ -134,6 +136,9 @@ cli('valid uniquely linked', [{ body: valid }], undefined, true);
 cli('valid explicit linked', [{ body: valid }], source(2), true);
 const paginated = [{ body: { dataSources: [primary([2])], nextPageToken: 'next with + /' } }, { body: { dataSources: [supplemental(2)] } }];
 cli('valid complete pagination', paginated, undefined, true);
+await assert.rejects(resolveSampleTitleDS('offline-token', { fetchImpl: async () => ({ ok: true, json: async () => tests[0][1][0].body }) }), /source listing declares both primary and supplemental types/);
+count++;
+console.log('PASS | resolver rejects dual source types with self-link');
 const pageLimitFetch = async () => ({ ok: true, json: async () => ({ ...valid, nextPageToken: 'more' }) });
 await assert.rejects(resolveSampleTitleDS('offline-token', { fetchImpl: pageLimitFetch, maxPages: 1 }), /page limit reached/);
 count++;
diff --git a/verification/tk11307-source-guard-e2e-proof.json b/verification/tk11307-source-guard-e2e-proof.json
index 189f991..38a8d26 100644
--- a/verification/tk11307-source-guard-e2e-proof.json
+++ b/verification/tk11307-source-guard-e2e-proof.json
@@ -15,8 +15,8 @@
     {
       "verdict": "PASS",
       "name": "CLI boundary negatives and stub happy path",
-      "assertions": 78,
-      "evidence": "/var/folders/rq/j8g1f7nn6jv6_lr1cfmqym6w0000gn/T/mdc-source-proof-mKrYkA"
+      "assertions": 82,
+      "evidence": "/var/folders/rq/j8g1f7nn6jv6_lr1cfmqym6w0000gn/T/mdc-source-proof-sgfHzG"
     },
     {
       "verdict": "PASS",
@@ -46,6 +46,11 @@
     {
       "verdict": "PASS",
       "name": "Existing TK10993 verification/e2e-proof.json preserved byte-for-byte from base 8bc8356; MDC evidence stored at ticket-scoped path"
+    },
+    {
+      "verdict": "PASS",
+      "name": "Dual primary and supplemental resource with self-link rejects across resolver, default pusher, explicit pusher, and builder",
+      "evidence": "/Users/macstudio3/Projects/ticket-system/data/codex-yoloforever/cycle-20260909T1836Z.rZWqkR/mdc-offline-tests.txt"
     }
   ],
   "retained_state": "All temporary fixtures and worktree retained; no cleanup deletion",

← 853cb8f Preserve historical proof and scope MDC evidence to its tick  ·  back to Gmc Titlefix  ·  TK-11233: 62-Malibu publish lane is DISQUALIFIED, not deprio a9c80ef →