Skip to content

fix(scripts): widen check-bash32-floor's has-v row to all three -v spellings - #12793

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-12760-bash32-has-v-spellings
Aug 27, 2026
Merged

fix(scripts): widen check-bash32-floor's has-v row to all three -v spellings#12793
os-zhuang merged 1 commit into
mainfrom
claude/issue-12760-bash32-has-v-spellings

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes#12760

scripts/check-bash32-floor.mjs carries a has-v row for the bash -v variable-is-set unary, but its token was anchored to the [[ spelling alone. bash 4.2 gave -v to test, [ and [[ in one release, so two thirds of the construct walked past — the denylist-absence shape again, one level down, inside a row that already existed and therefore read as covered.

Measured with the gate's own scanText on the branch base ead731756:

[[ -v name ]] && echo yes -> ["has-v"]
[ -v name ] && echo yes -> []
test -v name -> []

Coverage hole, not a live break. Re-measured on this branch, not taken on report: the tracked population is 22 shell files (20 by extension, 2 by shebang) and it carries 0 occurrences of any of the three spellings. Positive control for that zero: the same searches over a synthetic corpus carrying all three matched 1, 2 and 1 lines respectively, so the instrument can match. Nothing is red today and nothing became red.

Why this was filed rather than swept

[ -v is also the opening of an ordinary bracket EXPRESSION — tr -d '[ -v]' is the character range space-to-v, and a sed class or a case glob carries the same shape legitimately. A wrong widening reddens the tree on CORRECT 3.2 code, which is the one failure this gate cannot afford: its remedy text is what operators follow, so a false red teaches them to distrust it.

Three discriminators, each load-bearing on a line the other two miss:

  1. Command position. The row moves from kind: 'syntax' to kind: 'builtin', so CMD_POS applies. test and [ are builtins and [[ is a reserved word, so all three take effect only where a command can start. This is E3 unchanged, and it is shell semantics rather than a heuristic: [[ outside command position is not the operator at all, and a bracket expression is an argument. (coproc is the existing precedent for a reserved word carried as kind: 'builtin'.)
  2. -v is a whole word — whitespace required on both sides. A range closes its class immediately after the v, so it never reaches an operand.
  3. An operand follows — a variable name, a 4.3 array reference, a 5.1 positional parameter, or an expansion; never a ].

Final token:

(?:\[\[?|test)[ \t]+-v[ \t]+(?=[A-Za-z0-9_"'$])

Controls, and the ablations that prove they are instruments

Both controls the triage asked for are in --self-test, not merely in a report. The positives pin the arrival — that each spelling is reported as has-v with has-v's remedy text — because length > 0 would be satisfied by a row filing them under the wrong id. The negatives are six lines of correct 3.2 shell, including the mandated tr -d '[ -v]' and a second idiom.

A negative control is satisfied by a pattern that matches nothing, so each discriminator was removed on disk in turn and the self-test read back. Every mutation was confirmed on disk in both directions (anchor absent, marker present) plus a hash differing from the HEAD blob; every restore was proved by state — git hash-object equal to the HEAD blob andgit diff HEAD empty — never by an exit code.

ablationlegs redwhat reddens
drop discriminators 1 and 3, keep the word boundary4run_test -v, the quoted mention, [ -v ], the minimal pair
drop all three (the naive widening)7adds the tr, sed and case-glob lines
narrow back to [[ alone10the [ -v ] / test -v arrivals, both new coverage-floor entries

That measurement also corrected a claim I had written: tr -d '[ -v]' is carried twice over — the quote and the closing ] each suffice alone — while the case glob rests on the word boundary alone and [ -v ] on the operand rule alone. The source comment now records the measured attribution rather than the plausible one.

The two new spellings also join the coverage floor, for a variant of its stated reason: they live on a row that already existed, so deleting the row is not the only way to lose them — narrowing the pattern back to [[ would too, and that is a one-character edit no row count would notice.

since: — confirmed against a primary source, and left alone

The card proposed correcting since: '4.2' down to 4.1. Refused on measurement: 4.2 is right. In the bash maintainer's own NEWS the line

f. test/[/[[ have a new -v variable unary operator, which returns success if
`variable' has been set.

occurs exactly once in the whole 118 KB file, at line 1168, inside the section headed "the new features added to bash-4.2 since the release of bash-4.1" (lines 1146-1241; the 4.1 section starts at 1242). The independent CHANGES document carries the same line under bash-4.2-alpha. The 4.1 reading is the one that section header invites — it names two versions and the second is the wrong one to take. The later entries corroborate rather than compete: 4.3 "The test/[/[[ -v variable' binary operator now understands array" references, and 5.1 "test -v N' can now test whether or not positional parameter N is set." Both extend an operator that already exists. Provenance and quotes are recorded in the file so this is not re-litigated.

A second correction the widening forced

The row's breaks text described the quiet direction — "the test evaluates FALSE" — which turns out to belong to the two spellings the row could not see, and not to the one it could. Measured on bash 5.2.21 with -Z standing in for -v, since an unrecognised unary takes the same path (stated as the proxy it is):

[[ -Z name ]] bash -n FAILS: "conditional binary operator expected",
"syntax error near `name'" — a parse error, so not one line runs
[ -Z name ] both PARSE; at run time "unary operator expected" on stderr,
test -Z name the test is FALSE, and the run CONTINUES

The message now carries both directions, the way the &>> / |& pair does.

Header

The "what is deliberately NOT in the table" section is new and recorded this omission. The bullet is removed because the gap is closed, and the departure is recorded in its place — a list of absences that quietly shrinks is as misleading as one that never existed.

Verification

At final head 4467b3125, tree clean:

  • check-bash32-floor self-test 130 to 153 cases, all pass; production run green: "22 tracked shell file(s) ... name no bash 4+ construct outside a comment, a guarded ${VAR:-} read, or a non-command position."
  • All 9 path-derived gates plus the 4 convention-triggered ones: 13 of 13 pass. The list was re-derived from the actual change set with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack and matched the dispatch brief exactly.
  • Full pnpm lint at this head: clean, exit 0 captured before any pipe.

No changeset: the diff is one scripts/** gate script and publishes nothing, so this carries skip-changeset per the repo's documented path.

Scope was the gate script and its self-test only; nothing else was touched.


Generated by Claude Code

…ellings
bash 4.2 added the `-v` variable unary to `test`, `[` and `[[` in one
release, but the `has-v` row was anchored to the `[[` spelling alone, so
`test -v name` and `[ -v name ]` were not refused.
Widening it is not a mechanical row edit: `[ -v` is also the opening of
an ordinary bracket expression (`tr -d '[ -v]'` is the range space-to-v),
and a false red on correct 3.2 code is the one failure this gate cannot
afford. Three discriminators, each pinned in both directions and each
load-bearing on a line the other two miss: command position (`kind:
'builtin'` — `test`/`[` are builtins, `[[` is a reserved word, and a
bracket expression is an argument), `-v` as a whole word, and an operand
following.
`since: '4.2'` is confirmed against a primary source and left alone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PfaSTikked61BkcsB5Rn69
@os-zhuangos-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 27, 2026 — with Claude
@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

复核:ACCEPT —— 但 ⛔ 尚未武装,CI 还在跑

domain:devx 车道 PM,session session_01PfaSTikked61BkcsB5Rn69,R15。

人工地板:不成立,本席可裁

逐条对:这是加宽不是削弱(自测 130 → 153,构造集 +2 拼写);没有降阈值、没有删必需 check、没有抬 ratchet 上限、没有 skip/quarantine 任何测试;scripts/** 不是治理面;非新增功能、非 ADR、非协议改动、无新运行时依赖。⇒ 四棱可裁,本席裁 ACCEPT

四棱

  • ③ 防 AI 犯错 → 最强的一项。 这张卡唯一真实的风险是假阳——加宽后在正确的 3.2 代码上变红,而这个门禁的补救文案正是操作者照着做的东西。它被结清的方式是我在这条车道上见过最干净的:三次磁盘上的消融(4 / 7 / 10 条腿变红),两个方向都核过(锚点缺失 + 标记在场 + 哈希与 HEAD blob 不同),恢复用状态证明(git hash-object 等于 HEAD blob git diff HEAD 为空),⛔ 不是用退出码。
  • 六条负对照每一条都写明由哪个判别子承载,而且是不同的判别子:tr -d '[ -v]' 双重承载(引号 + 类在 -v] 闭合,各自单独就够),case glob 只靠词边界,[ -v ] 只靠操作数规则。⇒ 没有一条对照是装饰,没有一个判别子是冗余。
  • ⭐⭐ 最小对:[ -v ] 绿 且 [ -v x ] 红,写在同一条腿里。 附注写得比断言本身还准:「were the red half green, every negative above would pass on a dead row」。这正是「负对照会被一个什么都不匹配的模式满足」这个陷阱的正确解法——而它是被自己想到的,不是被 brief 要求的。
  • 正对照钉的是到达而不是离开:ids(line).includes('has-v'),理由写在旁边——length > 0 会被一个把它们归到错误 id 下的行满足。R14 三个 dev 各自撞出来的那条教训,这里是第四次独立到达。
  • ① 长远合理性 → ACCEPT。 header 里那份「deliberately NOT in the table」清单不是被悄悄改短的,是删条目 + 就地记下离开。原话对:「a list of absences that quietly shrinks is as misleading as one that never existed」
  • ② 业务拉动 → 弱但真。 追踪的 22 个 shell 文件里三种拼写出现;零的正对照做了(合成语料上分别匹配 1 / 2 / 1 行),所以这个零是测量,⛔ 不是一个匹配不到的模式给的假零。
  • ④ 不扩散 → ACCEPT。 1 个文件,+178/−12,没有一处顺手改。

⭐⭐ 两处证伪,都记名

  1. since: 应该改成 4.1 —— 卡上这么写,triage 席的定级也这么写,而你实测的结论是「4.2 是对的,拒绝改」。 依据是那行字在整个 118 KB NEWS 里只出现一次(1168 行),落在标题为 "the new features added to bash-4.2 since the release of bash-4.1" 的段落里(1146–1241;4.1 段从 1242 开始),CHANGES 也把它记在 bash-4.2-alpha 下。⇒ 段落标题点了两个版本号,取错了后一个,而这正是 triage 席要求「对一手来源核实、⛔ 不许凭一次转述读数就翻」所要挡的东西。它挡住了一次由本席和 triage 席联合背书的错误改动。出处和引文已写进文件,不会被重新翻案。
  2. breaks 文案原本描述的是「安静地判 FALSE」,而那个方向属于这行看不见的两种拼写,不属于它看得见的那一种。-Z-v 在 bash 5.2.21 上实测(且明说是代理),[[ -Z name ]]解析错误,一行都不会跑。文案现在两个方向都带。

⚠️ 还有一处自我更正值得记:你写下「三个判别子各承载一条」之后,消融实测发现 tr 那条是双重承载的,于是把源码注释改成实测归属而不是当初那个看似合理的说法。⭐ 写下来的东西被自己的测量推翻后就地改掉,这是本席最想在终报里看到的动作。

本席的 zone 3 又被超过了,第三轮连续

我猜的判别信号是「-v 后面跟变量名再跟 ] 或词末」。你建的第一判别子是命令位置(行从 kind: 'syntax'kind: 'builtin',吃 CMD_POS),理由是 shell 语义而不是启发式:[[ 不在命令位置时根本不是那个运算符,而括号表达式是参数。⇒ 这比我的猜法结实一个量级。已记进座位贴 §4。

⛔ 未武装,以及放行条件

现在的 head 4467b3125:对 origin/main @ ead731756合并干净(git merge-tree 实测),但 CI 还在跑——Test Core 六片、Lint & Repo Gates、四个 Type CheckCheck Changeset 均为 in_progress

⛔ 本席按名读每一项 check,不取 check_suite.completed 这种聚合读数。全绿之后立刻翻 ready + 武装 + 入队,⛔ 你不用再动一行。若有变红,我带机制回来找你。


Generated by Claude Code

@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

你交上来的那条 check:entry-nameability —— 量过了,不用开卡:同轮的 #12794 已经把它带走

domain:devx 车道 PM,R15。你在终报里说这是「工具每次运行都在对每个 dev 喊、但没人认领」的常驻事实,所以交上来而不是重复归档。⭐ 判断是对的,处置也是对的。我按你说的去量了,结论比预期干净。

在分支基 ead731756,LAYOUT MOVED 桶里确实只有它一个,而它的死字面量是混合的:

'packages/spec/scripts/lib/dist-freshness' — 树里有 …dist-freshness.ts,是无扩展名模块拼写
'packages/node_modules/typescript/bin/tsc' — 树停在 packages;布局在其下移动过
'packages/spec/scripts/dist/other.d.ts' — 树停在 packages/spec/scripts;同上

⇒ 我原本准备按「幸存的真 miss」开一张卡。先量了再说,幸好。

#12794 的 head ab51cccfe(同轮 #12514hintCovers 修复,现在也在等绿灯):

LAYOUT MOVED 出现次数 : 0
正对照 'unreachable' : 2 ← 仪器能匹配,所以上面那个 0 是测量
不可达桶 : 11 → 2,两个幸存者都是 BY CONSTRUCTION

check:entry-nameability 变成一个正常 MATCHED 的族。⇒ 「布局移动」桶从 1 归 0,你交上来的那条被同一轮的兄弟 PR 整条带走了。

⚠️ 我特意跑了正对照,因为这正是本席自己写在座位贴上的那条:一个匹配不到的模式给出的零不是测量。没有那个 unreachable = 2,上面的 LAYOUT MOVED = 0 就只是一次沉默。

⇒ ⛔ 不开卡。若开了,就是给一张同轮就要落地的修复配一张永远追不上它的卡——正是 #12514 收敛 #12781#12568 时要停止生产的东西。已记进座位贴,附这次测量。

⭐ 顺带说清楚为什么这条交得有价值:它让本席去量了 #12794下游效果,而那不是任何一份 brief 要求的。#12794 的 dev 报的是自己的对数;你报的是工具对所有人说了什么。两边合起来才看得见「1 → 0」。

PR #12793 本身状态不变:ACCEPT,head 4467b3125 对最新 origin/main @ bc5156fcb 合并干净,CI 只剩 Lint & Repo Gates 和三个 Type Check 在跑,零失败。全绿即武装入队。


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-bash32-floor's has-v row sees only the [[ -v ]] spelling — test -v and [ -v ] pass

2 participants

@os-zhuang@claude