diff --git a/packages/core/test/pack-pins.test.ts b/packages/core/test/pack-pins.test.ts
new file mode 100644
index 0000000..4c87bc7
--- /dev/null
+++ b/packages/core/test/pack-pins.test.ts
@@ -0,0 +1,59 @@
+// Packaging pin assertions (#212): the opencode triple-pin moves together,
+// every release manifest declares an explicit `files` allowlist with no test
+// paths, and the `--release` rendering keeps the `
` contract
+// `publish.yml` parses. File reads only: no network, no registry, no pack.
+// Structural fixes (a peer range, narrower allowlists, publish parsing,
+// the runtime-matrix wording) route to S9 via CROSS-SQUAD REQUEST.
+import { describe, expect, test } from "bun:test"
+import { readFile } from "node:fs/promises"
+import path from "node:path"
+import { releasePackages, tarballSlug, workspacePackages } from "../../../scripts/packages.ts"
+
+const root = path.resolve(import.meta.dir, "../../..")
+const manifest = async (dir: string) =>
+ JSON.parse(await readFile(path.join(root, "packages", dir, "package.json"), "utf8")) as Record
+
+describe("the opencode triple-pin moves together", () => {
+ test("@opencode/plugin, @opencode/sdk and the e2e CLI share one version", async () => {
+ const opencode = await manifest("opencode")
+ const plugin = opencode.dependencies?.["@opencode/plugin"] as string
+ const sdk = opencode.devDependencies?.["@opencode/sdk"] as string
+ expect(plugin).toMatch(/^\d+\.\d+\.\d+$/)
+ expect(sdk).toBe(plugin)
+ const e2e = await readFile(path.join(root, ".github/workflows/e2e-feedback.yml"), "utf8")
+ const cliPins = [...e2e.matchAll(/@opencode\/cli-linux-x64@([0-9A-Za-z._-]+)/g)].map((match) => match[1])
+ expect(cliPins.length).toBeGreaterThan(0)
+ expect(new Set(cliPins).size).toBe(1)
+ expect(cliPins[0]).toBe(plugin)
+ })
+})
+
+describe("release tarball allowlists", () => {
+ test("every release manifest declares an explicit files allowlist with no test paths", async () => {
+ for (const pkg of await releasePackages(root)) {
+ const files = (await manifest(pkg.dir)).files as unknown
+ expect(Array.isArray(files) && (files as unknown[]).length > 0).toBe(true)
+ for (const entry of files as string[]) {
+ expect(typeof entry).toBe("string")
+ expect(entry).not.toMatch(/test|spec|__tests__/i)
+ }
+ }
+ })
+
+ test("fleet and web stay private (never released)", async () => {
+ const all = await workspacePackages(root)
+ for (const dir of ["fleet", "web"]) {
+ expect(all.find((pkg) => pkg.dir === dir)?.private).toBe(true)
+ }
+ })
+})
+
+describe("the --release rendering contract", () => {
+ test('every line is " " with the slug derived from the manifest name', async () => {
+ for (const pkg of await releasePackages(root)) {
+ const line = `${pkg.dir} ${tarballSlug(pkg.name)}`
+ expect(line).toMatch(/^\S+ \S+$/)
+ expect(tarballSlug(pkg.name)).not.toContain(" ")
+ }
+ })
+})
diff --git a/packages/opencode/src/pr.ts b/packages/opencode/src/pr.ts
index a5c8fca..3ddc254 100644
--- a/packages/opencode/src/pr.ts
+++ b/packages/opencode/src/pr.ts
@@ -1,6 +1,10 @@
-// Release PR opener backed by the GitHub CLI. It pushes only the run's
-// integration branch (never the base branch) and opens a draft PR for a human
-// to review and merge.
+// Factory release PR opener backed by the GitHub CLI. Every factory push
+// path goes through here: it pushes only the run's `factory/` integration
+// branch — `branch === base` or a non-`factory/` branch is refused before
+// any push — and opens a draft PR for a human to review and merge. (Other
+// push paths exist outside the factory: `es improve --open-pr` pushes
+// `improve/*` branches on its own human-run path; phase fast-forwards and
+// fleet landings never touch the base branch; only a human merges.)
import type { PrOpener } from "@heretek-ai/es-core"
import { buildPrArgs, git, run } from "@heretek-ai/es-core"
diff --git a/packages/opencode/test/pr-guard.test.ts b/packages/opencode/test/pr-guard.test.ts
new file mode 100644
index 0000000..c3a3a0d
--- /dev/null
+++ b/packages/opencode/test/pr-guard.test.ts
@@ -0,0 +1,80 @@
+// PR-opener guard (#238): `ghPrOpener.open()` refuses `branch === base`
+// and non-`factory/` branches before any git call, and builds the draft-PR
+// argv through the shared builder. No network and no `gh` invocation: every
+// refusal throws before the push, and the draft shape is asserted on the
+// argv (the `gh pr create` spawn itself never runs in tests).
+import { describe, expect, test } from "bun:test"
+import { mkdtemp, rm } from "node:fs/promises"
+import { tmpdir } from "node:os"
+import path from "node:path"
+import { buildPrArgs } from "@heretek-ai/es-core"
+import { ghPrOpener } from "../src/pr.ts"
+
+const open = (root: string, overrides: { branch: string; base?: string; preset?: string } = { branch: "x" }) =>
+ ghPrOpener.open({
+ root,
+ branch: overrides.branch,
+ base: overrides.base ?? "rewrite",
+ title: "t",
+ body: "b",
+ ...(overrides.preset !== undefined ? { preset: overrides.preset } : {}),
+ })
+
+describe("ghPrOpener.open() branch guard", () => {
+ test("branch === base is refused", async () => {
+ const root = await mkdtemp(path.join(tmpdir(), "es-pr-guard-"))
+ try {
+ await expect(open(root, { branch: "rewrite", base: "rewrite" })).rejects.toThrow(/refusing to push "rewrite"/)
+ } finally {
+ await rm(root, { recursive: true, force: true })
+ }
+ })
+
+ test("non-factory/ branches are refused", async () => {
+ const root = await mkdtemp(path.join(tmpdir(), "es-pr-guard-"))
+ try {
+ for (const branch of ["main", "feature/x", "improve/20260101", "factory"]) {
+ await expect(open(root, { branch })).rejects.toThrow(/refusing to push/)
+ }
+ } finally {
+ await rm(root, { recursive: true, force: true })
+ }
+ })
+
+ test("the refusal precedes any git call (a non-repo root still reports the refusal)", async () => {
+ // `root` is not a git repository: had the push run first, the failure
+ // would be git's, not the guard's. The guard's error proves the order
+ // (`pr.ts` checks the branch before `git push --set-upstream`).
+ const root = await mkdtemp(path.join(tmpdir(), "es-pr-guard-"))
+ try {
+ await open(root, { branch: "main", base: "rewrite" })
+ expect.unreachable("a non-factory/ branch must be refused")
+ } catch (error) {
+ expect((error as Error).message).toMatch(/refusing to push "main"/)
+ expect((error as Error).message).not.toMatch(/git/i)
+ } finally {
+ await rm(root, { recursive: true, force: true })
+ }
+ })
+
+ test("the branch guard runs before the preset-base guard", async () => {
+ const root = await mkdtemp(path.join(tmpdir(), "es-pr-guard-"))
+ try {
+ await open(root, { branch: "main", base: "main", preset: "self-dogfood" })
+ expect.unreachable("branch == base must be refused first")
+ } catch (error) {
+ expect((error as Error).message).toMatch(/refusing to push "main"/)
+ } finally {
+ await rm(root, { recursive: true, force: true })
+ }
+ })
+})
+
+describe("release PR argv", () => {
+ test("a factory branch opens a draft PR against the base", () => {
+ const argv = buildPrArgs({ branch: "factory/run-1/integration", base: "rewrite", title: "t", body: "b" })
+ expect(argv.slice(0, 4)).toEqual(["gh", "pr", "create", "--draft"])
+ expect(argv.slice(argv.indexOf("--head") + 1, argv.indexOf("--head") + 2)).toEqual(["factory/run-1/integration"])
+ expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual(["rewrite"])
+ })
+})
diff --git a/packages/opencode/test/release.test.ts b/packages/opencode/test/release.test.ts
new file mode 100644
index 0000000..c3ffcf5
--- /dev/null
+++ b/packages/opencode/test/release.test.ts
@@ -0,0 +1,109 @@
+// Release base-branch safety against a local bare remote (#238): the
+// factory release pushes a `factory/` branch only, `branch == base` (or a
+// non-`factory/` branch) is refused before anything is pushed, and the base
+// SHA on the remote is byte-identical afterwards. Local bare-remote fixture
+// only (no real remote, no network); `gh` is never invoked — the authorized
+// push runs the same `git push --set-upstream origin :`
+// argv `pr.ts` uses, and the draft shape is asserted on the shared argv.
+import { describe, expect, test } from "bun:test"
+import { mkdtemp, rm, writeFile } from "node:fs/promises"
+import { tmpdir } from "node:os"
+import path from "node:path"
+import { buildPrArgs, git } from "@heretek-ai/es-core"
+import { ghPrOpener } from "../src/pr.ts"
+
+const BASE = "rewrite"
+const BRANCH = "factory/run-test-1/integration"
+
+interface Remote {
+ readonly root: string
+ readonly remote: string
+ readonly baseSha: string
+ cleanup(): Promise
+}
+
+/** A repo with a base branch pushed to a local bare remote, plus one factory-branch commit. */
+async function bareRemote(): Promise {
+ const root = await mkdtemp(path.join(tmpdir(), "es-release-"))
+ const remote = await mkdtemp(path.join(tmpdir(), "es-release-remote-"))
+ const cleanup = async () => {
+ await rm(root, { recursive: true, force: true })
+ await rm(remote, { recursive: true, force: true })
+ }
+ try {
+ await git(remote, ["init", "--bare", "-q"])
+ await git(root, ["init", "-q", "-b", BASE])
+ await writeFile(path.join(root, "README.md"), "# release fixture\n")
+ await git(root, ["add", "-A"])
+ await git(root, ["-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "-m", "init"])
+ await git(root, ["remote", "add", "origin", remote])
+ await git(root, ["push", "-q", "-u", "origin", `${BASE}:${BASE}`])
+ const baseSha = (await git(root, ["rev-parse", BASE])).stdout.trim()
+ await git(root, ["checkout", "-q", "-b", BRANCH])
+ await writeFile(path.join(root, "work.txt"), "factory work\n")
+ await git(root, ["add", "-A"])
+ await git(root, ["-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "-m", "factory work"])
+ return { root, remote, baseSha, cleanup }
+ } catch (error) {
+ await cleanup()
+ throw error
+ }
+}
+
+const remoteSha = async (remote: string, ref: string) =>
+ (await git(remote, ["rev-parse", "--verify", ref])).stdout.trim()
+
+const remoteBranches = async (remote: string) =>
+ (await git(remote, ["for-each-ref", "--format=%(refname)", "refs/heads/"])).stdout
+ .split("\n")
+ .map((line) => line.trim())
+ .filter(Boolean)
+
+describe("release against a local bare remote", () => {
+ test("branch == base is refused and the remote is untouched", async () => {
+ const fx = await bareRemote()
+ try {
+ await expect(ghPrOpener.open({ root: fx.root, branch: BASE, base: BASE, title: "t", body: "b" })).rejects.toThrow(
+ `refusing to push "${BASE}"`,
+ )
+ expect(await remoteBranches(fx.remote)).toEqual(["refs/heads/rewrite"])
+ expect(await remoteSha(fx.remote, "refs/heads/rewrite")).toBe(fx.baseSha)
+ } finally {
+ await fx.cleanup()
+ }
+ })
+
+ test("a non-factory/ branch is refused and the remote is untouched", async () => {
+ const fx = await bareRemote()
+ try {
+ await expect(
+ ghPrOpener.open({ root: fx.root, branch: "feature/x", base: BASE, title: "t", body: "b" }),
+ ).rejects.toThrow(/refusing to push/)
+ expect(await remoteBranches(fx.remote)).toEqual(["refs/heads/rewrite"])
+ expect(await remoteSha(fx.remote, "refs/heads/rewrite")).toBe(fx.baseSha)
+ } finally {
+ await fx.cleanup()
+ }
+ })
+
+ test("the authorized push lands only the factory branch; the base SHA is byte-identical", async () => {
+ const fx = await bareRemote()
+ try {
+ const local = (await git(fx.root, ["rev-parse", BRANCH])).stdout.trim()
+ // The same argv `pr.ts` pushes with (the `gh pr create` spawn never
+ // runs in tests: no `gh` invocation here).
+ await git(fx.root, ["push", "--set-upstream", "origin", `${BRANCH}:${BRANCH}`])
+ expect(await remoteBranches(fx.remote)).toEqual(
+ ["refs/heads/factory/run-test-1/integration", "refs/heads/rewrite"].sort(),
+ )
+ expect(await remoteSha(fx.remote, `refs/heads/${BRANCH}`)).toBe(local)
+ expect(await remoteSha(fx.remote, "refs/heads/rewrite")).toBe(fx.baseSha)
+ // The release PR itself is a draft against the base.
+ const argv = buildPrArgs({ branch: BRANCH, base: BASE, title: "t", body: "b" })
+ expect(argv.slice(0, 4)).toEqual(["gh", "pr", "create", "--draft"])
+ expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual([BASE])
+ } finally {
+ await fx.cleanup()
+ }
+ })
+})