@@ -3741,12 +3741,57 @@ function formatPurityPercent(value) {
37413741
37423742function buildPurityObservations ( summary ) {
37433743 const lines = [ ] ;
3744- const analysis = summary ?. analysis ;
37453744 const totalRuns = Number . isFinite ( summary ?. totalRuns ) ? summary . totalRuns : 0 ;
3746- if ( ! analysis || ! totalRuns ) {
3745+ const impureRuns = Number . isFinite ( summary ?. impureRuns ) ? summary . impureRuns : 0 ;
3746+ const runs = Array . isArray ( summary ?. runs ) ? summary . runs : [ ] ;
3747+ const checkerErrorRuns = runs . filter ( ( run ) => run && run . didError ) . length ;
3748+ const hasCheckerError = Boolean ( summary ?. didCheckerError ) || checkerErrorRuns > 0 ;
3749+ const statusLabel = hasCheckerError ? 'ERROR' : summary ?. hasAnyError ? 'FAILED' : 'OK' ;
3750+ const detailParts = [ ] ;
3751+ if ( totalRuns <= 0 ) {
3752+ detailParts . push ( 'No runs analyzed.' ) ;
3753+ } else {
3754+ if ( impureRuns > 0 ) {
3755+ detailParts . push ( `${ impureRuns } /${ totalRuns } runs show drift between raw and share totals.` ) ;
3756+ }
3757+ if ( checkerErrorRuns > 0 ) {
3758+ detailParts . push ( `${ checkerErrorRuns } /${ totalRuns } runs had checker errors.` ) ;
3759+ }
3760+ if ( detailParts . length === 0 ) {
3761+ detailParts . push ( `All ${ totalRuns } runs match raw and share totals.` ) ;
3762+ }
3763+ }
3764+ lines . push ( `Overall: Purity status: ${ statusLabel } . ${ detailParts . join ( ' ' ) } ` ) ;
3765+
3766+ const analysis = summary ?. analysis ;
3767+ if ( ! analysis || totalRuns <= 0 ) {
37473768 return lines ;
37483769 }
37493770
3771+ const avgRaw = formatPurityMinutes ( analysis . avgRawTotal ) ;
3772+ const avgShare = formatPurityMinutes ( analysis . avgShareTotal ) ;
3773+ const avgSequence = formatPurityMinutes ( analysis . avgSequenceTotal ) ;
3774+ lines . push (
3775+ `Overall: Average total minutes per run: raw=${ avgRaw } , share=${ avgShare } , sequence=${ avgSequence } .`
3776+ ) ;
3777+
3778+ const shareDiff = ( Number ( analysis . avgShareTotal ) || 0 ) - ( Number ( analysis . avgRawTotal ) || 0 ) ;
3779+ const percentDiff = analysis . avgRawTotal === 0 ? null : shareDiff / analysis . avgRawTotal ;
3780+ const driftMagnitudeText = formatPurityMinutes ( Math . abs ( shareDiff ) ) ;
3781+ const percentText = formatPurityPercent ( percentDiff ) ;
3782+ const percentSuffix = percentText === 'n/a' ? '' : ` (≈ ${ percentText } )` ;
3783+ if ( shareDiff < 0 ) {
3784+ lines . push (
3785+ `Overall: On average, share is missing ${ driftMagnitudeText } minutes per run compared to raw${ percentSuffix } .`
3786+ ) ;
3787+ } else if ( shareDiff > 0 ) {
3788+ lines . push (
3789+ `Overall: On average, share is over-reporting ${ driftMagnitudeText } minutes per run compared to raw${ percentSuffix } .`
3790+ ) ;
3791+ } else {
3792+ lines . push ( 'Overall: On average, share matches raw totals per run.' ) ;
3793+ }
3794+
37503795 const activityThresholdMinutes = 60 ;
37513796 const activityThresholdPercent = 0.1 ;
37523797 const activityAnalyses = Array . isArray ( analysis . activities ) ? analysis . activities . slice ( ) : [ ] ;
@@ -3769,13 +3814,13 @@ function buildPurityObservations(summary) {
37693814
37703815 significantActivities . forEach ( ( activity ) => {
37713816 const label = activity . label || activity . key || 'activity' ;
3772- const avgRawText = formatPurityMinutes ( activity . avgRawPerRun ) ;
3773- const avgShareText = formatPurityMinutes ( activity . avgSharePerRun ) ;
3774- const driftMinutesText = formatPurityMinutes ( activity . avgShareDriftMinutes || 0 ) ;
3817+ const driftMinutes = Number ( activity . avgShareDriftMinutes ) || 0 ;
3818+ const driftMagnitudeText = formatPurityMinutes ( Math . abs ( driftMinutes ) ) ;
37753819 const percentText = formatPurityPercent ( activity . avgShareDriftPercent ) ;
3776- const percentSuffix = percentText === 'n/a' ? '' : ` (≈ ${ percentText } )` ;
3820+ const percentSuffix = percentText === 'n/a' ? '' : ` (≈ ${ percentText } vs raw)` ;
3821+ const descriptor = driftMinutes < 0 ? 'undercounted' : 'overcounted' ;
37773822 lines . push (
3778- `ACTIVITY | ${ label } | avg raw/ share per run: ${ avgRawText } / ${ avgShareText } | drift: ${ driftMinutesText } min ${ percentSuffix } `
3823+ `Activity ' ${ label } ' is ${ descriptor } in share by an average of ${ driftMagnitudeText } minutes per run ${ percentSuffix } . `
37793824 ) ;
37803825 } ) ;
37813826
@@ -3800,52 +3845,13 @@ function buildPurityObservations(summary) {
38003845 }
38013846 const minMagnitudeText = formatPurityMinutes ( minMagnitude ) ;
38023847 lines . push (
3803- `RUNS | worst total drift: ${ runLabels . join ( ', ' ) } (share ${ descriptor } ≥ ${ minMagnitudeText } min each)`
3848+ `Runs: Largest total drift in runs ${ runLabels . join ( ', ' ) } (share ${ descriptor } ≥ ${ minMagnitudeText } minutes each). `
38043849 ) ;
38053850 }
38063851
38073852 return lines ;
38083853}
38093854
3810- function buildPuritySummaryLines ( summary , observations ) {
3811- const lines = [ ] ;
3812- const totalRuns = Number . isFinite ( summary ?. totalRuns ) ? summary . totalRuns : 0 ;
3813- const pureRuns = Number . isFinite ( summary ?. pureRuns ) ? summary . pureRuns : 0 ;
3814- const impureRuns = Number . isFinite ( summary ?. impureRuns ) ? summary . impureRuns : 0 ;
3815- const runs = Array . isArray ( summary ?. runs ) ? summary . runs : [ ] ;
3816- const checkerErrorRuns = runs . filter ( ( run ) => run && run . didError ) . length ;
3817- const hasCheckerError = Boolean ( summary ?. didCheckerError ) ;
3818- const errorCount = hasCheckerError ? Math . max ( 1 , checkerErrorRuns ) : checkerErrorRuns ;
3819- const statusLabel = hasCheckerError ? 'ERROR' : summary ?. hasAnyError ? 'FAILED' : 'OK' ;
3820- const statusParts = [ `runs: ${ totalRuns } total, ${ pureRuns } pure, ${ impureRuns } impure` ] ;
3821- if ( errorCount > 0 ) {
3822- statusParts . push ( `checker errors: ${ errorCount } ` ) ;
3823- }
3824- lines . push ( `STATUS | ${ statusLabel } | ${ statusParts . join ( ' | ' ) } ` ) ;
3825-
3826- const analysis = summary ?. analysis ;
3827- if ( analysis && totalRuns > 0 ) {
3828- const avgRaw = formatPurityMinutes ( analysis . avgRawTotal ) ;
3829- const avgShare = formatPurityMinutes ( analysis . avgShareTotal ) ;
3830- const avgSequence = formatPurityMinutes ( analysis . avgSequenceTotal ) ;
3831- lines . push ( `TOTALS | avg raw/share/sequence per run: ${ avgRaw } / ${ avgShare } / ${ avgSequence } ` ) ;
3832-
3833- const shareDiff = ( Number ( analysis . avgShareTotal ) || 0 ) - ( Number ( analysis . avgRawTotal ) || 0 ) ;
3834- const percentDiff = analysis . avgRawTotal === 0 ? null : shareDiff / analysis . avgRawTotal ;
3835- const driftMinutesText = formatPurityMinutes ( shareDiff ) ;
3836- const percentText = formatPurityPercent ( percentDiff ) ;
3837- const percentSuffix = percentText === 'n/a' ? '' : ` (≈ ${ percentText } )` ;
3838- lines . push ( `TOTALS | avg drift (share vs raw): ${ driftMinutesText } min per run${ percentSuffix } ` ) ;
3839- }
3840-
3841- if ( Array . isArray ( observations ) && observations . length > 0 ) {
3842- lines . push ( ...observations ) ;
3843- }
3844-
3845- lines . push ( 'NOTE | Full raw purity data continues below…' ) ;
3846- return lines ;
3847- }
3848-
38493855function cancelBatchFitMeasurement ( ) {
38503856 if (
38513857 batchState . pendingFitFrame &&
@@ -4112,18 +4118,20 @@ function logBatchPurityReport(summary) {
41124118 const pureRuns = Number . isFinite ( summary . pureRuns ) ? summary . pureRuns : 0 ;
41134119 const impureRuns = Number . isFinite ( summary . impureRuns ) ? summary . impureRuns : 0 ;
41144120 const runs = Array . isArray ( summary . runs ) ? summary . runs : [ ] ;
4115- const errorRuns = runs . filter ( ( run ) => run && run . didError ) . length ;
4116- const logLevel = summary . hasAnyError ? 'warn' : 'info' ;
4121+
4122+ appendLogEntry ( {
4123+ level : 'warn' ,
4124+ message : `[Purity] Batch analysis: ${ totalRuns } runs, pure=${ pureRuns } , impure=${ impureRuns } ` ,
4125+ } ) ;
41174126
41184127 try {
41194128 const observationLines = buildPurityObservations ( summary ) ;
4120- const summaryLines = buildPuritySummaryLines ( summary , observationLines ) ;
4121- summaryLines . forEach ( ( line ) => {
4122- appendLogEntry ( { level : logLevel , message : `[Purity][Summary] ${ line } ` } ) ;
4129+ observationLines . forEach ( ( line ) => {
4130+ appendLogEntry ( { level : 'warn' , message : `[Purity] ${ line } ` } ) ;
41234131 } ) ;
41244132 } catch ( error ) {
41254133 const message = error instanceof Error ? error . message : String ( error ) ;
4126- appendLogEntry ( { level : 'warn' , message : `[Purity][Summary] ERROR building summary: ${ message } ` } ) ;
4134+ appendLogEntry ( { level : 'warn' , message : `[Purity] ERROR building summary: ${ message } ` } ) ;
41274135 }
41284136
41294137 try {
0 commit comments