Update the architecture diagram and env var table to reflect the
ContributionStore tier and incremental fetch, document the
graph:contributions:refetch command, and note composer phpstan next to
the existing test commands.
README had drifted from the codebase: wrong test path, a stale /health
sample response, a docker-compose volume mount pointing at a path the
image never writes to (with no SQLite data volume at all), a missing
CONTRIBUTIONS_RETENTION_DAYS example, and an architecture diagram
predating the SQLite store/refetch command. Also fixes
docker-compose.prod.yml, which was missing the data volume and
CONTRIBUTIONS_RETENTION_DAYS env var present in docker-compose.yml —
without it, a prod deployment on this file loses contribution history
on every container recreate.
Add --memory-limit=512M to the phpstan composer script — the default
128M crashed the level-8 run under parallel workers. Update
PHP-Stan-Errors.md to note all 22 catalogued findings are now resolved.
Add professional PHPDoc (summary + @param/@return) to the remaining
public methods that had none or only partial coverage: GraphController's
actions, ContributionStore's accessors, SvgRenderer's render() and
private helpers (with concrete array-shape annotations), ProbeTrait,
ProviderHealthChecker, ProviderStatus, and the Contribution entity.
Also adds the matching @return shapes on SvgRendererTest's data
providers.
array_map() over the variadic constructor can produce non-sequential or
string keys, which doesn't match the array<int, Contribution> property
type; re-index with array_values(). Also documents the iterator/count
accessors.
date() expects int|null, but strtotime() returns int|false; cast to make
the type explicit for PHPStan level 8 (the date strings are always
well-formed here, so strtotime() never actually returns false).
Bound each provider's startFetch()/resolveFetch() to an optional
since/until window, wire a SQLite-backed ContributionStore into
ContributionAggregator (fetch only the trailing window past the last
stored date, merge into the store, prune by CONTRIBUTIONS_RETENTION_DAYS),
and add a graph:contributions:refetch command to force a full or ranged
re-fetch in <=365-day chunks. This is the root fix for the full
365-day-refetch timeout that used to hit on every cache miss.
docker-compose.override.yml only overrode the dockerfile, so it kept
inheriting target: final from docker-compose.yml — but Dockerfile.dev
is single-stage and has no final stage, so the dev build failed. Name
the stage in Dockerfile.dev and target it explicitly from the
override.
- Fix add()'s upsert SQL (was invalid SQL from typos) and use it for
the new merge() as well.
- Fix remove()'s missing FROM keyword and drop the unindexed LIKE scan
in favor of an exact match.
- Add latestDate(), all() with optional sinceDays filtering, and
prune() with a retention-day cutoff.
- Add Contribution (immutable value object) and ContributionCollection
(IteratorAggregate + Countable) so all() returns something typed
instead of a raw date => count array.
- Cover all of the above with unit tests against an in-memory SQLite
database.
The eightpoints/guzzle-bundle and idci/graphql-client-bundle only
existed to build one near-static query string, and the bundle's own
HTTP transport was already bypassed in favor of Symfony HttpClient.
Build the query with sprintf/json_encode instead, drop both bundles
from config/bundles.php, and update the test double accordingly.