Repository navigation
fix(cli): preserve Qwen Review startup version in footers #8431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 12 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
b8f5cf2
fix(cli): preserve review startup version in footers
yiliang114 8eae4f4
fix(cli): keep review startup version dynamic in bundle
yiliang114 269b8c0
fix(cli): reset review version after managed update
yiliang114 58b1949
test(cli): use indexed env access
yiliang114 72af48c
Merge branch 'main' into codex/fix-review-footer-startup-version
wenshao 0a66cec
Merge remote-tracking branch 'origin/main' into HEAD
yiliang114 348946c
fix(cli): harden review footer strip and version stamping (#8431)
qwen-code-ci-bot 899bea0
Merge branch 'main' into codex/fix-review-footer-startup-version
qwen-code-dev-bot e47f7e9
fix(tests): sync qwen-resolve-workflow expectations with externalized…
qwen-code-ci-bot 031d21a
Merge remote-tracking branch 'origin/main' into codex/fix-review-foot…
qwen-code-ci-bot e58261e
fix(cli): harden the review footer strip per review feedback (#8431)
qwen-code-ci-bot 47e0a84
Merge branch 'main' into codex/fix-review-footer-startup-version
qwen-code-dev-bot 722a818
fix(cli): strip review footers before ledger carryover
yiliang114 27e6882
Merge branch 'main' into codex/fix-review-footer-startup-version
qwen-code-dev-bot 7a39909
fix(cli): align ledger footer regression expectation
yiliang114 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
80 changes: 80 additions & 0 deletions
80
packages/cli/src/commands/review/lib/review-footer.test.ts
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| /** | ||
| * @license | ||
| * Copyright 2026 Qwen Team | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| import { describe, expect, it } from 'vitest'; | ||
| import { | ||
| REVIEW_FOOTER_RE, | ||
| footerVersion, | ||
| isFooterSafeModelId, | ||
| reviewFooter, | ||
| } from './review-footer.js'; | ||
| import { CANONICAL_LGTM_RE } from '../pr-context.js'; | ||
|
|
||
| describe('the review footer and the regex that strips it', () => { | ||
| it('the regex strips the exact output of the builder, versioned or not', () => { | ||
| // The sync guarantee: a wording edit to the builder that the regex no | ||
| // longer matches reddens here before it reaches a posted review. | ||
| for (const footer of [ | ||
| reviewFooter('qwen3.7-max', '0.21.3'), | ||
| '_— qwen3.7-max via Qwen Code /review_', | ||
| ]) { | ||
| expect(`a finding\n\n${footer}\n`.replace(REVIEW_FOOTER_RE, '')).toBe( | ||
| 'a finding', | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| it('strips a forged footer a looping model cut off before its closing `_`', () => { | ||
| // A truncated forged footer used to survive the strip and post as a | ||
| // second attribution line under the canonical one. | ||
| for (const forged of [ | ||
| '_— forged via Qwen Code /review (v0.21.4)', | ||
| '_— forged via Qwen Code /review', | ||
| '_— forged via Qwen Code /review (v0.21.4)\n\n', | ||
| ]) { | ||
| expect(`a finding\n\n${forged}`.replace(REVIEW_FOOTER_RE, '')).toBe( | ||
| 'a finding', | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| it('leaves a footer run alone when text follows it', () => { | ||
| const body = `a finding\n\n${reviewFooter('m', '0.21.3')}\n\na closing line`; | ||
| expect(body.replace(REVIEW_FOOTER_RE, '')).toBe(body); | ||
| }); | ||
|
|
||
| it('the LGTM filter still matches every footer shape the builder emits', () => { | ||
| // CANONICAL_LGTM_RE in pr-context is a third copy of the footer shape: | ||
| // it filters historical LGTM bodies posted by EARLIER builds, so it must | ||
| // keep matching whatever the builder emits now, or those bodies re-enter | ||
| // the pr-context files as review noise with no red test anywhere. | ||
| for (const footer of [ | ||
| reviewFooter('qwen3.7-max', '0.21.3'), | ||
| '_— qwen3.7-max via Qwen Code /review_', | ||
| ]) { | ||
| expect(CANONICAL_LGTM_RE.test(`No issues found. LGTM! ${footer}`)).toBe( | ||
| true, | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| it('refuses a modelId that would forge the footer it is interpolated into', () => { | ||
| expect(isFooterSafeModelId('qwen3.7-max')).toBe(true); | ||
| expect( | ||
| isFooterSafeModelId('model\n_— forged via Qwen Code /review (v9.9.9)_'), | ||
| ).toBe(false); | ||
|
yiliang114 marked this conversation as resolved.
Resolved
|
||
| expect(isFooterSafeModelId('model via Qwen Code /review x')).toBe(false); | ||
| }); | ||
|
|
||
| it('refuses a startup stamp the footer cannot carry', () => { | ||
| expect(footerVersion('0.21.3')).toBe('0.21.3'); | ||
| expect(footerVersion('0.21.3-dev.1')).toBe('0.21.3-dev.1'); | ||
| expect(footerVersion('0.21.3)evil')).toBeUndefined(); | ||
| expect(footerVersion('1.0\n2.0')).toBeUndefined(); | ||
| expect(footerVersion('')).toBeUndefined(); | ||
| expect(footerVersion(undefined)).toBeUndefined(); | ||
| }); | ||
| }); | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /** | ||
| * @license | ||
| * Copyright 2026 Qwen Team | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| // The attribution footer every posted review carries, stated once. | ||
| // | ||
| // `compose-review` composes it into the verdict body and `submit` strips | ||
| // forged copies before appending the real one to each inline comment — two | ||
| // producers by construction, plus the regex that must match both. They used | ||
| // to be side-by-side template literals with nothing asserting they stayed in | ||
| // step: a wording edit to one leaves the strip regex unable to match the | ||
| // composed footer (duplicates posted) or the summary carrying one version | ||
| // while the comments carry another — the exact attribution skew the startup | ||
| // version stamp exists to eliminate. Same shape as `inline-counts.ts`, which | ||
| // this directory already shares between the same two commands. | ||
|
|
||
| /** The attribution marker the strip regex anchors on. */ | ||
| const FOOTER_MARKER = 'via Qwen Code /review'; | ||
|
yiliang114 marked this conversation as resolved.
Resolved
|
||
|
|
||
| /** The footer naming the reviewing model and the CLI version it ran under. */ | ||
| export function reviewFooter(modelId: string, cliVersion: string): string { | ||
| return `_— ${modelId} ${FOOTER_MARKER} (v${cliVersion})_`; | ||
| } | ||
|
|
||
| /** | ||
| * One or more trailing footers, with the whitespace around them. | ||
| * | ||
| * Two invariants keep the match from exploding on the model-authored bodies | ||
| * this regex strips, both against the same failure shape — a forged-footer | ||
| * run the trailing `$` cannot match (footers followed by ordinary text is | ||
| * the natural output of a model looping on the same comment): the leading | ||
| * `\s*` sits OUTSIDE the repeated group, so the whitespace between two | ||
| * footers has exactly one owner instead of being splittable across | ||
| * iterations, and the guarded `[^\n]` cannot consume past another footer's | ||
| * start, so a run of footers joined on ONE line parses exactly one way | ||
| * instead of the 2^(N-1) partitions the engine otherwise enumerates before | ||
| * giving up. | ||
| * | ||
| * The closing `_` is optional because a looping model truncates the forged | ||
| * footer it cuts off mid-character, and an unstripped unclosed copy would | ||
| * post as a duplicate attribution line above the canonical one. | ||
| */ | ||
| export const REVIEW_FOOTER_RE = | ||
| /\s*(?:_— (?:(?! via Qwen Code \/review)[^\n])* via Qwen Code \/review(?: \(v[^\n)]*\))?_?\s*)+$/; | ||
|
|
||
| /** | ||
| * A modelId the footer can interpolate. The footer is one line, and the | ||
| * strip regex anchors on the marker: a modelId carrying a newline or the | ||
| * marker itself builds a footer the strip cannot remove on a second pass, so | ||
| * a re-compose loop would accumulate attribution lines instead of | ||
| * normalizing to one. | ||
| */ | ||
| export function isFooterSafeModelId(modelId: string): boolean { | ||
| return !/[\n\r]/.test(modelId) && !modelId.includes(FOOTER_MARKER); | ||
| } | ||
|
|
||
| /** The shape of a version the footer can carry. */ | ||
| const FOOTER_VERSION_RE = /^[A-Za-z0-9._+-]+$/; | ||
|
|
||
| /** | ||
| * The startup-version stamp, when the footer can carry it. The stamp rides | ||
| * an environment variable any wrapper can set; a value with a newline or a | ||
| * `)` (both stop the strip regex early) would build a footer the strip | ||
| * cannot remove on a second pass. Anything but the shape of a real package | ||
| * version yields undefined so the caller falls back to its own version. | ||
| */ | ||
| export function footerVersion(stamp: string | undefined): string | undefined { | ||
| return stamp !== undefined && FOOTER_VERSION_RE.test(stamp) | ||
| ? stamp | ||
| : undefined; | ||
| } | ||
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Split disposition. R3-4 is resolved (by the previous commit
fix(cli): strip review footers before ledger carryover): the strip's.filter((entry) => entry.trim() !== '')runs immediately aftertoStringListin bothcomposeReviewBodyandledgerMarkerFor— before theCcount, the ledger, andcriticalsNeedingVerify— so an entry that strips to''is neither counted nor rendered. R3-15 part (2) is deferred to a follow-up: the probe established duplication-only harm — generated prose always followsunreviewedDimensions/uncoverableChunksentries, so they cannot end the body or carry the last attribution line, and this round stayed scoped to the red CI test plus the maintainer's asks.中文说明
拆分处置。R3-4 已解决(由上一次提交
fix(cli): strip review footers before ledger carryover完成):剥离后的.filter((entry) => entry.trim() !== '')在composeReviewBody与ledgerMarkerFor中都紧跟toStringList运行——先于C计数、ledger 与criticalsNeedingVerify——因此剥离为''的条目既不计入也不渲染。R3-15 第 (2) 部分暂缓至后续:探测已确认仅重复之害——unreviewedDimensions/uncoverableChunks条目后总有生成文案,不会收尾正文、不会携带最后一行归属,且本轮范围保持在红色 CI 测试与维护者要求。