Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/agent-delivery-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ Before a merge, an orchestrator or operator calls `merge_precondition` (`POST /a
| `head_moved` | The pull request's head changed since CI ran. The story goes back to `implementing` over `base_moved`, and the recorded head is cleared, so the gate is not called again until the story is back at `ci`. |
| `unevaluated` | 503 with `Retry-After`. After repeated `unevaluated` answers the story escalates. |

### Gate B on the repositories that gate the loop

Gate B reads one trigger list per repository from `DELIVERY_GATES_CONFIG` (`Loopctl.DeliveryGates.Triggers`, loaded by `Loopctl.DeliveryGates.Config`). A repository with no entry there escalates EVERY change, at triage and at the merge gate, with `{:unknown_repo, repo}` (`Loopctl.DeliveryGates.GateB`, `repo_triggers/2`); `test/loopctl/delivery_gates/gate_b_test.exs` pins it.

`mkreyman/claude-config` and `mkreyman/chg` have no entry today. Every change to them therefore goes to a human, which is what issue #986 asks for: the loop must not be able to change, unreviewed, the code that lands, reverts or gates its own work, or the hooks that stop a session from doing what it must not.

**The rule: when either repository gets an entry, its `human_paths` MUST name that repository's self-modification code**, which the loop must never change unreviewed:
- for chg, its gate, the code that advances `verified/master`, culprit and revert, its adapters, and its CI;
- for claude-config, the deny lists and hook wiring, the hooks that block or grant, the plugins that gate commits, merges and secrets, its git hooks (the commit gate) and its CI.

The exact glob lists live in the operator's knowledge base (article `4a86157b`), not here: both repositories are private, and a list of their safety code does not belong in a public document. That article is a floor. Add to it, never remove from it, and re-check each glob against the repository before pasting it, because a pattern that matches no file is a stale trigger and escalates every change.

## 7. After the merge

- **`deployed` to `verified`, or to `escalated`:** `PostDeployVerification` compares the merge commit with the repository's deployment records.
Expand Down
17 changes: 15 additions & 2 deletions docs/measurements/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ SHIPPED gate code over history and count.
|---|---|
| `gate_b_<date>.json` | the machine-readable Gate B result: outcome counts, three strata with their rates, the false-negative rows |
| `gate_b_<date>.md` | the same run's human summary, including every bias |
| `gate_a_<date>.json` | the Gate A result: two strata, the sensitivity run, the escalated issue numbers |
| `gate_a_<date>.json` | the Gate A result: four strata, the sensitivity run over the same four, the escalated issue numbers |
| `gate_a_<date>.md` | that run's human summary |

Add a new dated pair per run rather than overwriting: the point of these is a TRACK RECORD, and
Expand Down Expand Up @@ -168,13 +168,20 @@ Every Gate B artifact carries `scope_note` saying so.

## Reading a Gate A result

Two strata plus a sensitivity run:
Four strata plus a sensitivity run over the same four:

- **intake** — the tickets Gate A is actually for: the ones the in-app chat filed with a
`[Bug] <agency>: ` / `[Feature] <agency>: ` prefix. The headline, because that split was
decided at FILING time and carries no hindsight.
- **all** — every issue in the repository, classified by label. Wider, and biased by labels
applied after the outcome was known.
- **chg_loop** — the tickets carrying the `chg-loop` label, which chg stamps on every issue it
files: machine-written, with a failure fingerprint, occurrences, a root cause or "unknown,
investigate", and acceptance criteria written as checks (US-47.11, issue #986). Measured apart
because such a ticket is expected to escalate far less than one a person wrote. It is replayed
exactly like any other ticket; the label only decides which stratum counts it.
- **human** — every ticket WITHOUT the `chg-loop` label. `chg_loop` and `human` partition `all`,
so their totals add up to it. `human` is not `intake`: it holds the engineering issues too.
- **sensitivity** — the same corpus with `workflow_change_not_defect_fix` suppressed. Gate A's
rate turns almost entirely on whether a feature request counts as a workflow change, so the
result is a RANGE and the report says so rather than picking one end.
Expand All @@ -186,6 +193,12 @@ the `[Feature]` share of the corpus, and the harness has measured the intake cha
mix rather than any judgement Gate A made. The field is computed, not asserted, so a later run
where the inversion trigger also fires reports `false` and the comparison regains its force.

`chg_loop` and `human` were added in US-47.11 BESIDE `intake` and `all`, which kept their
definitions, so an artifact from before then still compares with one from after on those two.
An artifact from before then has no `chg_loop`/`human` keys at all; that is the absence of the
split, not an empty `chg_loop` stratum. An EMPTY `chg_loop` stratum (rate `null`) means chg filed
nothing in the window.

The `all` stratum is the whole corpus and therefore MIXES the two classifiers: it contains the
intake tickets, which keep the filing-time prefix reading, plus everything else, classified by
label. It is not a second, disjoint corpus.
Expand Down
Loading
Loading