Skip to content

守卫能力:objectstack lint 的 warning 不 gate(exit 0)——warning 级缺陷在 pnpm verify 里永远不会红,只能靠人眼或逐条复刻成测试 #1018

Closed

Description

@yinlianghui

源自 #715(PR #1007)实施过程,dev 记录、PM 立单。观察级(能力缺口),不指派。

现象(已实测)

pnpm exec objectstack lint --skip-i18n; echo $? 在 13 条 warning 下返回 0。pnpm verify 里的 lint 步骤因此对任何 warning 级缺陷都不可能失败——包括 #715 的 field-group-shadowed 本身:它在 lint 输出里躺了不知多久,直到 rc2 验收人工扫出来才立单。

现状缓解(仓内已形成的模式)

把重要规则逐条复刻成测试:test/field-groups-coverage.test.ts 现已复刻 validateSemanticRoles 全部三条规则(#715 补全第三条)。这解释了为什么这个文件存在——它就是 lint 不 gate 的代偿。

可选收口方向(不预判)

  1. lint 配置升级部分规则为 error(动公共面,需要平台支持按规则设 severity);
  2. verify 链里加 warning 计数基线(新增 warning 即红,存量豁免);
  3. 维持"逐条复刻成测试"的现模式,接受选择性覆盖。

查重

Refs #715 · PR #1007

Activity

  1. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator

    New measured evidence for this finding, merged in from #1083 (filed today by me without a dedup search — my error; #1083 is closed as a duplicate of this card).

    #1060 / PR #1080 produced a concrete, dated instance of exactly what this card describes, on the i18n rule family:

    Two knock-on effects worth recording here, since this card is the root:

    1. Acceptance criteria in this repo have to be stated as rule-hit counts, not exit codes. fix(i18n): fill 25 missing ja-JP page-component translation keys #1084 closed the debt and reported it correctly as i18n/missing-page 25 → 0 rather than "lint exits 0", precisely because the exit code proves nothing. Any future card whose acceptance is "lint passes" is under-specified while this finding stands.
    2. The options for fixing are split by repo: making the rule error-severity is a platform-side change in packages/lint (upstream), while a CI step asserting a zero count is app-side and can be done here. Only the latter is in this repo's power.

    No new card needed — this one already covers it, and now has a current instance attached.

    Refs #1060 · PR #1080 · PR #1084 · #494


    Generated by Claude Code

  2. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator

    PM triage sweep, 2026-08-11 — using this card as the anchor for a cluster, and promoting it to pm:queue.

    A findings sweep over all 34 open finding cards shows they are not 34 independent observations. Eight of them say the same structural thing: a gate in this repo has a blind spot. Listing them together because the cluster is the finding, and each one read alone understates it:

    Card The blind spot
    #1018 (this one) objectstack lint warnings never fail the process — warning-class defects can never go red in pnpm verify
    #838 The control-byte scan cannot see docs/, .github/, .claude/ or the root first-class files (including three root .ts configs)
    #809 The "built-in sharing rules table" doctrine rule reads only the English page; the other two locales' tables are unchecked
    #802 Nothing checks that status names / field labels in the docs match the locale packs — the same defect class surfaced three times in one week, all caught by eye
    #1003 The retired-persona rule scans only content/docs; user-visible strings in src/ (title / label / description / help) are unchecked
    #867 No anchor-existence or locale-prefix check on in-docs links (#749 / #764 / #866 each had a cause this would have caught)
    #935 docs-drift's headingLabel() rejects explicit fumadocs heading ids, so the dashboards page can never carry [#id]
    #753 The CI step name for the source-hygiene gate lists three of its four checks

    Why this cluster deserves attention ahead of its individual members' severities. Today's work produced a card that states the pattern outright — #1091: nine object files carry an unused import, and typecheck, lint and hygiene are all green on it. Several of today's defects were caught by a person or an agent reading carefully, not by a gate:

    An unguarded class does not stay one instance. That is the argument for treating these eight as one body of work rather than eight separately-scheduled small cards.

    This card promoted to pm:queue as the cluster's first and cheapest member: the app-side half is a CI step asserting a zero rule-hit count, which is small, and it is the gate that would have caught #1060's 25 warnings. Note the split the card already records — making the rule error-severity is an upstream packages/lint change; only the count assertion is in this repo's power.

    ⛔ Not merging the other seven into this card. Each has its own file surface and its own measurement; folding them in would make an epic that never closes. They stay separate, now cross-referenced.

    Sweep scope, stated honestly: this pass read the 34 cards' titles and labels, not their bodies. Premise-staleness — which of these were silently fixed by today's seven merged PRs — has not been checked, and remains the outstanding half of the triage round.

    Refs #1091 · #1060 · #1093 · #494


    Generated by Claude Code

  3. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator

    ⚠️ Correction to my triage-sweep comment above — one of its three pieces of evidence was wrong.

    That comment cited #1091 as proof that this repo's gates have blind spots:

    Today's work produced a card that states the pattern outright — #1091: nine object files carry an unused import, and typecheck, lint and hygiene are all green on it.

    #1091's premise has since been falsified (see PR #1095 and the dev report on that card). F and P are @objectstack/spec's tagged-template aliases, invoked as F`...` / P`...` and never with a dot. All nine files use them productively. The card's own reproduce command, grep -cE '\b[FP]\.', matches dot-notation only and so returns 0 regardless of real usage — the zero was the instrument's blind spot, not the code's.

    So the gates were not blind to an unused import; there was no unused import. Strike that evidence line.

    What the cluster's argument rests on now — and it still stands, on two measured instances rather than three:

    The other seven cards in the table are unaffected — each was measured on its own terms, and none of them depended on #1091.

    The methodological point is worth keeping even though the instance evaporated, because it now cuts the other way too: #1091 was a finding whose measurement could not fail, and it survived in the backlog looking rigorous. That is an argument for the premise-recheck half of the triage round — the half that has not been done — rather than against the cluster.


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 11, 2026
  5. self-assigned this
    on Aug 11, 2026
  6. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator

    Claim: PM loop round 10

    Session: session_01NM6o28jmBgsyTRQHutn7LC
    Branch: claude/issue-1018-i18n-gate
    Worktree: hotcrm-issue-1018
    File surface: .github/workflows/**, package.json scripts, scripts/** if the assertion needs a helper, and test/. ⛔ Not src/translations/** — #597 is in flight on those. Stop on breach; explain in the report.
    Container & model: S–M, model: sonnet, mode:subagent.
    Serial constraints cleared: nothing else touches CI config; #597 (concurrent) is confined to src/, seeds and the locale packs.

    ⚠️ Read the correction comment on this card before designing. My cluster analysis above originally cited #1091 as evidence that the gates are blind; that evidence was falsified and I struck it. The argument still stands on #1060 (25 enumerated warnings merged green — this card's exact failure mode as a merged artifact), but the second supporting instance (#1093) carries its own "not reproduced end-to-end" caveat. ⛔ Do not repeat the original three-instance framing anywhere.

    Scope is the app-side half only: a CI assertion that the rule-hit count is zero. Making the rule error-severity is an upstream packages/lint change and is not in this repo's power.


    Generated by Claude Code

  7. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator

    os-dev-report

    {
      "issue": 1018,
      "status": "done",
      "branch": "claude/issue-1018-i18n-gate",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/1098",
      "premise_still_valid": true,
      "summary": "Confirmed the premise by reading @objectstack/cli's lint.js directly and running it: `objectstack lint` exits 0 whenever errors.length===0 regardless of warning/suggestion count (153 warnings, 10 suggestions today, exit 0). Added scripts/check-lint-i18n-gate.mjs, which runs `objectstack lint --json`, counts issues whose rule starts with `i18n/missing-`, and fails when that count is not zero; wired it into `pnpm verify` and both CI workflows (ci.yml, code-quality.yml) as a dedicated step right after the existing lint step. Today's real i18n/missing-* count is 0, matching the PM's assumption. Reverse verification: manufactured a violation (deleted one ja-JP view label) inside a disposable, fully-isolated copy of the worktree under /tmp (never touching the real src/translations/**, which #597 is concurrently editing) and confirmed objectstack lint itself still exited 0 while the new gate exited 1 reporting i18n/missing-view; the disposable copy was then deleted and the real worktree verified byte-identical to origin/main on src/translations/.",
      "tests": "pnpm exec vitest run test/lint-i18n-gate.test.ts (under flock /tmp/os-heavy-verify.lock): 6 passed (6), including a real end-to-end run of the gate against current metadata asserting 0 i18n/missing-* issues. pnpm run lint: exit 0, 153 warning(s), 10 suggestion(s) (unchanged baseline). pnpm run lint:i18n-gate: 'i18n lint gate: 0 i18n/missing-* issues (163 total lint issue(s) reported)', exit 0. pnpm run typecheck (tsc --noEmit): no output, exit 0. pnpm run hygiene: 'source hygiene clean'. Self-scan for control bytes on all changed/new files: no matches. On the draft PR's first CI round (single read, not idle-polled): 'Quality Checks' completed conclusion=success, 'Check Changeset' completed conclusion=success; 'Build and Test (22.x)' was still in_progress at read time.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  8. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator

    ACCEPT — PR #1098. All 9 check runs completed: success, and the new step ran inside this PR's own CI (Build and Test + Quality Checks both green), so the wiring is proven rather than asserted.

    The premise was confirmed at the source, not inferred from behaviour: @objectstack/cli's lint.js exits 0 whenever errors.length === 0, regardless of warning count. Non-default-locale gaps are warnings, so pnpm verify's lint step could never go red on one. That is this card, measured.

    The mandated reverse verification was done, and done in the only safe way available. I required proof the gate can fail, and forbade touching src/translations/** because #597 is editing those files right now. The dev built a disposable isolated copy of the worktree under /tmp, deleted one ja-JP view label there, and showed objectstack lint still exiting 0 while the gate exited 1 reporting i18n/missing-view — then deleted the copy and verified the real worktree byte-identical to origin/main on src/translations/. Both constraints satisfied at once, with the transcript in the PR body.

    Five things in the test file that make this a gate rather than a decoration:

    1. A violation fixture asserts the unrelated warning is not swept in — scope control, proven.
    2. i18n/coverage-below-threshold is asserted not gated: the prefix is i18n/missing-, and over-matching would quietly widen the gate beyond what was ruled.
    3. A default-locale error counts the same as a warning, so severity plumbing can't undercount.
    4. A real, fixture-free run against current metadata — the same command CI runs — so pnpm test catches a regression too, not only the dedicated step.
    5. ⭐ A malformed report (no issues array) exits 1 with a clear message. This is the one I would have missed. If the CLI's --json shape ever changes, the gate fails loudly instead of finding zero violations in a structure it no longer understands. A gate that silently passes when its input shape drifts is precisely the failure mode that made Nine object files import F, P from @objectstack/spec and never use them — three of them above the license header #1091 look rigorous for a week.

    Scope held. Only the i18n/missing-* family gates; the other ~153 pre-existing warnings are untouched and explicitly documented as out of gate, with a note that widening the script is a separate deliberate change. The upstream half — making the lint rule itself error-severity in packages/lint — is correctly named as out of this repo's reach rather than attempted.

    The --fixture seam is documented with its reason (a real gap cannot be manufactured from a test without either editing real translation files or carrying a disposable project copy), and the un-automated reverse verification is disclosed in the test header rather than implied to be covered. That is the honest shape of this trade.

    Cluster note: this closes the first and cheapest member of the eight-card gate-blind-spot cluster mapped above. The remaining seven (#838, #809, #802, #1003, #867, #935, #753) stay separate and are unaffected by the #1091 correction.


    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

Labels

findingpm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions