The repository had no licence at all, so every source was all-rights-reserved
by default. Add the verbatim MIT text at LICENSE (Copyright (c) 2026 Martino
Ferrari) and a two-line SPDX header to each of the 113 first-party sources --
48 include/plot, 26 src, 10 adapters, 5 examples, 21 tests, 3 tools -- so the
grant travels with each file rather than relying on the root document alone.
Scripts keep their shebang on line 1; the SPDX lines follow it. Genuinely
vendored code is untouched: third_party/stb_truetype.h carries no header of
ours. src/render/embedded_mono_font.cpp is generated, so tools/fonts/gen_atlas.py
emit() now writes the two lines into its header comment and the file was
regenerated -- gen_atlas.py --check passes and the regeneration adds only those
comment lines to the previously committed file.
README's provenance section now states MIT and links LICENSE.
No code, test or public API change.
Four review findings on the Task 7 renderer:
- InstanceStore::epoch() is derived from gl::Buffer::generation() -- advanced by
every allocation, deletion and move -- plus the slot the instances were written
to, so the orphan fallback of grow_persistent() can no longer leave the vertex
array's attribute pointers bound to a deleted buffer: the epoch reports the new
identity and upload() re-points them before the draw.
- PlotLayout reserves y_title_area/x_title_area outside axes (sized from the text
metrics) and draw_titles() draws in them, so a title can no longer land on the
data, whatever the envelope does.
- draw() computes plot.layout() once and hands it to the helpers, instead of six
times per frame, each of which rebuilt the ticks and the grid.
- A refused fence is no longer dropped: mark_drawn() falls back to the orphan path
and reports the failure rather than rotating through an unsynchronised slot.
New smoke cases force both refusals through the GL entry points (glMapBufferRange,
glFenceSync) and assert the pixels plus the live transfer mode; the title case
asserts the title colour is absent from the bands inside axes and present in both
strips. All three fail against the pre-fix sources.
- loader: add available(), true only after load() has resolved every
required entry point (false at the start of load()). Program::from_source
and the other fallible constructors return ErrorCode::Unsupported while it
is false, so a failed load() with a partially populated table can no longer
fault on an unresolved entry point.
- caps: GL_CONTEXT_PROFILE_MASK only exists from 3.2, so query it only on
3.2+ contexts. A 3.0/3.1 query latches GL_INVALID_ENUM in the context, which
the next unrelated glGetError() (e.g. read_pixel) then reports as failure.
- tests: gate the dlsym/libGL.so.1 fallback assertions on libGL being
resolvable, so the GL suite also passes on a GLES-only host.