Repository navigation
[Phase 2] Author a machine-readable interaction contract for every agentic role, per the standard #1404
Copy link
Copy link
Closed
Labels
dev-leadFor dev-lead agent pickupFor dev-lead agent pickupinitiativeEpic / initiative tracking issueEpic / initiative tracking issue
Description
Story
As a maintainer of the agentic fleet,
I want a checked-in, machine-readable interaction contract for each agentic role, in the location and shape the standard fixes,
so that each role's triggers, timers, emitted events, idempotency key, concurrency lane, stop markers, and budget are declared and diffable, so the CI enforcer (Story 4) has something to validate against.
Acceptance Criteria
- Every existing agentic role has a machine-readable interaction contract checked into the repo: the personas (dev-lead, pr-review, business-analyst, devops-lead, qa-lead, scrum-master, security-lead, sre-lead) and the non-persona agents (dev-lead runtime, pr-review runtime, ci-failure-analyst) covered per the standard's location decision.
- Each contract declares triggers.events[], triggers.timers[] (each with cron + timer_role + justification + stop_condition + event_fast_path), emits[] (event + identity token/PAT), idempotency_key, concurrency_lane, stop_markers[], and budget.
- Each contract's declared triggers match the role's actual deployed on: block (verified against the workflow source), so the contract is not aspirational.
- The contract location matches the decision recorded in docs/agentic-interaction-model.md; if contracts extend persona.yml, the persona validator/schema dependency is respected (org-first or a standalone file), with no failing validate-personas.
- A hermetic parse/consistency check (or the existing persona validator, if extended) confirms every contract is well-formed.
Tasks / Subtasks
- Per the standard's location decision, add the interaction-contract block to each personas//persona.yml (or a standalone per-role file), populating triggers/emits/idempotency_key/concurrency_lane/stop_markers/budget from each role's real wiring. (AC: test issue from agent #1, Go-live improvements for PR review agent #2, Optimize review: small-PR and incremental fast paths #4)
- Add standalone contracts for the three non-persona runtimes (dev-lead, pr-review, ci-failure-analyst) whose triggers live in .github/workflows/ rather than a persona manifest. (AC: test issue from agent #1, Go-live improvements for PR review agent #2)
- Cross-check each contract's triggers.events[]/timers[] against the workflow's actual on: block (dev-lead.yml, pr-review-trigger.yml, pr-review-sweep.yml, dev-lead-retry.yml, ci-failure-analyst.lock.yml, persona-mention.yml, etc.) and reconcile any divergence into the contract. (AC: Add @claude delegation, auto-merge, and rebase handling #3)
- If extending persona.yml, confirm personas/validate-personas.py and the fetched persona.schema.json accept the new fields (org-first sequencing per the standard); otherwise place contracts in a standalone file and add a hermetic well-formedness check. (AC: Optimize review: small-PR and incremental fast paths #4, feat: add Copilot engine support via REVIEW_ENGINE toggle #5)
Dev Notes
- The persona manifests already model triggers as structured data — see personas/dev-lead/persona.yml (triggers.surfaces[] with surface/events/mode/gate_label/notes). The interaction contract extends this concept with the timer/emit/idempotency fields; reuse the existing structure rather than inventing a parallel one.
- personas/validate-personas.py fetches persona.schema.json from petry-projects/.github (SCHEMA_REPO). New persona.yml fields must be permitted by that schema or validate-personas fails — this is the cross-repo prerequisite in the epic. Prefer the location the standard chose in Story 1; do not silently pick a different one.
- The non-persona agents' real triggers: dev-lead.yml (labeled issues, review, check_run), pr-review-trigger.yml (event fast-path), pr-review-sweep.yml (schedule + workflow_run), dev-lead-retry.yml (schedule), ci-failure-analyst.lock.yml (check_run:completed). Note ci-failure-analyst.lock.yml is a byte-frozen ring-0 caller stub — the contract must describe it, not edit it.
- idempotency_key / concurrency_lane values already exist implicitly as concurrency.group in each workflow (e.g. ci-failure-analyst-${{ head_sha }}); source the declared values from those, do not invent new ones.
- Testing standard: a hermetic (no-network) parse test in the bats suite, mirroring tests/test_validate_personas.bats. If extending persona.yml, add cases to the persona validator test set.
- Split option (if the org persona.schema.json decision stalls): the three standalone runtime contracts (dev-lead, pr-review, ci-failure-analyst) have NO external-schema gate and can land in a first PR; the 8 persona.yml extensions, which depend on the org schema permitting the new fields, can follow in a second PR. Keep them one story only if the schema decision from Story 1 is already settled.
Project Structure Notes
Contracts live under personas// (extending persona.yml) and/or a standalone per-role file for the three non-persona agents, exactly as the Story 1 standard dictates. This is the data Story 4's validator consumes.
References
- personas/dev-lead/persona.yml
- personas/validate-personas.py
- personas/pr-review/persona.yml
- .github/workflows/dev-lead.yml
- .github/workflows/pr-review-trigger.yml
- .github/workflows/ci-failure-analyst.lock.yml
Likely target surface
personas/dev-lead/persona.ymlpersonas/pr-review/persona.ymlpersonas/validate-personas.py
Story prepared by the BMAD Scrum Master (Bob) for epic #1402. Status: ready-for-dev.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
dev-leadFor dev-lead agent pickupFor dev-lead agent pickupinitiativeEpic / initiative tracking issueEpic / initiative tracking issue
Activity
don-petry commentedon Aug 2, 2026
Dev-Lead Implementation Plan
Issue: #1404 — [Phase 2] Author a machine-readable interaction contract for every agentic role, per the standard
Scope
Author a standalone, machine-readable
interaction:contract for each of the 11 agentic roles (8 personas + 3 non-persona runtimes) per the schema fixed indocs/agentic-interaction-model.md§8.1, using the standard's §8.2 location decision (option b: standalone repo-local files, notpersona.yml) — so there is no cross-repopersona.schema.jsondependency andvalidate-personas.pyis untouched — plus a hermetic well-formedness validator + bats test.Location decision (per §8.2)
personas/<id>/interaction.yml— co-located, standalone (NOT apersona.ymlblock).interaction-contracts/{dev-lead,pr-review,ci-failure-analyst}.yml— standalone, keyed by workflow path.Trigger cross-check (AC #3 — matched against real
on:blocks)dev-lead.yml(Class 1: pull_request, pull_request_review, pull_request_review_comment, issue_comment, issues, check_run, repository_dispatch; no schedule).pr-review-trigger.yml(Class 1: check_suite, pull_request_review, pull_request, workflow_dispatch, repository_dispatch:pr-review-mention).persona-mention.yml(Class 1: issue_comment, pull_request_review_comment, discussion_comment) → persona-runner. Contracts declare the deployed router triggers, reconciling the broader-but-not-wired surfaces inpersona.yml.dev-lead.yml+dev-lead-retry.yml(Class 2 self-heal cron15 */2 * * *, event_fast_path: null — the §6.3 leak).pr-review-trigger.yml+pr-review-sweep.yml(Class 2 backstop cron2,17,32,47 * * * *, event_fast_path: workflow_run:[completed]).ci-failure-analyst.lock.yml(Class 1: check_run:[completed]; byte-frozen ring-0 stub — described, never edited).idempotency_key/concurrency_lanesourced from each workflow's realconcurrency.group(dev-lead-reusable per-PR/per-issue lanes, pr-review per-PR lane,ci-failure-analyst-${head_sha},dev-lead-retry,pr-review-sweep-${branch}, persona-runner lane).stop_markersfrom the human-gate markers in use (needs-human-review, dev-lead:needs-human,<id>:hands-off).budget:pr-automation-budgetfor write runtimes,nonefor advisory mention personas.Implementation Checklist
interaction-contracts/validate-interaction-contracts.py— hermetic (no-network) well-formedness validator: required fields, timer sub-fields, Class-2 timers carry timer_role+stop_condition, emits/events disjointness (narrow §7-rule-1 guard),workflows[]paths exist. Deepon:-block match is Story 4 ([Phase 3] Add scripts/validate-interaction-model.sh + bats, wired into lint.yml as a PR-triggered check #1406).personas/<id>/interaction.yml× 8interaction-contracts/{dev-lead,pr-review,ci-failure-analyst}.yml× 3.github/workflows/lint.yml— addvalidate-interaction-contractsjob (mirrorsvalidate-personas) + register the new bats file in thebatsjob list.Tests to Write
tests/test_validate_interaction_contracts.bats— hermetic, mirrorstests/test_validate_personas.bats: accepts a well-formed fixture; rejects missing required field, malformed timer, Class-2 timer with no timer_role, emit that self-triggers, non-existent workflow path.Test Command
bats tests/test_validate_interaction_contracts.bats(full suite:scripts/run-bats.sh tests/*.bats); lint gate:bash .dev-lead/scripts/dev-lead-lint.sh; contracts checked live viapython3 interaction-contracts/validate-interaction-contracts.py.don-petry commentedon Aug 2, 2026
Story 2 / #1404 — complete
Authored a machine-readable interaction contract for every agentic role and wired a hermetic CI gate to keep them honest.
What shipped
11 contracts (§8.2 decision: option (b) — standalone repo-local files, so
persona.schema.json/validate-personas.pyare untouched, no cross-repo dependency):personas/<id>/interaction.ymlfor dev-lead, pr-review, business-analyst, devops-lead, qa-lead, scrum-master, security-lead, sre-lead.interaction-contracts/{dev-lead,pr-review,ci-failure-analyst}.yml(deployment view; dev-lead/pr-review fold in their Class-2 backstop timers, ci-failure-analyst has no persona manifest).Each contract declares the full §8.1 shape:
triggers.events[],triggers.timers[](cron + role + justification + stop_condition + event_fast_path),emits[],idempotency_key,concurrency_lane,stop_markers[],budget.Acceptance criteria
on:block, not aspiration. The advisory personas record onlypersona-mention.yml's wiredissue_comment/pull_request_review_comment/discussion_comment; the broader persona.yml surfaces (reviewer-assignment etc.) are noted as not-yet-wired (finding 10 / Persona framework — pre-rollout review findings .github#755). ✅interaction-contracts/validate-interaction-contracts.py, gated inlint.yml(newvalidate-interaction-contractsjob +tests/test_validate_interaction_contracts.batsregistered in thebatsjob). ✅Modeling notes
pr-review-sweepis a textbook §2 Class-2 backstop (event_fast_path: workflow_run:[completed]);dev-lead-retryis the §6.3 self-heal leak (event_fast_path: null). Both re-dispatches are modeled as the timer's stop-condition-gated mechanism, not freedispatch:emits — so the §7 feat: implement issue #846 — [Phase 2] Cache the LSP index and enforce the 30s cold-start SLA with auto-skip #860 rule-1 self-trigger check stays meaningful without false-positiving legitimate gated backstops.dispatch:Xemit must not name a subscribedrepository_dispatch:X; no literal event/emit collision). Deep semantic self-trigger analysis is deferred to Story 4 / [Phase 3] Add scripts/validate-interaction-model.sh + bats, wired into lint.yml as a PR-triggered check #1406 as scoped.Verification
bats tests/test_validate_interaction_contracts.bats→ 13/13 pass (TDD: written red before the validator existed).python3 interaction-contracts/validate-interaction-contracts.py .→ OK: 11 interaction contract(s) valid, invariants hold.bash .dev-lead/scripts/dev-lead-lint.sh→ all checks passed (shellcheck 98 files + agent-profile/CODEOWNERS frontmatter).lint.ymlre-validated as YAML after the edits.CI owns the commit/PR.
feat: implement issue #1404 — [Phase 2] Author a machine-readable int…