// Local in-memory validation of the two delivered inputs; no network or production access. import fs from 'node:fs'; import crypto from 'node:crypto'; import assert from 'node:assert/strict'; import {createApplication} from '../server/src/core.mjs'; import {runWorkshop} from '../skills/ksf-workshop-runtime/scripts/ksf-workshop.mjs'; const data=JSON.parse(fs.readFileSync('server/references/runtime-data.json','utf8')); const rows={},copy=structuredClone;let seq=0; const store={ async transaction(fn){const before=copy(rows);try{return await fn(this);}catch(e){for(const key of Object.keys(rows))delete rows[key];Object.assign(rows,before);throw e;}}, async lock(){}, async get(name,id){return copy(rows[name]?.find(r=>r.objectId===id)||null);}, async list(name,{where={},afterId,limit=200}={}){return copy((rows[name]||[]).filter(r=>Object.entries(where).every(([key,v])=>JSON.stringify(r[key]??null)===JSON.stringify(v))).filter(r=>!afterId||r.objectId>afterId).sort((a,b)=>a.objectId.localeCompare(b.objectId)).slice(0,limit));}, async create(name,fields){for(const key of Object.keys(fields))assert.ok(data.tables[name][key],name+'.'+key);const row={...copy(fields),objectId:'local'+String(++seq).padStart(7,'0'),createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};(rows[name]??=[]).push(row);return copy(row);}, async update(name,id,fields){for(const key of Object.keys(fields))assert.ok(data.tables[name][key],name+'.'+key);const row=rows[name]?.find(r=>r.objectId===id);assert.ok(row);Object.assign(row,copy(fields));return copy(row);} }; const app=createApplication({store,crypto,Parse:{},data,settings:{signingSecret:'local-verification-only'},files:{}}); const actor={id:'local-input-check',kind:'service',scopes:['source:write','data:read','ingest:write'],projectIds:['*']}; const call=async(name,p)=>(await app.dispatch(name,JSON.parse(JSON.stringify(p)),actor)).data; const agentProfile={key:'agent-account:'+'a'.repeat(64),name:'本地输入校验夹具',role:'workshop',identityBasis:'runtime_config',runtimePlatform:'hermes',channel:'wecom',registeredAt:'2026-11-02T00:00:00Z',account:{platform:'wecom',accountId:'fixture-local-only'}}; const checks=[]; for(const key of ['suning','huajie']){ const task=JSON.parse(fs.readFileSync('work/current-product-observations-20261102/'+key+'.input.json','utf8')); assert.equal(task.source.sourceKind,'ecommerce');assert.equal(task.source.dataMode,'live');assert.equal(task.source.collectedAt,null); assert.equal(task.records.length,1);const record=task.records[0];assert.equal(record.className,'KfEvidence');assert.equal(record.data.productRef,undefined); assert.equal(record.data.collectedAt,null);assert.equal(record.data.evidenceKind,'retailer_listing_observation'); const locator=record.data.locator; assert.equal([...task.source.rawText].slice(locator.startCharacter,locator.endCharacter).join(''),record.data.quote); const preflight=await runWorkshop('workshop-dedup',task,{requestId:key+':preflight',call}); assert.equal(preflight.readOnly,true); assert.equal((rows.KfSource??[]).length,key==='suning'?0:1,'preflight does not create a source'); const result=await runWorkshop('workshop-import',task,{requestId:key+':import',agentProfile,call}); assert.equal(result.status,'succeeded');assert.equal(result.items.length,1);assert.equal(result.counts.created,1); const source=await store.get('KfSource',result.sourceId),evidence=await store.get('KfEvidence',result.items[0].objectId); for(const [field,value]of Object.entries(task.source))assert.deepEqual(source[field],value,key+' source '+field); for(const [field,value]of Object.entries(record.data))assert.deepEqual(evidence[field],value,key+' evidence '+field); assert.equal(evidence.sourceRef.objectId,result.sourceId);assert.equal(evidence.productRef??null,null); const replay=structuredClone(task);replay.records[0].externalKey+=':local-reuse-check'; const reused=await runWorkshop('workshop-import',replay,{requestId:key+':reuse-check',agentProfile,call}); assert.equal(reused.sourceOperation,'unchanged');assert.equal(reused.counts.created,0);assert.equal(reused.counts.unchanged,1); assert.equal(reused.sourceId,result.sourceId);assert.equal(reused.items[0].objectId,result.items[0].objectId); checks.push({source:key,inputMatchesSchema:true,importAndReadback:true,quoteUnicodeLocatorMatched:true,unknownTimePreserved:true,duplicateReused:true}); } assert.equal(rows.KfSource.length,2);assert.equal(rows.KfEvidence.length,2); for(const name of ['KfCategory','KfProduct','KfSession','KfArtifact','KfProject','KfConcept'])assert.equal((rows[name]??[]).length,0,name); const result={ok:true,operation:'page-observation-inputs-validated-locally',at:new Date().toISOString(),scope:'in-memory real backend and workshop wrapper; not production',checks,networkRequests:0,productionBusinessWrites:0,localSourceCount:2,localEvidenceCount:2,localProductCount:0,localReportCount:0}; fs.writeFileSync('work/current-product-observation-inputs-verified-20261102.json',JSON.stringify(result,null,2)+'\n'); console.log(JSON.stringify(result,null,2));