| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 |
- // Captured deployed handlers in RAM only. No imports of the production client.
- import fs from 'node:fs';
- import vm from 'node:vm';
- import crypto from 'node:crypto';
- import assert from 'node:assert/strict';
- import {runWorkshop} from '../skills/ksf-workshop-runtime/scripts/ksf-workshop.mjs';
- const output='work/S5-completion-isolated-simulation-20261103.json';assert.equal(fs.existsSync(output),false);
- const dir='work/S5-classroom-completion-20261103/';
- const rb=JSON.parse(fs.readFileSync(dir+'runbook.json','utf8'));
- const template=JSON.parse(fs.readFileSync(dir+'01-project-complete.template.json','utf8'));
- const audit=JSON.parse(fs.readFileSync('work/S5-completion-live-preflight-20261103.json','utf8'));assert.equal(audit.ok,true);
- const sha=x=>crypto.createHash('sha256').update(x).digest('hex'),copy=structuredClone;
- function embedded(code,name){
- const marker='runDatabaseFunction('+JSON.stringify(name)+',request,response,',at=code.lastIndexOf(marker)+marker.length;assert.ok(at>=marker.length);
- let depth=0,quoted=false,escaped=false;
- for(let i=at;i<code.length;i++){const c=code[i];if(quoted){if(escaped)escaped=false;else if(c==='\\')escaped=true;else if(c==='"')quoted=false;continue;}
- if(c==='"')quoted=true;else if(c==='{')depth++;else if(c==='}'&&--depth===0)return JSON.parse(code.slice(at,i+1));}
- throw Error('Missing embedded runtime schema');
- }
- const modules=new Map(),proof=[];let data,step='load captured deployed code';
- for(const s of audit.deployedCodeChecks){const code=fs.readFileSync(s.file,'utf8');assert.equal(sha(code),s.sha256);const runtime=embedded(code,s.name);if(data)assert.deepEqual(runtime,data);else data=runtime;
- modules.set(s.name,vm.runInNewContext(code+'\n;({createApplication,attachWorkshopHandlers});',{Buffer,structuredClone,URL,URLSearchParams,TextEncoder,TextDecoder},{timeout:5000}));proof.push({name:s.name,sha256:s.sha256});}
- const actor={id:'isolated-completion-service',kind:'service',scopes:['ingest:write','data:read'],projectIds:['*']};
- const attribution={agentKey:'agent-account:'+'1'.repeat(64),agentName:'隔离模拟助手',agentKeyOrigin:'runtime_config',participantKey:'isolated-completion-participant',participantName:'隔离模拟参与者',channel:'wecom',agentRuntimePlatform:'hermes',agentChannel:'wecom'};
- const agentProfile={key:attribution.agentKey,name:attribution.agentName,identityBasis:'runtime_config',role:'workshop',runtimePlatform:'hermes',channel:'wecom'};
- const sessionId='simCompletionSession',projectId='simCompletionProject',conceptId='simCompletionConcept',ptr=(className,objectId)=>({__type:'Pointer',className,objectId});
- const db=new Map(),rows=n=>{if(!db.has(n))db.set(n,[]);return db.get(n);};let serial=0;
- const equal=(a,b)=>JSON.stringify(a??null)===JSON.stringify(b??null);
- const store={
- async transaction(fn){const before=copy(db);try{return await fn(this);}catch(e){db.clear();for(const [k,v]of before)db.set(k,v);throw e;}},
- async lock(){},async get(name,id){return copy(rows(name).find(r=>r.objectId===id)??null);},
- async list(name,{where={},afterId,limit=1000}={}){return copy(rows(name).filter(r=>Object.entries(where).every(([k,v])=>equal(r[k],v))).filter(r=>!afterId||r.objectId>afterId).sort((a,b)=>a.objectId.localeCompare(b.objectId)).slice(0,limit));},
- async create(name,input){for(const k of Object.keys(input))assert.ok(data.tables[name][k],name+'.'+k);const r={...copy(input),objectId:'simComplete'+String(++serial).padStart(5,'0'),createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};rows(name).push(r);return copy(r);},
- async update(name,id,input){for(const k of Object.keys(input))assert.ok(data.tables[name][k],name+'.'+k);const r=rows(name).find(x=>x.objectId===id);assert.ok(r);Object.assign(r,copy(input),{updatedAt:new Date().toISOString()});return copy(r);}
- };
- rows('KfSession').push({objectId:sessionId,dataMode:'live',workshopKey:'isolated-completion-workshop',stageCode:'S5',groupId:'',meta:{ownerId:actor.id,attribution}});
- rows('KfProject').push({objectId:projectId,dataMode:'live',workshopKey:'isolated-completion-workshop',stageCode:'S5',stage:'S5 场景与卖点设计',status:'workshop',progressState:'in_progress',ownerSession:sessionId,sessionRef:ptr('KfSession',sessionId),currentConceptRef:ptr('KfConcept',conceptId),artifactCount:3,groupId:'',meta:{ownerId:actor.id,participantKey:attribution.participantKey,attribution,pendingQuestions:copy(rb.pendingQuestions)}});
- rows('KfConcept').push({objectId:conceptId,projectRef:ptr('KfProject',projectId),version:1,reviewState:'pending',specification:{netWeightG:null,noodleWeightG:null,packCount:null,currentSku:null},productionSteps:[]});
- for(const [i,s]of ['S3','S4','S5'].entries())rows('KfArtifact').push({objectId:'simReport'+i,stageCode:s,projectRef:ptr('KfProject',projectId),version:1,rawText:'unchanged report '+s,meta:{workshop:{pendingQuestions:copy(i===2?rb.pendingQuestions:rb.pendingQuestions.slice(0,8)),formalConfirmation:false}},dashboardPublishedAt:null});
- for(const [i,t]of ['project_created','task_submitted','stage_changed','task_submitted','stage_changed','concept_saved','task_submitted'].entries())rows('KfStageEvent').push({objectId:'simOldEvent'+i,eventType:t,sequence:i+1,projectRef:ptr('KfProject',projectId)});
- const initial=copy(db),reset=()=>{db.clear();for(const [k,v]of copy(initial))db.set(k,v);};
- const apps=new Map();for(const [name,impl]of modules){const app=impl.createApplication({store,crypto,Parse:{},data,settings:{},files:{}});impl.attachWorkshopHandlers(app,{store,data});apps.set(name,app);}
- const call=async(name,params)=>copy((await (apps.get(name)||apps.get('ksfTransitionProject')).dispatch(name,copy(params),actor)).data);
- const input={...copy(template),sessionId,projectId},statusInput={sessionId,projectId};
- const checks=[],calls=[];const check=(name,fn)=>{step=name;fn();checks.push({name,passed:true});};
- const execute=async(i,task)=>{const s=rb.steps[i];const result=await runWorkshop(s.command,copy(task),{requestId:s.requestId,agentProfile,call});calls.push({step:s.step,command:s.command,requestId:s.requestId,ok:true});return result;};
- try{
- for(const control of ['missing-S5-report','missing-concept','not-S5']){
- reset();if(control==='missing-S5-report')rows('KfArtifact').splice(2,1);else if(control==='missing-concept')delete rows('KfProject')[0].currentConceptRef;else rows('KfProject')[0].stageCode='S4';
- const p={...copy(input),requestId:'isolated-completion-negative-'+control,...(control==='not-S5'?{toStage:'S4'}:{})};const before=copy(db);let code;
- try{await call('ksfTransitionProject',p);}catch(e){code=e.code;}
- check(control+' refuses completion without any mutation',()=>{assert.equal(code,control==='not-S5'?'VALIDATION_FAILED':'STAGE_REQUIREMENTS_NOT_MET');assert.deepEqual(db,before);assert.equal((db.get('KsfOperation')||[]).length,0);});
- }
- reset();const before=await execute(0,statusInput);
- check('precheck has S5 in progress, three reports, one concept, seven events and thirteen questions',()=>{assert.equal(before.project.stageCode,'S5');assert.equal(before.project.progressState,'in_progress');assert.equal(before.artifacts.length,3);assert.equal(before.concept.objectId,conceptId);assert.equal(before.events.length,7);assert.deepEqual(before.project.meta.pendingQuestions,rb.pendingQuestions);});
- const completed=await execute(1,input),final=await execute(2,statusInput);
- check('one completion updates progress and emits exact eighth event without approval',()=>{
- assert.equal(completed.projectId,projectId);assert.equal(completed.stageCode,'S5');assert.equal(completed.progressState,'completed');assert.equal(completed.enterpriseApproval,false);
- assert.equal(final.project.progressState,'completed');assert.equal(final.project.status,'workshop');assert.equal(final.events.length,8);const ev=final.events[7];assert.equal(ev.objectId,completed.eventId);assert.equal(ev.eventType,'completed');assert.equal(ev.sequence,8);assert.equal(ev.fromStage,'S5');assert.equal(ev.toStage,'S5');assert.equal(ev.note,template.note);
- });
- check('reports, concept, formula state and thirteen questions remain unchanged',()=>{
- assert.deepEqual(rows('KfArtifact'),initial.get('KfArtifact'));assert.deepEqual(rows('KfConcept'),initial.get('KfConcept'));assert.deepEqual(final.formulaItems,[]);assert.deepEqual(final.project.meta.pendingQuestions,rb.pendingQuestions);assert.equal(final.artifacts.length,3);assert.deepEqual(final.events.slice(0,7),initial.get('KfStageEvent'));assert.equal(rows('KfSource').length,0);
- });
- const snapshot=copy(db);const replay=await call('ksfTransitionProject',{...copy(input),requestId:rb.steps[1].requestId});
- check('RAM-only replay of exact key returns same receipt without a second event',()=>{assert.deepEqual(replay,completed);assert.deepEqual(db,snapshot);assert.equal(rows('KsfOperation').length,1);});
- let duplicateCode;try{await call('ksfTransitionProject',{...copy(input),requestId:'isolated-completion-different-key'});}catch(e){duplicateCode=e.code;}
- check('different key after completed is refused without another write',()=>{assert.equal(duplicateCode,'VERSION_CONFLICT');assert.deepEqual(db,snapshot);assert.equal(rows('KsfOperation').length,1);});
- check('three CLI steps, one committed operation and no new business entities',()=>{
- assert.equal(calls.length,3);assert.deepEqual(calls.map(c=>c.requestId),rb.steps.map(s=>s.requestId));assert.equal(rows('KsfOperation').length,1);assert.equal(rows('KsfOperation')[0].requestId,rb.steps[1].requestId);
- for(const n of ['KfOpportunity','KfFormulaItem','KfProduct','KfSku','KfMaterial','KfProcess'])assert.equal(rows(n).length,0);
- });
- const r={ok:true,at:new Date().toISOString(),simulatedOnly:true,productionCalls:0,productionWrites:0,checksPassed:checks.length,checks,calls,deployedCode:proof,
- negativeControls:['S5 report required','current concept required','only S5 may complete'],idempotencyControls:['Same-key replay in RAM only','Different key after completed refused'],
- expectedAfter:rb.expectedAfter,limits:['Synthetic identity and RAM store, not a production transaction or current-message identity test.','Exact-key replay here is an isolated test; production runbook still forbids automatic retries.']};
- fs.writeFileSync(output,JSON.stringify(r,null,2)+'\n',{flag:'wx'});console.log(JSON.stringify({ok:true,checksPassed:checks.length,commandSteps:3,negativeControls:3,idempotencyControls:2,productionCalls:0,productionWrites:0}));
- }catch(error){const r={ok:false,step,checksPassed:checks.length,commandsCompleted:calls.length,errorCode:'S5_COMPLETION_SIMULATION_STOPPED',failureCode:error.code??null,checkLine:error.stack?.match(/simulate-S5-completion\.mjs:(\d+):/)?.[1]??null,productionCalls:0,productionWrites:0};fs.writeFileSync(output,JSON.stringify(r,null,2)+'\n',{flag:'wx'});console.error(JSON.stringify(r));process.exitCode=1;}
|