| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- // Production access is exclusively read-only. No writes, transition or concept save.
- 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-classroom-live-preflight-20261103.json';
- assert.equal(fs.existsSync(output),false);
- const folder='work/S5-classroom-next-20261103/';
- const runbook=JSON.parse(fs.readFileSync(folder+'runbook.json','utf8'));
- const report=JSON.parse(fs.readFileSync(folder+'03-project-S5-save.template.json','utf8'));
- const concept=JSON.parse(fs.readFileSync(folder+'02-concept-save.template.json','utf8'));
- const s4=JSON.parse(fs.readFileSync('work/S4-classroom-next-20261103/05-project-S4-save.template.json','utf8'));
- const ptr=(className,objectId)=>({__type:'Pointer',className,objectId});
- const sha=x=>crypto.createHash('sha256').update(x).digest('hex');
- const checks=[],snapshots=[];let step='local fields and CLI';
- const check=(name,fn)=>{step=name;fn();checks.push({name,passed:true});};
- const read=async(className,objectId)=>{
- const r=await invoke('ksfGetRecord',{className,objectId});assert.equal(r.http,200);assert.equal(r.body?.result?.ok,true);
- assert.equal(r.body.result.data.record?.objectId,objectId);return r.body.result.data.record;
- };
- const group=async tasks=>{
- const r=await Promise.allSettled(tasks);assert.deepEqual(r.flatMap((x,i)=>x.status==='rejected'?[i]:[]),[],'Read failure indexes');return r.map(x=>x.value);
- };
- const block=(text,start,end)=>{const at=text.indexOf(start),finish=text.indexOf(end,at+start.length);assert.ok(at>=0&&finish>at);return text.slice(at,finish).trim();};
- const normalize=s=>s.replace(/\r\n/g,'\n');
- try{
- check('fixed source evidence, original eight questions and all thirteen align',()=>{
- assert.deepEqual(report.sourceIds,s4.sourceIds);assert.deepEqual(report.report.evidence,s4.report.evidence);
- assert.deepEqual(report.pendingQuestions.slice(0,8),s4.pendingQuestions);assert.equal(report.pendingQuestions.length,13);
- assert.deepEqual(report.pendingQuestions,concept.concept.pendingQuestions);
- const tables=report.report.chapters.flatMap(c=>c.sections).filter(s=>s.kind==='table');
- const pending=tables.find(s=>s.title==='待补问题');assert.equal(pending.data.rows.length,13);
- assert.deepEqual(pending.data.rows.map(x=>x[1]),report.pendingQuestions);
- assert.equal(concept.sessionId,null);assert.equal(concept.projectId,null);assert.equal(report.sessionId,null);assert.equal(report.projectId,null);
- assert.deepEqual(concept.formulaItems,[]);assert.deepEqual(concept.concept.productionSteps,[]);
- for(const k of ['netWeightG','noodleWeightG','packCount','currentSku'])assert.equal(concept.concept.specification[k],null);
- for(const k of ['name','targetAudience','usageScenario','valueProposition'])assert.ok(concept.concept[k].length<=200);
- assert.equal('opportunity' in report,false);assert.equal(report.artifactType,'concept');assert.equal(report.stageCode,'S5');
- });
- check('eight stable keys and legal CLI command spelling',()=>{
- assert.equal(runbook.steps.length,8);assert.equal(new Set(runbook.steps.map(s=>s.requestId)).size,8);
- for(const s of runbook.steps){
- const args=[s.command,'--input','F:/isolated-s5-check/'+s.inputFile,'--request-id',s.requestId];
- if(['project-progress','concept-save','workshop-save'].includes(s.command))args.push('--agent-profile','agent-registration:11111111-1111-4111-8111-111111111111');
- assert.equal(parseArgs(args).command,s.command);
- }
- });
- const names=['ksfTransitionProject','ksfSaveWorkshopConcept','ksfSaveWorkshopTask','ksfGetWorkshopProject'];
- step='live state, source, contract, audit and handler read';
- const [statusResponse,savedS4,session,contractResponse,functions,ops,concepts,s5Rows]=await group([
- invoke('ksfGetWorkshopProject',{sessionId:runbook.sessionId,projectId:runbook.projectId}),
- read('KfArtifact',runbook.priorS4ArtifactId),read('KfSession',runbook.sessionId),invoke('ksfGetIngestContract',{}),
- classRows('Function',{name:{$in:names}}),classRows('KsfOperation',{requestId:{$in:runbook.steps.map(s=>s.requestId)}}),
- classRows('KfConcept',{projectRef:ptr('KfProject',runbook.projectId)}),
- classRows('KfArtifact',{reportSeriesKey:runbook.taskKey})
- ]);
- assert.equal(statusResponse.http,200);assert.equal(statusResponse.body?.result?.ok,true);
- assert.equal(contractResponse.http,200);assert.equal(contractResponse.body?.result?.ok,true);
- const status=statusResponse.body.result.data,contract=contractResponse.body.result.data;
- check('current project remains S4, two reports, four events, no concept or pending S5 writes',()=>{
- assert.equal(status.project.objectId,runbook.projectId);assert.equal(status.project.stageCode,'S4');assert.equal(status.project.progressState,'in_progress');
- assert.equal(status.project.ownerSession,runbook.sessionId);assert.equal(status.project.dataMode,'live');assert.equal(status.project.status,'workshop');
- assert.deepEqual(status.project.opportunityRef,ptr('KfOpportunity',runbook.opportunityId));
- assert.deepEqual(status.artifacts.map(x=>x.objectId).sort(),[runbook.priorS3ArtifactId,runbook.priorS4ArtifactId].sort());
- assert.equal(status.events.length,4);assert.deepEqual(status.events.map(x=>x.eventType),['project_created','task_submitted','stage_changed','task_submitted']);
- assert.equal(status.concept,null);assert.equal(status.project.currentConceptRef??null,null);assert.deepEqual(status.formulaItems,[]);
- assert.equal(ops.length,0);assert.equal(concepts.length,0);assert.equal(s5Rows.length,0);assert.equal(session.stageCode,'S4');
- });
- check('independently read S4 still matches original template and ownership',()=>{
- assert.equal(savedS4.rawText,s4.rawText);assert.deepEqual(savedS4.report.chapters,s4.report.chapters);assert.deepEqual(savedS4.report.evidence,s4.report.evidence);
- assert.deepEqual(savedS4.meta.workshop.pendingQuestions,s4.pendingQuestions);assert.deepEqual(savedS4.meta.workshop.sourceIds,s4.sourceIds);
- assert.equal(savedS4.reportSeriesKey,s4.taskKey);assert.equal(savedS4.dataMode,'live');assert.equal(savedS4.dashboardState,'workshop');
- assert.deepEqual(savedS4.projectRef,ptr('KfProject',runbook.projectId));assert.deepEqual(savedS4.meta.attribution,session.meta.attribution);
- });
- const evidenceIds={'consumer-cq-03':'_9mUml0wmh','consumer-cq-04':'2bFGP7axMo','public-p1':'xHGV_mRWcG','public-p2':'CeAEIwymTa'};
- step='exact source and evidence read';
- const entries=Object.entries(report.report.evidence);
- const sources=await group(entries.flatMap(([key,e])=>[read('KfSource',e.sourceId),read('KfEvidence',evidenceIds[key])]));
- for(const [i,[key,e]]of entries.entries())check(key+' preserved quotation and binding',()=>{
- const source=sources[i*2],evidence=sources[i*2+1];assert.equal(source.dataMode,'live');assert.equal(evidence.dataMode,'live');
- assert.equal(evidence.quote,e.quotes[0].text);assert.ok(source.rawText.includes(evidence.quote));assert.ok(report.rawText.includes(evidence.quote));
- assert.deepEqual(evidence.sourceRef,ptr('KfSource',source.objectId));assert.equal(evidence.productRef??null,null);
- });
- 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 blocks=[
- ...names.map(n=>({name:n,code:block(workshop,'app.handlers.'+n+'=',n==='ksfGetWorkshopProject'?'\n const fields=':'\n app.handlers.')})),
- {name:'reportOf',code:block(workshop,'function reportOf(','\n app.handlers.ksfSaveWorkshopTask=')},
- {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('current deployed '+name+' matches relevant command and helpers',()=>{
- const matches=functions.filter(f=>f.name===name);assert.equal(matches.length,1);const f=matches[0];assert.equal(typeof f.code,'string');
- const code=normalize(f.code);const relevantBlocks=blocks.map(b=>({name:b.name,matched:code.includes(b.code)}));assert.ok(relevantBlocks.every(b=>b.matched));
- const filename='work/S5-deployed-'+name+'-20261103.js';fs.writeFileSync(filename,f.code,{flag:'wx'});
- snapshots.push({name,functionObjectId:f.objectId,sha256:sha(f.code),file:filename,relevantBlocks});
- });
- check('workshop capability and table field mapping',()=>{
- assert.equal(contract.schemaVersion,'ksf.v0.7');assert.equal(contract.capabilities.workshopProjectTransition,true);
- assert.ok(contract.reportTypes.some(t=>t.code==='workshop_auxiliary'));
- const conceptBlock=blocks.find(b=>b.name==='ksfSaveWorkshopConcept').code;
- assert.ok(conceptBlock.includes('pendingQuestions:p.concept.pendingQuestions'));assert.ok(conceptBlock.includes('currentConceptRef:pointer'));
- });
- const result={ok:true,at:new Date().toISOString(),readOnly:true,productionBusinessWrites:0,
- operation:'S5-current-state-and-command-preflight',checksPassed:checks.length,checks,sessionId:runbook.sessionId,projectId:runbook.projectId,
- currentStage:'S4',progressState:'in_progress',projectArtifacts:status.artifacts.map(a=>a.objectId),projectEventCount:4,conceptCount:concepts.length,
- s5ArtifactCount:s5Rows.length,matchedFutureWriteAuditRows:ops.length,checkedRequestIds:runbook.steps.map(s=>s.requestId),
- contract:{schemaVersion:contract.schemaVersion,contractRevision:contract.contractRevision,release:contract.release},deployedCodeChecks:snapshots,
- pendingQuestionCount:13,originalS4QuestionsUnchanged:true,sourceEvidencePairs:entries.map(([k,e])=>({key:k,sourceId:e.sourceId,evidenceId:evidenceIds[k]})),
- remoteFilesystemAndProcessesInspected:false,currentMessageIdentityVerifiedByCodex:false,browserVerified:false,
- limits:['Remote fixed input files, current-message registration, process state and CLI logs remain operator checks.',
- 'No future read-request invocation count inferred from committed operation audit.','A successful preparation is not a production save.']};
- fs.writeFileSync(output,JSON.stringify(result,null,2)+'\n',{flag:'wx'});
- console.log(JSON.stringify({ok:true,checksPassed:checks.length,currentStage:'S4',conceptCount:0,s5ArtifactCount:0,matchedFutureWriteAuditRows:0,
- deployedCommandsChecked:snapshots.length,productionBusinessWrites:0},null,2));
- }catch(error){
- const result={ok:false,at:new Date().toISOString(),step,checksPassed:checks.length,readOnly:true,productionBusinessWrites:0,
- errorCode:'S5_PREFLIGHT_STOPPED',checkLine:error.stack?.match(/verify-S5-classroom-preflight\.mjs:(\d+):/)?.[1]??null};
- fs.writeFileSync(output,JSON.stringify(result,null,2)+'\n',{flag:'wx'});console.error(JSON.stringify(result,null,2));process.exitCode=1;
- }
|