import fs from 'node:fs' import path from 'node:path' const root=process.cwd(); const failures=[] const read=p=>fs.readFileSync(path.join(root,p),'utf8') const must=(text,needles,scope)=>needles.forEach(n=>{if(!text.includes(n))failures.push(`${scope} missing ${n}`)}) const manifest=read('packages/kbx-ui/src/registry/templateManifest.ts') for(const code of ['T01','T02','T03','T04','T05','T06','T07','T08','T09']){ if(!manifest.includes(`code:'${code}'`)) failures.push(`template manifest missing ${code}`) } for(const key of ['coreComponents','completionChecks']){ if((manifest.match(new RegExp(key,'g'))??[]).length<9) failures.push(`all T01-T09 must declare ${key}`) } must(manifest,['KbxFormGrid','KbxRecordLifecycle','KbxDataGrid','KbxJobProgress','KbxBarcodeCapture'],'template manifest core coverage') const formSection=read('packages/kbx-ui/src/components/KbxFormSection.vue') must(formSection,['--kbx-form-section-gap','--kbx-form-section-heading-gap',' ')}`) const scaffoldTest=read('scripts/validate-scaffolder.mjs') must(scaffoldTest,['fast-entry','KbxFastEntryPage','queue','KbxQueuePage','wms-mobile','KbxWmsMobilePage'],'scaffolder validator') const homeLogic=read('packages/kbx-ui/src/shell/homeNavigation.ts') must(homeLogic,['buildKbxHomeWorkbench','sourceLabel','dirty','favorite','recent','homePriority','seen'],'Home workbench policy') const home=read('packages/kbx-ui/src/shell/KbxHomePage.vue') must(home,['바로 시작','buildKbxHomeWorkbench','미저장 업무','모듈별 업무','sourceLabel'],'Home IA') if(home.includes('kbx-home__personal')) failures.push('Home must not restore duplicate legacy personal navigation blocks') const security=read('packages/kbx-ui/src/shell/navigationSecurity.ts') must(security,['matchKbxRoutePattern','resolveKbxSafeWorkspacePath','candidate.origin!==base.origin',"segment.startsWith(':')"],'navigation capability security') const routes=read('apps/web/src/router/appRoutes.ts') must(routes,['screenRoutePatterns','/:pathMatch(.*)*','NotFoundRoutePage.vue'],'router recovery') const app=read('apps/web/src/shell/KbxAppFrame.vue') if(app.includes(':screen-title=')||app.includes(':screen-id='))failures.push('AppFrame must not reveal denied screen metadata by default') must(app,['safeWorkspacePath','screenRoutePatterns','reconcilePreference','allowedEntries.value.find(item=>item.screenId===requested.screenId)','frameEmit(\'profile\')','권한 변경으로'],'AppFrame security/recovery') const access=read('packages/kbx-ui/src/shell/KbxAccessDenied.vue') must(access,['menuSearch','홈으로 이동','다른 업무 찾기','revealDetails?:boolean','revealDetails:false'],'AccessDenied recovery') const notFound=read('packages/kbx-ui/src/shell/KbxRouteNotFound.vue') must(notFound,['menuSearch','홈으로 이동','화면을 찾을 수 없습니다'],'RouteNotFound recovery') const header=read('packages/kbx-ui/src/shell/KbxGlobalHeader.vue') must(header,['modules?: string[]','moduleSelect','