fix(upstream): pin Marker Guard to the upstream baseline tag; refuse cross-major merges

AN
4h ago
+319-46

Issue for this PR

Closes #1504

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Marker Guard has been silently passing everything since upstream promoted OpenCode v2 into dev (2026-10-10). It decided which files are upstream-shared by listing upstream/dev, then upstream/main. v2 has no packages/opencode paths, so every v1 file looked fork-only and unmarked edits passed --strict.

Changes:

  • Ownership comes from a pinned tag. upstreamBaseline: "v1.17.9" in script/upstream/utils/config.ts is the upstream tag we are based on; bump it when a merge lands. getUpstreamFiles() lists files only from refs/upstream-baseline/<tag>, a dedicated ref namespace, so a fork-owned tag or local branch with the same name can't shadow it. If that ref is missing, it fetches it once from upstream; if that also fails, it warns with the fetch command and uses the old directory fallback.
  • CI fetches the tag. The marker-guard job reads it with the new analyze.ts --print-baseline and fetches refs/tags/<baseline>:refs/upstream-baseline/<baseline>, because the job fetches with --no-tags. The tooling bun install step moved before the fetch so the script can run. runMarkerCheck no longer fetches the moving branch. If the baseline ref is missing locally, getUpstreamFiles fetches it once before warning.
  • No cross-major merges. merge.ts (including --continue) and bridge-merge.ts refuse a target whose major differs from the baseline, before touching git. list-versions.ts lists only same-major releases and prints a note when newer majors exist. v2 replaces packages/opencode and the plugin/session/server APIs, so it is a re-port, not a merge with these scripts.

Behavior changes:

  • PRs that add unmarked code to files present at upstream v1.17.9 now fail Marker Guard again. They have been passing since 2026-10-10. Open PRs touching upstream files may go red on their next run; that is the guard working, not a regression.
  • list-versions.ts stops suggesting v2 tags.

Follow-up, not in this PR: nothing bumps upstreamBaseline automatically after a same-major merge. The README says to bump it; this only matters if we do another v1 merge.

Not changed: the parser still ignores import/export lines and comment-only lines. That is an existing heuristic, and an unmarked export const x = 1 still passes.

How did you verify your code works?

  • cd script/upstream && bun test: 42 pass, 0 fail. New tests: baseline ownership vs moving branches; missing baseline is fetched once, then warns and falls back; the major-version helper; and merge-guard.test.ts, which runs the real merge.ts/bridge-merge.ts with a v2 target and asserts exit 1 with HEAD/status unchanged.
  • Side-by-side on throwaway worktrees of origin/main, appending the unmarked statement const __markerProbe = Date.now() + 1 to packages/opencode/src/session/prompt.ts and running analyze.ts --markers --base <parent> --strict:
    • old tooling: ok No upstream-shared files modified, exit 0
    • this branch: fetches refs/upstream-baseline/v1.17.9 on its own, flags the file, error --strict mode — failing CI, exit 1
  • Not run: the GitHub Actions job itself. The new tag-fetch step runs for the first time on this PR's CI.

Screenshots / recordings

N/A

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Upstream release suggestions now stay within the configured baseline’s major version, with warnings when newer major releases are excluded.
    • Merge attempts for other major versions are rejected before repository state changes, with guidance to re-port instead.
    • File ownership checks use the configured baseline, attempt to fetch it when unavailable, and fall back to directory-based checks if fetching fails.
    • Added a command to display the configured baseline.
  • Documentation
    • Updated the runbook with baseline, version-filtering, merge, and ownership-check guidance.

[!NOTE]
Medium Risk
Changes CI enforcement for upstream-shared files (PRs touching v1 paths may fail until marked) and merge tooling behavior, but does not alter runtime product code paths.

Overview
Fixes Marker Guard silently passing after upstream v2 landed on dev: shared-file detection no longer follows upstream/dev or upstream/main. It uses a pinned upstreamBaseline (v1.17.9 in utils/config.ts) and lists files from refs/upstream-baseline/<tag> so moving branches and same-named fork tags cannot clear the guard. analyze.ts --print-baseline supports CI; the marker-guard job installs merge tooling first, then fetches that tag into the namespaced ref. Missing baseline triggers a one-time fetch, then a warning and directory fallback. runMarkerCheck no longer fetches the moving upstream branch.

Adds major-version policy: merge.ts (including --continue) and bridge-merge.ts call assertSameMajor before touching git; list-versions.ts only suggests same-major releases and warns when newer majors exist. README documents baseline bumps and v2 as a re-port.

Extensive tests cover baseline ownership, fetch/fallback, version parsing, and dry-run merge guards leaving the repo unchanged.

Reviewed by Cursor Bugbot for commit 0cb93b790db2b2b1325f53a4ec44c465623f03ca. Bugbot is set up for automated code reviews on this repo. Configure here.