> ## Documentation Index
> Fetch the complete documentation index at: https://haico.gr/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# AI-assisted pathway

> The as-built AI-assisted contribution pathway (Coco), run by gh-aw on the repo's own key.

# 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`)](https://github.github.com/gh-aw/) on the repository's own API key, **not** GitHub Copilot.
That choice is deliberate (see [ADR-0002](/docs/adr/0002-dual-development-pathways)). 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](/docs/github-projects-copilot-automation); where the two differ, **this document wins**.

***

## 1. The flow

```mermaid theme={null}
flowchart TD
    A["issue"] -->|"maintainer adds path:ai-automation"| B["ai-plan<br/>Coco opens a DRAFT PR · plan posted as its first comment"]
    B -->|"maintainer comments /approve-plan on the PR"| C["ai-implement<br/>Coco pushes the implementation to the same PR · ci.yml runs"]
    C -->|"maintainer comments /promote dev"| D["promote-dev<br/>pushes dev-vX.Y.Z tag"]
    D --> E["ci.yml deploys to dev · notify-dev-ready pings the assignee"]
    E --> F["assignee tests on dev, iterating with /coco change<br/>ai-iterate pushes commits, CI re-runs, until satisfied"]
    F -->|"assignee clicks Ready for review"| G["ai-review · COMMENT only<br/>posts 🟢/🟡/🔴 and pings the assignee"]
    G -->|"assignee clicks Approve"| H["notify-approved pings the reviewer to merge"]
    H -->|"reviewer merges · human gate"| I["issue auto-closes"]
```

