jenkins 9204773e7d av-test: publish bounded continuity watch and recent observations
Vendor tested Lesavka observer assets and numeric receipt schema from 6b97ceb1. Verified 65 JS tests, 10 service tests, kustomize render and Kubernetes client dry-run. No media upload, capture activation or changes to the homepage and private OCR.
2026-10-22 21:47:13 -05:00

90 lines
5.5 KiB
JavaScript

// Receiver observation only: no media, identities, storage, network or PASS.
// A watch never resets capture to produce a new reporting interval.
export const WATCH_MS=10*60*60*1000;
export const WATCH_ROWS=1202; // One row per 30 seconds plus start/terminal.
const integer=value=>Number.isSafeInteger(value)&&value>=0;
const number=value=>Number.isFinite(value)&&value>=0;
export class ContinuityWatch {
constructor(now=()=>performance.now(),wall=()=>Date.now()) {
this.now=now;this.wall=wall;this.started=now();this.wallStarted=wall();
this.previousAt=this.started;this.nextRow=0;this.rows=[];this.findings={};
this.previous=null;this.lastAudioProgress=0;this.lastVideoProgress=0;
this.latest=null;this.terminal=null;this.maxObserverGap=0;
}
note(kind,elapsed) {
const finding=this.findings[kind]??{firstObservedMs:elapsed,observations:0};
finding.observations++;this.findings[kind]=finding;
}
observe(sample,audioReceivedAt) {
if(this.terminal)return;
const now=this.now(),elapsed=now-this.started,wallElapsed=this.wall()-this.wallStarted;
if(!number(elapsed)||!number(wallElapsed)||now<this.previousAt) {
this.note('observer-clock-invalid',this.latest?.elapsedMs??0);return;
}
const gap=now-this.previousAt;
this.previousAt=now;this.maxObserverGap=Math.max(this.maxObserverGap,gap);
if(gap>2500)this.note('observer-coverage-gap',elapsed);
if(Math.abs(wallElapsed-elapsed)>2000)this.note('wall-monotonic-divergence',elapsed);
const audio=sample.audio??{},video=sample.video??{};
const audioAge=audioReceivedAt===null?elapsed:now-audioReceivedAt;
const row={elapsedMs:elapsed,videoCallbacks:video.callbacks,videoResets:video.clockOrCounterResets,
audioInputFrames:audio.inputFrames,audioProcessedFrames:audio.processedFrames,
audioZeroFrames:audio.exactZeroFrames,audioRate:audio.sampleRate,
audioMessageAgeMs:number(audioAge)?audioAge:null,
videoCallbackAgeMs:number(video.observationAgeMs)?video.observationAgeMs:null,
videoGapMaxMs:number(video.callbackGapMaxMs)?video.callbackGapMaxMs:null};
for(const key of ['videoCallbacks','videoResets','audioInputFrames','audioProcessedFrames','audioZeroFrames','audioRate']) {
if(!integer(row[key]))row[key]=null;
}
if(row.audioMessageAgeMs===null||row.audioMessageAgeMs>2000)this.note('audio-observation-stale',elapsed);
if(row.videoCallbackAgeMs===null||row.videoCallbackAgeMs>2000) {
if(elapsed>2000)this.note('video-observation-stale',elapsed);
}
if(row.audioInputFrames===null||row.audioProcessedFrames===null||row.audioRate===null) {
if(elapsed>2000)this.note('audio-counters-unavailable',elapsed);
}
const previous=this.previous;
if(previous) {
for(const key of ['videoCallbacks','audioInputFrames','audioProcessedFrames','audioZeroFrames']) {
if(row[key]!==null&&previous[key]!==null&&row[key]<previous[key])this.note('counter-reset',elapsed);
}
if(row.audioRate!==null&&previous.audioRate!==null&&row.audioRate!==previous.audioRate)
this.note('audio-rate-changed',elapsed);
if(row.videoResets!==null&&previous.videoResets!==null&&row.videoResets>previous.videoResets)
this.note('video-clock-or-counter-reset',elapsed);
}
if(row.audioInputFrames!==null&&(!previous||row.audioInputFrames>previous.audioInputFrames))this.lastAudioProgress=elapsed;
if(row.videoCallbacks!==null&&(!previous||row.videoCallbacks>previous.videoCallbacks))this.lastVideoProgress=elapsed;
if(elapsed-this.lastAudioProgress>2000)this.note('audio-input-not-advancing',elapsed);
if(elapsed-this.lastVideoProgress>2000)this.note('video-not-advancing',elapsed);
this.previous=row;this.latest=row;
if(elapsed>=this.nextRow) {
this.append(row);this.nextRow=elapsed+30000;
}
}
append(row) {
if(this.rows.length<WATCH_ROWS)this.rows.push({...row});
else this.note('timeline-capacity-reached',row.elapsedMs);
}
finish(reason) {
if(this.terminal)return;
if(this.latest&&this.rows.at(-1)?.elapsedMs!==this.latest.elapsedMs)this.append(this.latest);
const duration=this.latest?.elapsedMs??0;
const observerIncomplete=['observer-clock-invalid','observer-coverage-gap',
'wall-monotonic-divergence','timeline-capacity-reached'].some(key=>this.findings[key]);
this.terminal=reason==='10 hours observed'&&duration>=WATCH_MS&&!observerIncomplete?
(Object.keys(this.findings).length?'duration-observed-with-findings':'duration-observed'):'incomplete';
}
summary(includeTimeline=false) {
return {kind:'receiver-continuity-diagnostic',state:this.terminal??'observing',
startedUnixMs:integer(this.wallStarted)?this.wallStarted:null,wallClockCalibrated:false,
requestedDurationMs:WATCH_MS,observedDurationMs:this.latest?.elapsedMs??0,
maxObserverGapMs:this.maxObserverGap,findings:structuredClone(this.findings),
retainedRows:this.rows.length,rowLimit:WATCH_ROWS,latest:this.latest?{...this.latest}:null,
...(includeTimeline?{timeline:this.rows.map(row=>({...row}))}:{}),
releaseEligible:false,mediaRecorded:false,uploaded:false,
note:'Browser observations only. Silence is not loss; clocks and callback gaps do not prove audible quality or A/V sync.'};
}
}