verify-S4-classroom-contract.mjs 6.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // Production access is read only: function source, contract, write audit and current scope.
  2. import fs from 'node:fs';
  3. import crypto from 'node:crypto';
  4. import assert from 'node:assert/strict';
  5. import {invoke,classRows} from '../server/scripts/platform-client.mjs';
  6. const output='work/S4-classroom-contract-check-20261103-03.json';
  7. assert.equal(fs.existsSync(output),false,'Preserve prior audit');
  8. const taskBase='KSF-真实资料-20261102-01',sessionId='btrFqkz0yw';
  9. const normalize=s=>s.replace(/\r\n/g,'\n');
  10. const workshop=normalize(fs.readFileSync('server/src/workshop.mjs','utf8').replace(/^export /gm,''));
  11. const core=normalize(fs.readFileSync('server/src/core.mjs','utf8').replace(/^export /gm,''));
  12. const names=['ksfSaveWorkshopTask','ksfCreateProjectFromOpportunity','ksfTransitionProject','ksfGetWorkshopProject'];
  13. const block=(text,start,end)=>{
  14. const at=text.indexOf(start);assert.ok(at>=0);const finish=text.indexOf(end,at+start.length);assert.ok(finish>at);
  15. return text.slice(at,finish).trim();
  16. };
  17. const relevantBlocks=[
  18. ...names.map(name=>({name,code:block(workshop,'app.handlers.'+name+'=',name==='ksfGetWorkshopProject'?'\n const fields=':'\n app.handlers.')})),
  19. {name:'reportOf',code:block(workshop,'function reportOf(','\n app.handlers.ksfSaveWorkshopTask=')},
  20. {name:'session/project/event helpers',code:block(workshop,'const own=','\n const contract=app.handlers.ksfGetIngestContract;')},
  21. {name:'write idempotency operation',code:block(core,'async function operation(','\n function validateValue(')}
  22. ];
  23. const handoff=fs.readFileSync('F:/文档/工作坊当前资料/47-发给王刚001-S4课堂项目与桌面评估.txt','utf8');
  24. const requestIds=[...handoff.matchAll(/requestId=(KSF-[^\s。;]+)/gu)].map(m=>m[1]);
  25. assert.equal(requestIds.length,12);assert.equal(new Set(requestIds).size,12);
  26. const series=['S3-份量候选','S3-项目基线','S4'].map(s=>taskBase+':'+s);
  27. const pointer={__type:'Pointer',className:'KfSession',objectId:sessionId};
  28. let step='read source and audit';
  29. try{
  30. const inspected=JSON.parse(fs.readFileSync('work/S4-classroom-deployed-handler-inspection-20261103.json','utf8'));
  31. const functionSnapshots=names.map(name=>{
  32. const info=inspected.find(x=>x.name===name),code=fs.readFileSync('work/S4-deployed-'+name+'-20261103.js','utf8');
  33. assert.equal(crypto.createHash('sha256').update(code).digest('hex'),info.codeSha256);
  34. return{name,objectId:info.id,path:'/'+name,code};
  35. });
  36. const settled=await Promise.allSettled([
  37. invoke('ksfGetIngestContract',{}),Promise.resolve(functionSnapshots),
  38. classRows('KsfOperation',{requestId:{$in:requestIds}}),
  39. classRows('KfArtifact',{reportSeriesKey:{$in:series}}),
  40. classRows('KfOpportunity',{$or:[{opportunitySeriesKey:taskBase+':portion-fit'},{sessionRef:pointer}]}),
  41. classRows('KfProject',{sessionRef:pointer}),
  42. invoke('ksfGetRecord',{className:'KfSession',objectId:sessionId})
  43. ]);
  44. const failed=settled.flatMap((r,i)=>r.status==='rejected'?[i]:[]);assert.deepEqual(failed,[],'Read group failed (indexes only)');
  45. const [contractResponse,functions,operations,artifacts,opportunities,projects,sessionResponse]=settled.map(r=>r.value);
  46. assert.equal(contractResponse.http,200);assert.equal(contractResponse.body?.result?.ok,true);
  47. assert.equal(sessionResponse.http,200);assert.equal(sessionResponse.body?.result?.ok,true);
  48. const contract=contractResponse.body.result.data,session=sessionResponse.body.result.data.record;
  49. step='deployed command implementation';
  50. const sourceChecks=[];
  51. for(const name of names){
  52. const matches=functions.filter(x=>x.name===name);assert.equal(matches.length,1);
  53. const row=matches[0],code=normalize(row.code);
  54. const blockChecks=relevantBlocks.map(part=>({name:part.name,matched:code.includes(part.code)}));
  55. const check={name,functionObjectId:row.objectId,path:row.path,
  56. sha256:crypto.createHash('sha256').update(row.code).digest('hex'),
  57. includesExactLocalWorkshopModule:code.includes(workshop),includesExactLocalCoreModule:code.includes(core),
  58. relevantCommandBlocks:blockChecks};
  59. sourceChecks.push(check);
  60. assert.ok(blockChecks.every(x=>x.matched),name+' relevant command and helper code equality');
  61. }
  62. step='current scope and write history';
  63. assert.equal(operations.length,0);assert.equal(artifacts.length,0);assert.equal(opportunities.length,0);assert.equal(projects.length,0);
  64. assert.equal(session.stageCode,'S3');assert.equal(session.dataMode,'live');assert.equal(session.workshopKey,taskBase);
  65. const reportTypes=contract.reportTypes.map(x=>x.code);
  66. assert.ok(reportTypes.includes('workshop_auxiliary'));assert.ok(!reportTypes.includes('opportunity_card'));assert.ok(!reportTypes.includes('assessment_report'));
  67. const result={ok:true,at:new Date().toISOString(),readOnly:true,productionBusinessWrites:0,
  68. contract:{schemaVersion:contract.schemaVersion,contractRevision:contract.contractRevision,release:contract.release,
  69. workshopProjectTransition:contract.capabilities.workshopProjectTransition,projectTransition:contract.capabilities.projectTransition,
  70. reportTypes,workshop:contract.workshop},
  71. deployedCodeChecks:sourceChecks,checkedRequestIds:requestIds,matchedWriteAuditRows:operations.length,
  72. history:{newSeriesArtifacts:0,opportunities:0,projects:0},systemStage:'S3',
  73. decisions:{artifactTypeSeparateFromReportType:true,reportType:'workshop_auxiliary',artifactSubtype:'classroom_task',
  74. commandPayloadSeparateFromTableFields:true,projectKeyStoredInMeta:true,toStageAppliedToStageCode:true,
  75. enterpriseApprovalIsResponseOnly:true,opportunityKeyBecomesSeriesKey:true,claimKindSuppliedByHandler:'hypothesis',
  76. opportunityVersionInheritsReportVersion:true,statusBeforeProject:'candidate'},
  77. limits:['No remote filesystem/process inspection by Codex','No claim of local CLI read-request count from KsfOperation audit',
  78. 'Whole-module equality check retained separately; this audit compares the relevant command bodies and helper functions',
  79. 'Read-only live checks do not guarantee future write outcome; stop rules remain in force']};
  80. fs.writeFileSync(output,JSON.stringify(result,null,2)+'\n',{flag:'wx'});
  81. console.log(JSON.stringify({ok:true,contractRevision:result.contract.contractRevision,deployedFunctionsMatched:sourceChecks.length,
  82. matchedWriteAuditRows:0,newSeriesArtifacts:0,opportunities:0,projects:0,systemStage:'S3',decisions:result.decisions},null,2));
  83. }catch(error){
  84. const result={ok:false,step,errorCode:'S4_CLASSROOM_CONTRACT_CHECK_STOPPED',readOnly:true,productionBusinessWrites:0,
  85. checkLine:error.stack?.match(/verify-S4-classroom-contract\.mjs:(\d+):/)?.[1]??null};
  86. fs.writeFileSync(output,JSON.stringify(result,null,2)+'\n',{flag:'wx'});console.error(JSON.stringify(result,null,2));process.exitCode=1;
  87. }