fix(repo): remove the two files that conflict on every rebase - #3932
Merged
Yeachan-Heo merged 1 commit intoAug 6, 2026
Merged
Conversation
yazzang-homelab
force-pushed
the
fix/changelog-released-section-guard
branch
from
August 6, 2026 10:31
01b2c22 to
1b4f765
Compare
This was referenced Aug 6, 2026
Two artifacts made a rebase onto `dev` fail for reasons unrelated to the change being rebased, and both are removable rather than manageable. `packages/*/CHANGELOG.md merge=union` only ever applied locally. GitHub does not honour the driver when it computes mergeability, so it reported `dirty` on every PR that touched a CHANGELOG while git merged them cleanly: 15 of 24 open PRs marked CONFLICTING merged with zero conflicts under the repo's own attributes, and disabling the union line reproduced GitHub's answer exactly (100% agreement, 24/24, no exceptions). Worse than the phantom, union never conflicts -- it concatenates both sides of an overlapping hunk. Release commits insert `## [X.Y.Z]` directly beneath the surviving `## [Unreleased]` heading, so a branch that added entries under Unreleased overlaps exactly that region and its entries land *inside* the version that already shipped, with no marker and nothing visible in the PR diff. Reproduced on Yeachan-Heo#3697: its entry resolves under `## [0.12.12]`. An audit of `dev` finds 35 entries already sitting in released sections across 5 packages, including one authored 2026-08-06 filed under `## [0.12.8] - 2026-08-02`. `docs-index.generated.ts` inlines each doc's full body onto a single source line, so two edits to the same doc produce a whole-line conflict git cannot three-way merge. It is already listed in .gitignore -- and also tracked, which makes the ignore rule inert, because 23983ef added both in one commit. 6 of the 10 real conflicts among open PRs are this file; 4 PRs are blocked by it alone. It churned in 31 commits in 30 days. Untracking is safe: the root `prepare` hook regenerates it on every `bun install` (verified, including `--frozen-lockfile`), every CI job that compiles or tests runs that install, and `npm pack` still ships it because the `files` allowlist outranks ignore rules (verified three ways -- 1436 files packed either way). A real conflict an author resolves is strictly better than a silent misfile, so no replacement merge driver is introduced. Constraint: the published tarball must keep the generated docs index Rejected: keep union, add a CI guard against released-section edits | leaves the phantom conflicts and the forced rebases in place Rejected: emit one line per paragraph so union can merge | shrinks the conflict without removing it, and GitHub still disagrees Confidence: high Scope-risk: narrow Reversibility: easy Tested: bun test scripts/check-public-version-sync.test.ts packages/coding-agent/test/docs-index-lazy.test.ts packages/coding-agent/test/{bot-integration-docs,package-files,docs-utility-surface-cleanup}.test.ts (34 pass); both new guards verified non-vacuous by re-adding the file; bun run check:tools and packages/coding-agent check:types clean Not-tested: a full release publish
yazzang-homelab
force-pushed
the
fix/changelog-released-section-guard
branch
from
August 6, 2026 11:03
1b4f765 to
b4ac52b
Compare
Contributor
Author
|
dev( 리베이스 중 충돌이 하나 났는데, 하필 이 PR이 없애려는 바로 그 파일이었다 — dev가 재검증: 34 pass / 0 fail (5개 파일), |
This was referenced Aug 6, 2026
Contributor
Author
|
현재 head 자체 승인은 BLOCK이라 verdict는 머지 후 효과: 열린 PR 중 |
This was referenced Aug 6, 2026
Yeachan-Heo
pushed a commit
that referenced
this pull request
Aug 6, 2026
Removing `packages/*/CHANGELOG.md merge=union` in #3932 was correct -- union never conflicts, it concatenates both sides of an overlapping hunk, which silently filed entries into versions that had already shipped (35 such entries audited on dev, #3929). What it did not account for is the transition: these files now conflict on rebase for the first time, and a bad resolution drops the whole history with no marker. That is not hypothetical. #3932 merged at 11:25:32Z. Between 11:29:29Z and 11:35:02Z, ten open pull requests across six authors force-pushed heads whose CHANGELOG was a single newline -- every released section gone. #3920 #3697 #3870 #3908 #3887 #3864 #3729 #3869 #3866 #3873. Nothing caught it: the files still parse, no test reads them, and the loss looks like a large deletion inside an otherwise legitimate diff. The guard asserts the one property that matters and nothing more: every `## [X.Y.Z]` heading present at the merge base must still be present at the head. Additions pass, rewording passes, and a release commit that consumes `## [Unreleased]` into a new version passes. Only losing a released section fails, and the message names the recovery command. Runs in `affected-plan`, which already checks out full history and carries the immutable event base sha, so it costs one bun invocation and needs no new job. Constraint: a release bump must still be able to add a version heading Constraint: must not depend on byte-size heuristics -- a legitimately small changelog is not a violation Rejected: threshold on deleted line count | fires on large legitimate edits and misses a small changelog emptied completely Rejected: restore merge=union | reinstates the silent misfiling this replaced, and GitHub ignores the driver anyway Confidence: high Scope-risk: narrow Reversibility: trivial Tested: bun test scripts/changelog-history-guard.test.ts (11 pass); guard run against the three real broken heads (#3873 #3920 #3869) exits 1 and names the lost sections; clean range exits 0; bun run check:tools exit 0 Not-tested: a real release-bump PR end to end
Yeachan-Heo
pushed a commit
that referenced
this pull request
Aug 6, 2026
Removing `packages/*/CHANGELOG.md merge=union` in #3932 was correct -- union never conflicts, it concatenates both sides of an overlapping hunk, which silently filed entries into versions that had already shipped (35 such entries audited on dev, #3929). What it did not account for is the transition: these files now conflict on rebase for the first time, and a bad resolution drops the whole history with no marker. That is not hypothetical. #3932 merged at 11:25:32Z. Between 11:29:29Z and 11:35:02Z, ten open pull requests across six authors force-pushed heads whose CHANGELOG was a single newline -- every released section gone. #3920 #3697 #3870 #3908 #3887 #3864 #3729 #3869 #3866 #3873. Nothing caught it: the files still parse, no test reads them, and the loss looks like a large deletion inside an otherwise legitimate diff. The guard asserts the one property that matters and nothing more: every `## [X.Y.Z]` heading present at the merge base must still be present at the head. Additions pass, rewording passes, and a release commit that consumes `## [Unreleased]` into a new version passes. Only losing a released section fails, and the message names the recovery command. Runs in `affected-plan`, which already checks out full history and carries the immutable event base sha, so it costs one bun invocation and needs no new job. Constraint: a release bump must still be able to add a version heading Constraint: must not depend on byte-size heuristics -- a legitimately small changelog is not a violation Rejected: threshold on deleted line count | fires on large legitimate edits and misses a small changelog emptied completely Rejected: restore merge=union | reinstates the silent misfiling this replaced, and GitHub ignores the driver anyway Confidence: high Scope-risk: narrow Reversibility: trivial Tested: bun test scripts/changelog-history-guard.test.ts (11 pass); guard run against the three real broken heads (#3873 #3920 #3869) exits 1 and names the lost sections; clean range exits 0; bun run check:tools exit 0 Not-tested: a real release-bump PR end to end
This was referenced Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Removes the two files that make a rebase onto
devfail for reasons unrelated to the change being rebased:packages/*/CHANGELOG.md merge=unionfrom.gitattributes.packages/coding-agent/src/internal-urls/docs-index.generated.ts(already in.gitignore) and adds two guards so it cannot come back.Closes #3928. Closes #3929.
Why
merge=unionnever applied where it matteredGitHub does not honour the driver when computing mergeability. Controlled A/B — same base
1f735b7b, same head (#3892f18ae6ff), only the union line differing:packages/ai/CHANGELOG.md,packages/coding-agent/CHANGELOG.mdGitHub REST reports
mergeable: false/dirtyfor that head. Across all 24 open PRs the correlation is exact, no exceptions: every PR GitHub calleddirtyconflicts only on CHANGELOG once union is disabled; every PR it calledcleanconflicts under neither rule. So the driver bought nothing on GitHub and cost a phantomCONFLICTINGbadge on 15 PRs.And where it did apply, it silently corrupted release notes
Union never conflicts — it concatenates both sides of an overlapping hunk. Release commits insert
## [X.Y.Z] - <date>directly beneath the surviving## [Unreleased]heading (0e9e9447d), so a branch that added entries under Unreleased overlaps exactly that region.Reproduced by union-merging #3697 into current
dev:An unmerged PR's entry lands inside a version that already shipped. No conflict marker, no CI signal, invisible in the PR diff — it only exists in the rebase result.
Audit of
dev(blame every released section; flag lines whose authoring commit is not an ancestor of the commit that created the section): 35 entries already sit in released sections across 5 packages. Worst case ispackages/coding-agent/CHANGELOG.mdL111–L112, authoredb621997eon 2026-08-06, filed under## [0.12.8] - 2026-08-02. Full breakdown in #3929; remediation is a separate change so this PR stays mechanical.docs-index.generated.tscannot be merged at allThe generator emits one line per doc, each holding an entire document as a single JSON string. Two edits to the same doc are a whole-line conflict with no three-way resolution.
It was already in
.gitignore— and also tracked, which makes the ignore rule inert, because23983ef4aadded the ignore entry and the file to the index in the same commit (a commit about test hermeticity; its message never mentions either). So this restores the intent that was already recorded, it does not reverse a decision.Impact: 6 of the 10 real conflicts among open PRs are this file, 4 PRs are blocked by it alone (#3918, #3868, #3788, #3741), and it churned in 31 commits in 30 days — including #3922/#3923/#3924, which are ACP and model changes.
Safety of untracking
package.jsonpreparerunsgenerate-docs-index. Verifiedbun installfires rootprepare, including--frozen-lockfile. Every CI job that compiles or tests runsbun install --frozen-lockfilefirst; the three jobs that do not (affected-plan,affected-evidence-producer,affected) only run standalonescripts/ci-*.tsand never import the package.npm pack --dry-runships the file in all three cases — no ignore entry, root.gitignoreentry, and a package-local.gitignoreentry — because thefilesallowlist outranks ignore rules. 1436 files packed in every case.prepackalso regenerates it.Guards added
.gitignorealone cannot prevent this recurring, since git only consults it for untracked paths. Two independent checks:scripts/check-public-version-sync.tsgains a violation when the artifact is tracked, with thegit rm --cachedremedy in the message.packages/coding-agent/test/docs-index-lazy.test.tsreplaces its now-moot "committed index matches committed docs" case with "index stays untracked". The other four cases in that file already cover worktree-vs-docs parity and lazy loading, and are unchanged.Both were verified non-vacuous by re-adding the file to the index and confirming each fails.
CONTRIBUTING.mdgains a "Rebasing ontodev" section: CHANGELOG conflicts are now normal and are resolved by keeping both entries under## [Unreleased], never by moving anything into a released section.Testing
No CHANGELOG entry: this changes contributor workflow and repository layout, not shipped behaviour — the published tarball is byte-equivalent. Adding one would also manufacture an instance of the conflict class this PR removes.
CI가 잡아낸 것 (수정 반영됨)
첫 푸시에서
Local public surfaces가 실패했다.public-site-sync.yml의 두 job은bun install을 돌지 않아 (저장소 메타데이터만 읽으므로) 루트prepare훅이 발화하지 않고, 따라서 생성 파일이 없는 채로check:public-sync가 "Generated docs index is missing"으로 떨어진다. 내가dev-ci.yml만 조사하고 이 워크플로를 놓쳤다.두 job 모두에 명시적 생성 단계를 추가했다.
live-public-sync도 필요하다 —--live는checkLivePublicVersionSync에 더해checkPublicVersionSync()를 무조건 호출하므로(스크립트 608행) 야간 스케줄도 같은 이유로 깨진다.생성기는
node:fs/node:path/Bun.Glob만 쓰므로node_modules없이 돈다. 실패 커밋을 그대로 체크아웃해 재현하고 수정을 검증했다:이 실패는 이 PR이 없애려는 문제 자체의 사례이기도 하다: 추적되던 생성 파일이 "install 없이 도는 job"의 누락된 의존성을 가려주고 있었다.
GJC verdict
Self-approval is BLOCK; this needs an independent architect/critic/human verdict.
devbun checkpasses (check:tools,check:types)