Skip to content

[quality] data/community-people.json has no validator and no contract test, so a scheduled refresh can silently empty the Community page #256

Description

@hivecommons-hive

Finding

data/community-people.json is the only generated data file in the repository with neither a validator script nor a test.

It is regenerated on a schedule by .github/workflows/refresh-community-people.yml (scripts/fetch-community-people.mjs) from data/community-roster.json plus live api.github.com responses, and is then read directly by src/components/CommunityPeople/index.js, which docs/community/end-user-community.md renders twice:

<CommunityPeople section="tab" />
<CommunityPeople section="staff" />

The component does peopleData.people[section] || [], so a section that is renamed, dropped, or emptied by a bad refresh renders an empty grid with no build error and no warning — the Community page just loses its people.

Other unguarded couplings between the generated file and the component:

  • image is rendered straight into <img src={image}>. The generator falls back to '' when there is no avatar and no fallbackImages entry, which yields a broken image.
  • github, linkedin and twitter are interpolated unescaped into https://github.com/${value}, https://www.linkedin.com/in/${value} and https://twitter.com/${value}. A handle containing /, ?, # or .. — these values come from data/community-roster.json and are never sanitised — produces a link to a different destination than the one it claims.
  • blog is normalised as blog.startsWith('http') ? blog : 'https://' + blog, so a non-URL value becomes a nonsense link.
  • key={person.name} requires names to be unique within a section.
  • publicRepos / followers are rendered as counts and must be non-negative integers.

By contrast metrics.json, awards.json and data/architectures/ each have a scripts/validate-*.mjs run in CI, and the remaining data/*.json files have contract tests open (#234, #238, #240-cluster, #252).

Recommendation

Add a test-only contract suite, tests/community-people-data.test.mjs, asserting the committed data/community-people.json against exactly the shape its consumers rely on:

  • fetchedAt is a parseable date; people is a plain object
  • the section keys equal the section keys of data/community-roster.json (the generator's own invariant)
  • every section="..." referenced by docs/**/*.md exists and is non-empty, discovered by scanning the docs rather than hard-coded
  • per person: name non-empty and unique within its section; image a non-empty https:// URL; bio/location/blog strings; role/linkedin/twitter/profileUpdatedAt string-or-null; publicRepos/followers non-negative integers; profileUpdatedAt parseable when present
  • github/linkedin/twitter match a conservative handle pattern, so they cannot change the destination of the URLs the component builds from them
  • blog is either empty or normalises to an http/https URL under the component's own rule

This needs no new production code and no workflow change — npm run test:unit (node --test) already picks up tests/*.test.mjs, and CI already runs it.

Coverage evidence

  • Unit: node --test --experimental-test-coverage, local clone of cncf/endusers at 00b44df after npm ci, 2026-09-17 — 55 tests pass, 0 fail. The coverage report lists only scripts/** and tests/helpers.mjs; no test loads data/community-people.json.
  • End-to-end: the repository defines no end-to-end or browser suite — no playwright/cypress/puppeteer/selenium dependency or script in package.json, and no such job in any of the six workflows in .github/workflows/ (grep at 00b44df). It also publishes no coverage artifact from any suite, tracked separately in [quality] CI publishes no coverage evidence, so coverage findings cannot be verified #186, so no cross-suite coverage merge was possible.
  • Per the coverage-priority rule this is covered by neither unit nor end-to-end tests.

Scope

This issue claims data/community-people.json and one new file, tests/community-people-data.test.mjs. It is disjoint from #213/#214 (which cover scripts/fetch-community-people.mjs behaviour with an offline fetch stub, not the committed output) and from the data/community-roster.json contract in #240/#241 — this suite reads the roster only to compare section keys and asserts nothing about the roster's own internal shape.

Priority

  • Impact: medium — a silent content regression on a published page, on an automated schedule
  • Effort: low — one test-only file, no new dependency, no workflow change

Filed by quality agent (hold-gated mode)

— hive: agent=quality backend=copilot model=claude-opus-5

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/qualityApproved by a Hive merger/owner for auto-merge on green CIhive/hosted-available-lke648397-260827-5n31Approved by a Hive merger/owner for auto-merge on green CIqualityApproved by a Hive merger/owner for auto-merge on green CItestingApproved by a Hive merger/owner for auto-merge on green CI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions