Chart geometry bugs live in the browser, and the browser is usually on a different machine from whoever is debugging it — so "it passes in my headless run" keeps being said about a chart that is unusable on someone's screen. This session spent hours on that gap: a screenshot showed the crosshair reading 22:42 while the snap label read 22:58, sixteen bars apart on a 1m chart, and no headless run reproduced it at any viewport, any device pixel ratio, before or after a resize, or across ten scripted gestures. Opening the chart with ?diag=1 makes every snap post what the client computed — the cursor's time, price and x, the snapped time and price, how many bars are held, the first and last of them, and the chart width — which the server logs as SNAPDBG. ?diag=0 turns it off; the setting is remembered. Throttled to about one report a second, and off by default, so it costs nothing when unused. Kept as a permanent facility rather than scaffolding to delete: this will not be the last geometry puzzle, and the endpoint takes whatever fields SnapReport declares. Documented in AGENTS.md alongside the note about where things run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
108 lines
4.9 KiB
Markdown
108 lines
4.9 KiB
Markdown
# Working on this repo
|
|
|
|
## Tests earn their place by catching a real bug
|
|
|
|
When a bug is found, ask whether a unit test could reasonably have caught it. If
|
|
yes, write that test with the fix. If no — a rendering artefact, a browser
|
|
quirk, a data-source oddity — say so and don't add one.
|
|
|
|
The bar is "would this have failed before the fix, and would it fail again if
|
|
someone reintroduced it". Tests that restate the implementation, assert
|
|
constructor defaults, or exercise paths nothing depends on are noise; they make
|
|
the suite slow to run and expensive to change, which is how a suite stops being
|
|
trusted.
|
|
|
|
What has actually paid off here: bar aggregation and bucket boundaries, the
|
|
store's replace-vs-append rules, level and alert arithmetic, parsing real
|
|
market-data payloads (fixtures are trimmed real responses, not invented), and
|
|
the invariants that would otherwise be silent — a comment must never become a
|
|
level, a tick must never overwrite a settled bar, volume must be counted once.
|
|
|
|
Name the test after the failure, not the function: `test_a_tick_cannot_overwrite
|
|
_a_settled_bar` beats `test_put`.
|
|
|
|
## Where things run
|
|
|
|
**The agent works on a remote machine over SSH. The user's browser runs on a
|
|
different machine.** Consequences, all learned the hard way:
|
|
|
|
- You cannot see the user's screen, console, or cursor. Screenshots and pasted
|
|
console output are the only window into it. Browser extensions that drive
|
|
"your" Chrome do not help — they attach to the machine the browser is on.
|
|
- Headless Chromium here renders on server hardware: different screen, window
|
|
size and device pixel ratio from the user's. "Works in my headless run" is not
|
|
evidence that it works for them. When a UI bug will not reproduce, match their
|
|
viewport and `deviceScaleFactor` explicitly before concluding anything.
|
|
- The dev stack is served to them over the network (e.g. `hera.local:8010`),
|
|
which is the same app the headless browser reaches as `http://api:8000`.
|
|
|
|
When a visual bug resists reproduction, prefer putting the numbers **on screen**
|
|
in the app over asking for another console paste — one screenshot then carries
|
|
the whole diagnosis.
|
|
|
|
## Verify UI in a real browser
|
|
|
|
Chart bugs are invisible from the outside — the API, the socket and the
|
|
frontend source can each be correct while the screen is wrong. Drive the
|
|
Playwright container against the dev stack:
|
|
|
|
```
|
|
docker exec -i chart-playwright-1 node - <<'EOF'
|
|
const { chromium } = require('/usr/lib/node_modules/playwright');
|
|
// launch with args:['--lang=en-US'] — see below
|
|
EOF
|
|
```
|
|
|
|
**Always launch Chromium with `args: ['--lang=en-US']`.** The container has no
|
|
usable locale, so Chromium reports `en-US@posix`, `Intl` throws, and the chart
|
|
renders as a blank canvas that looks exactly like a broken app.
|
|
|
|
`window.__chart` is a deliberate debug handle. Querying it separates "the data
|
|
is missing" from "the data is off-screen" — which is how a viewport bug that
|
|
three passing API checks had missed was finally found.
|
|
|
|
## Diagnostic mode
|
|
|
|
Chart geometry bugs live in the browser, which is usually on a different machine
|
|
from whoever is debugging them. Rather than asking for console pastes:
|
|
|
|
```
|
|
open the chart with ?diag=1 # remembered until ?diag=0
|
|
docker compose logs api | grep SNAPDBG
|
|
```
|
|
|
|
With it on, every snap the trendline tool computes is posted to
|
|
`/api/debug/snap` and logged server-side — the cursor's time, price and x, the
|
|
snapped time and price, how many bars were held, the first and last bar, and the
|
|
chart's width. Throttled to about one a second. It reads the client's own
|
|
numbers, which is exactly what "works in my headless run" cannot tell you.
|
|
|
|
Extend it when the next geometry puzzle appears; the endpoint takes whatever
|
|
fields `SnapReport` declares.
|
|
|
|
## Running tests
|
|
|
|
```
|
|
docker exec chart-api-1 sh -c "cd /app && python -m pytest -q"
|
|
```
|
|
|
|
pytest + pytest-asyncio, declared in `requirements-dev.txt`. Tests live in
|
|
`tests/`, import from `app.*`, and use `tmp_path` for anything that persists.
|
|
Async paths are driven with `asyncio.run(...)` directly rather than async test
|
|
markers.
|
|
|
|
## Things that will cost you an hour
|
|
|
|
- **Never write scratch `.py` files into the repo root.** It is bind-mounted, so
|
|
`--reload` restarts the app, and startup takes ~82 seconds. Pipe throwaway
|
|
scripts over stdin instead: `docker exec -i chart-api-1 python - <<'EOF'`.
|
|
Screenshots into `artifacts/` are safe; only `.py` triggers the reloader.
|
|
- **Dev and production keep separate drawing stores.** Dev writes
|
|
`data/manual_lines.json`; production has its own Coolify volume. A fix that
|
|
"didn't land" is often the other store.
|
|
- **Rebuild the image after touching `requirements.txt`.** The bind mount makes
|
|
source edits look live while an added dependency is simply absent.
|
|
- **A deploy resets alert cooldowns**, so production may re-alert on whatever
|
|
price is sitting on. There is no durable state yet.
|
|
- Times are epoch seconds, UTC, everywhere. Only the display is localised —
|
|
never shift the stored values.
|