Repository navigation
Verify persona addressing teams in CI #1280
Copy link
Copy link
Closed
Labels
dev-leadFor dev-lead agent pickupFor dev-lead agent pickupdocumentationDocumentation changesDocumentation changesenhancementFeature requestsFeature requests
Description
Context
The persona standard (§4.1) requires a persona's address.handle to be an org team with specific live properties. Four places already assert that CI verifies this. Nothing does. See combsmith/.github#755 (finding 3).
The claim appears in:
standards/personas/persona.schema.json— "Verified by CI, not by this schema (needs the network)"standards/persona-standards.md§4.1 — "verified by a separate CI step"standards/personas/TEMPLATE/persona.yml— "CI verifies those live properties"personas/qa-lead/persona.yml— "verified by CI, not by the schema"
Why it matters
Without the check, a persona can ship with:
privacy: secret→ the team is unmentionable. The persona is silently dead; every mention does nothing and nobody learns why.notification_setting: notifications_enabled→ every mention pages the team's members. The handle exists to route a webhook, not to notify humans.- A team that does not exist at all → every mention silently no-ops.
All three fail silently, which is the failure mode this framework keeps producing. This is O(N) manual setup today with zero verification — it will not survive more personas.
Task
Add a CI check that, for every personas/<id>/persona.yml declaring an address block, verifies the live team:
- The team
address.handlenames exists. privacy == "closed"(secret teams cannot be mentioned).notification_setting == "notifications_disabled".- The team slug equals
id(already enforced hermetically bypersonas/validate-personas.py; do not duplicate — this check is about live state only).
Constraints (important)
- Do NOT put this in
personas/validate-personas.py. That validator is deliberately hermetic, andtests/test_validate_personas.batsis a hermetic suite. Its hermeticity is worth more than the check — that is precisely why this check was split out. Adding a network call there breaks the suite's contract. - Add it as a separate job in
.github/workflows/lint.yml, alongside the existingvalidate-personasjob (follow that job's shape). - Requires an org-scoped token to read teams (
GH_PAT_WORKFLOWSis available as an org secret).gh api orgs/petry-projects/teams/<slug>returnsprivacyandnotification_setting. - A persona without an
addressblock must pass (address is optional unless thementionsurface is enabled). - Fail with a clear diagnostic, never a traceback — match the
::error::persona manifest invalid: ...style already used byvalidate-personas.py. - Fail closed: an API error must NOT be read as "the team is fine". Conflating an error with a negative answer is the systemic bug this framework already has four instances of (feat: implement issue #737 — [Phase 1] Instrument auto-rebase: agentic-conflict-resolution rate + fan-out CI-run volume report #755). If the team cannot be read, fail the check.
Files that exist today (verified)
personas/validate-personas.py— the hermetic validator (do not add network here)personas/qa-lead/persona.yml— the only persona;address.handle: petry-projects/qa-lead.github/workflows/lint.yml— has thevalidate-personasjob to model this ontests/test_validate_personas.bats— hermetic suite, 20 tests
Acceptance criteria
- CI fails when a manifest's
address.handlenames a team that does not exist. - CI fails when the team is
privacy: secret. - CI fails when the team has
notification_setting: notifications_enabled. - CI fails (does not pass) when the teams API errors.
- CI passes for
qa-leadas it stands today (teampetry-projects/qa-leadis closed, notifications disabled, 0 members). - Personas with no
addressblock pass. -
tests/test_validate_personas.batsstill passes and remains hermetic (no network). - New logic is shellcheck-clean if shell; if Python, it does not import into the hermetic validator's path.
- The four docs above are updated only if the check's name/location differs from what they describe.
Ground truth for the happy path
$ gh api orgs/petry-projects/teams/qa-lead --jq '{slug,privacy,notification_setting,members_count}'
{"members_count":0,"notification_setting":"notifications_disabled","privacy":"closed","slug":"qa-lead"}
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
dev-leadFor dev-lead agent pickupFor dev-lead agent pickupdocumentationDocumentation changesDocumentation changesenhancementFeature requestsFeature requests
Activity
github-actions commentedon Jul 17, 2026
Thanks for such a thorough write-up — the acceptance criteria, constraints, and ground-truth API output make this very actionable. One clarifying question: do you have a preference for the implementation language of the new job (shell script vs. Python)? The issue leaves both open, and the choice affects how the diagnostic output is structured and whether it fits naturally alongside the existing
validate-personas.pytooling.don-petry commentedon Jul 17, 2026
Dev-Lead Implementation Plan
Issue: #1280 — Verify persona addressing teams in CI
Scope
Add a separate, network-touching CI check (
verify-persona-teams) that, for everypersonas/<id>/persona.ymldeclaring anaddressblock, verifies the LIVE team named byaddress.handle: it exists,privacy == "closed", andnotification_setting == "notifications_disabled"— failing closed on any API error. The hermeticvalidate-personas.pyis left untouched.Implementation Checklist
scripts/verify-persona-teams.sh— new shell script (POSIX bash,set -euo pipefail, shellcheck-clean). Extractsaddress.handleslugs (python3+pyyaml), fetchesgh api orgs/<org>/teams/<slug>, asserts exists +privacy=closed+notification_setting=notifications_disabled. Fails closed on any API/parse error with a::error::persona team invalid: ...diagnostic. Personas with noaddressblock pass..github/workflows/lint.yml— newverify-persona-teamsjob alongsidevalidate-personas(installs pyyaml, runs the script withGH_TOKEN: secrets.GH_PAT_WORKFLOWS).tests/verify_persona_teams.bats— hermetic unit/fixture suite (stubsgh, no network)..github/workflows/lint.ymlbats job list — addtests/verify_persona_teams.bats.tests/test_validate_personas.batsalready references theverify-persona-teamsjob by name.)Tests to Write
privacy: secret→ fail (diagnostic mentions privacy/closed)notification_setting: notifications_enabled→ failaddressblock → passtests/test_validate_personas.batsstill passes and stays hermeticTest Command
bats tests/verify_persona_teams.bats tests/test_validate_personas.bats+shellcheck --severity=warning -x scripts/verify-persona-teams.shdon-petry commentedon Jul 17, 2026
Dev-Lead: Implementation Complete
Plan Execution
scripts/verify-persona-teams.sh— the LIVE, network-touching half of persona addressing validation.verify-persona-teamsjob to.github/workflows/lint.yml(alongsidevalidate-personas, modeled on its shape).tests/verify_persona_teams.batsand registered it in thebatsjob list.personas/validate-personas.pyand its hermetic suite untouched — no network added there.What the check does
For every
personas/<id>/persona.ymldeclaring anaddressblock, it resolvesaddress.handleto a team slug and callsgh api orgs/petry-projects/teams/<slug>, then asserts the LIVE team:privacy == "closed"(secret teams are unmentionable),notification_setting == "notifications_disabled"(routes a webhook, never pages members).Fails closed: any API/parse error is a failure, never a pass (#755). Diagnostics use the
::error::persona team invalid: ...style, never a traceback. Personas with noaddressblock pass. The slug==id invariant is not re-checked (already hermetic invalidate-personas.py).Acceptance criteria
privacy: secret.notification_setting: notifications_enabled.qa-leadas it stands today — verified live end-to-end:personas/qa-lead/persona.yml → petry-projects/qa-lead (exists, closed, notifications disabled).addressblock pass.tests/test_validate_personas.batsstill passes and stays hermetic (no network).--severity=warning -x); actionlint-clean;.dev-lead/scripts/dev-lead-lint.shpasses.tests/test_validate_personas.batsalready references theverify-persona-teamsjob by that exact name — so no doc edits were required.Test Results
Files Changed
scripts/verify-persona-teams.sh(new): the live team check (bash + python3/PyYAML + jq)..github/workflows/lint.yml: newverify-persona-teamsjob (tokenGH_PAT_WORKFLOWS) +tests/verify_persona_teams.batsadded to the bats list.tests/verify_persona_teams.bats(new): 14 hermetic tests (ghstubbed, no network).Notes
GH_TOKEN: secrets.GH_PAT_WORKFLOWSbecausegithub.tokencannot read org teams. On a token-less context the read fails and the job fails closed — the intended behavior. The Lint workflow'spersonas/**path filter scopes the job to persona changes.vpt_team_api/gh, keeping the suite hermetic (mirrors thevalidate-personassplit).feat: implement issue #1280 — Verify persona addressing teams in CI (#…