Stop rebuilding chart overlays on every live tick.

Ticks were force-redrawing trendline overlays and, in diagnostic mode,
sampling canvas pixels. That maxed CPU. Also document production log
and capture access for agents.
This commit is contained in:
Chris Amow 2026-08-26 01:36:17 -05:00
parent e94bd3d3d2
commit c2ad2e8d7d
2 changed files with 30 additions and 9 deletions

View file

@ -111,6 +111,35 @@ trendlines: historical/future canonical price changes, their screen slopes, and
whether the future canvas point exists. Include it in a capture when a line
looks kinked at the live edge; it separates bad geometry from a bad renderer.
## Production logs and captures
Do not ask the user to paste console output or screenshots when these work.
This is not a production shell.
```
ssh -i ~/.ssh/chart_debug_ed25519 -o BatchMode=yes \
chart-debug@chart.amow.com 'logs --since 20m'
ssh -i ~/.ssh/chart_debug_ed25519 -o BatchMode=yes \
chart-debug@chart.amow.com status
ssh -i ~/.ssh/chart_debug_ed25519 -o BatchMode=yes \
chart-debug@chart.amow.com container-state
ssh -i ~/.ssh/chart_debug_ed25519 -o BatchMode=yes \
chart-debug@chart.amow.com recent-deploy
```
Allowed commands only: `logs --since <Ns|Nm|Nh|Nd>`, `status`,
`container-state`, `recent-deploy`, `capture-read c-…`,
`capture-delete c-…`. Anything else is denied. Log output is redacted
and capped. Never print tokens, env, or the private key.
**Captures:** user opens `?diag=1`, hits Capture diagnostic (or
`Alt+Shift+C`), and shares the PNG URL
`https://chart.amow.com/api/debug/captures/{id}`. Inspect only a URL they
explicitly share. Fetch the PNG (public by id), then immediately
`DELETE` it — via that URL or
`ssh … chart-debug@chart.amow.com 'capture-delete c-XXXXXXXXXXXX'`.
Do not inspect unsolicited capture ids.
## Future whitespace is a high-risk boundary
Drawing bugs repeatedly appear to the right of the last real candle. Treat any

View file

@ -811,7 +811,7 @@ class ConfluenceChart {
else this.bars.push(bar);
if (advanced) this.extendManualLineSeries(last, bar);
if (advanced) this.syncRthLines();
this.scheduleOverlays(true);
if (advanced) this.scheduleOverlays(true);
}
extendManualLineSeries(previous, bar) {
@ -2578,14 +2578,6 @@ class ConfluenceChart {
`#${row.level.number} ${row.level.tf} dp ${((row.current.p ?? 0) - (row.previous.p ?? 0)).toFixed(3)}/${((row.next.p ?? 0) - (row.current.p ?? 0)).toFixed(3)} px ${(row.historical ?? 0).toFixed(3)}/${(row.future ?? 0).toFixed(3)} paint ${painted ? `${painted.historical?.toFixed(3) ?? '?'}/${painted.future?.toFixed(3) ?? '?'}` : '?/?'} ${row.series == null ? 'MISSING' : 'OK'}`;
this.projectionDiagnostic.textContent = lines.map(row => text(row)).join('\n');
this.projectionDiagnostic.hidden = !lines.length;
if (this.projectionDiagnosticFrame != null) cancelAnimationFrame(this.projectionDiagnosticFrame);
this.projectionDiagnosticFrame = requestAnimationFrame(() => {
this.projectionDiagnosticFrame = null;
this.projectionDiagnostic.textContent = lines.map(row => text(row, {
historical: this.paintedSlopeBetween(row.previous, row.current, ConfluenceChart.levelColor(row.level)),
future: this.paintedSlopeBetween(row.current, row.next, ConfluenceChart.levelColor(row.level)),
})).join('\n');
});
}
paintedSlopeBetween(first, second, color) {