From e279e69451ba2333e4ef766b2e5a303d194d42aa Mon Sep 17 00:00:00 2001 From: Jason Roy Date: Thu, 25 Jun 2026 16:19:08 -0700 Subject: [PATCH] docs: correct branch-protection section; tighten cluster-safety test - auto-release.md: required conversation resolution is OFF (an auto-reviewer's unresolved threads would otherwise wedge the hands-off bump loop); include the required_pull_request_reviews sub-fields in the revert snippet so the API accepts the partial object. - redis.test.ts: assert the cluster-safety test's del calls hit exactly the expected single keys (k1 then k2), not just "some del happened". Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/auto-release.md | 20 +++++++++++++------ .../src/data-cache/redis.test.ts | 10 ++++++++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/docs/auto-release.md b/docs/auto-release.md index e447982..c635c2b 100644 --- a/docs/auto-release.md +++ b/docs/auto-release.md @@ -54,18 +54,26 @@ CI is green — the tag and publish still fire automatically from your merge. `main` requires the status checks `lint-and-typecheck`, `unit-tests`, and `test-summary`, and does **not** require an approving review — the checks are the -gate, and no bot can give itself an approval. Required conversation resolution -stays on (only matters when a human leaves review comments). Auto-merge and -delete-branch-on-merge are enabled at the repo level. +gate, and no bot can give itself an approval. **Required conversation resolution +is off**: an auto-reviewer (e.g. `gemini-code-assist`) leaves review threads on +every PR, and with conversation resolution required those unresolved threads +would block auto-merge — wedging the hands-off bump loop on every release. The +e2e status checks are the real gate. Auto-merge and delete-branch-on-merge are +enabled at the repo level. -To re-add a human approval gate later: +To re-add a human approval gate later (the `required_pull_request_reviews` +sub-fields are included because the GitHub API rejects a partial object): ```bash gh api -X PUT repos/{owner}/{repo}/branches/main/protection --input - <<'JSON' { "required_status_checks": { "strict": false, "contexts": ["lint-and-typecheck", "unit-tests", "test-summary"] }, "enforce_admins": false, - "required_pull_request_reviews": { "required_approving_review_count": 1 }, - "restrictions": null, "required_conversation_resolution": true } + "required_pull_request_reviews": { + "dismiss_stale_reviews": false, + "require_code_owner_reviews": false, + "required_approving_review_count": 1 + }, + "restrictions": null, "required_conversation_resolution": false } JSON ``` diff --git a/packages/cache-handler/src/data-cache/redis.test.ts b/packages/cache-handler/src/data-cache/redis.test.ts index 42d0da9..c02b9da 100644 --- a/packages/cache-handler/src/data-cache/redis.test.ts +++ b/packages/cache-handler/src/data-cache/redis.test.ts @@ -424,8 +424,14 @@ describe("RedisDataCacheHandler cluster safety", () => { vi.setSystemTime(new Date(BASE_TIME.getTime() + 5000)); await handler.get("k2", []); - // Both deletion paths must have run (otherwise the assertion is vacuous)... - expect(commands.some((c) => c.method === "del")).toBe(true); + // Both deletion paths must have run (otherwise the assertion is vacuous), + // and each must delete exactly the one expected key — tag-driven del of k1, + // then TTL-driven del of k2. + const delCommands = commands.filter((c) => c.method === "del"); + expect(delCommands).toEqual([ + { method: "del", keys: ["nextjs:data-cache:k1"] }, + { method: "del", keys: ["nextjs:data-cache:k2"] }, + ]); // ...and every command issued must target exactly one key. const multiKey = commands.filter((c) => c.keys.length !== 1); expect(multiKey).toEqual([]);