The AI-assisted pathway (implementation)
This is the as-built AI pathway for HAICO, implemented by an agent named Coco. It is provider-agnostic: the implementer is a workflow-driven coding agent run by GitHub Agentic Workflows (gh-aw) on the repository’s own API key, not GitHub Copilot.
That choice is deliberate (see ADR-0002). The default engine is Copilot (triggered by the bare path:ai-automation label), which requires a GitHub Copilot seat (COPILOT_GITHUB_TOKEN). Seat-free alternatives are Claude (path:ai-automation-claude, needs ANTHROPIC_API_KEY) and Codex (path:ai-automation-codex, needs OPENAI_API_KEY); any maintainer with write access and the relevant API key can drive those without a Copilot seat (see §2.4 and §2.6).
The original Copilot-based design is preserved as background in github-projects-copilot-automation.md; where the two differ, this document wins.
1. The flow
Each AI step (plan, implement, iterate, review) exists as three engine variants (
-claude, -codex, -copilot) that share one prompt body from .github/aw/shared/; the PR’s engine label (set by the plan step) selects which one runs, and the other two no-op on their if: guard. Pick the engine by label (see §2.4 and §2.6). You can also assign Coco (HAI-Coco, or the login in AI_BOT_LOGIN) to an issue and the path:ai-automation label is added automatically, kicking off the Copilot pipeline without a separate labelling step.
Stage labels: where each item is, at a glance
Every AI-pathway issue and its PR carry exactly onestage:* label that advances automatically as the flow progresses, so the Issues and Pull-requests lists show where each item is without opening it. The labels are managed by coco-stage-labels.yml (never set them by hand); warm colors (yellow/orange/green) mean it is your turn to act.
A
/coco that only asks a question does not change the stage. A /coco during stage:awaiting-plan-approval revises the plan only (no code push, stage stays awaiting-plan-approval). A /coco that pushes a code fix (in implementing, testing-dev, or reviewing) advances to awaiting-dev-promotion once CI passes (then /promote dev to redeploy).
Why /coco and not @coco?
gh-aw has no @mention trigger; it deliberately neutralizes @mentions in untrusted comment text as an anti-prompt-injection measure.
So you address Coco with the slash command /coco, which is the equivalent of Copilot’s @copilot loop: Coco revises and pushes new commits to the same PR branch, and CI re-runs.
Roles in the flow:
- Assignee: the issue’s assignee, carried onto the PR (
github.event.issue.assignee.login); if the issue is unassigned, this falls back to the maintainer who appliedpath:ai-automation(github.actor). The assignee shepherds the PR, tests on dev, and approves. - Final-merge reviewer: does the final human merge, pinged on approval by
notify-approved; set with theREVIEWER_LOGINrepo variable (defaults to the repo owner). - Requested PR reviewer(s): GitHub users requested for review on the plan PR, read from the linked issue’s optional Reviewer(s) form field by
coco-plan-comment.yml; defaults toREVIEWER_LOGINwhen the field is blank. This is distinct from the final-merge reviewer above, though both default topetrosrapto.
- The PR stays a draft through plan, implementation, and dev testing; marking it “Ready for review” is what triggers Coco’s automated review, and a human still does the final merge (the agents never do).
- Opening the plan PR triggers one CI run on the
.coco/PLAN.mdcommit; the meaningful CI run happens on the implementation push, and the AI review happens later, when the PR is marked ready for review (or on demand with/review). - gh-aw cannot comment on a PR it creates in the same run, so
coco-plan-comment.ymlis the small companion that posts the plan as the first comment.
2. One-time setup
2.1 Secrets (Settings → Secrets and variables → Actions → Secrets)
Each engine has its own key; you only need the key(s) for the engines you actually use (the barepath:ai-automation / Claude path needs only ANTHROPIC_API_KEY).
Each engine’s wrapper carries only its own key, so gh-aw strict mode (which allows just the active engine’s key per file) is satisfied with all three engines compiled side by side.
These are pay-as-you-go API keys, not subscriptions. A ChatGPT/Codex subscription does not include or yield an
OPENAI_API_KEY, and a Claude Pro/Max subscription does not include or yield an ANTHROPIC_API_KEY: in both cases the API key is a separate credential with separate billing (platform.openai.com / console.anthropic.com), and you cannot “extract” one from a subscription. gh-aw’s codex and claude engines accept only API keys: the codex engine takes OPENAI_API_KEY (or CODEX_API_KEY), and the claude engine explicitly ignores CLAUDE_CODE_OAUTH_TOKEN (the Claude-subscription token) and requires ANTHROPIC_API_KEY. Copilot is the only engine here that authenticates via a subscription seat (COPILOT_GITHUB_TOKEN). Using a personal ChatGPT or Claude subscription to drive shared CI also runs against both providers’ terms (Anthropic actively de-authorizes subscription OAuth tokens outside its own tools), so the pathway uses API keys for Claude and Codex by design.
AI_AUTOMATION_PAT scopes (fine-grained PAT on this repo): Contents: read & write, Pull requests: read & write, Issues: read & write.
Treat it as sensitive; anyone who can edit a workflow can use it.
2.2 Variables (Settings → Secrets and variables → Actions → Variables)
Each engine reads its own model variable, and each stage can optionally override it, so you can tune all three engines (and, per engine, each stage) independently with no code change.
Threat detection runs only on the code-pushing stages (
/approve-plan, /coco) and only when the agent actually pushed code (conditional on needs.agent.outputs.has_patch); it is off for plan, /review, and the ready-for-review review. See §3.5.
2.3 Compile the agentic workflows
The.github/workflows/ai-*.md files are gh-aw sources.
They do not run until compiled to *.lock.yml:
Each stage is one thin per-engine wrapper (ai-<stage>-<engine>.md) that imports its shared prompt body from .github/aw/shared/; five stages × three engines is fifteen wrappers compiling to fifteen *.lock.yml.
--approve acknowledges the restricted secrets gh-aw detects (ANTHROPIC_API_KEY, OPENAI_API_KEY, COPILOT_GITHUB_TOKEN, AI_AUTOMATION_PAT). Review them first; all are intentional.
The shared bodies live under .github/aw/shared/ (outside the .github/workflows compile glob) so gh-aw treats them as includes, never standalone workflows.
Commit both the .md and the generated .lock.yml.
Re-run gh aw compile --approve after editing any .md (or any shared body); never hand-edit a .lock.yml.
Note: gh aw compile also rewrites .github/dependabot.yml: it adds an ignore rule for the gh-aw-managed action pins, and (a known cosmetic side effect) re-serializes the file, dropping its comments. The config stays functionally complete.
2.4 Labels
path:ai-automation is the gate (see ADR-0002), and the engine is chosen by which pathway label a maintainer applies to the issue:
Apply exactly one engine label per issue (the bare
path:ai-automation counts as the Copilot one). You can also trigger the pipeline by assigning HAI-Coco to the issue: the coco-assign-label.yml workflow adds path:ai-automation automatically.
The plan step stamps both the umbrella path:ai-automation and the chosen engine label onto the PR, so the companions keep keying on the umbrella while every later stage routes on the engine label.
Apply the taxonomy with init-labels.sh after pulling the updated labels.yml.
2.5 Branch protection (the human gate)
Ondevelop, require a human approval and do not let the github-actions bot’s reviews count.
The agents are configured to only ever post COMMENT reviews, but branch protection is the backstop that keeps merge a human decision.
2.6 Choosing the engine and model (Claude / Codex / Copilot)
The engine is a compile-time choice, so it is selected per issue/PR by label, not by a variable.engine.id makes gh-aw install a different CLI and scaffold a different auth, log parser, and firewall allowlist into the .lock.yml, so it cannot be a ${{ vars.AI_ENGINE }} expression or a gh aw compile flag. We work around this by compiling all three engines side by side: each stage exists as three thin wrappers (ai-<stage>-{claude,codex,copilot}.md) that share one prompt body from .github/aw/shared/ and differ only in engine:, the label if: guard, and the per-engine key.
A maintainer picks the engine by applying one pathway label to the issue (see §2.4); the ai-plan-<engine> variant whose names: filter matches fires, opens the PR, and stamps that engine label onto it. From then on every stage (/approve-plan, /coco, /review, the ready-for-review review) is if:-gated on the PR’s engine label, so the same engine runs the whole PR through. The other engines’ workflows still activate on each command but no-op cheaply on their if: guard. The model stays runtime-selectable per engine via AI_MODEL_CLAUDE / AI_MODEL_CODEX / AI_MODEL_COPILOT, and optionally per stage via AI_MODEL_<ENGINE>_<STAGE> (§2.2); only the engine is fixed at compile time.
The three engine blocks, as they appear in the wrappers:
COPILOT_GITHUB_TOKEN; use path:ai-automation-claude or path:ai-automation-codex to keep a PR seat-free.
To change a stage’s prompt, edit the one shared body under .github/aw/shared/ (it applies to all three engines) and re-run gh aw compile --approve; to retune a model, set the matching AI_MODEL_<ENGINE> (or per-stage AI_MODEL_<ENGINE>_<STAGE>) variable (no recompile).
3. Security model (the four guardrails)
These are enforced in everyai-*.md:
- Write-access only.
roles: [admin, maintainer, write]on every agent, which is the gh-aw default. A non-collaborator cannot trigger any agent. The triggering gestures (path:ai-automation,/approve-plan,/promote dev) are maintainer actions, so the actor is always trusted; the issue/PR content, however, is still untrusted input the agent must treat with suspicion. - No secrets to forks. gh-aw blocks fork-originated triggers by default (repository-ID check), on top of GitHub’s standard rule that fork PRs get no secrets.
push-to-pull-request-branchadditionally refuses to push to forks. - Least privilege. The agent job runs read-only; all writes happen in separate, narrowly-scoped safe-output jobs. No agent has direct write access to the repo.
- Human merge gate.
ai-reviewpostsCOMMENTreviews only, neverAPPROVE, so an agent can never satisfy a required-approval rule. Merge is always a human action.
roles or hand the agent a long-lived token beyond AI_AUTOMATION_PAT.
3.5 Threat-detection screen
On the code-pushing stages (/approve-plan, /coco), gh-aw runs a second, lighter model pass over the agent’s output before the write jobs run, flagging prompt-injection, secret-leak, and malicious-patch patterns; if it flags anything the write is blocked (and it fails closed if the scan itself errors).
It is gated on needs.agent.outputs.has_patch, so it fires only when the agent actually produced a patch (a /coco that just answers a question skips it), and it uses a small model via AI_MODEL_<ENGINE>_DETECTION (§2.2).
It is off for plan, /review, and the ready-for-review review (no code is pushed there).
Independently, a static, non-AI protected-files gate runs on every PR-writing stage: it allows only the transient .coco/ plan file under dot-dirs and flags writes to dependency manifests, CI config, AGENTS.md/CLAUDE.md, and CODEOWNERS for human review.
4. Cost & billing
- AI tokens are billed per engine: the Claude engine bills
ANTHROPIC_API_KEY, Codex billsOPENAI_API_KEY, and the default Copilot engine consumes the Copilot seat behindCOPILOT_GITHUB_TOKEN(no API key), for every agent run. - Actions minutes are billed to the repo owner regardless of who triggered the run, which is how a non-Copilot colleague’s trigger still runs “on your account”.
- Review cost is bounded: the automated review runs once, at the ready-for-review checkpoint, not on every
/cocopush, and it posts a single review (there is no separate summary comment). Use/reviewonly when you want an extra pass. On the Claude engine, setAI_MODEL(orAI_MODEL_CLAUDE) to a Sonnet or Haiku ID to cut per-run cost further.
path:ai-automation) requires a GitHub Copilot seat (COPILOT_GITHUB_TOKEN). Use path:ai-automation-claude or path:ai-automation-codex for a seat-free run on API keys.