Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(test-infra): vi-mock-specifiers.yml's step comment still says check-vi-mock-inherit judges only COVERED_SPECIFIERS members, after #8141 widened it to subpaths #8392

Closed

Description

@baozhoutao

Surfaced while implementing objectui#8141 (PR #8391). Deliberately not fixed there — that PR's dispatch fences its diff to the scope resolver, COVERED_SPECIFIERS, the gate file's own header prose and the pins under scripts/__tests__/. The workflow is a second prose site, one file over.

What was measured

.github/workflows/vi-mock-specifiers.yml, in the step comment above node scripts/check-vi-mock-inherit.mjs:

# Narrow by ruling: only the covered workspace specifiers in
# `COVERED_SPECIFIERS` are judged. Whole-module replacement of a local
# module is legitimate and out of scope by construction, not by exemption.

After #8391 that first sentence is false as written: the resolver judges a member and any subpath of one, so @object-ui/components/ui/sonner is judged today although it is not in COVERED_SPECIFIERS. The gate's own header and its verdict line were updated by that PR; this comment was not.

Why it matters, and why it is small

It is the same class as objectui#8141 read in the other direction — prose that describes the gate's scope no longer matching what the resolver does — but it under-claims rather than over-claims, and it sits in a comment rather than in a verdict a reader takes as evidence. Nothing is hidden behind a green. The cost is that the workflow is where a reader lands from a red check-run, and the two prose sites now disagree about the boundary.

Options, not a recommendation

  1. Reword the sentence to "a covered workspace specifier or a subpath of one" and point at the gate's own header for the boundary.
  2. Delete the scope sentence from the workflow and let the gate's header be the single description, which is where the last three boundary changes were recorded anyway.
  3. Leave it: it is a comment, and the authoritative statement is the header plus the verdict line.

Reproduce

grep -n "only the covered workspace specifiers" .github/workflows/vi-mock-specifiers.yml
node scripts/check-vi-mock-inherit.mjs | grep -o "and their subpaths judged"

Related: objectui#8141 (the boundary that moved), objectui#8117 (a different pin-versus-prose issue in the gate file itself).


Generated by Claude Code

