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
11 changes: 11 additions & 0 deletions .github/scripts/enforce-pr-target.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ describe("enforce-pr-target workflow", () => {
assert.match(workflow, /collectPrQualityFailures/);
});

it("checks stacked bases via open PR heads before wrong_base enforcement", () => {
assert.match(workflow, /stackedBase/);
assert.match(workflow, /github\.rest\.pulls\.list/);
assert.match(workflow, /treating as stacked/);
const qualityCall = workflow.match(
/collectPrQualityFailures\(\{([\s\S]*?)\}\);/,
);
assert.ok(qualityCall, "must call collectPrQualityFailures");
assert.match(qualityCall[1], /stackedBase/);
});

it("strips stale WRONG BRANCH prefix on failure when base is corrected", () => {
const failureBlock = workflow.match(
/if \(failures\.length > 0\) \{([\s\S]*?)core\.setFailed\(/,
Expand Down
7 changes: 6 additions & 1 deletion .github/scripts/pr-quality.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,20 @@ function collectPrQualityFailures({
authorPermission,
permissionLookupFailed = false,
ancestryLookupFailed = false,
/** True when baseRef is another open PR's head (stacked child). */
stackedBase = false,
}) {
const failures = [];
const wrongBase = !allowedBases.includes(baseRef);
const wrongBase = !allowedBases.includes(baseRef) && !stackedBase;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve dev-only enforcement for stacked PRs

When stackedBase is true, this condition allows a PR targeting any non-allow-listed branch to pass the required target check, and the subsequent branch also skips ancestry enforcement. An open PR with a matching head therefore permits changes to be merged into a non-dev branch, contrary to the repository policy whose only stated exceptions are releases and maintainer promotions; retain wrong_base enforcement or obtain and document an explicit stacked-PR policy exception.

AGENTS.md reference: AGENTS.md:L132-L133

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept the stacked exemption (intentional review workflow, not a second integration line). Documented it in AGENTS.md: open-parent-head children skip wrong-base; after the parent lands/closes, retarget to dev.

Also addressed the P2s in this push: pull-requests: read on the release workflow, fail-closed takeover lookups (404 on source PR still leaves the line unchanged), and credit-takeovers on both carried + delta notes.

if (wrongBase) {
failures.push({ code: "wrong_base" });
} else {
// Permission lookup fails closed (still evaluate ancestry). Compare API
// failures skip ancestry — zeros would falsely pass the #644 heuristic.
// Stacked children skip ancestry against the integration base; their parent
// PR is the temporary target.
const skipAncestry =
stackedBase ||
ancestryLookupFailed ||
(!permissionLookupFailed && authorHasPushPermission(authorPermission));
if (
Expand Down
35 changes: 35 additions & 0 deletions .github/scripts/pr-quality.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -242,4 +242,39 @@ describe("collectPrQualityFailures", () => {
});
assert.ok(!failures.some((f) => f.code === "wrong_ancestry"));
});

it("skips wrong_base when stackedBase is set", () => {
const failures = collectPrQualityFailures({
baseRef: "feature/parent",
allowedBases: allowed,
body: [
"## Summary",
"This change updates the Windows tray launcher so it resolves CODEX_HOME through the shared helper instead of a hardcoded path.",
"",
"## Test plan",
"- Launch the tray app after setting CODEX_HOME",
"- Confirm the listener and launcher use the same workspace root",
].join("\n"),
behindMain: 0,
behindBase: 44,
aheadMain: 1,
authorPermission: "read",
stackedBase: true,
});
assert.ok(!failures.some((f) => f.code === "wrong_base"));
assert.ok(!failures.some((f) => f.code === "wrong_ancestry"));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});

it("still flags wrong_base for non-allow-list bases without stackedBase", () => {
const failures = collectPrQualityFailures({
baseRef: "main",
allowedBases: allowed,
body: "fix stuff",
behindMain: 0,
behindBase: 0,
authorPermission: "read",
stackedBase: false,
});
assert.ok(failures.some((f) => f.code === "wrong_base"));
});
});
36 changes: 35 additions & 1 deletion .github/workflows/enforce-pr-target.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,39 @@ jobs:
let ancestryLookupFailed = false;
const baseAllowed = ALLOWED_BASES.includes(pr.base.ref);

// Stacked PR exception: base is another open PR's head branch (same
// head repo as this PR's base repo). Closed/missing parent stays wrong_base.
let stackedBase = false;
if (!baseAllowed) {
try {
const openPrs = await github.paginate(github.rest.pulls.list, {
owner,
repo,
state: "open",
per_page: 100
});
const baseOwner =
pr.base.repo?.owner?.login ?? owner;
const baseName = pr.base.repo?.name ?? repo;
stackedBase = openPrs.some(
other =>
other.number !== pull_number &&
other.head?.ref === pr.base.ref &&
(other.head?.repo?.owner?.login ?? owner) === baseOwner &&
(other.head?.repo?.name ?? repo) === baseName
);
if (stackedBase) {
core.info(
`Base ${pr.base.ref} matches an open PR head; treating as stacked (skip wrong_base).`
);
}
} catch (error) {
core.warning(
`Could not list open PRs for stacked-base check: ${error.message}`
);
}
}

if (baseAllowed) {
const headSha = pr.head.sha;
try {
Expand Down Expand Up @@ -317,7 +350,8 @@ jobs:
aheadMain,
authorPermission,
permissionLookupFailed,
ancestryLookupFailed
ancestryLookupFailed,
stackedBase
});

if (failures.length > 0) {
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ on:
permissions:
contents: write # create the matching GitHub Release + version tag after npm publish
actions: read # verify the release commit already passed Cross-platform CI
pull-requests: read # credit-takeovers looks up landing/source PR authors via gh api
id-token: write # OIDC auth for Trusted Publishing + automatic provenance attestation

concurrency:
Expand Down Expand Up @@ -399,6 +400,22 @@ jobs:
echo "::notice::No previous channel tag; skipping generate-notes (commits-only notes)"
fi

# Rewrite takeover credits on both carried preview notes and the since-preview
# delta. Carried bodies may predate this helper and would otherwise keep
# landing-author-only attribution on stable releases.
if [ -s "$carried_file" ]; then
bun scripts/release-notes.ts credit-takeovers \
--repo "$GITHUB_REPOSITORY" \
--in "$carried_file" \
--out "$carried_file"
fi
if [ -s "$delta_file" ]; then
bun scripts/release-notes.ts credit-takeovers \
--repo "$GITHUB_REPOSITORY" \
--in "$delta_file" \
--out "$delta_file"
fi

if [ -n "$notes_range_start" ]; then
commit_range="${notes_range_start}..${GITHUB_SHA}"
git log --pretty=format:'- %s (%h)' "$commit_range" > "$commits_file"
Expand Down
39 changes: 12 additions & 27 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,33 +101,18 @@ non-trivial change. CI runs these on Linux, Windows, and macOS.
from `dev` (releases, docs deploys). Do not open feature PRs against `main`.
- `preview` — prerelease train (`x.y.z-preview.*` versions).

### The retired `dev2-go` line

The project previously ran a parallel `dev2-go` integration line that was
rebuilding the runtime as a Go native port, and every merge into `dev` had to be
carried onto it. That dual-track policy is over: maintaining two integration
lines cost more than the port returned, and dogfooding the Go runtime kept
surfacing new defects.

`dev2-go` has been deleted, along with the `codex/260728-go-port-*` and
`tmp/dev2-go-source-export` side branches. The full history lives in
[lidge-jun/opencodex-go-archive](https://github.com/lidge-jun/opencodex-go-archive)
and the final tip is tagged `archive/dev2-go` in this repository. There is no
carry or port obligation attached to a `dev` merge any more, and the
`needs-go-port` label is gone.

Bun-native TypeScript is the only runtime line. If native code returns, the
expectation is an incremental module (for example Rust via N-API) landing on
`dev`, not a second full-runtime branch.

The Claude Desktop integration formerly carried on the `claudedesktop` branch is
now fully merged into `dev`, and that branch has been retired. Desktop work
continues as normal pull requests against `dev`.

Porting and rebase pull requests are welcome. Forward-porting a fix from one
integration line to another, or rebasing a stale branch onto the current head,
is ordinary maintenance rather than noise — open it as a normal pull request
and name the source commits in the description.
Bun-native TypeScript on `dev` is the only runtime line. If native code
returns, the expectation is an incremental module (for example Rust via N-API)
landing on `dev`, not a second full-runtime branch.

Stacked child pull requests that target another **open** PR's head branch are
an intentional review workflow, not an alternate integration line. The
**`enforce-target`** check skips the wrong-base gate for those children; after
the parent lands or closes, retarget the child to `dev`.

Rebase pull requests are welcome. Bringing a stale branch onto the current head
is ordinary maintenance — open it as a normal pull request and name the source
commits in the description.

The **`enforce-target`** CI check rejects pull requests whose head
ancestry sits on the **`main`** tip while far behind **`dev`**, and rejects
Expand Down
Loading
Loading