| Step            | Trigger ("button")                                       | Workflow                                                                                                                                                                                                                                        | What happens                                                                                                                                                                                                                                                                                                                                                        |
| --------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Plan            | add label `path:ai-automation[-<engine>]`                | `ai-plan-{claude,codex,copilot}.md` ([body](https://github.com/petrosrapto/HAICO/blob/main/.github/aw/shared/plan.body.md)) + [`coco-plan-comment.yml`](https://github.com/petrosrapto/HAICO/blob/main/.github/workflows/coco-plan-comment.yml) | Coco opens a **draft PR** → `develop` (with `Fixes #N`), posts the **plan as its first comment**, and requests the issue's **Reviewer(s)** (from the issue form, default `REVIEWER_LOGIN`) on the PR. No implementation code yet.                                                                                                                                   |
| Implement       | comment `/approve-plan` **on the PR**                    | `ai-implement-{claude,codex,copilot}.md` ([body](https://github.com/petrosrapto/HAICO/blob/main/.github/aw/shared/implement.body.md))                                                                                                           | Coco implements the approved plan **into the same PR** branch and removes the plan file.                                                                                                                                                                                                                                                                            |
| CI              | automatic on every push                                  | [`ci.yml`](https://github.com/petrosrapto/HAICO/blob/main/.github/workflows/ci.yml)                                                                                                                                                             | Existing pipeline (lint/test/build); re-runs on the implementation push and on each `/coco` push.                                                                                                                                                                                                                                                                   |
| Promote         | comment `/promote dev`                                   | [`promote-dev.yml`](https://github.com/petrosrapto/HAICO/blob/main/.github/workflows/promote-dev.yml)                                                                                                                                           | Pushes `dev-vX.Y.Z` on the PR head → ci.yml deploys to dev.                                                                                                                                                                                                                                                                                                         |
| Dev ready       | automatic on deploy success                              | [`notify-dev-ready.yml`](https://github.com/petrosrapto/HAICO/blob/main/.github/workflows/notify-dev-ready.yml)                                                                                                                                 | @-mentions the assignee to test on dev.                                                                                                                                                                                                                                                                                                                             |
| Iterate         | comment `/coco <instruction>`                            | `ai-iterate-{claude,codex,copilot}.md` ([body](https://github.com/petrosrapto/HAICO/blob/main/.github/aw/shared/iterate.body.md))                                                                                                               | **Stage-aware.** Before `/approve-plan` (plan not yet approved): Coco revises `.coco/PLAN.md` only (no code), replies with the full revised plan and ends with "comment `/approve-plan` to implement this revised plan". After `/approve-plan` (plan approved): Coco applies the code change, pushes to the PR branch, CI re-runs. No review fires on these pushes. |
| Review          | mark the PR **Ready for review**, or `/review` on demand | `ai-review-{claude,codex,copilot}.md` (ready-for-review) + `ai-review-ondemand-*` (`/review`) ([body](https://github.com/petrosrapto/HAICO/blob/main/.github/aw/shared/review.body.md))                                                         | Runs once at the human-review checkpoint, not on every push. Coco posts a single review (**COMMENT only, never approves**) that leads with a 🟢/🟡/🔴 decision and @-mentions the assignee.                                                                                                                                                                         |
| Approve → merge | native **Approve** review                                | [`notify-approved.yml`](https://github.com/petrosrapto/HAICO/blob/main/.github/workflows/notify-approved.yml)                                                                                                                                   | @-mentions the reviewer to do the final human merge.                                                                                                                                                                                                                                                                                                                |

Each AI step (plan, implement, iterate, review) exists as **three engine variants** (`-claude`, `-codex`, `-copilot`) that share one prompt body from [`.github/aw/shared/`](https://github.com/petrosrapto/HAICO/blob/main/.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 one** `stage:*` 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`](https://github.com/petrosrapto/HAICO/blob/main/.github/workflows/coco-stage-labels.yml) (never set them by hand); warm colors (yellow/orange/green) mean it is your turn to act.

| Stage label                    | Set when                                                    | Your next action                                                   |
| ------------------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------ |
| `stage:planning`               | the issue is labelled `path:ai-automation*`                 | wait for Coco to open the draft PR                                 |
| `stage:awaiting-plan-approval` | Coco opens the draft plan PR                                | read the plan, then comment `/approve-plan`                        |
| `stage:implementing`           | you comment `/approve-plan`                                 | wait for CI to pass                                                |
| `stage:awaiting-dev-promotion` | CI passes on the implementation (or on a `/coco` fix)       | comment `/promote dev`                                             |
| `stage:testing-dev`            | you comment `/promote dev`                                  | test on dev; mark **Ready for review**, or `/coco <change>` to fix |
| `stage:reviewing`              | the PR is marked Ready for review, or you comment `/review` | read Coco's review; click **Approve** when satisfied               |
| `stage:awaiting-merge`         | a human submits an **Approve** review                       | a human does the final squash merge (the issue auto-closes)        |

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).

```mermaid theme={null}
stateDiagram-v2
    direction TB
    [*] --> planning: maintainer labels the issue for AI automation
    planning --> awaiting_plan_approval: Coco opens the draft PR and posts the plan
    awaiting_plan_approval --> implementing: assignee comments /approve-plan
    implementing --> awaiting_dev_promotion: CI passes on the implementation
    awaiting_dev_promotion --> testing_dev: assignee comments /promote dev
    testing_dev --> reviewing: assignee marks Ready for review (or /review)
    testing_dev --> awaiting_dev_promotion: /coco fix, then CI passes
    reviewing --> awaiting_dev_promotion: /coco fix, then CI passes
    reviewing --> awaiting_merge: a human clicks Approve
    awaiting_merge --> [*]: a human squash-merges, issue auto-closes

    planning: planning
    awaiting_plan_approval: awaiting-plan-approval (your turn)
    implementing: implementing
    awaiting_dev_promotion: awaiting-dev-promotion (your turn)
    testing_dev: testing-dev (your turn)
    reviewing: reviewing
    awaiting_merge: awaiting-merge (your turn)
```

**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 applied `path: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 the `REVIEWER_LOGIN` repo 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 to `REVIEWER_LOGIN` when the field is blank. This is distinct from the final-merge reviewer above, though both default to `petrosrapto`.

**Notes on the draft PR:**

* 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.md` commit; 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.yml` is 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 bare `path:ai-automation` / Claude path needs only `ANTHROPIC_API_KEY`).

| Secret                 | Used by                                                                                                                                                               | Why                                                                                                                                                                                                                                                                                                                                       |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ANTHROPIC_API_KEY`    | the Claude engine variants (`ai-*-claude`)                                                                                                                            | The Claude engine's key. No Copilot seat needed. Required whenever a Claude pathway PR is in flight (`path:ai-automation-claude`).                                                                                                                                                                                                        |
| `OPENAI_API_KEY`       | the Codex engine variants (`ai-*-codex`)                                                                                                                              | Required whenever a Codex pathway PR is in flight (a PR labelled `path:ai-automation-codex`). Seat-free, like Claude.                                                                                                                                                                                                                     |
| `COPILOT_GITHUB_TOKEN` | the Copilot engine variants (`ai-*-copilot`)                                                                                                                          | A token from an identity that **holds a GitHub Copilot seat**. Copilot uses no API key; it authenticates through the seat. Required for the **default** engine (`path:ai-automation` or `path:ai-automation-copilot`).                                                                                                                    |
| `AI_AUTOMATION_PAT`    | every AI-pathway write step (plan, implement, iterate, review / review-ondemand) plus the classic companions (`promote-dev`, `coco-plan-comment`, `coco-approve-ack`) | A user **Personal Access Token** used for the *write* actions. It is required because actions taken with the default `GITHUB_TOKEN` do **not** trigger downstream workflows: a PR opened, branch pushed, or tag pushed by `GITHUB_TOKEN` would never fire `ci.yml` or `ai-review`. The PAT (from any maintainer account) makes them fire. |

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.

| Variable                     | Default                                | Purpose                                                                                                                                                                                                                                                                                                                                                                                                        |
| ---------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AI_MODEL_<ENGINE>_<STAGE>`  | none (falls through)                   | **Optional per-stage override.** `<ENGINE>` is `CLAUDE`/`CODEX`/`COPILOT`; `<STAGE>` is `PLAN`/`IMPLEMENT`/`ITERATE`/`REVIEW` (both review stages share `REVIEW`). Lets one stage use a different model than another (e.g. a cheaper plan, a stronger implement). Resolution per stage: `AI_MODEL_<ENGINE>_<STAGE>` → `AI_MODEL_<ENGINE>` → (Claude only) `AI_MODEL` → in-file default. 12 vars, all optional. |
| `AI_MODEL_CLAUDE`            | `AI_MODEL`, then `claude-opus-4-5`     | Model for the Claude engine variants. Falls back to `AI_MODEL` (kept for back-compat) and then the in-file default.                                                                                                                                                                                                                                                                                            |
| `AI_MODEL_CODEX`             | `gpt-5.5` (in-file fallback)           | Model for the Codex engine variants (an OpenAI Codex model ID, e.g. `gpt-5.5`, or `gpt-5.4-mini` to cut cost).                                                                                                                                                                                                                                                                                                 |
| `AI_MODEL_COPILOT`           | `claude-sonnet-4.6` (in-file fallback) | Model for the Copilot engine variants.                                                                                                                                                                                                                                                                                                                                                                         |
| `AI_MODEL`                   | `claude-opus-4-5` (in-file fallback)   | Legacy Claude model var, still honoured as the `AI_MODEL_CLAUDE` fallback. Set a Sonnet/Haiku ID to cut Claude cost.                                                                                                                                                                                                                                                                                           |
| `AI_MODEL_CLAUDE_DETECTION`  | `claude-haiku-4-5` (in-file fallback)  | Model for the **threat-detection** screen on the Claude code-writing stages (`/approve-plan`, `/coco`). A small/cheap model keeps the screen fast.                                                                                                                                                                                                                                                             |
| `AI_MODEL_CODEX_DETECTION`   | `gpt-5.4-mini` (in-file fallback)      | Detection model for the Codex code-writing stages.                                                                                                                                                                                                                                                                                                                                                             |
| `AI_MODEL_COPILOT_DETECTION` | not wired (no-op)                      | Currently a **no-op**, unlike the Claude/Codex detection stages: the Copilot detection pass deliberately carries no `engine:`/model var, so it inherits the main Copilot engine/model. No workflow reads this variable (it appears only in a code comment), so setting it has no effect.                                                                                                                       |
| `REVIEWER_LOGIN`             | repo owner (`petrosrapto`)             | GitHub login pinged for the final merge, and the default reviewer requested on the plan PR when the issue's **Reviewer(s)** field is blank.                                                                                                                                                                                                                                                                    |

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/`](https://github.com/petrosrapto/HAICO/blob/main/.github/aw/shared); five stages × three engines is fifteen wrappers compiling to fifteen `*.lock.yml`.

```bash theme={null}
gh extension install github/gh-aw     # one time
gh aw compile --approve               # generates .github/workflows/ai-*-*.lock.yml (15 files)
git add .github/workflows .github/aw .github/dependabot.yml   # the wrappers, generated *.lock.yml, shared bodies, dependabot
git commit -m "build: compile AI-pathway agentic workflows"
```

`--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`](https://github.com/petrosrapto/HAICO/blob/main/.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:

| Label on the issue           | Engine                                    | Key secret             |
| ---------------------------- | ----------------------------------------- | ---------------------- |
| `path:ai-automation`         | **Copilot** (default alias of `-copilot`) | `COPILOT_GITHUB_TOKEN` |
| `path:ai-automation-copilot` | Copilot                                   | `COPILOT_GITHUB_TOKEN` |
| `path:ai-automation-claude`  | Claude (seat-free)                        | `ANTHROPIC_API_KEY`    |
| `path:ai-automation-codex`   | Codex / OpenAI (seat-free)                | `OPENAI_API_KEY`       |

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`](https://github.com/petrosrapto/HAICO/blob/main/.github/scripts/init-labels.sh) after pulling the updated [`labels.yml`](https://github.com/petrosrapto/HAICO/blob/main/.github/labels.yml).

### 2.5 Branch protection (the human gate)

On `develop`, 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/`](https://github.com/petrosrapto/HAICO/blob/main/.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:

```yaml theme={null}
# Copilot (default). Requires a Copilot seat (COPILOT_GITHUB_TOKEN, no API key).
# Uses max-continuations, not the Claude-only max-turns.
engine:
  id: copilot
  model: "${{ vars.AI_MODEL_COPILOT_<STAGE> || vars.AI_MODEL_COPILOT || 'claude-sonnet-4.6' }}"
  max-continuations: 15

# Claude (seat-free). max-turns is Claude-only.
engine:
  id: claude
  model: "${{ vars.AI_MODEL_CLAUDE_<STAGE> || vars.AI_MODEL_CLAUDE || vars.AI_MODEL || 'claude-opus-4-5' }}"  # <STAGE> = PLAN/IMPLEMENT/ITERATE/REVIEW
  max-turns: 40            # per stage: 30 plan / 80 implement / 60 iterate / 40 review
  env:
    ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

# Codex / OpenAI (seat-free). No max-turns (Codex does not support it).
engine:
  id: codex
  model: "${{ vars.AI_MODEL_CODEX_<STAGE> || vars.AI_MODEL_CODEX || 'gpt-5.5' }}"
  env:
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
```

Copilot (the **default** engine) **needs a Copilot seat/entitlement** on the identity behind `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 every `ai-*.md`:

1. **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.
2. **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-branch` additionally refuses to push to forks.
3. **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.
4. **Human merge gate.** `ai-review` posts `COMMENT` reviews only, never `APPROVE`, so an agent can never satisfy a required-approval rule. Merge is always a human action.

gh-aw adds its own hardening on top: input sanitization, secret redaction in logs, and an egress allowlist (the Agent Workflow Firewall).
Even so, an agent with repo access reacting to attacker-controllable text is a documented prompt-injection surface.
Keep the four guardrails above intact, and never broaden `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 bills `OPENAI_API_KEY`, and the default Copilot engine consumes the Copilot seat behind `COPILOT_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 `/coco` push, and it posts a single review (there is no separate summary comment). Use `/review` only when you want an extra pass. On the Claude engine, set `AI_MODEL` (or `AI_MODEL_CLAUDE`) to a Sonnet or Haiku ID to cut per-run cost further.

The **default Copilot engine** (`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.
