labels.conf + CONTRIBUTING — the panel and triage rosters name identities that exist on this forge (#195) #196
4 changed files with 88 additions and 15 deletions
4
.github/labels.conf
vendored
4
.github/labels.conf
vendored
|
|
@ -1,5 +1,5 @@
|
|||
panel=claude-bot-andresmgsl codex-bot-andresmgsl grok-bot-andresmgsl kimi-bot-andresmgsl
|
||||
triage-actors=dan-claude-bot
|
||||
panel=codex-reviewer-andresmgsl kimi-reviewer-andresmgsl cluade-reviewer-andresmgsl
|
||||
triage-actors=cluade-reviewer-andresmgsl
|
||||
scope:release-flow|C5DEF5|The reusable release workflow, decide, the doors
|
||||
scope:guards|C5DEF5|changelog-armed / changelog-monotonic / drill-recorded
|
||||
scope:labels|C5DEF5|The labels workflow, reconciler, the taxonomy
|
||||
|
|
|
|||
|
|
@ -41,28 +41,37 @@ only ceremony-specific facts such as the roster and code conventions.
|
|||
|
||||
### Roster
|
||||
|
||||
Five identities share the work (org team `agents`), each living in its own
|
||||
Three identities share the work (org team `agents`), each living in its own
|
||||
[box](https://github.com/heavy-duty/box) — one box per credential, because
|
||||
the box is the blast-radius boundary; roles are what a session is told, and
|
||||
[AGENTS.md](AGENTS.md) routes from there:
|
||||
|
||||
| identity | box (rig tenant) | standing work |
|
||||
|---|---|---|
|
||||
| `dan-claude-bot` | `triage` (claude-box) | **triage** — the only door issues come through; this identity mints issues and nothing else writes them (#18's `triage-actors`) |
|
||||
| `claude-bot-andresmgsl` | claude-box | build (release-flow and guards machinery) + review |
|
||||
| `codex-bot-andresmgsl` | codex-box | build (scaffolding, conversions) + review |
|
||||
| `grok-bot-andresmgsl` | grok-box | review |
|
||||
| `kimi-bot-andresmgsl` | kimi-box | review — builder trial on a small mechanical issue once its verdicts have a track record |
|
||||
| `cluade-reviewer-andresmgsl` | `triage` (claude-box) | **triage** — the only door issues come through; this identity mints issues and nothing else writes them (#18's `triage-actors`) — and review. It does not build. |
|
||||
| `codex-reviewer-andresmgsl` | codex-box | build + review |
|
||||
| `kimi-reviewer-andresmgsl` | kimi-box | review |
|
||||
|
||||
**The review panel for any PR is every bench identity except its author** —
|
||||
recusal by construction, enforced by the reconciler (#10): the required
|
||||
verdicts are the panel minus the PR's author, so convergence always means
|
||||
three cross-vendor approvals of the current head. Builders and triage
|
||||
default to different models so the issue contract is honestly exercised —
|
||||
a spec gap should surface as a question on the issue, not be silently filled
|
||||
by shared priors. Humans (`danmt`) decide in discussions and merge; the
|
||||
roster is config, not doctrine — swapping a vendor is an edit to this table
|
||||
(and to `panel=` in `.github/labels.conf` once #10 lands), nothing more.
|
||||
verdicts are the panel minus the PR's author. On this roster that resolves
|
||||
to **two** cross-vendor approvals of the current head, because the only
|
||||
builder is itself on the panel and recuses from its own PRs; the rule is
|
||||
panel-minus-author, and two is what it currently comes to, not a second
|
||||
rule. Builders and triage default to different models so the issue contract
|
||||
is honestly exercised — a spec gap should surface as a question on the
|
||||
issue, not be silently filled by shared priors. Humans (`andres`) decide in
|
||||
discussions and merge; the roster is config, not doctrine — swapping a
|
||||
vendor is an edit to this table (and to `panel=` in
|
||||
`.github/labels.conf` once #10 lands), nothing more.
|
||||
|
||||
The identities named here must be the identities `.github/labels.conf`
|
||||
names, and both must exist on the forge the repo lives on. A roster that
|
||||
agrees with itself and disagrees with the instance is the failure #195
|
||||
records: `panel=` naming absent users cannot converge and
|
||||
`triage-actors=` naming an absent user makes every issue a stray mint that
|
||||
nobody can normalize. `test/labels.test.sh` holds this table and the conf
|
||||
to the same set, in both directions.
|
||||
|
||||
Each governed repo names its own roster in its CONTRIBUTING; this one is
|
||||
ceremony's. Its `scope:*` set is the same kind of repo-specific fact:
|
||||
|
|
|
|||
16
changelog.d/195.md
Normal file
16
changelog.d/195.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
### Fixed
|
||||
|
||||
- `.github/labels.conf` names identities that exist on the forge this repo
|
||||
lives on. All five it named before were absent, so `panel=` could never
|
||||
converge a review round and `triage-actors=` made every issue a stray mint
|
||||
nobody could normalize (#195).
|
||||
|
||||
- `CONTRIBUTING.md`'s roster table matches the conf: three identities, the
|
||||
human row is `andres`, and the approval count states what panel-minus-author
|
||||
actually resolves to on this roster rather than a stale three (#195).
|
||||
|
||||
### Added
|
||||
|
||||
- `test/labels.test.sh` holds the conf's roster and `CONTRIBUTING.md`'s roster
|
||||
table to the same set, in both directions, so a roster edit that touches one
|
||||
file and not the other goes red instead of drifting quietly (#195).
|
||||
|
|
@ -126,4 +126,52 @@ sed 's/opened, closed/closed, opened/' "$STUB" >"$mut_stub"
|
|||
check "an issue-list reorder in one file only goes red" 1 "" \
|
||||
types_in_sync issues "$CALLER" "$mut_stub"
|
||||
|
||||
# --- #195: the conf's roster and CONTRIBUTING's roster table are one set ----
|
||||
# The rot this catches: labels.conf named five identities, CONTRIBUTING named
|
||||
# the same five, and none of the five existed on the forge — two files in
|
||||
# perfect agreement with each other and none with reality. No offline check
|
||||
# can reach the second half; what it can hold is that a roster edit touching
|
||||
# one file and not the other goes red, which is the drift that turns a
|
||||
# deliberate swap into a silent one.
|
||||
|
||||
# roster_from_conf <conf> — every identity panel= and triage-actors= name.
|
||||
roster_from_conf() {
|
||||
sed -nE 's/^(panel|triage-actors)=//p' "$1" | tr ' ' '\n' | sed '/^$/d' | sort -u
|
||||
}
|
||||
|
||||
# roster_from_doc <contributing> — the identities the "### Roster" table's
|
||||
# first column names. Anchored to the section rather than to the table's
|
||||
# shape: another table elsewhere in the file must not be able to join the
|
||||
# roster by looking like one.
|
||||
# shellcheck disable=SC2016 # the backticks below are the table's, not a subshell
|
||||
roster_from_doc() {
|
||||
awk '/^### Roster$/ { inside = 1; next }
|
||||
inside && /^#+ / { exit }
|
||||
inside' "$1" |
|
||||
sed -nE 's/^\| `([^`]+)`.*/\1/p' | sort -u
|
||||
}
|
||||
|
||||
roster_in_sync() { # <conf> <contributing>
|
||||
local conf="$1" doc="$2" drift
|
||||
drift="$(diff <(roster_from_conf "$conf") <(roster_from_doc "$doc"))" && return 0
|
||||
echo "roster drift ('<' conf only, '>' table only):" >&2
|
||||
printf '%s\n' "$drift" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
CONF="$ROOT/.github/labels.conf"
|
||||
CONTRIB="$ROOT/CONTRIBUTING.md"
|
||||
check "the real conf and the real roster table name the same identities" 0 "" \
|
||||
roster_in_sync "$CONF" "$CONTRIB"
|
||||
# the failing cases, in both directions — a one-way check would have passed
|
||||
# all week on the rot that produced #195
|
||||
mut_conf="$TMP/mut-labels.conf" mut_contrib="$TMP/mut-contributing.md"
|
||||
sed 's/^panel=/panel=ghost-bot /' "$CONF" >"$mut_conf"
|
||||
check "an identity in the conf but not the table goes red" 1 "ghost-bot" \
|
||||
roster_in_sync "$mut_conf" "$CONTRIB"
|
||||
# shellcheck disable=SC2016 # the backticks are the table's, not a subshell
|
||||
sed 's/^| `kimi-reviewer-andresmgsl`/| `ghost-bot`/' "$CONTRIB" >"$mut_contrib"
|
||||
check "an identity in the table but not the conf goes red" 1 "ghost-bot" \
|
||||
roster_in_sync "$CONF" "$mut_contrib"
|
||||
|
||||
summary
|
||||
|
|
|
|||
Loading…
Reference in a new issue