| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- // Read-only recovery audit. Never invokes import/save/create/transition.
- import fs from 'node:fs';
- import assert from 'node:assert/strict';
- import crypto from 'node:crypto';
- import {invoke,classRows} from '../server/scripts/platform-client.mjs';
- const output='work/S4-step08-resume-audit-20261103.json';
- assert.equal(fs.existsSync(output),false,'Preserve prior recovery audit');
- const base='KSF-真实资料-20261102-01',sessionId='btrFqkz0yw';
- const ids={candidateArtifactId:'V2z4ox7Ehx',candidateSourceId:'CFjbZlMWp5',opportunityId:'QE9hjtCj8E',
- projectId:'S_CikxZeBs',baselineArtifactId:'XF4h-Z7l5o',baselineSourceId:'H_eFL1eT-d'};
- const dir='work/S4-classroom-next-20261103/';
- const checks=[];
- const check=(name,fn)=>{fn();checks.push({name,passed:true});};
- const read=async(className,objectId)=>{
- const response=await invoke('ksfGetRecord',{className,objectId});
- assert.equal(response.http,200);assert.equal(response.body?.result?.ok,true);
- const row=response.body.result.data.record;assert.equal(row?.objectId,objectId);return row;
- };
- const ptr=(className,objectId)=>({__type:'Pointer',className,objectId});
- const templates=Object.fromEntries(['01-candidate-save.template.json','03-project-S3-save.template.json','04-project-S4-progress.template.json','05-project-S4-save.template.json'].map(name=>[name,JSON.parse(fs.readFileSync(dir+name,'utf8'))]));
- const handoff=fs.readFileSync('F:/文档/工作坊当前资料/47-发给王刚001-S4课堂项目与桌面评估.txt','utf8');
- const requestIds=[...handoff.matchAll(/requestId=(KSF-[^\s。;]+)/gu)].map(m=>m[1]);
- assert.equal(requestIds.length,12);
- let step='read current records, status and audit';
- try{
- const all=await Promise.allSettled([
- read('KfArtifact',ids.candidateArtifactId),read('KfSource',ids.candidateSourceId),
- read('KfOpportunity',ids.opportunityId),read('KfProject',ids.projectId),
- read('KfArtifact',ids.baselineArtifactId),read('KfSource',ids.baselineSourceId),
- read('KfSession',sessionId),invoke('ksfGetWorkshopProject',{sessionId,projectId:ids.projectId}),
- classRows('KsfOperation',{requestId:{$in:requestIds}}),
- classRows('KfArtifact',{workshopKey:base,reportSeriesKey:base+':S4'}),
- classRows('KfOpportunity',{sessionRef:ptr('KfSession',sessionId)}),
- classRows('KfProject',{sessionRef:ptr('KfSession',sessionId)})
- ]);
- const failed=all.flatMap((r,i)=>r.status==='rejected'?[i]:[]);assert.deepEqual(failed,[],'Read failure indexes only');
- const [candidate,candidateSource,opp,project,baseline,baselineSource,session,statusResponse,operations,s4Artifacts,opportunities,projects]=all.map(r=>r.value);
- assert.equal(statusResponse.http,200);assert.equal(statusResponse.body?.result?.ok,true);
- const status=statusResponse.body.result.data;
- step='original handoff and wrapper command spelling';
- check('47 step08 and packaged handoff use project-progress',()=>{
- assert.match(handoff,/08\.[^\n]*;project-progress,requestId=KSF-真实资料-20261102-01:portion-fit-project:to-S4:v1/u);
- assert.equal(handoff.includes('project-transition'),false);
- assert.ok(fs.readFileSync('skills/ksf-workshop-runtime/scripts/ksf-secure.mjs','utf8').includes("'project-progress'"));
- });
- step='report template and scope equality';
- for(const [name,row,source,projectId]of [
- ['01-candidate-save.template.json',candidate,candidateSource,null],
- ['03-project-S3-save.template.json',baseline,baselineSource,ids.projectId]
- ]){
- const template=templates[name];
- check(name+' exact saved fields and source body',()=>{
- for(const key of ['rawText','title','version','stageCode','artifactType'])assert.deepEqual(row[key],template[key]);
- assert.equal(row.reportSeriesKey,template.taskKey);assert.equal(row.reportType,'workshop_auxiliary');assert.equal(row.artifactSubtype,'classroom_task');
- assert.equal(row.dataMode,'live');assert.equal(row.dashboardState,'workshop');assert.equal(row.ownerSession,sessionId);
- assert.equal(row.projectId??null,projectId);assert.deepEqual(row.projectRef??null,projectId?ptr('KfProject',projectId):null);
- assert.deepEqual(row.meta.workshop.sourceIds,template.sourceIds);assert.deepEqual(row.meta.workshop.pendingQuestions,template.pendingQuestions);
- assert.deepEqual(row.sourceRef,ptr('KfSource',source.objectId));assert.equal(source.rawText,template.rawText);
- for(const field of ['chapters','evidence','method','sources','subtitle'])assert.deepEqual(row.report[field],template.report[field]);
- assert.deepEqual(row.report.executive,['课堂过程成果,未经员工正式确认;待验证项不作为市场结论。',...template.report.executive]);
- assert.equal(row.report.title,template.title);assert.equal(row.evidenceCount,4);
- assert.equal(row.meta.attribution.agentKey,session.meta.attribution.agentKey);assert.equal(row.agentName,'王刚001');
- assert.equal(row.dashboardPublishedAt??null,null);assert.equal(row.contentConfirmedAt??null,null);
- });
- }
- step='opportunity and actual project state after step07';
- check('one selected classroom opportunity and one existing project',()=>{
- assert.deepEqual(opportunities.map(x=>x.objectId),[ids.opportunityId]);assert.deepEqual(projects.map(x=>x.objectId),[ids.projectId]);
- assert.equal(opp.claimKind,'hypothesis');assert.equal(opp.version,1);assert.equal(opp.status,'approved');
- assert.equal(opp.opportunitySeriesKey,base+':portion-fit');assert.deepEqual(opp.linkedArtifacts,[ids.candidateArtifactId]);
- assert.equal(opp.linkedProject,ids.projectId);assert.deepEqual(opp.sessionRef,ptr('KfSession',sessionId));
- const expected=templates['01-candidate-save.template.json'].opportunity;
- for(const key of ['title','demandSummary','targetAudience','usageScenario','fusionDefinition'])assert.equal(opp[key],expected[key]);
- });
- check('current project S3/in_progress includes exactly the saved baseline',()=>{
- assert.equal(project.stageCode,'S3');assert.equal(project.progressState,'in_progress');assert.equal(project.dataMode,'live');assert.equal(project.status,'workshop');
- assert.equal(project.ownerSession,sessionId);assert.deepEqual(project.opportunityRef,ptr('KfOpportunity',ids.opportunityId));
- assert.equal(project.meta.projectKey,base+':portion-fit-project');assert.equal(project.artifactCount,1);
- assert.equal(status.project.objectId,ids.projectId);assert.equal(status.project.stageCode,'S3');assert.equal(status.project.progressState,'in_progress');
- assert.deepEqual(status.artifacts.map(x=>x.objectId),[ids.baselineArtifactId]);assert.equal(status.events.length,2);
- assert.deepEqual(status.events.map(x=>x.eventType),['project_created','task_submitted']);
- assert.equal(status.events[0].objectId,'GGDTcg5YTy');assert.equal(status.events[1].artifactRef.objectId,ids.baselineArtifactId);
- assert.equal(status.concept,null);assert.equal(status.formulaItems.length,0);assert.equal(session.stageCode,'S3');
- });
- step='committed write audit and unused resume key';
- const expectedCommitted=[requestIds[0],requestIds[3],requestIds[5]];
- check('exactly first three writes committed, no step08 or later mutation receipts',()=>{
- assert.equal(operations.length,3);assert.deepEqual(operations.map(x=>x.requestId).sort(),expectedCommitted.slice().sort());
- const c=operations.find(x=>x.requestId===requestIds[0]);assert.equal(c.receipt.artifactId,ids.candidateArtifactId);assert.equal(c.receipt.opportunityId,ids.opportunityId);
- const p=operations.find(x=>x.requestId===requestIds[3]);assert.equal(p.receipt.projectId,ids.projectId);
- const b=operations.find(x=>x.requestId===requestIds[5]);assert.equal(b.receipt.artifactId,ids.baselineArtifactId);assert.equal(b.receipt.projectId,ids.projectId);
- assert.equal(operations.some(x=>x.requestId===requestIds[7]),false);assert.equal(s4Artifacts.length,0);
- });
- const expectedInput={...templates['04-project-S4-progress.template.json'],sessionId,projectId:ids.projectId};
- const result={ok:true,at:new Date().toISOString(),operation:'S4-step08-read-only-recovery-audit',readOnly:true,productionBusinessWrites:0,
- checksPassed:checks.length,checks,sessionId,...ids,systemStage:'S3',projectStage:'S3',progressState:'in_progress',
- candidateReportTemplateMatched:true,projectS3BaselineTemplateMatched:true,baselinePresentInProjectStatus:true,
- projectArtifacts:status.artifacts.map(x=>({artifactId:x.objectId,stageCode:x.stageCode})),
- projectEvents:status.events.map(x=>({eventId:x.objectId,eventType:x.eventType,sequence:x.sequence,artifactId:x.artifactRef?.objectId??null})),
- committedWriteRequests:operations.map(x=>({requestId:x.requestId,operation:x.operation})),
- resume:{command:'project-progress',requestId:requestIds[7],committedWriteReceiptPresent:false,expectedInput,
- expectedInputSemanticHash:crypto.createHash('sha256').update(JSON.stringify(expectedInput,Object.keys(expectedInput).sort())).digest('hex'),
- rerunSteps01To07:false,newInputNeeded:false,retainOriginalFailedLogs:true},
- sourceIds:templates['03-project-S3-save.template.json'].sourceIds,
- reportSeriesNote:'Input taskKey is explicit; handler assigns reportSeriesKey=p.taskKey. Input has no reportSeriesKey property, rather than an explicitly empty value.',
- commandSpellingNote:'Codex local 47 original, delivered package handoff, and wrapper use project-progress; claimed remote/transcribed project-transition spelling not independently obtained.',
- failureBoundaryBasis:'Operator reports local USAGE before network. Independent checks establish no committed transition receipt/event and current project still S3; they do not prove zero historical HTTP attempts.',
- remoteFilesAndExitcodesBasis:'Operator report; original failed command and stderr not obtained by Codex',browserVerified:false};
- fs.writeFileSync(output,JSON.stringify(result,null,2)+'\n',{flag:'wx'});
- console.log(JSON.stringify({ok:true,checksPassed:checks.length,...ids,projectStage:'S3',progressState:'in_progress',baselinePresentInProjectStatus:true,
- projectArtifactCount:status.artifacts.length,projectEventCount:status.events.length,committedWriteCount:operations.length,step08Committed:false,
- resumeCommand:'project-progress',resumeRequestId:requestIds[7],productionBusinessWrites:0},null,2));
- }catch(error){
- const result={ok:false,at:new Date().toISOString(),step,errorCode:'S4_STEP08_RECOVERY_AUDIT_STOPPED',
- checkLine:error.stack?.match(/verify-S4-step08-resume\.mjs:(\d+):/)?.[1]??null,checksPassed:checks.length,readOnly:true,productionBusinessWrites:0};
- fs.writeFileSync(output,JSON.stringify(result,null,2)+'\n',{flag:'wx'});console.error(JSON.stringify(result,null,2));process.exitCode=1;
- }
|