| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- // Production reads only; completion is reserved for the real-message executor.
- import fs from 'node:fs';
- import crypto from 'node:crypto';
- import assert from 'node:assert/strict';
- import {invoke,classRows} from '../server/scripts/platform-client.mjs';
- import {parseArgs} from '../skills/ksf-workshop-runtime/scripts/ksf-secure.mjs';
- const output='work/S5-completion-live-preflight-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 saved=JSON.parse(fs.readFileSync('work/S5-classroom-next-20261103/03-project-S5-save.template.json','utf8'));
- const conceptTemplate=JSON.parse(fs.readFileSync('work/S5-classroom-next-20261103/02-concept-save.template.json','utf8'));
- const sha=x=>crypto.createHash('sha256').update(x).digest('hex'),ptr=(className,objectId)=>({__type:'Pointer',className,objectId});
- let step='local template',readCalls=0;const checks=[],snapshots=[];
- const check=(name,fn)=>{step=name;fn();checks.push({name,passed:true});};
- const fetchRead=async(name,p)=>{readCalls++;const r=await invoke(name,p);assert.equal(r.http,200);assert.equal(r.body?.result?.ok,true);return r.body.result.data;};
- const read=async(className,objectId)=>{const r=await fetchRead('ksfGetRecord',{className,objectId});assert.equal(r.record?.objectId,objectId);return r.record;};
- const rows=async(name,where)=>{readCalls++;const r=await classRows(name,where);assert.ok(r.length<1000);return r;};
- const group=async tasks=>{const rs=await Promise.allSettled(tasks);assert.deepEqual(rs.flatMap((r,i)=>r.status==='rejected'?[i]:[]),[],'Read failure indexes');return rs.map(r=>r.value);};
- const normalize=x=>x.replace(/\r\n/g,'\n');
- const block=(text,start,end)=>{const a=text.indexOf(start),b=text.indexOf(end,a+start.length);assert.ok(a>=0&&b>a);return text.slice(a,b).trim();};
- try{
- check('one fixed completion template, three stable requests and legal CLI commands',()=>{
- assert.equal(sha(fs.readFileSync(dir+'01-project-complete.template.json')),rb.templateSha256);
- assert.deepEqual(Object.keys(template),['sessionId','projectId','toStage','state','note']);assert.equal(template.sessionId,null);assert.equal(template.projectId,null);
- assert.equal(template.toStage,'S5');assert.equal(template.state,'completed');assert.ok(template.note.length<=500);assert.ok(template.note.includes('13项'));
- assert.equal(rb.steps.length,3);assert.equal(new Set(rb.steps.map(s=>s.requestId)).size,3);assert.deepEqual(rb.steps.map(s=>s.command),['project-status','project-progress','project-status']);
- for(const s of rb.steps){const args=[s.command,'--input','F:/isolated-completion/'+s.inputFile,'--request-id',s.requestId];if(s.command==='project-progress')args.push('--agent-profile','agent-account:'+'1'.repeat(64));assert.equal(parseArgs(args).command,s.command);}
- assert.deepEqual(rb.pendingQuestions,saved.pendingQuestions);assert.equal(rb.pendingQuestions.length,13);assert.equal(rb.retainedS5SaveRequestIdDeviation.expectedKeyCommitted,false);
- });
- step='live read-only prerequisites';
- const names=['ksfTransitionProject','ksfGetWorkshopProject'];
- const [status,session,artifact,concept,opportunity,contract,functions,ops,concepts]=await group([
- fetchRead('ksfGetWorkshopProject',{sessionId:rb.sessionId,projectId:rb.projectId}),read('KfSession',rb.sessionId),read('KfArtifact',rb.s5ArtifactId),read('KfConcept',rb.conceptId),read('KfOpportunity',rb.opportunityId),
- fetchRead('ksfGetIngestContract',{}),rows('Function',{name:{$in:names}}),
- rows('KsfOperation',{requestId:{$in:[...rb.steps.map(s=>s.requestId),rb.retainedS5SaveRequestIdDeviation.expected,rb.retainedS5SaveRequestIdDeviation.actual]}}),
- rows('KfConcept',{projectRef:ptr('KfProject',rb.projectId)})
- ]);
- check('same S5 project and trusted-message owner attribution remain consistent',()=>{
- const p=status.project;assert.equal(p.objectId,rb.projectId);assert.equal(p.stageCode,'S5');assert.equal(p.progressState,'in_progress');assert.equal(p.status,'workshop');assert.equal(p.dataMode,'live');
- assert.equal(p.ownerSession,rb.sessionId);assert.deepEqual(p.sessionRef,ptr('KfSession',rb.sessionId));assert.equal(session.workshopKey,'KSF-真实资料-20261102-01');assert.equal(session.stageCode,'S5');
- assert.deepEqual(p.meta.attribution,session.meta.attribution);assert.deepEqual(artifact.meta.attribution,session.meta.attribution);assert.equal(p.agentId,session.meta.attribution.agentKey);
- assert.deepEqual(p.opportunityRef,ptr('KfOpportunity',rb.opportunityId));assert.equal(opportunity.linkedProject,rb.projectId);assert.equal(opportunity.claimKind,'hypothesis');assert.equal(opportunity.status,'approved');
- });
- check('three project reports and exact original seven events, without completion',()=>{
- assert.equal(status.project.artifactCount,3);assert.deepEqual(status.artifacts.map(x=>x.objectId).sort(),rb.projectArtifactIds.toSorted());
- assert.deepEqual(status.events.map(x=>x.objectId),rb.existingEventIds);assert.deepEqual(status.events.map(x=>x.sequence),[1,2,3,4,5,6,7]);
- assert.deepEqual(status.events.map(x=>x.eventType),['project_created','task_submitted','stage_changed','task_submitted','stage_changed','concept_saved','task_submitted']);
- });
- check('same concept v1, thirteen unresolved questions and empty formula/process remain',()=>{
- assert.deepEqual(status.project.currentConceptRef,ptr('KfConcept',rb.conceptId));assert.equal(status.concept.objectId,rb.conceptId);assert.equal(concept.version,1);assert.deepEqual(concepts.map(c=>c.objectId),[rb.conceptId]);
- for(const k of ['name','targetAudience','usageScenario','specification','valueProposition','flavor','sellingPoints','productionSteps'])assert.deepEqual(concept[k],conceptTemplate.concept[k]);
- assert.deepEqual(status.formulaItems,[]);assert.deepEqual(concept.productionSteps,[]);assert.deepEqual(status.project.meta.pendingQuestions,rb.pendingQuestions);assert.deepEqual(artifact.meta.workshop.pendingQuestions,rb.pendingQuestions);
- });
- check('existing S5 report fully matches accepted template and stays unconfirmed/unpublished',()=>{
- for(const k of ['title','rawText','version','stageCode','artifactType'])assert.deepEqual(artifact[k],saved[k]);
- assert.equal(artifact.reportSeriesKey,saved.taskKey);assert.equal(artifact.dashboardState,'workshop');assert.equal(artifact.dataMode,'live');assert.deepEqual(artifact.projectRef,ptr('KfProject',rb.projectId));
- for(const k of ['chapters','evidence','method','sources','subtitle'])assert.deepEqual(artifact.report[k],saved.report[k]);
- assert.deepEqual(artifact.meta.workshop.sourceIds,saved.sourceIds);assert.equal(artifact.evidenceCount,4);
- assert.equal(artifact.contentConfirmedAt??null,null);assert.equal(artifact.dashboardPublishedAt??null,null);assert.equal(artifact.meta.workshop.formalConfirmation,false);
- });
- check('completion write has no success audit; S5 save deviation is preserved',()=>{
- assert.equal(ops.filter(o=>rb.steps.some(s=>s.requestId===o.requestId)).length,0);assert.equal(ops.length,1);
- const op=ops[0];assert.equal(op.requestId,rb.retainedS5SaveRequestIdDeviation.actual);assert.equal(op.receipt.artifactId,rb.s5ArtifactId);assert.equal(op.receipt.sourceId,rb.s5OutputSourceId);
- assert.equal(op.receipt.formalReportPublished,false);assert.equal(op.receipt.employeeConfirmed,false);assert.equal(op.receipt.boardVisible,true);
- });
- const workshop=normalize(fs.readFileSync('server/src/workshop.mjs','utf8').replace(/^export /gm,''));
- const core=normalize(fs.readFileSync('server/src/core.mjs','utf8').replace(/^export /gm,''));
- const common=[
- {name:'session/project/event',code:block(workshop,'const own=','\n const contract=app.handlers.ksfGetIngestContract;')},
- {name:'idempotency',code:block(core,'async function operation(','\n function validateValue(')}
- ];
- for(const name of names)check('deployed '+name+' completion/state helpers match inspected implementation',()=>{
- const found=functions.filter(f=>f.name===name);assert.equal(found.length,1);const code=normalize(found[0].code);
- const own={name,code:block(workshop,'app.handlers.'+name+'=',name==='ksfGetWorkshopProject'?'\n const fields=':'\n app.handlers.')};
- assert.ok([own,...common].every(b=>code.includes(b.code)));
- const file='work/S5-completion-deployed-'+name+'-20261103.js';fs.writeFileSync(file,found[0].code,{flag:'wx'});snapshots.push({name,file,sha256:sha(found[0].code)});
- });
- check('classroom completion capability requires S5 report and concept; preserves separate approval',()=>{
- assert.equal(contract.schemaVersion,'ksf.v0.7');assert.equal(contract.capabilities.workshopProjectTransition,true);
- const f=block(workshop,'app.handlers.ksfTransitionProject=','\n app.handlers.');
- for(const x of ["p.state==='completed'&&p.toStage!=='S5'","p.state==='completed'&&!proj.currentConceptRef","artifacts.some(a=>a.stageCode===proj.stageCode)","p.state==='completed'?'completed'","enterpriseApproval:false"])assert.ok(f.includes(x));
- });
- const result={ok:true,at:new Date().toISOString(),readOnly:true,productionBusinessWrites:0,readCalls,checksPassed:checks.length,checks,
- currentStage:'S5',progressState:'in_progress',projectId:rb.projectId,conceptId:rb.conceptId,artifactCount:3,eventCount:7,pendingQuestionCount:13,
- futureWriteRequestId:rb.steps[1].requestId,matchedFutureWriteAudits:0,priorSaveDeviationRetained:true,deployedCodeChecks:snapshots,
- contract:{schemaVersion:contract.schemaVersion,contractRevision:contract.contractRevision,release:contract.release},
- currentMessageIdentityVerifiedByCodex:false,remoteFilesystemAndProcessesInspected:false,browserVerified:false,
- limits:['Current-message identity, remote files and in-flight processes still require executor checks.','Read request IDs are not guaranteed to create KsfOperation rows.','Preparation does not mark the production project completed.']};
- fs.writeFileSync(output,JSON.stringify(result,null,2)+'\n',{flag:'wx'});console.log(JSON.stringify({ok:true,checksPassed:checks.length,readCalls,currentStage:result.currentStage,progressState:result.progressState,productionBusinessWrites:0}));
- }catch(error){const r={ok:false,step,checksPassed:checks.length,readCalls,productionBusinessWrites:0,errorCode:'S5_COMPLETION_PREFLIGHT_STOPPED',checkLine:error.stack?.match(/verify-S5-completion-preflight\.mjs:(\d+):/)?.[1]??null};fs.writeFileSync(output,JSON.stringify(r,null,2)+'\n',{flag:'wx'});console.error(JSON.stringify(r));process.exitCode=1;}
|