A disposable fixture repo, `petrova-codes/wip-cap-fixture`, for IR-007's real WIP-cap E2E test — cross-runner locking scoped out to a follow-up
Date: 2026-09-13 Status: closed Supersedes: none Superseded-by: none — current
Context
Section titled “Context”irina/tickets/IR-007-wip-cap-correctness-test.xml asks for proof the
fleet-wide WIP cap (host/src/tools/wip-cap.ts, IR-001) refuses a real 4th
concurrent PETROVA-authored PR, not just a unit-level count check. The
existing suite (host/tests/wip-cap.test.ts,
host/tests/wip-cap-wiring.test.ts) mocks ActsSource entirely — it
proves the count/threshold logic and the process-local serialization
withWipCapGuard adds, but never touches real GitHub state.
Two problems surfaced while scoping this:
- No safe target repo exists.
ActsSource.recent()(host/src/sources/acts.ts:88) always resolves its target list viaPetrovaSource.loadRegistry()— i.e.registry.yaml. Every registered repo is eitherfleets_allowed: []by design (no agent automation permitted at all — using one to open/close throwaway test PRs would contradict that lock) or, forkahn-hq, has a second human and CODEOWNERS (real noise a churn test shouldn’t produce there). withWipCapGuard’s lock is process-local. It serializes check-then-act within one Node process (host/src/tools/wip-cap.ts’s own header comment says this explicitly). IR-007’s C1 — two separateirina-cycle.ymlruns (two separate GitHub Actions runners, i.e. two separate processes) not both slipping through — is not covered by today’s design at all. No amount of E2E test-writing proves a claim the code doesn’t make; building a real cross-process/cross-runner lock is its own piece of work, not a test.
Decision
Section titled “Decision”Fixture repo, not a registry entry. Created
petrova-codes/wip-cap-fixture — a new, empty, private, disposable GitHub
repo under the same org, covered automatically by the existing petrova-act
App installation (repository_selection: all). It is never added to
registry.yaml. Instead, the E2E test constructs its own
PetrovaSource pointed at a small fixture registry file
(host/tests-e2e/fixtures/registry.yaml, one entry, this repo’s URL) and
wires that into ActsSource directly — using the same production code
path (PetrovaSource → ActsSource → wipCapGuard), against real GitHub
API responses, without ever touching the real fleet’s governance
source-of-truth. This keeps registry.yaml’s PR-only, human-reviewed
nature (see CLAUDE.md’s “Registry edits are PR-only”) untouched by a
throwaway test fixture.
C1 (cross-runner race) is scoped OUT of this ticket, not answered by a
weaker test. withWipCapGuard’s process-local lock genuinely cannot
pass a real two-runner concurrent test — there’s nothing to test that
would pass honestly. Rather than write a same-process test and call it C1
(which the existing mocked suite already does, more cheaply), this act
files IR-012 for a real cross-process/cross-runner lock (candidate
shapes: a committed lease file pushed with optimistic concurrency, or a
GitHub Deployments-API-based lock) as its own ticket. IR-007 closes on
C2/C3 (real 4th-PR refusal, real sequential success) plus the existing
mocked concurrency suite for same-process C1; the cross-runner gap is
tracked, not silently dropped.
Manual workflow_dispatch only. The E2E test opens and closes real
PRs against the fixture repo — real GitHub API writes with real (if small)
cost and churn. It runs only on-demand
(.github/workflows/wip-cap-e2e.yml, workflow_dispatch trigger, no
schedule or pull_request trigger), never as part of normal CI on this
repo’s own PRs.
Alternatives considered
Section titled “Alternatives considered”- Register the fixture repo in
registry.yaml— rejected: would give a throwaway test fixture the same governance weight as a real consumer repo (PR-only edits, decision-doc citations,fleets_allowedreview), for a repo that exists solely to be spammed with disposable branches. ThePetrovaSourceconstructor already accepts an arbitrary registry path — using that seam is a two-line test fixture instead of a registry PR. - Reuse
kahn-hq— rejected: real second human, real CODEOWNERS, real production repo. Opening/closing 4 PRs there per test run is noise in someone else’s inbox, not just this repo’s problem. - Build the cross-process lock now, inside IR-007 — rejected (for now):
materially larger scope (a new distributed-locking primitive, its own
failure modes to test) than “write the E2E test the ticket already
describes.” Deferred to
IR-012rather than silently narrowing IR-007’s claim without a paper trail.
Consequences
Section titled “Consequences”For code: host/tests-e2e/wip-cap-e2e.test.ts (new, not run by default
npm test — a separate workflow_dispatch-only CI job invokes it) plus a
one-entry fixture registry file. No change to host/src/tools/wip-cap.ts
or registry.yaml.
For docs: this doc; irina/tickets/IR-007-wip-cap-correctness-test.xml
gets an inline note pointing here; new ticket IR-012 files the
cross-runner lock as separate work.
For in-flight phases: none.
For invariants: none changed. This is infrastructure for a test, not a policy change — the WIP cap’s ratified value (3) and mechanism are untouched.
References
Section titled “References”irina/tickets/IR-007-wip-cap-correctness-test.xml(the ticket this act closes C2/C3 of)irina/tickets/IR-001-wip-cap-enforcement.xml(the cap this test verifies)host/src/tools/wip-cap.ts(implementation and its own honest comment about the process-local lock’s scope)host/src/sources/acts.ts:88(ActsSource.recent()’s registry-derived target list — the seam this act uses without touching the real registry)CLAUDE.md(“Registry edits are PR-only”)
Sign-off
Section titled “Sign-off”- Subagent: claude-sonnet-5 (session_01488gmoyur1UAMfpNMgUAhC)
- Human: alex@devarno.com — ratifies creating
petrova-codes/wip-cap-fixtureas a disposable, non-registered fixture repo, scoping IR-007’s C1 out to a new IR-012 ticket, and running the resulting E2E test only via manualworkflow_dispatch.
Countersigned by human:alex@devarno.com on 2026-09-13 — commit 9eae4c3c
(this file’s creation commit) is authored by the human directly, per
docs/decisions/2026-09-05-proxy-countersign-retired.md’s post-retirement
definition. Line added retroactively by the agent as scribe.