Wire homepage investor count to the live API - #82
Conversation
The "N Eesti inimest kogub Tuleva indeksfondides" figure on the front page was typed in by hand in wp-admin, and had not been touched since early May 2026 — 85 224 displayed against an actual 86 233. onboarding-service PR #1643 shipped GET /v1/statistics/investor-count in June specifically to automate this, but nothing ever consumed it. This is the missing half: credentials.php and team-hero.php now read the live count, falling back to the members_count ACF field if the call fails, so the block can never render blank or zero. Replaces the dead get_member_count() helper, which was referenced nowhere and pointed at /v1/members (ühistu members, 9 729 — the wrong metric). It also called stream_context_set_default(), which mutated the default stream context for the whole request. The API response is cached for a day. mv_kpi_new only moves once a month, so the displayed number changes about monthly while staying at most a day behind. A failed call is cached for five minutes and the 1s timeout is bounded (verified: an unresponsive host returns after 1010ms), so an outage costs one slow render per five minutes rather than one per visitor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ate value
Follow-up to the review pass on this branch. Four fixes:
1. team-hero.php no longer calls the API. It assigns $members_count but never
renders it (the assignment was already dead code on master), so wiring it up
turned a free get_field() into a blocking HTTP call for a number that is never
displayed. Reverted to match master exactly.
2. A failed refresh no longer falls back to the hand-entered ACF field. The last
count the API returned successfully is kept in an option, so an outage keeps
showing yesterday's real number instead of dropping to members_count, which
can be months stale. Previously, one failed refresh after a day of correct
values would have put 85 224 back on the front page.
3. Added the localhost guard the four fund templates already use, so local dev
stops calling the production API and polluting its observability.
4. Value validation: require is_numeric, so a malformed {"count":"70000abc"} is
rejected rather than cast to 70000, and mirror the endpoint's own SQL sanity
bounds with an upper limit of 500000 alongside the existing floor.
Verified with a harness that runs the real function source against stubbed
WordPress functions, with get_transient modelling the options-table backend
returning scalars as strings: 21/21 checks pass, covering the live endpoint,
cache hit, failure-keeps-last-good, cold-start-falls-through-to-ACF, the
localhost guard, and eight value-validation cases.
Cache stampede on transient expiry is a known remaining gap, reduced but not
closed by the last-good fallback. Left for a follow-up.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Second round: review fixes (571929b)Ran two independent adversarial reviews over the first commit. Four findings fixed:
VerificationHarness runs the real function source against stubbed WordPress functions, with Known remaining gapCache stampede on transient expiry. There is no lock, so when the day-long transient expires, concurrent requests each make their own API call. Reduced but not closed by the last-good fallback. Worth a follow-up if front-page concurrency justifies it — the call is bounded at 1s and the endpoint is |
Why
The "N Eesti inimest kogub Tuleva indeksfondides" figure on the front page is typed in by hand in wp-admin. It was last changed between 12 April and 11 May 2026 and has been frozen since — the site shows 85 224 against an actual 86 233, about 1 000 people short.
onboarding-service #1643 shipped
GET /v1/statistics/investor-counton 2026-06-04 specifically to automate this ("Automates the ... figure on the tuleva.ee front page, which is currently edited by hand in wp-admin"). The endpoint works, but nothing ever consumed it — an org-wide code search finds zero callers. This is the missing half.What changed
helpers/extras.php— replaces the deadget_member_count()withget_investor_count(), which reads/v1/statistics/investor-countand caches it in a transient./v1/members(ühistu members, 9 729 — the wrong metric), and calledstream_context_set_default(), mutating the default stream context for the whole request.components/credentials.phpandcomponents/team-hero.php— one line each:$members_count = get_investor_count() ?: get_field('members_count', 'option');The
members_countACF field stays as the fallback. The existingif ($members_count && $members_count_description)guard already hides the block on a falsy value, so the worst case is today's behaviour, never a blank or a zero.Caching / load
mv_kpi_newonly moves once a month, so the response is cached for a day: the displayed number changes about monthly and is never more than a day stale.Render-time safety, since this is the highest-traffic page:
fund-stocks-content.php:20-29). Verified bounded: an unresponsive host returns after 1010ms.Cache-Control: public, max-age=1hserver-side.Test plan
Ran the real
get_investor_count()source against the live endpoint with stubbed transient functions:86 233, cached for a day0 ?: $acffalls through to the ACF value; a real count wins over itphp -lclean on all three filescurl -s https://tuleva.ee/ | grep -A2 membercounttrackscurl -s https://onboarding-service.tuleva.ee/v1/statistics/investor-countNote
Worth bumping the ACF field to the current value in wp-admin regardless, since it stays as the fallback.
🤖 Generated with Claude Code