Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
156 commits
Select commit Hold shift + click to select a range
2a0fb46
docs(plan): record 0.2.24 release and deployment evidence (wp5)
lidge-jun Sep 8, 2026
2a77559
docs: close and archive the reader-documents / deep-research / 0.2.24…
lidge-jun Sep 8, 2026
6d70ef4
Merge pull request #90 from lidge-jun/codex/release-0.2.24
lidge-jun Sep 8, 2026
5302993
fix: make executor canonical across dispatch and exit verification
thisisjun786 Sep 8, 2026
2f8a137
feat: register executor explicitly without overwriting user roles
thisisjun786 Sep 8, 2026
541991f
test: verify registration conflicts and publish measured suite total
thisisjun786 Sep 8, 2026
5484d70
docs: record local executor proof and hook activation requirement
thisisjun786 Sep 8, 2026
b23d5e1
docs: align remaining executor setup and QA guidance
thisisjun786 Sep 8, 2026
4b0bd15
docs: archive executor workflow delivery evidence
thisisjun786 Sep 8, 2026
a202ecf
test: cover Windows short-path source bindings
parkjs101 Sep 8, 2026
eb39a12
fix(subagents): persist effort, add global defaults and refresh OCX m…
thisisjun786 Sep 8, 2026
393da86
Merge pull request #92 from parkjs101/codex/windows-short-path-regres…
lidge-jun Sep 8, 2026
31f397c
Merge origin/dev into fix/subagent-settings-pr
lidge-jun Sep 8, 2026
ccf990e
Merge pull request #93 from thisisjun786/fix/subagent-settings-pr
lidge-jun Sep 8, 2026
9479f77
docs: replace the symlink dogfood track with a real dev install
lidge-jun Sep 8, 2026
2683eba
docs: correct three claims in the dev-install sections
lidge-jun Sep 8, 2026
88cf189
docs(plan): close the dev-install track with outcome, held-PR rationa…
lidge-jun Sep 8, 2026
6edae54
docs(plan): record delivery of the PR 91 hold to its author
lidge-jun Sep 8, 2026
6e97e73
docs(plan): record final-head verification for the dev-install track
lidge-jun Sep 8, 2026
11d41b8
fix(executor): preserve upgrade dispatch and refresh managed roles
thisisjun786 Sep 8, 2026
9a546f0
docs(executor): record backup recovery and review evidence
thisisjun786 Sep 8, 2026
6798a39
docs(plan): agent-swarm repo hygiene roadmap, research ledger and dif…
lidge-jun Sep 8, 2026
7d2f794
dev-devops: align branch-lifecycle keep rules with the shipped planne…
lidge-jun Sep 8, 2026
46a9bc9
dev-devops: tighten planner line cite, --merged wording and namespace…
lidge-jun Sep 8, 2026
0bc7d19
dev-devops: add repo-bootstrap reference (ruleset-first setup for age…
lidge-jun Sep 8, 2026
3e7302e
dev-devops: repo-bootstrap check table no longer expects the unverifi…
lidge-jun Sep 8, 2026
1cacbc9
dev-devops: add agent-pr-intake reference (identity, draft-first, sup…
lidge-jun Sep 8, 2026
6ca0c3d
dev-devops: agent-pr-intake evidence paths and a dangling table refer…
lidge-jun Sep 8, 2026
babd5ec
dev-devops: add local-gc reference (worktree/branch GC contract), cro…
lidge-jun Sep 8, 2026
f2b29e8
dev-devops: local-gc contract names the report path, gc repo scope, g…
lidge-jun Sep 8, 2026
e686f99
docs(plan): record publish receipt for the agent-swarm hygiene chain …
lidge-jun Sep 8, 2026
ccfdf7f
docs(plan): note the receipt commit's own CI on the L4 head
lidge-jun Sep 8, 2026
0324229
docs(plan): merge roadmap for the agent-swarm hygiene chain
lidge-jun Sep 9, 2026
fcfb55a
docs(plan): merge roadmap fixes from the A-phase audit (full SHAs, UN…
lidge-jun Sep 9, 2026
a72d73c
docs(plan): pin PR 97's current head and record pre-merge state
lidge-jun Sep 9, 2026
c45e515
Merge pull request #94 from lidge-jun/codex/agent-swarm-hygiene-l1
lidge-jun Sep 9, 2026
2d3c95a
Merge pull request #95 from lidge-jun/codex/agent-swarm-hygiene-l2
lidge-jun Sep 9, 2026
44f2617
Merge pull request #96 from lidge-jun/codex/agent-swarm-hygiene-l3
lidge-jun Sep 9, 2026
1f81109
Merge pull request #97 from lidge-jun/codex/agent-swarm-hygiene-l4
lidge-jun Sep 9, 2026
5898d1c
docs(plan): merge receipt for the agent-swarm hygiene chain (#94-#97 …
lidge-jun Sep 9, 2026
0cacdc8
Merge pull request #98 from lidge-jun/codex/agent-swarm-hygiene-merge…
lidge-jun Sep 9, 2026
8e81a3d
docs(plan): record the receipt PR in the merge receipt table
lidge-jun Sep 9, 2026
04721e6
docs(plan): roadmap the memory-upgrade L0 implementation
lidge-jun Sep 9, 2026
0f19a8f
Merge pull request #99 from lidge-jun/codex/agent-swarm-hygiene-recei…
lidge-jun Sep 9, 2026
90b8756
dev-devops: drop the chain-delivery phrasing from the §2.9 owner rows
lidge-jun Sep 9, 2026
d21ae0c
Merge pull request #101 from lidge-jun/codex/devops-drop-chain-phrasing
lidge-jun Sep 9, 2026
de64b4d
Gate memory writes behind an explicit user request
lidge-jun Sep 9, 2026
841c40a
Move every published hook count from 23 to 24
lidge-jun Sep 9, 2026
9567e14
Publish the test count the gate suite now measures
lidge-jun Sep 9, 2026
b06abaf
Record what the memory-upgrade delivery actually shipped
lidge-jun Sep 9, 2026
4f4d90f
Record the wp1 delivery receipt
lidge-jun Sep 9, 2026
b089975
Record the wp2 delivery receipt
lidge-jun Sep 9, 2026
426a2a2
Record the wp4 delivery receipt
lidge-jun Sep 9, 2026
fa87a94
Note the wp2 merge order for the shared search file
lidge-jun Sep 9, 2026
0b27f4a
Record the wp3 delivery receipt
lidge-jun Sep 9, 2026
988bf02
Record the wp4 delivery receipt
lidge-jun Sep 9, 2026
089d3e1
Record the wp5 delivery receipt
lidge-jun Sep 9, 2026
34700a8
Merge pull request #100 from lidge-jun/codex/memory-upgrade-l0-plan
lidge-jun Sep 9, 2026
645ce22
Merge pull request #102 from lidge-jun/codex/wp1a-memory-write-gate
lidge-jun Sep 9, 2026
5842292
Turn memories.dedicated_tools on at install time
lidge-jun Sep 9, 2026
a5a4cc0
Publish the test count this branch's suite measures
lidge-jun Sep 9, 2026
8598783
Merge pull request #106 from lidge-jun/codex/wp1b-dedicated-tools
lidge-jun Sep 9, 2026
2c75c17
recall: render the injected block under a line-granular budget
lidge-jun Sep 9, 2026
216a0aa
recall: stop emitting a PostCompact envelope the runtime cannot accept
lidge-jun Sep 9, 2026
c80bde9
recall: list sessions by cwd instead of searching for the directory name
lidge-jun Sep 9, 2026
35f614d
recall: cap the injection after compaction, densify it, and date it
lidge-jun Sep 9, 2026
57a8d1e
Publish the test count this branch's suite measures
lidge-jun Sep 9, 2026
05690d7
Merge pull request #104 from lidge-jun/codex/wp4-hook-injection
lidge-jun Sep 9, 2026
b934d2b
feat: add architect for domain-specific review model selection
thisisjun786 Sep 9, 2026
9757916
Match symbol-shaped memory queries on word boundaries
lidge-jun Sep 9, 2026
e91be87
Reach uninflected memories from inflected Korean queries
lidge-jun Sep 9, 2026
ec7e86e
Merge upstream dev and preserve architect changelog entry
thisisjun786 Sep 9, 2026
cae8351
Publish the test count this branch's suite measures
lidge-jun Sep 9, 2026
a5b381e
docs: sync test badges with measured CI total
thisisjun786 Sep 9, 2026
75a568d
Merge pull request #105 from lidge-jun/codex/wp2-query-normalization
lidge-jun Sep 9, 2026
7709cb7
Rank chat search by relevance instead of pure recency
lidge-jun Sep 9, 2026
64ecbbd
Expose the chat search ordering choice as --recent/--rank
lidge-jun Sep 9, 2026
7ff54a0
Publish the test count this branch's suite measures
lidge-jun Sep 9, 2026
b19b1b8
feat(subagents): add per-role first fallback dispatch
thisisjun786 Sep 9, 2026
3e9e72f
Merge pull request #103 from lidge-jun/codex/wp5a-chat-ranking
lidge-jun Sep 9, 2026
84af176
Rank memory hits by the project they came from
lidge-jun Sep 9, 2026
01b20c6
Answer from raw chat when the memory store has nothing
lidge-jun Sep 9, 2026
aa1517a
Compare working directories in one canonical form
lidge-jun Sep 9, 2026
7ee7de1
fix(subagents): align fallback hook with packaged inventory
thisisjun786 Sep 9, 2026
19e4316
Publish the test count this branch's suite measures
lidge-jun Sep 9, 2026
6ff3c13
docs(subagents): record fallback verification and scope limits
thisisjun786 Sep 9, 2026
4fc00f5
Merge pull request #108 from lidge-jun/codex/wp3-cwd-scope-chat-fallback
lidge-jun Sep 9, 2026
2b2a562
fix(subagents): recognize native Codex fallback errors
thisisjun786 Sep 9, 2026
5d6a82f
Remove conflict markers left in the README badges
lidge-jun Sep 9, 2026
7661793
Merge pull request #114 from lidge-jun/codex/fix-readme-conflict-markers
lidge-jun Sep 9, 2026
146580e
Rotate the auto-injected thread list away from repeats
lidge-jun Sep 9, 2026
d2fa38d
Publish the test count this branch's suite measures
lidge-jun Sep 9, 2026
3f9d22e
Merge pull request #107 from lidge-jun/codex/wp5b-hit-count-penalty
lidge-jun Sep 9, 2026
1669d46
Wait for the attachment temp directory, not just the file
lidge-jun Sep 9, 2026
2e728ed
docs(subagents): attach fallback settings UI evidence
thisisjun786 Sep 9, 2026
c00ade1
Merge upstream dev and reconcile fallback hook inventory
thisisjun786 Sep 9, 2026
5f11fb8
Merge pull request #115 from lidge-jun/codex/fix-discord-attachment-f…
lidge-jun Sep 9, 2026
24d0da7
refactor(skills): rename cxc-dev-diagram-viewer to cxc-dev-visualizer
lidge-jun Sep 9, 2026
46a4ecf
feat(dev-visualizer): report writing rules, A4 paged template and exp…
lidge-jun Sep 9, 2026
1cd2dab
docs(skills): fold report-quality lessons into dev, kwrite, uiux-desi…
lidge-jun Sep 9, 2026
d900d15
docs(plan): visualizer report-quality unit and HOTL roadmap for the m…
lidge-jun Sep 9, 2026
5c9fd59
Merge pull request #117 from lidge-jun/codex/dev-visualizer-rename
lidge-jun Sep 9, 2026
831b37f
Merge origin/dev into codex/subagent-first-fallback; fold review notes
lidge-jun Sep 9, 2026
57c6090
Merge latest dev into architect proposal and refresh measured counts
thisisjun786 Sep 9, 2026
97f293d
Merge latest dev into executor registration and reconcile setup docs
thisisjun786 Sep 9, 2026
bef704f
Merge pull request #116 from thisisjun786/codex/subagent-first-fallback
lidge-jun Sep 9, 2026
9d3159e
ci(release): guard the dispatch, fail closed on re-publish, attest th…
lidge-jun Sep 9, 2026
fc12bde
Merge pull request #112 from lidge-jun/codex/release-dispatch-hardening
lidge-jun Sep 9, 2026
3fe44dc
ci: label pull requests by type and close stale needs-info issues
lidge-jun Sep 9, 2026
d1f08e4
Merge pull request #113 from lidge-jun/codex/pr-lifecycle-hygiene
lidge-jun Sep 9, 2026
3534a2d
ci: delete head branches of closed-unmerged pull requests on a schedule
lidge-jun Sep 9, 2026
1ca63c8
Merge pull request #111 from lidge-jun/codex/closed-pr-branch-cleanup
lidge-jun Sep 9, 2026
03ff1ed
Integrate architect with current dev and fix installed fallback dispatch
thisisjun786 Sep 9, 2026
6f54506
Merge remote-tracking branch 'origin/dev' into pr91
lidge-jun Sep 9, 2026
369ed0e
Merge pull request #110 from thisisjun786/codex/architect-role-proposal
lidge-jun Sep 9, 2026
47d9ca9
Merge origin/dev (architect role, #110) into fix/executor-role-regist…
lidge-jun Sep 9, 2026
c2558a4
docs: sync test badge with the CI-measured total (2902)
lidge-jun Sep 9, 2026
9dd8ae7
Merge pull request #91 from thisisjun786/fix/executor-role-registration
lidge-jun Sep 9, 2026
c1e8077
docs: record the visualizer report-quality rules in the changelog and…
lidge-jun Sep 9, 2026
b00c819
Document why Codex loses background completions
lidge-jun Sep 9, 2026
8cd5eea
Wake the agent when a background task finishes
lidge-jun Sep 9, 2026
bf3681e
Record the bg-wake design and the Windows measurements
lidge-jun Sep 9, 2026
3fdd8da
Merge pull request #119 from lidge-jun/codex/bg-wake-background-compl…
lidge-jun Sep 9, 2026
a4396f2
Merge pull request #120 from lidge-jun/codex/visualizer-loop-record
lidge-jun Sep 9, 2026
d72ef83
docs(plan): CI modernization roadmap (sharded Windows legs, aggregate…
lidge-jun Sep 9, 2026
3f31e9c
ci: shard the Windows test legs, add an aggregate ci check, keep one …
lidge-jun Sep 9, 2026
2535483
ci(wsl): run the WSL lane on integration-line pushes and dispatch onl…
lidge-jun Sep 9, 2026
50f6609
docs: sync the tests badge with the four new shard tests (2945)
lidge-jun Sep 9, 2026
ba45886
Merge pull request #121 from lidge-jun/codex/ci-modernization
lidge-jun Sep 9, 2026
7305f3d
docs(plan): record the CI modernization outcome (PR #121, ruleset upd…
lidge-jun Sep 9, 2026
13cc123
Merge pull request #122 from lidge-jun/codex/ci-modernization-record
lidge-jun Sep 9, 2026
dd57a7d
docs(plan): memory-followup L1 roadmap — diagnosis, web survey, and d…
lidge-jun Sep 9, 2026
2650304
docs(plan): wp0 receipt for the memory-followup roadmap cycle
lidge-jun Sep 9, 2026
db4e941
Merge pull request #123 from lidge-jun/codex/memory-l1-wp0-roadmap
lidge-jun Sep 9, 2026
1e4c589
fix(pabcd-state): classify memory-write gate shell commands by write …
lidge-jun Sep 9, 2026
64e1a1d
docs(plan): wp1 receipt for the memory-write gate cycle
lidge-jun Sep 9, 2026
c7415ca
test(pabcd-state): compare the gate target on the platform path form;…
lidge-jun Sep 9, 2026
9174543
Merge pull request #124 from lidge-jun/codex/memory-l1-wp1-gate
lidge-jun Sep 9, 2026
0190283
fix(recall): treat dotted versions as VERSION words and relax only th…
lidge-jun Sep 9, 2026
497d573
docs(plan): wp2 receipt for the symbol-boundary cycle
lidge-jun Sep 9, 2026
e23e128
Merge pull request #125 from lidge-jun/codex/memory-l1-wp2-symbol
lidge-jun Sep 9, 2026
21a0f7b
docs(recall): rewrite the cxc-recall skill to match the CLI and add a…
lidge-jun Sep 10, 2026
81e36d2
docs(plan): wp3 receipt for the recall skill cycle
lidge-jun Sep 10, 2026
b60d0ca
Merge pull request #126 from lidge-jun/codex/memory-l1-wp3-skill
lidge-jun Sep 10, 2026
222ff8e
feat(recall): key project scope by normalized git origin so managed w…
lidge-jun Sep 10, 2026
444056b
docs(plan): wp4 receipt for the project identity cycle
lidge-jun Sep 10, 2026
91908b3
docs: set the tests badge to the measured total (2974)
lidge-jun Sep 10, 2026
19e2dd5
Merge pull request #127 from lidge-jun/codex/memory-l1-wp4-identity
lidge-jun Sep 10, 2026
69a3115
feat(recall): relax long natural-language queries with a required/opt…
lidge-jun Sep 10, 2026
f47c966
docs(plan): wp5 receipt for the natural-language query cycle
lidge-jun Sep 10, 2026
9384a4f
Merge pull request #128 from lidge-jun/codex/memory-l1-wp5-nl-query
lidge-jun Sep 10, 2026
7f21ed6
feat(recall): source-aware session briefing, targeted recall hints, f…
lidge-jun Sep 10, 2026
7b26088
docs(plan): wp6 receipt with the native-integration observations
lidge-jun Sep 10, 2026
1e75752
docs: set the tests badge to the measured total (3008)
lidge-jun Sep 10, 2026
9cd5276
Merge pull request #129 from lidge-jun/codex/memory-l1-wp6-native
lidge-jun Sep 10, 2026
a267b39
fix(subagents): preserve role routing and assign discovery early (#130)
thisisjun786 Sep 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
247 changes: 247 additions & 0 deletions .github/scripts/closed-pr-branch-cleanup.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
"use strict";

/**
* Deletion planning for branches left behind by closed-without-merge pull
* requests.
*
* GitHub's repository-level `delete_branch_on_merge` only fires on merge, so a
* PR that is closed unmerged leaves its head branch in the repository forever.
* This module decides which of those branches may be deleted; the workflow
* performs the deletion.
*
* Kept as a pure module so the safety rules can be unit-tested without Actions
* and without a live repository.
*/

/** Branches that may never be deleted regardless of pull-request state. */
const PROTECTED_BRANCHES = Object.freeze(["main", "dev", "preview", "gh-pages"]);

/** Branch namespaces explicitly reserved for disposable pull-request work. */
// codexclaw declares only codex/ disposable. fix/, docs/ and every other
// prefix are persistent work: a name pattern is not evidence of abandonment,
// so they stay with the human-judgment row even when a closed PR used them.
const DISPOSABLE_BRANCH_PREFIXES = Object.freeze(["codex/"]);

/** Default grace period before a closed PR's head branch becomes eligible. */
const DEFAULT_GRACE_DAYS = 14;

function normalizeBranchName(value) {
// Git permits non-ASCII whitespace in ref names, while String#trim removes
// it. Preserve API-provided branch identity byte-for-byte so two distinct
// refs cannot collapse into one deletion candidate.
return typeof value === "string" ? value : "";
}

/**
* A commit id, lowercased for comparison.
*
* The REST and GraphQL APIs are not consistent about case, and a full 40-character
* sha compared case-sensitively against an abbreviated or upper-case one silently
* reads as "different" - which here would mean "keep", so the failure direction is
* safe, but it would make the guard useless rather than protective. Anything that
* is not a plausible hex object id becomes null, i.e. unknown.
*/
function normalizeOid(value) {
const text = String(value || "").trim().toLowerCase();
return /^[0-9a-f]{7,64}$/.test(text) ? text : null;
}

function isProtectedBranch(name) {
return PROTECTED_BRANCHES.includes(normalizeBranchName(name));
}

function toTimestamp(value) {
if (!value) return null;
const ms = Date.parse(String(value));
return Number.isFinite(ms) ? ms : null;
}

/**
* Reasons a candidate branch is kept. Exported so the workflow can log a
* stable, greppable verdict per branch instead of a free-form sentence.
*/
const KEEP_REASONS = Object.freeze({
PROTECTED: "protected-branch",
MERGED: "pull-request-merged",
OPEN: "open-pull-request",
BASE_OF_OPEN: "base-of-open-pull-request",
CROSS_REPOSITORY: "cross-repository-head",
MISSING_CLOSED_AT: "missing-closed-at",
WITHIN_GRACE: "within-grace-period",
OUTSIDE_DISPOSABLE_NAMESPACE: "outside-disposable-namespace",
MOVED_SINCE_CLOSE: "branch-moved-since-close",
UNKNOWN_HEAD_SHA: "unknown-head-sha",
});

/**
* Plan deletions for head branches of closed-unmerged pull requests.
*
* Every rule here is a safety rule, and each one exists because the opposite
* behavior destroys work that is still referenced:
*
* - A branch is a candidate only when *every* pull request that ever used it as
* a head is closed and unmerged. One open or merged PR on the same branch
* keeps it, because reopening a PR whose head branch is gone cannot restore
* the commits.
* - A branch that is the base of an open pull request is kept. Deleting it
* closes the stacked child PR that targets it.
* - Cross-repository (fork) heads are never touched: they live in the
* contributor's repository and this token has no business there.
* - A grace period after `closed_at` leaves room to reopen a PR that was
* closed by mistake.
* - Only branches under namespaces explicitly reserved for disposable pull-
* request work are eligible. Pull-request history alone must not authorize
* deletion of an unrelated persistent branch.
* - Branch names are compared and emitted byte-for-byte. Normalizing Unicode
* whitespace can merge distinct valid refs and delete the wrong branch.
* - The branch must still POINT AT a commit one of those closed pull requests
* had as its head. Matching by NAME alone deletes reused work: `codex/`-style
* names get picked up again all the time, and a branch recreated for new work
* inherits the closed history of every PR that ever used that name. The tip
* moved, so the branch is not the closed PR's branch any more - it only shares
* its label.
* - A branch whose current tip cannot be determined is kept. An unknown tip is
* not evidence of an abandoned branch, and this job's mistakes are not
* recoverable.
*
* @param {object} input
* @param {Array<object>} input.pullRequests Pull requests with
* `headRefName`, `headRefOid`, `baseRefName`, `state`, `merged`, `closedAt`,
* and `isCrossRepository`.
* @param {Array<string|{name: string, oid?: string}>} input.branches Branches
* that currently exist. A bare string carries no tip, which is treated as an
* unknown tip and kept.
* @param {number} [input.now] Current time in milliseconds.
* @param {number} [input.graceDays] Days to wait after `closedAt`.
* @returns {{ deletions: Array<{branch: string, pullRequests: number[]}>,
* keeps: Array<{branch: string, reason: string}> }}
*/
function planClosedPrBranchDeletions({
pullRequests = [],
branches = [],
now = Date.now(),
graceDays = DEFAULT_GRACE_DAYS,
}) {
// Accepts both shapes so an older caller passing bare names still works - it
// just gets the conservative answer, because a name without a tip cannot be
// proven safe to delete.
/** @type {Map<string, string|null>} */
const existing = new Map();
for (const entry of branches) {
const name = normalizeBranchName(typeof entry === "string" ? entry : entry && entry.name);
if (!name) continue;
const oid = typeof entry === "string" ? null : normalizeOid(entry && entry.oid);
existing.set(name, oid);
}
const graceMs = Math.max(0, Number(graceDays) || 0) * 24 * 60 * 60 * 1000;

/** @type {Map<string, object[]>} */
const byHead = new Map();
const openBases = new Set();

for (const pr of pullRequests) {
const head = normalizeBranchName(pr && pr.headRefName);
if (head) {
const list = byHead.get(head) || [];
list.push(pr);
byHead.set(head, list);
}
const isOpen = String(pr && pr.state).toUpperCase() === "OPEN";
if (isOpen) {
const base = normalizeBranchName(pr && pr.baseRefName);
if (base) openBases.add(base);
}
}

const deletions = [];
const keeps = [];

for (const branch of [...existing.keys()].sort()) {
if (isProtectedBranch(branch)) {
keeps.push({ branch, reason: KEEP_REASONS.PROTECTED });
continue;
}

const related = byHead.get(branch) || [];
if (related.length === 0) continue; // No PR ever used it; out of scope.

if (related.some((pr) => pr && pr.isCrossRepository === true)) {
keeps.push({ branch, reason: KEEP_REASONS.CROSS_REPOSITORY });
continue;
}
if (related.some((pr) => pr && pr.merged === true)) {
keeps.push({ branch, reason: KEEP_REASONS.MERGED });
continue;
}
if (related.some((pr) => String(pr && pr.state).toUpperCase() === "OPEN")) {
keeps.push({ branch, reason: KEEP_REASONS.OPEN });
continue;
}
if (openBases.has(branch)) {
keeps.push({ branch, reason: KEEP_REASONS.BASE_OF_OPEN });
continue;
}

const closedTimestamps = related.map((pr) => toTimestamp(pr && pr.closedAt));
if (closedTimestamps.some((ts) => ts === null)) {
keeps.push({ branch, reason: KEEP_REASONS.MISSING_CLOSED_AT });
continue;
}
const newestClosedAt = Math.max(...closedTimestamps);
if (now - newestClosedAt < graceMs) {
keeps.push({ branch, reason: KEEP_REASONS.WITHIN_GRACE });
continue;
}

if (!DISPOSABLE_BRANCH_PREFIXES.some((prefix) => branch.startsWith(prefix))) {
keeps.push({ branch, reason: KEEP_REASONS.OUTSIDE_DISPOSABLE_NAMESPACE });
continue;
}

// The tip check, last because it is the most expensive claim to satisfy and
// the cheaper rules above have already excluded most branches.
//
// A closed PR's head branch is only THIS branch if the branch still points at
// a commit that PR had as its head. Without this, a name reused for new work
// is deleted on the strength of an unrelated PR that happened to share the
// label months earlier - and a deleted branch whose commits were never pushed
// anywhere else is gone.
const currentOid = existing.get(branch) || null;
if (!currentOid) {
keeps.push({ branch, reason: KEEP_REASONS.UNKNOWN_HEAD_SHA });
continue;
}
const closedOids = new Set(
related.map((pr) => normalizeOid(pr && pr.headRefOid)).filter(Boolean),
);
// An empty set means the API gave us no head SHA for any of them, which is the
// unknown case again rather than a licence to delete.
if (closedOids.size === 0) {
keeps.push({ branch, reason: KEEP_REASONS.UNKNOWN_HEAD_SHA });
continue;
}
if (!closedOids.has(currentOid)) {
keeps.push({ branch, reason: KEEP_REASONS.MOVED_SINCE_CLOSE });
continue;
}

deletions.push({
branch,
pullRequests: related
.map((pr) => Number(pr && pr.number))
.filter((n) => Number.isFinite(n))
.sort((a, b) => a - b),
});
}

return { deletions, keeps };
}

module.exports = {
DEFAULT_GRACE_DAYS,
DISPOSABLE_BRANCH_PREFIXES,
KEEP_REASONS,
PROTECTED_BRANCHES,
isProtectedBranch,
planClosedPrBranchDeletions,
};
Loading
Loading