chart/AGENTS.md
Chris Amow 7ce6d33bb5 Show the price the anchor will use, and record where things run
The snap dot said where an anchor would land but not what it was, which made
"the dot is in the wrong place" and "the dot is on the wrong bar" impossible to
tell apart from a screenshot. It now carries a label with the price, whether it
is a high or a low, and the bar's time.

AGENTS.md gains the environment fact this session kept rediscovering: the agent
runs on a remote machine over SSH while the user's browser runs on another, so
headless runs here render on different hardware at a different size and pixel
ratio, and "it passes in my headless run" is not evidence the user's problem is
fixed. Three consecutive reproductions passed server-side while the chart was
unusable on the user's screen. When a visual bug will not reproduce, match their
viewport and deviceScaleFactor explicitly — and prefer putting the numbers on
screen over asking for another console paste.

Device pixel ratio is ruled out for the current trendline complaint: the
bottom-sweep check lands 115 of 115 at 1600x1000 and at 1900x1400 with ratios of
1, 1.25, 1.5 and 2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 22:35:40 -05:00

89 lines
4.2 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.
## 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.