import fs from 'node:fs' const read=p=>fs.readFileSync(p,'utf8') const failures=[] const check=(ok,msg)=>{if(!ok)failures.push(msg)} const manifest=read('packages/kbx-ui/src/registry/componentManifest.ts') const componentVersion=(name)=>{const match=manifest.match(new RegExp(`${name}[^\\n]+version:\\s*['\"]([^'\"]+)['\"]`));return match?.[1]??''} const atLeast=(actual,minimum)=>{const a=actual.split('.').map(Number),m=minimum.split('.').map(Number);for(let i=0;i<3;i++){if((a[i]??0)>(m[i]??0))return true;if((a[i]??0)<(m[i]??0))return false}return true} const boundary=read('packages/kbx-ui/src/components/KbxTemplateStateBoundary.vue') for(const token of ['errorRetryable?: boolean','errorRetryable:true',"props.errorRetryable ? retryLabel : ''"])check(boundary.includes(token),`TemplateStateBoundary retry truth missing: ${token}`) check(atLeast(componentVersion('KbxTemplateStateBoundary'),'1.3.0'),'KbxTemplateStateBoundary must be >= 1.3.0') for(const rel of ['KbxListPage.vue','KbxMasterPage.vue','KbxTransactionPage.vue','KbxFastEntryPage.vue','KbxMasterDetailPage.vue','KbxQueuePage.vue','KbxReconcilePage.vue','KbxImportPage.vue']){ const src=read(`packages/kbx-ui/src/components/${rel}`) check(src.includes('errorRetryable'),`${rel} must expose errorRetryable`) check(src.includes(':error-retryable="errorRetryable"'),`${rel} must propagate errorRetryable to the state boundary`) } const workQueue=read('packages/kbx-ui/src/components/KbxWorkQueuePage.vue') check(workQueue.includes(':error-retryable="errorRetryable"'),'KbxWorkQueuePage must preserve retryability through the T06 alias') const wmsShell=read('packages/kbx-ui/src/wms/KbxWmsMobilePage.vue') check(wmsShell.includes('errorRetryable?: boolean')&&wmsShell.includes(':error-retryable="errorRetryable !== false"'),'T09 shell must expose non-retryable errors') const workApi=read('apps/web/src/modules/wms/work/workApi.ts') check(workApi.includes("code:'WMS_WORK_QUERY_NOT_CONNECTED'")&&workApi.includes('retryable:false'),'Permanent WMS API-not-connected problem must not claim retryability') const workPage=read('apps/web/src/modules/wms/work/WmsWorkPage.vue') check(workPage.includes(':error-retryable="queryProblem?.retryable !== false"'),'WMS Work must bind problem retryability to T06') const home=read('packages/kbx-ui/src/shell/KbxHomePage.vue') for(const token of ['attentionToggleLabel','`우선 ${attentionExpandedLimit}개 보기`','attentionOperationCount','attentionNotificationCount','kbx-home__attention-centers','작업 센터 전체','알림 센터 전체'])check(home.includes(token),`Home overflow truth/navigation missing: ${token}`) check(atLeast(componentVersion('KbxHomePage'),'1.10.0'),'KbxHomePage must be >= 1.10.0') check(atLeast(componentVersion('KbxQueuePage'),'1.9.0'),'KbxQueuePage must be >= 1.9.0') const ref=read('apps/web/fe-reference/kbx-fe.js') for(const token of ['Array.from({length:18}','const attentionCap=24','`우선 ${attentionCap}개 보기`','attentionOverflow','homeJobsAll','homeNoticesAll',"showSystemPanel('jobs')","showSystemPanel('notices')"])check(ref.includes(token),`Reference Home overflow parity missing: ${token}`) const css=read('apps/web/fe-reference/kbx-fe.css') check(css.includes('.attention-centers'),'Reference theme must style Home attention overflow center links') const catalog=read('packages/kbx-ui/src/catalog/componentCatalog.ts') check(catalog.includes("id:'error-no-retry'")&&catalog.includes('재시도 불가 오류'),'Component catalog must reproduce non-retryable error state') if(failures.length){console.error('FE operational truth v50 validation failed:');for(const f of failures)console.error(`- ${f}`);process.exit(1)} console.log('FE operational truth v50 PASS: truthful retryability + bounded Home attention overflow routes + Light/Dark reference parity.')