rushabhcodes/fidget-counter
A tscircuit package created by rushabhcodes.
- Version
- 1.0.31
- License
- unset
- Stars
- 0
validation/check-passive-substitutions.mjs
import assert from 'node:assert/strict'
import {execFileSync} from 'node:child_process'
import {readFileSync,writeFileSync,existsSync} from 'node:fs'
import {createHash} from 'node:crypto'
import {fittedComponentRecords} from './fitted-component-records.mjs'
import {records} from './check-cr2450-circuit.mjs'
import {verifyFreshRouting} from './check-fresh-routing.mjs'
import {substituteRecords,substitutions} from './passive-substitutions.mjs'
import {approvedNordicFittedRecords} from './nordic-electrical-revision.mjs'
export const comparisonCommit='31c224875298f3c41c1698029c5d0f01bee24db3'
const baseline=()=>JSON.parse(execFileSync('git',['show',`${comparisonCommit}:published.circuit.json`],{maxBuffer:16e6}))
export function verifyPassiveSubstitutions(current,old=baseline()){
assert.deepEqual(fittedComponentRecords(current),approvedNordicFittedRecords(substituteRecords(fittedComponentRecords(old))),
'Only six specified supplier identities and CAD paths may change; all lands, values and poses must remain identical')
if(existsSync('validation/fresh-routing-reference.json')){
const fresh=verifyFreshRouting(current)
for(const s of substitutions)for(const ext of ['obj','step'])assert.deepEqual(readFileSync(`imports/${s.model}/${s.model}.${ext}`),readFileSync(`imports/${s.oldPart}/${s.oldPart}.${ext}`))
return {...fresh,substitutions,changed_fitted_identities:6,other_25_values_lands_poses_preserved:true,bounded_nordic_electrical_correction:true,all_routed_traces_pads_vias_connectivity_layers_masks_preserved:false,approved_fresh_routing:true,supplier_obj_step_bytes_identical:true,L2_tolerance:{before_percent:2,after_percent:5,RF_qualification_required:true},stock_reserved:false}
}
const a=records(old),b=records(current)
assert.deepEqual(b.connections,a.connections,'Connectivity or routing constraints changed')
assert.deepEqual(b.portAttributes,a.portAttributes,'Electrical pin attributes changed')
for(const type of ['pcb_board','pcb_trace','pcb_via','pcb_hole','pcb_plated_hole','pcb_smtpad','pcb_port']){
const items=(c,r)=>c.filter(e=>e.type===type).map(e=>{
const v=JSON.parse(r.copper(e))
if(e.type==='pcb_copper_pour')v.net=c.find(n=>n.type==='source_net'&&n.source_net_id===e.source_net_id)?.name
return JSON.stringify(v)
}).sort()
assert.deepEqual(items(current,b),items(old,a),type+': physical geometry, net, layer or mask changed')
}
// A separate fail-closed check approves only the later NPTH ground-pour trim.
const holeAudit=JSON.parse(execFileSync('python',['validation/check-npth-copper.py','--stdin'],{input:JSON.stringify({before:old,current}),maxBuffer:16e6}))
assert(holeAudit.passed)
assert(!current.some(e=>e.type.endsWith('_error')),'Build errors remain')
// Supplier models really are identical, rather than merely sharing a package label.
for(const s of substitutions)for(const ext of ['obj','step'])assert.deepEqual(
readFileSync(`imports/${s.model}/${s.model}.${ext}`),readFileSync(`imports/${s.oldPart}/${s.oldPart}.${ext}`),
`Supplier ${ext} geometry changed: ${s.id}`)
return {passed:true,comparison_commit:comparisonCommit,substitutions,changed_fitted_identities:6,
all_28_values_lands_poses_preserved:true,all_routed_traces_pads_vias_connectivity_layers_masks_preserved:true,
approved_npth_ground_pour_trim:holeAudit,
supplier_obj_step_bytes_identical:true,
L2_tolerance:{before_percent:2,after_percent:5,RF_qualification_required:true},
stock_reserved:false,physical_tested:false}
}
if(import.meta.main){
const report=verifyPassiveSubstitutions(JSON.parse(readFileSync('published.circuit.json')))
const sha=bytes=>createHash('sha256').update(bytes).digest('hex')
let retained=[]
const pushfit=JSON.parse(readFileSync('mechanical/enclosure-parameters.json')).cap_retention?.mode==='push_fit'
if(pushfit){
const cad=JSON.parse(readFileSync('validation/pushfit-cad-audit.json'));assert(cad.passed&&!cad.physical_tested&&cad.product_rigid_parts===2&&cad.nominal_diametral_interference_mm===.05)
for(const [p,h]of Object.entries(cad.source_sha256))assert.equal(sha(readFileSync(p)),h,'Stale bounded push-fit CAD proof: '+p)
report.approved_mechanical_revision=`Two-part push fit; exact CAD audit vs ${cad.comparison_commit}`
report.retained_print_evidence={new_slicer_execution:true,report:'validation/print-readiness.json'}
}else{
const prior=path=>execFileSync('git',['show',`${comparisonCommit}:${path}`],{maxBuffer:32e6})
const tracked=execFileSync('git',['ls-tree','-r','--name-only',comparisonCommit],{encoding:'utf8'}).trim().split('\n')
retained=tracked.filter(p=>/^mechanical\/(Print_STL|STEP_Parts)\//.test(p)&&/\.(stl|step)$/.test(p))
retained.push('mechanical/build_enclosure.py','mechanical/enclosure-parameters.json','validation/print-readiness.json','previews/bearing-layout.png')
report.retained_geometry_sha256=Object.fromEntries(retained.map(p=>{
const bytes=readFileSync(p);assert.deepEqual(bytes,prior(p),'Unexpected printed geometry or retained evidence change: '+p)
return [p,sha(bytes)]
}))
const slicing=JSON.parse(readFileSync('validation/print-readiness.json'))
assert(slicing.passed&&slicing.actual_slicer_executed&&!slicing.physical_tested)
for(const [p,h] of Object.entries(slicing.source_sha256))assert.equal(sha(readFileSync(p)),h,'Stale retained slicer input: '+p)
for(const part of slicing.parts){assert.equal(part.slicer_exit_code,0);assert.equal(part.slicer_warnings.length,0)}
assert.equal(sha(readFileSync('dist/index/3d.glb')),JSON.parse(prior('cloud-assets/manifest.json')).artifacts.find(a=>a.output==='dist/published/3d.glb').sha256,'Rendered assembly geometry changed')
report.retained_print_evidence={slicer:slicing.slicer,parts:slicing.parts_checked,new_slicer_execution:false,reason:'All manufacturing exports and slicer inputs byte-identical to 1.0.26; prior toolpaths retained'}
}
const paths=['published.circuit.json','passives.tsx','validation/passive-substitutions.mjs','validation/check-passive-substitutions.mjs','validation/jlc-imports.json',...substitutions.flatMap(s=>['tsx','obj','step'].map(ext=>`imports/${s.model}/${s.model}.${ext}`))]
if(report.approved_fresh_routing)paths.push('validation/check-fresh-routing.mjs','validation/fresh-routing-reference.json')
paths.push(...retained,'dist/index/3d.glb',...(pushfit?['validation/pushfit-cad-audit.json','mechanical/enclosure-parameters.json']:[]))
report.source_sha256=Object.fromEntries(paths.map(p=>[p,createHash('sha256').update(readFileSync(p)).digest('hex')]))
writeFileSync('validation/passive-substitution-audit.json',JSON.stringify(report,null,2)+'\n')
console.log(report.approved_fresh_routing?'PASS: six sourcing substitutions retained with bounded Nordic correction; explicit approved fresh routing verified':'PASS: six substitutions; previous copper and fitted geometry retained')
}