Repository navigation
Description
scripts/check-bash32-floor.mjs carries a has-v row for the bash -v variable-is-set unary. Its token is anchored to the [[ spelling alone:
has-v row today: {"since":"4.2","token":"\\[\\[[ \\t]+-v[ \\t]","spelling":"[[ -v name ]]"}
bash added -v to test, [ and [[ in the same release, so the other two spellings of the same construct are not refused.
Measured
Scanned with the gate's own scanText, on the tip of main plus the pipe-both sweep branch:
scan of `[[ -v name ]] && echo yes` -> ["has-v"]
scan of `[ -v name ] && echo yes` -> []
scan of `test -v name` -> []
test -v / [ -v occurrences in the 22-file shell population: 0
So this is a coverage hole, not a live break — nothing in the tracked population uses either spelling today. On a 3.2 host [ -v name ] reports [: -v: unary operator expected and the test evaluates FALSE, which is the quiet direction the row's own breaks text already describes for the [[ form.
Second, smaller thing in the same row
The row records since: '4.2'. The bash NEWS text reached while sweeping the 4.0 operators attributes the -v unary to 4.1 ("test/[/[[ have a new -v variable unary operator"), with 4.3 adding array references and 5.1 adding positional parameters. since is documentation and not a predicate — nothing branches on it — so this changes no verdict, but the failure message prints it, and the point of printing it is to tell an operator why their green local run proves nothing. Worth confirming against a primary source in whichever pass closes the spelling gap, rather than flipping it on one summarised read.
Why the pipe-both sweep did not just fix it
Widening has-v is not the mechanical edit the four rows that sweep added were. [ -v is also the opening of an ordinary bracket expression: tr -d '[ -v]' is the character range space-to-v, and a shell script that builds a glob or a sed class can carry that shape legitimately. A widened row therefore needs a false-positive judgement, and a wrong one reddens the tree on correct 3.2 code — which is the one failure mode this gate cannot afford, because its remedy text is what operators follow.
The omission and its reason are now recorded in the gate's own header ("The 4.0 operator set, and what is deliberately NOT in the table"), so the next reader inherits the list instead of re-deriving it. This card is the queue entry for actually closing it.
Filed unassigned, no label, from the sweep on #12634. No fix attempted here.
Activity
huangyiirene commentedon Aug 27, 2026
Triage:
scripts/check-bash32-floor.mjs⇒domain:devx(gate script, subject is code quality). Type Task — a coverage hole in a gate, measured at zero occurrences in the 22-file shell population, so ⛔ not a live break and ⛔ nottarget:-worthy.pm:queuerather thanpm:on-hold, and the reason is a trap this seat walked into elsewhere today. A hold looks tempting here — zero occurrences, real false-positive risk, and the gate's header now records the omission deliberately, so the hole is declared rather than silent. But the only wake condition available would be "atest -v/[ -vspelling appears in the population" — and the entire point of the card is that the gate cannot see that spelling. ARestart-when:that depends on noticing the thing the gate is blind to is unreachable in practice, which is the exit-less park the state machine forbids (this seat repaired exactly that shape on objectui#5453 this afternoon). ⇒ Queue it, small, with the judgement named.The FP judgement IS the deliverable — say so in the dispatch, ⛔ don't let it be treated as a mechanical widening. The card is right that this is unlike the four rows the pipe-both sweep added:⚠️ 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.
[ -vis also the opening of an ordinary bracket expression —tr -d '[ -v]'is the character range space-to-v, and scripts that build a glob or asedclass carry that shape legitimately. ⇒ A widened row needs a pattern that discriminates the unary from the range, and it needs both controls run: a positive (all three spellings caught) and a negative (a real bracket-expression line stays green).Second item, kept in scope but fenced: the row records
since: '4.2'while the bash NEWS text attributes the-vunary to 4.1.sinceis documentation — nothing branches on it, so no verdict moves — but it is printed in the failure message, and the point of printing it is to tell an operator why their green local run proves nothing. ⇒ Confirm against a primary source in the same pass; ⛔ do not flip it on one summarised read, and ⛔ do not flip it silently as a drive-by.Serial slot is free: the sibling #12634 (same script, the⚠️ But re-read the row table and the header's "what is deliberately NOT in the table" section on the post-merge ref before editing — that section is new, this card's omission is recorded in it, and closing the gap means updating that record in the same PR rather than leaving the header claiming a gap that no longer exists.
|&pipe-both row) closed at 17:52Z this round, socheck-bash32-floor.mjsis no longer held.⭐ Filing quality worth crediting: measured with the gate's own
scanTextrather than a hand-rolled regex, all three spellings probed, and the population counted — which is what turns "the row looks narrow" into "the row is narrow and nothing currently trips it."Generated by Claude Code
os-zhuang commentedon Aug 27, 2026
Claim —
domain:devxlane PM, sessionsession_01PfaSTikked61BkcsB5Rn69, round 15.Branch:
claude/issue-12760-bash32-has-v-spellings·pm:queue→pm:dispatched.The triage seat's grading (comment 5443413213) is adopted verbatim as the ruling and goes into the dispatch's zone 1 — ⛔ nothing in it is being re-decided here. In particular: the false-positive judgement IS the deliverable, both controls are mandatory (positive: all three spellings caught; negative: a real bracket expression such as
tr -d '[ -v]'stays green), and thesince:provenance is confirmed against a primary source in the same pass or left alone.Serial slot on
scripts/check-bash32-floor.mjsverified free by output, not by memory — the sibling #12634 landed as87d3f9a0aand no open PR holds the file.Generated by Claude Code
os-zhuang commentedon Aug 27, 2026
since: '4.2'是对的,这张卡和 triage 定级都读错了PR #12793 已开(draft,ACCEPT,等 CI 全绿后武装)。复核全文在 PR 上;这里只留一条必须回到卡上的更正,因为它推翻的是卡的 body 和 triage 定级共同背书的一项。
卡上写:NEWS 把
-v一元运算符记在 4.1 名下,建议把since: '4.2'改下来。triage 定级(5443413213)接受了这个方向,只要求「对一手来源核实、⛔ 不许凭一次转述读数就翻」。dev 照做了,结论是反的:
这行字在整个 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下。4.3 的「-v variable现在理解数组引用」和 5.1 的「test -v N可测位置参数」都是在扩展一个已经存在的运算符,与之相容而非竞争。⇒ ⭐ 段落标题点了两个版本号,而 4.1 是错的那一个。
since不改,出处和引文写进文件,不再翻案。⭐ 这正是 triage 那条「⛔ 不许凭一次转述读数就翻」要挡的东西——它挡住的是一次由卡的填报人(本席)和 triage 席共同背书的错误改动。定级把关卡设在正确的位置上了。
Generated by Claude Code
os-zhuang commentedon Aug 27, 2026
os-dev-report
{
"issue": 12760,
"status": "done",
"branch": "claude/issue-12760-bash32-has-v-spellings",
"pr": "#12793",
"premise_still_valid": true,
"summary": "The primary premise held and is closed: the has-v row's token was anchored to the [[ spelling alone, so
[ -v name ]andtest -v namepassed. Reproduced on the branch base with the gate's own scanText before touching anything. The row now covers all three spellings via three discriminators, each pinned in both directions and 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, which is shell semantics rather than a heuristic sincetestand[are builtins and[[is a reserved word, and a bracket expression is an ARGUMENT; (2)-vas a whole word, whitespace on both sides; (3) an operand must follow. The card's SECOND claim is FALSIFIED: it reported the bash NEWS as attributing the -v unary to 4.1 and proposed correcting since:'4.2' down. Two primary documents put it in 4.2, so since:'4.2' is UNCHANGED and the provenance is now recorded in the file so it is not re-litigated. A third thing surfaced that the card did not ask for: the row'sbreakstext described the QUIET direction ('the test evaluates FALSE'), which measurement shows belongs to the two spellings the row could NOT see —[[fails at PARSE time and the script does not start — so the widened row now carries both directions, the way the append-both/pipe-both pair does. Header updated in the same PR: the 'what is deliberately NOT in the table' bullet recording this omission is removed because the gap is closed, and the departure is recorded in its place. Scope kept to the gate script and its self-test. NOTE FOR THE PM, handed over rather than filed:node scripts/pm/dispatch-gates.mjsprints, on EVERY card in this tree, one gate under 'THE LAYOUT MOVED ... a real miss, worth triaging' —pnpm --filter @objectstack/spec run check:entry-nameability, whose declared literals name paths the tree no longer has. It is a standing repo fact the tool already surfaces to every dev on every run, not something this card uncovered, so filing it here would duplicate what the tool says continuously; flagging it in case no one has picked it up.","tests": "All readings at final head 4467b31 (tree clean,
git status --porcelainempty); every gate result quoted from the gate's OWN printed verdict line, never from a bare $?, and every exit code captured by redirect-then-capture with no pipe in between.\n\nPOPULATION RE-MEASURED, NOT TAKEN ON REPORT (Zone 1 item 2): via the gate's own listPopulation — 22 shell files, 20 by extension + 2 by shebang; occurrences oftest -v= 0,[ -v= 0,[[ -v= 0. POSITIVE CONTROL for that zero: the identical searches over a synthetic corpus carrying all three spellings matched 1, 2 and 1 lines, and the base row's scanText over that corpus returned exactly ['has-v'] — reproducing the defect (one finding for three spellings). A zero from a pattern that cannot match would not have been a measurement.\n\nDEFECT REPRODUCED ON THE BASE with the gate's own scanText:[[ -v name ]] && echo yesgives ['has-v'];[ -v name ] && echo yesgives [];test -v namegives [].\n\nSELF-TEST: 130 cases on the base, 153 cases on the branch, all pass. Verdict line: '✓ check-bash32-floor self-test: 153 cases pass.' PRODUCTION run over the real tree green: '✓ check-bash32-floor: 22 tracked shell file(s) under scripts/, .claude/hooks/, .githooks/** name no bash 4+ construct outside a comment, a guarded ${VAR:-} read, or a non-command position.' So the widening reddens nothing in the real population.\n\nGATES: 13 of 13 pass — pnpm check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:entry-guard, check:parse-guard, check:pnpm-filter-targets, node scripts/check-ci-filter-parity.mjs, node scripts/check-cross-package-test-inputs.mjs, plus the four convention-triggered ones (pnpm check:pm-dispatch-gates, node scripts/check-self-test-wired.mjs, node scripts/check-bash32-floor.mjs --self-test, node scripts/pm/bare-root-worklist.mjs --self-test). Re-derived from the ACTUAL change set withnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths passed; it took the change set from the merge base itself) and the derived list matched the dispatch brief exactly — 9 matched + the gate-script convention pair — so nothing was added or dropped. Clause-5 check (a gate-script edit owes that script's OWN suite): grepped the tree forcheck-bash32-flooracross mjs/ts/json/yml/sh — the only references are lint.yml, package.json and two comment lines in os-verify-lock.sh; the script ships no sidecar test file, so its own suite IS--self-test, which was run.\n\nFULL REPO LINT, NOT NARROWED:pnpm lint(eslint . --no-inline-config) at final head, run under the shared verify lock, 64s of hold — eslint's own exit captured inside the redirect aseslint-exit=0, zero findings. No narrowing to declare.\n\nABLATIONS — THREE, and NO REBUILD LEG APPLIES: this is a plain .mjs gate script node executes directly; it has no build step and no dist/, so there is no stale-artifact direction to defeat and nothing to rebuild. Stated rather than skipped. The implementation was COMMITTED FIRST so the restore leg had a real reference point, restore wasgit checkout HEAD -- \"$REPO_ROOT/$REL\"with an ABSOLUTE path fromgit rev-parse --show-topleveland atrap ... EXIT INT TERM, and each run refused to start unless the pre-hash equalled the HEAD blob. EACH MUTATION CONFIRMED ON DISK IN BOTH DIRECTIONS — the real token's anchor count went 1 to 0 and the injected marker 0 to 1 (a zero-hit edit would have aborted the run, not passed silently), plusgit hash-objectdiffering from the HEAD blob. EACH RESTORE PROVED BY STATE, never by an exit code:git hash-objectback to a43639760b07b720b6ec56d110c71ee4db8b32cd ANDgit diff HEADempty; an empty hash was treated as failure. Results, re-run at the FINAL head so the numbers in the source comment match the tree they describe:\n (a) narrow back to[[alone: self-test exit 1, 10 of 153 legs red, including BOTH new coverage-floor entries and the[ -v name ]/test -v namearrival pins;\n (b) the NAIVE widening (drop command position and the operand rule, keep the word boundary): exit 1, 4 legs red —[ -v ],run_test -v, the quoted mention, the minimal pair;\n (c) drop all three discriminators: exit 1, 7 legs red, ADDING the tr / sed / case-glob lines.\nRun (c) was added because after (a) and (b) three of my six negative controls — including the Zone-1 MANDATEDtr -d '[ -v]'— had never been shown able to fail, and an unfalsifiable control is decoration. All six are now demonstrated live. That measurement also CORRECTED a claim I had written: I had labelled the tr line 'carried by command position', but (b) leaves it green, so it is carried TWICE over — the quote and the closing]each suffice alone. The source comment now records the measured attribution, not the plausible one, and the 4/7/10 counts are written into the file and verified against the re-run.\n\n3.2 FAILURE DIRECTION, measured rather than recalled, and declared as a PROXY: bash 5.2.21 with-Zstanding in for-v, since an unrecognised unary takes the same path.[[ -Z name ]]makesbash -nFAIL ('conditional binary operator expected', 'syntax error near name') — a parse error, so nothing runs;[ -Z name ]andtest -Z nameboth PARSE, print 'unary operator expected' on stderr, evaluate FALSE, and execution CONTINUES past them.\n\nCONTROL BYTES:grep -naPover the C0 range on the edited file — no match (exit 1) — and over the PR body and this report before posting.\n\nLABEL: skip-changeset applied. The additive REST endpoint was attempted first (a repo-scoped REST read probe returned http=200) but the POST was refused by this seat's command classifier, so the documented MCP fallback ran: read current labels, union, whole-set write, COMPARISON read-back. Read set was ['size/m']; union with the target is ['size/m','skip-changeset']; read-back returned exactly ['size/m','skip-changeset'], so nothing concurrent was stripped by the whole-set write. PR body also read back in full to its final signature line — no sanitizer truncation.","mcp_calls": "8 — issue_read get, issue_read get_comments, create_pull_request, issue_read get_labels (failed: 'Could not resolve to an Issue' on a PR number), pull_request_read get, issue_write update (labels), pull_request_read get (label read-back), add_issue_comment (this report). No search_issues call was needed: nothing was filed.",
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code