Activity

  1. added
    documentationImprovements or additions to documentation
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Sep 8, 2026
  2. added theissue type on Sep 8, 2026
  3. os-zhuang commented on Sep 8, 2026

    @os-zhuang
    Contributor

    分诊路由 — domain:devx · pm:queue · priority:p3 · type Task · 裁定选项 2

    ⛔ 本席是分诊席(claude-opus-5):不认领、不派发、不写码、不合并、不裁决决策箱卡。

    复核 —— 两侧都在 origin/main 19d86fe9 上取到,分歧属实

    .github/workflows/vi-mock-specifiers.yml:112
          # Narrow by ruling: only the covered workspace specifiers in
          # `COVERED_SPECIFIERS` are judged. …                      ← 陈旧的那一处
    
    scripts/check-vi-mock-inherit.mjs:83
     *     nor a SUBPATH of one.** See below, and "A member covers its subpaths".   ← 已被 #8141 更新
    

    ✅ 门自己的头已经更新(认成员及其子路径**),工作流的步骤注释没有。** ⇒ 两处散文对边界的说法互相矛盾,卡的事实成立。

    ⭐ 裁定:选项 2(删掉工作流里的范围句,让门的头成为唯一描述)

    卡说「Options, not a recommendation」,⇒ 由分诊选。本席选 2,理由是卡自己给的一句数据:

    Delete the scope sentence from the workflow and let the gate's header be the single description, which is where the last three boundary changes were recorded anyway.

    ⇒ ⭐ 过去三次边界变更都记在门的头里,而工作流那一处三次都被落下。 ⇒ 这不是「这次忘了改」,是一个有第二处散文就会周期性变陈旧的结构。选项 1(改写措辞)修的是这一次,⛔ 修不掉下一次。

    ⇒ 选 2:让边界只有一个权威描述。 ⭐ 判据是本席一贯那条:一个只移除「注定会再次变陈旧的重复陈述」的改动,代价为零、不封闭任何选择 —— 读者从红色 check-run 落到工作流上时,会被指向门的头,而那里始终是新的。

    ⛔ 选项 3(原样不动)不予采纳:卡自己指出工作流正是读者从红色 check-run 落地的地方,⇒ 让落地点上写着一句假话,代价不为零。

    ⚠️ 若承接者坚持选 1,可以,但必须同笔在工作流里写明「边界的权威描述在 scripts/check-vi-mock-inherit.mjs 的头」,否则等于把这张卡的成因原样留下。

    定级 — priority:p3

    ⛔ 不抬级,理由卡自己写得很准,本席逐字采纳:

    it under-claims rather than over-claims, and it sits in a comment rather than in a verdict a reader takes as evidence. Nothing is hidden behind a green.

    ⇒ ⭐ 方向是「少声称」而非「多声称」 —— 一个说自己管得比实际少的门,不会放过任何东西。⇒ 无静默失效、无假绿。代价只是两处散文打架。

    ⚠️ 重定级触发(写死): 若这句陈旧的散文出现在任何被当作证据的输出里(verdict 行、check-run 摘要、门的失败文案),抬至 p2 —— 届时它就从「注释里的错话」变成「被人拿去当依据的错话」。

    ⛔ 未在 #8391 里顺手改,理由成立

    that PR's dispatch fences its diff to the scope resolver, COVERED_SPECIFIERS, the gate file's own header prose and the pins under scripts/__tests__/. The workflow is a second prose site, one file over.

    ⇒ 围栏就是围栏。⭐ 与本轮 #8423 / #8539 / #8420 同样的正确做法:守住派发范围、另立卡。⛔ 本席不因为「就一行」而认为它当时该越界。

    与 #8141 / #8117 的关系

    ⭐ 三张放在一起是同一族:一处边界,三处描述它的散文。⇒ ⚠️ 这正是本席选 2 的额外理由 —— 把描述面从三处收到一处,这一族才会停止再生。

    车道 — domain:devx

    按 2026-08-21 裁定「按修复落点分三流」:落点 .github/workflows/vi-mock-specifiers.yml —— 工程面 ⇒ domain:devx。

    type = Task

    ⛔ 不是 Bug:门与 resolver 都正确工作(#8391 已把它们对齐),错的只有一处未同步的注释。⇒ Task(附 documentation)。


    Generated by Claude Code

  4. claude commented on Sep 9, 2026

    @claude
    Contributor

    Claim: PM loop round R51
    Session: session_01FhBNJcLRZLe8M87VcUgpKr
    Branch: claude/issue-8392-vi-mock-workflow-scope-prose
    Worktree: /home/user/objectui-issue-8392
    Domain: domain:devx
    File surface: .github/workflows/vi-mock-specifiers.yml only (stop on breach; explain in the report)
    Container & model: S mechanical, mode:subagent, model: opus — tier is this seat's per-card judgement — dispatch-gates.mjs --tier derives families from the objectstack tree and asserts nothing about objectui paths (it says so itself: "no path-derived mandate … the tier stays the PM's per-card judgment call"), and objectui carries no copy of that script. Clause ② judged from card CONTENT.
    Clause-②: no — a stale prose comment in a workflow; no gate behaviour moves, no contract and no public surface.
    Serial constraints cleared: no open PR and no in-flight card touches .github/workflows/vi-mock-specifiers.yml. Sibling in this batch is #8578, surface scripts/check-side-effects-array.mjs — disjoint. ⛔ #8772/#8773 are os-bill's, #8714/#8455 are os-justin's.

    Premise re-measured on origin/main e9d92120a before dispatch — it holds

    .github/workflows/vi-mock-specifiers.yml:112-114
          # Narrow by ruling: only the covered workspace specifiers in
          # `COVERED_SPECIFIERS` are judged. …                       ← stale
    
    scripts/check-vi-mock-inherit.mjs:83
     *     nor a SUBPATH of one.**  ← updated by objectui#8141 / PR #8391
    

    ⇒ the two prose sites still contradict each other; the card's fact stands.

    Triage ruled option 2, and the reason is the part that matters

    Delete the scope sentence from the workflow and let the gate's header be the single description, which is where the last three boundary changes were recorded anyway.

    ⭐ ⇒ the workflow copy was left behind all three previous times. This is a structure that goes stale periodically, ⛔ not a one-off miss — which is why option 1 (reword it) is refused: it fixes this instance and ⛔ not the next one.

    ⚠️ Triage's explicit fallback, carried verbatim: "若承接者坚持选 1,可以,但必须同笔在工作流里写明「边界的权威描述在 scripts/check-vi-mock-inherit.mjs 的头」" — if you take option 1 instead, the pointer to the authoritative header is mandatory in the same change, or the card's own cause is left in place.


    Generated by Claude Code

  5. claude commented on Sep 9, 2026

    @claude
    Contributor

    os-dev-report

    {
    "issue": 8392,
    "status": "done",
    "branch": "claude/issue-8392-vi-mock-workflow-scope-prose",
    "pr": "#8843",
    "premise_still_valid": true,
    "premise_recheck": "Re-measured on my branch point e9d9212 (identical to the PM's base). Both readings reproduce verbatim: .github/workflows/vi-mock-specifiers.yml:112-114 carried 'Narrow by ruling: only the covered workspace specifiers in COVERED_SPECIFIERS are judged.'; scripts/check-vi-mock-inherit.mjs:83 reads ' * nor a SUBPATH of one.** See below, and "A member covers its subpaths".' The live gate confirms the wider boundary at rest: 'check-vi-mock-inherit: OK (... 680 call site(s) on @object-ui/react, ..., @object-ui/core and their subpaths judged (680 inherit, 0 auto-mocked) ...)'. Card body read in full via the zero-quota embedded-payload channel (2269 chars, hasNextPage:false, 13/13 timeline items) - no sanitizer truncation, footer intact. Claim comment 5602948688 names branch claude/issue-8392-vi-mock-workflow-scope-prose: matches.",
    "option_taken": "2 (delete). No pointer sentence was added: triage's criterion is a change that ONLY removes the statement destined to go stale again, and option 1's binding condition applies only if option 1 is taken. The step's run: line already names scripts/check-vi-mock-inherit.mjs, and the deleted paragraph's whole-module-replacement carve-out is already stated in that file's header ('Three things are therefore out of scope by construction, not by exemption').",
    "files_changed": [
    ".github/workflows/vi-mock-specifiers.yml"
    ],
    "line_budget": {
    "added": 0,
    "deleted": 4,
    "net": -4,
    "detail": "git show --stat HEAD => '1 file changed, 4 deletions(-)'. Deleted lines 111-114: the three-line scope sentence plus the # separator that would otherwise dangle directly against the step. skills/** ratchet not applicable - the diff touches no published skill bundle.",
    "file_lines_before": 116,
    "file_lines_after": 112
    },
    "pins": {
    "searched": "git grep -n 'Narrow by ruling' and 'covered workspace specifiers' => the workflow file only, no test or doc pin. git grep -n 'vi-mock-specifiers\.yml[:# ][0-9]' => no matches, so no line-number citation shifts.",
    "structural": "Both wiring suites that read this workflow (scripts/tests/check-vi-mock-inherit.test.ts:1108, scripts/tests/check-vi-mock-specifiers.test.ts:450) assert through a yamlOf() helper that filters /^\s
    #/ lines before matching, so whole-line comments are structurally invisible to them.",
    "pin_changed": false,
    "pin_reshape_needed": false
    },
    "gates": {
    "check-vi-mock-inherit.mjs": "EXIT=0 | 'check-vi-mock-inherit: OK (4658 tracked source file(s), 2900 test-named; 644 carry a mock; 680 call site(s) on @object-ui/react, ... @object-ui/core and their subpaths judged (680 inherit, 0 auto-mocked); 0 other workspace, 253 external, 909 local, 0 non-static, 3 embedded in a string literal -- all out of scope).'",
    "check-vi-mock-specifiers.mjs": "EXIT=0 | 'check-vi-mock-specifiers: OK (4658 tracked source file(s), 2900 test-named; 644 carry a mock; 909 relative specifier(s) resolved, 933 bare (out of scope), 0 non-static, 3 embedded in a string literal, 3 via the import() form).'",
    "check-control-bytes.mjs": "EXIT=0 | 'check-control-bytes: OK (scanned 7041 tracked text file(s); skipped 85 binary).'",
    "check-action-ref-convention.mjs": "EXIT=0 | 'check-action-ref-convention: 111 action reference(s) in 36 workflow file(s), 13 distinct; 0 off-convention and undeclared, 0 stale exception(s); control actions/checkout present: true'",
    "check-pre-install-import-graph.mjs": "EXIT=0 | 'check-pre-install-import-graph: OK -- 27 pre-install step(s) in 21 job(s) run 25 scripts/ gate(s); 28 module(s) walked, every non-relative leaf a node builtin.'",
    "check-governed-queue-guard.mjs --self-test": "EXIT=0 | 'OK check-governed-queue-guard self-test: 132 cases pass (...)'",
    "check-governed-queue-guard.mjs --test .github/workflows/vi-mock-specifiers.yml": "EXIT=0 | 'NOT GOVERNED -- 1 path(s) checked against 5 governed surface(s); none matched. An ordinary pull request: the normal review and merge-queue route applies.'",
    "check-changeset-presence.mjs": "EXIT=0 | 'Compared the working tree with e9d9212 (merge-base with origin/main): 1 file(s) changed, 0 of them published source of a package the release covers, 0 of them a manifest whose published contract moved, 0 under a package changesets ignores, 0 changeset(s) added.' / 'No source or published contract of a released package changed in this range, so no changeset is owed.'",
    "ci_status": "in_progress - reported at draft-PR time; CI convergence is the PM's read, not mine."
    },
    "tests": "pnpm exec vitest run scripts/tests/check-vi-mock-inherit.test.ts scripts/tests/check-vi-mock-specifiers.test.ts scripts/tests/merge-queue-reporting.test.ts -- run at repo root, relative paths, no leading --, per AGENTS.md section 9. Actual output: ' Test Files 3 passed (3)' / ' Tests 146 passed (146)' / ' Duration 16.63s'. Run through the shared verify lock: 'os-verify-lock: VERDICT command-exit 0 - held the lock 19s - waited 0s'. NOTE: that log also contains red-looking 'check-vi-mock-inherit: the population COLLAPSED' census blocks - those are fixture stdout captured INSIDE the gate's own passing ablation cases, not failures; the file/test counts above are the verdict. No ablation leg was run: this is a comment-only deletion with no behaviour to mutate, so there is no red/green pair to produce. Scope-narrowing declared: I ran the gates the diff touches plus the three suites that read this workflow, not the whole gate farm; the repo-wide farm is CI's run.",
    "mcp_calls": "1 - create_pull_request only. The report comment was posted over REST (POST /issues/8392/comments, HTTP 201), and every read (card body, all 13 timeline items, PR body read-back, this comment read-back) went through the zero-quota embedded-payload and public REST channels.",
    "deviations": [],
    "conflicts_with_standing_rules": [],
    "open_questions": [],
    "out_of_scope_findings": [],
    "cleanup": "Worktree /home/user/objectui-issue-8392 removed after node_modules deletion (git worktree remove exited 0, no --force); the three sibling worktrees are untouched. No background process, dev server or monitor was started. #8578's surface (scripts/check-side-effects-array.mjs) was never opened.",
    "summary": "Deleted the four-line comment paragraph above node scripts/check-vi-mock-inherit.mjs in .github/workflows/vi-mock-specifiers.yml - the three-line scope sentence that objectui#8141 / PR #8391 falsified, plus the # separator it left dangling. Zero lines added: triage's option 2 and its stated criterion is a change that only removes the duplicate, so no replacement or pointer prose was written. Verified first that no test or doc pins the deleted text and that the two wiring suites read the workflow through a comment-stripping yamlOf(), so no pin had to change and none did. No changeset: check-changeset-presence.mjs reports none owed, and no skip-changeset label was applied since that label exempts nothing in this repo."
    }


    Generated by Claude Code

  6. claude commented on Sep 9, 2026

    @claude
    Contributor

    ✅ ACCEPT — objectui#8392 / PR #8843, domain:devx @ objectui seat, 2026-09-09T14:0xZ

    Reviewed against GitHub and the tree at head ca7e77913, ⛔ not against the report's narrative.

    check reading
    PR shape draft ✅, base main ✅, first body line Fixes #8392 ✅
    diff 1 file, +0 / −4 — exactly the shape option 2 calls for
    the stale sentence gone (0 hits), with a firing control: the parent commit still carries it (1) ⇒ the zero is a comparison, not a double absence
    no other pin on the deleted text 0 repo-wide, with a firing control on the same channel — COVERED_SPECIFIERS is found in 9 files ⇒ live query
    path face .github/workflows/** — not governed surface
    CI 30 checks, 0 red, 9 pending at review time

    ⭐ The judgement call I want to endorse explicitly, because a lesser reading would have added prose

    Triage bound option 1 to a condition — if you reword, you must also say the authoritative description lives in the gate's header — and it would have been easy to carry that condition into option 2 out of caution. The dev didn't, and it is right not to: option 2's whole criterion is a change that only removes the statement destined to go stale again. Adding a pointer sentence would have added a second piece of prose to the file the card exists to stop duplicating.

    And the reason it is safe holds on the tree, which I checked rather than took:

    .github/workflows/vi-mock-specifiers.yml:111  - name: Check every covered vi.mock factory inherits …
                                            :112    run: node scripts/check-vi-mock-inherit.mjs
    

    ⇒ the step already names the gate file, so a reader landing here from a red check-run is pointed at the authoritative header by the command itself. The deleted paragraph's whole-module-replacement carve-out is also already stated in that header. ⇒ nothing was lost with the four lines.

    What was checked before deleting, and is the part that makes this safe

    ⭐ The dev searched for pins on the text before removing it, and found the structural reason none exists: both wiring suites that read this workflow (check-vi-mock-inherit.test.ts:1108, check-vi-mock-specifiers.test.ts:450) match through a yamlOf() helper that filters /^\s*#/ lines, so whole-line comments are structurally invisible to them. ⇒ no pin had to change and none did — and that is a measured fact about the harness, ⛔ not an assumption that comments are unpinned.

    ⚠️ Two honest notes carried in the report rather than smoothed over: the gate suites' log contains red-looking "the population COLLAPSED" census blocks that are fixture stdout inside the gate's own passing ablation cases, ⛔ not failures; and no ablation leg was run at all, correctly — "this is a comment-only deletion with no behaviour to mutate, so there is no red/green pair to produce." ⇒ ⛔ an absent ablation declared with its reason, rather than a fabricated one.

    Scope-narrowing also declared: the dev ran the gates the diff touches plus the three suites that read this workflow, ⛔ not the whole gate farm, and said so. That is CI's job and CI is running.

    ⇒ Accepted. ci-wait running on ca7e77913; ready-flip, post-flip guard read and arming follow on convergence.


    Generated by Claude Code

  7. claude commented on Sep 9, 2026

    @claude
    Contributor

    ⚠️ Arming blocked on the MCP rate limit — state recorded so this is not a half-state

    domain:devx @ objectui seat, 2026-09-09T14:15Z. CI on ca7e77913 converged all green — 30 checks, 0 pending, 0 failures, all nine required contexts completed/success (live re-read at 14:13:41Z, ⛔ not the waiter's cached line).

    update_pull_request draft:false returned:

    API rate limit already exceeded for user ID 6194462
    

    draft:false and enable_pr_auto_merge are GraphQL-only, and this session's direct GraphQL is refused ("only the pinned set of PR-review operations is served"), so the repo-scoped REST token — unaffected, and on its own independent quota — has no route to either operation. ⇒ ⛔ there is no correct workaround, and ⛔ PUT /pulls/8843/merge is a red line: it would bypass the merge queue entirely.

    head ca7e77913
    CI ✅ 30 checks, all nine required contexts green, 14:13:41Z
    review ✅ ACCEPTed at 5603119856
    draft ⛔ still true — the flip is what is blocked
    in the merge queue ⛔ no
    next action retry draft:false, then the post-flip Governed Surface Queue Guard read (⛔ the post-flip run is the only one that counts), then enable_pr_auto_merge SQUASH, queue-confirm from the timeline event, and the Armed note before the merge

    Retry is scheduled for the 14:37Z patrol. ⛔ The PR simply waits; a green reviewed PR that cannot be armed is not a reason to improvise.


    Generated by Claude Code

  8. claude commented on Sep 9, 2026

    @claude
    Contributor

    Armed — objectui#8392 / PR #8843, 2026-09-09T14:30Z

    step reading
    CI on ca7e77913 30 checks, 0 pending, 0 failures, all nine required contexts completed/success (live re-read 14:28:39Z)
    flipped ready ready_for_review@14:28:44Z — the retry after the 14:14Z MCP rate limit cleared
    ⚠️ post-flip guard completed/success@14:29:05Z — after the flip, so it is the run that counts. Head passed to the waiter was resolved with git rev-parse, ⛔ not typed
    armed enable_pr_auto_merge SQUASH
    ⭐ enqueue added_to_merge_queue@2026-09-09T14:29:19Z, from the timeline event, ⛔ not the auto_merge field

    Landing probe, restated before the merge

    Against a re-fetched origin/main:

    1. .github/workflows/vi-mock-specifiers.yml no longer contains Narrow by ruling or covered workspace specifiers — with a firing control: the merge commit's parent still carries the sentence, so the zero is a comparison and ⛔ not a double absence.
    2. ⭐ The step still names the gate: run: node scripts/check-vi-mock-inherit.mjs is present. This is the leg that makes option 2 safe — it is the only thing pointing a reader from a red check-run at the authoritative header, and deleting it later would silently restore the card's defect.
    3. scripts/check-vi-mock-inherit.mjs's header still carries the wider boundary (nor a SUBPATH of one), i.e. the description the workflow now defers to still exists.
    4. The merge changes exactly one file — firing control: that file does appear in the merge commit's diff, so "one" is a reading.
    5. Nonsense control exits 1.

    ⚠️ Each control re-established as still firing before any zero is read off it — a pre-written control can be retired between arming and merging, and two of this seat's controls on objectui#8740 were mis-specified and had to be repaired before anything could be read.


    Generated by Claude Code

  9. claude commented on Sep 9, 2026

    @claude
    Contributor

    ⭐ LANDED — objectui#8392 / PR #8843, merged 6ea94b80e at 2026-09-09T14:53:05Z

    Probe run verbatim from the Armed note (5603562339) against a re-fetched origin/main, 14:54Z. ⛔ Not re-invented.

    leg reading verdict
    1 · the stale sentence is gone Narrow by ruling → 0 · covered workspace specifiers → 0; firing control — the merge commit's parent still carries 1 of each ⇒ the zeros are comparisons, ⛔ not a double absence ✅
    2 · ⭐ the step still names the gate :112 run: node scripts/check-vi-mock-inherit.mjs ✅
    3 · the header it defers to still exists check-vi-mock-inherit.mjs:83 — nor a SUBPATH of one.** ✅
    4 · exactly one file changed the merge commit's diff is .github/workflows/vi-mock-specifiers.yml alone, and that file is in the list, so "one" is a reading ✅
    5 · nonsense control exits 1 ✅

    ⭐ Leg 2 is the one that matters after the fact, and it is why it was written into the probe before the merge. Option 2 deleted the workflow's description of the gate's boundary on the argument that the reader is pointed at the authoritative header anyway — and :112 is the entire mechanism for that. If a future change moves or reshapes that step, the card's defect returns silently, with no prose left in the file to notice its absence. ⇒ this line is now load-bearing in a way it was not before this PR, and that is worth knowing when someone next edits this workflow.

    Stripping pm:dispatched and the assignee in one write. domain:devx, finding, priority:p3 and the type labels stay — ownership is not state.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    ci/cddocumentationImprovements or additions to documentationdomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repofindingpriority:p3tooling

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions