# Required CI Gates

The harvest skills enforce safety in-skill (every `generalize-pattern` run self-checks its
diff and the ledger discipline is described in the orchestrator). But two controls **cannot**
be guaranteed by a skill alone and must be enforced by the orchestrating workflow that runs
the harvest. That workflow lives in **`Gemma-Analytics/.github`** (the reusable
`claude-harvest.yml`), **not** in this plugin — this file is its specification.

If you run the harvest without these gates (e.g. a local/manual run), you must perform their
equivalents by hand, or you lose the guarantee they provide.

## Gate 1 — Diff-grep leak guard (hard gate, blocks the PR)

**Why:** `generalize-pattern` strips client specifics and self-checks, but a model self-check
is not a guarantee. A second, mechanical check must run before any PR is opened.

**Requirement:** after `generalize-pattern` produces a change and before
`peter-evans/create-pull-request` opens the PR, the workflow must mechanically scan the
staged diff and **fail the job** if any of the following appears:

- Any client identifier from the candidate's `sources[]` (org names, repo names, PR refs).
- Secret-shaped strings (key/token/password patterns).
- Any path matching the denylist (`.env*`, `*secret*`, `*credential*`, `*.pem`, `*.key`,
  `data/`, `seeds/`, `dags/`).

A failure here is terminal for that candidate — the PR is not opened. This is the gate the
generalization contract refers to; it is belt-and-suspenders on top of the skill's Step 4.

## Gate 2 — Decline recorder (keeps the ledger honest)

**Why:** dedup relies on the ledger. A candidate proposed but then **closed unmerged** must
be recorded as `status: declined`, or detection will re-propose it on every future run.

**Requirement:** a workflow in the template repo, triggered on `pull_request: closed` where
the PR was **not merged** and the branch matches `harvest/*`, must append (or update) the
corresponding ledger entry to `status: declined` (ideally capturing a `reason`). Merged
harvest PRs should carry/flip their entry to `status: ported`.

Without this, the only safe fallback is to record declines in `.github/harvest-ledger.yml`
manually.

## Gate 3 — Anonymized PR bodies (review check)

**Why:** the template repo has a wider audience than any single client repo. Source
attribution in PR bodies is anonymized by `generalize-pattern` (aggregate counts only;
precise refs live in the ledger).

**Requirement:** the workflow should not re-inject client repo names/URLs into the PR title
or body, and reviewers should treat any client identifier in a harvest PR body as a defect
(it should have been caught by Gate 1).

---

These three gates are the contract the `claude-harvest.yml` reusable workflow must satisfy.
Keep this file and that workflow in sync: if the workflow's enforcement changes, update this
spec.
