Skip to content

[finding] five more private suggest/distance ("Did you mean?") copies remain in packages/lint after #14268 consolidated the ruled three #14577

Description

@claude

Follow-up to #14268 (PR #14575), filed by the domain:devx execution seat (session session_01WLJQhde67SeTccsmnBVarV, seat post #6023) from that card's out_of_scope_findings; unassigned, for triage.

#14268's ruling named THREE rules; after PR #14575 those three (validate-object-references.ts, validate-sortable-fields.ts, validate-widget-bindings.ts) import suggestName / nearestName from object-graph.ts, which now carries the one shared "Did you mean?" helper (containment pre-pass, then edit distance with budget max(2, floor(len/3))). The consolidation stopped at the ruling's three, but packages/lint/src still carries five more private copies of the same suggest + distance pair and two deliberately different neighbours. Line numbers below are on origin/main f60ab90ae (pre-#14575 merge; the file:line for these files do not move with that PR).

Tier 1 — byte-for-byte the same shape as the three just consolidated (same Levenshtein, same budget, same message shape). Pure rewire to suggestName, delete the copy:

  • validate-action-name-refs.ts:84 distance, :101 suggest (budget at :111)
  • validate-chart-bindings.ts:83 distance, :100 suggest (budget at :110)
  • validate-searchable-fields.ts:289 distance, :275 suggest (budget at :285)

Tier 2 — same budget + message, plus a one-line pre-pass that should stay in the rule and wrap the shared helper (prefix/namespace check first, then suggestName):

  • validate-ai-tool-references.ts:70 distance, :87 suggest — :93 first tries known.has(prefix + target) (a missing tool-name prefix), then the same Levenshtein budget (:105-106).
  • validate-translation-references.ts:159 distance, :186 suggest — :191 first tries a namespace-segment match, then the same budget (:202-203).

Not the same thing — listed so the follow-up does not over-reach; leave them unless triage rules otherwise:

  • validate-react-page-props.ts:167 nearestKnown — fixed budget <= 2 (not max(2, floor(len/3))), null return, own editDistance. Same idea, different contract; consolidating changes which typos get a hint.
  • validate-rule-schema-formats.ts:266 nearestRegisteredFormat — exported, budget min(3, floor(len/2)), case-insensitive on the authored side, alphabetical tie-break "so the suggestion is stable", with its own docblock justifying each choice against ajv's case-sensitive lookup. A different helper on purpose.

Why it matters (same argument as #14268): the containment pre-pass is now the shared helper's behaviour for every caller, so the five Tier 1/2 rules once again give "a different quality of hint depending on which rule you tripped" — amount → sum_amount is suggested by widget bindings and not by chart bindings, one file over. That is the drift #14268 was opened to end.

Suggested shape if promoted: pm:queue, one PR, @objectstack/lint patch changeset; Tier 1 rewire + Tier 2 wrap; ⛔ no change to object-graph.ts's helper (already ruled in #14268); ⛔ validate-react-page-props.ts and validate-rule-schema-formats.ts out of scope unless triage says otherwise. Serial: after PR #14575 lands (it is in CI now); no other open PR touches these five files at filing time.

Dedup: no open issue matches did you mean / suggest / levenshtein / nearest in its title except #14268 itself (481 open issues scanned 12:5xZ).


Generated by Claude Code

Activity

  1. huangyiirene commented on Sep 2, 2026

    @huangyiirene
    Collaborator

    Triage — graded p3, finding cleared, pm:queue, routed domain:devx. Promoted with your suggested shape adopted verbatim, including both exclusions.

    ⭐ Your serial dependency is discharged — this is dispatchable now

    You wrote "Serial: after PR #14575 lands (it is in CI now)". It has landed. At origin/main 2aa8456:

    • packages/lint/src/object-graph.ts:373 export function nearestName(...), :394 export function suggestName(...)
    • the ruled three (validate-object-references, validate-sortable-fields, validate-widget-bindings) all reference them

    So no Blocked-by line and no pm:blocked. The card goes straight into the queue.

    The five sites, re-measured

    Each of the five still carries its own function distance and its own function suggest at origin/main — one private pair per file, five pairs, none removed by #14575. Population confirmed at five, exactly as filed.

    Your headline example checks out arithmetically, which is why this grades as a real defect and not tidying

    The shared helper's pre-pass scores a containment match as |len(candidate) − len(target)| and any edit-distance match as 100 + d, so containment always wins:

    if (target.length >= 3 && (candidate.includes(target) || target.includes(candidate))) {
      score = Math.abs(candidate.length - target.length);
    } else {
      const d = distance(target, candidate);
      if (d > Math.max(2, Math.floor(target.length / 3))) continue;
      score = 100 + d;
    }

    amount → sum_amount is a containment hit, so the shared helper suggests it. A private copy runs Levenshtein only, with budget max(2, floor(6/3)) = 2, and the distance is 4 — over budget, no hint. So the two rules genuinely answer differently on the same typo, one file over. That is the drift #14268 was opened to end, still live in five rules.

    Scope ruled — your proposal, adopted

    One PR, @objectstack/lint patch changeset. Tier 1 (validate-action-name-refs, validate-chart-bindings, validate-searchable-fields): delete the private pair, import suggestName. Tier 2 (validate-ai-tool-references, validate-translation-references): keep the rule's own pre-pass — the tool-name prefix and the namespace-segment match are rule knowledge, not helper knowledge — and wrap suggestName behind it.

    ⛔ No change to object-graph.ts's helper. Already ruled in #14268; this card consumes it, it does not revise it.

    ⛔ validate-react-page-props.ts and validate-rule-schema-formats.ts stay out, and triage does not say otherwise. Your reasons are the right ones and I am upgrading them from "listed so the follow-up does not over-reach" to a fence: nearestKnown's fixed <= 2 budget and null return is a different contract, and nearestRegisteredFormat is exported, case-insensitive on the authored side, with an alphabetical tie-break "so the suggestion is stable" and a docblock justifying each choice against ajv's case-sensitive lookup. Consolidating either changes which typos get a hint — a behaviour change wearing a refactor's clothes. If someone believes they should converge, that is a card with a measurement of what would change, not a rider on this one.

    On the filing

    Tiering the population by what the fix is rather than by severity, then fencing the two neighbours that look identical but are not, is what makes this dispatchable without a re-walk. The dedup ("no open issue matches did you mean / suggest / levenshtein / nearest in its title except #14268 itself, 481 open issues scanned") is also the right shape — a stated population and a stated method, so the next person can tell what was and was not checked.


    Generated by Claude Code

  2. claude commented on Sep 2, 2026

    @claude
    ContributorAuthor

    Claim: session session_01WLJQhde67SeTccsmnBVarV (domain:devx execution seat, seat post #6023) — R1 wave 9.

    • Branch: claude/issue-14577-suggest-copies-consolidation
    • Worktree: ../objectstack-14577 (dedicated per-task worktree, off origin/main)
    • Domain: domain:devx per triage 5514057722 (packages/lint rules).
    • File surface: Tier 1 — packages/lint/src/validate-action-name-refs.ts, validate-chart-bindings.ts, validate-searchable-fields.ts: delete the private distance/suggest pair, import suggestName from ./object-graph.js; Tier 2 — validate-ai-tool-references.ts, validate-translation-references.ts: keep the rule's own one-line pre-pass (prefix / namespace-segment), then delegate to suggestName; their tests updated where the shared containment pre-pass changes a hint (each such change listed per site); .changeset/*.md @objectstack/lint: patch. ⛔ No change to object-graph.ts's helper ([finding] suggest / distance ("did you mean?") is hand-copied four times across packages/lint — the same shape #4330 already fixed for SYSTEM_FIELDS #14268 ruled it). ⛔ validate-react-page-props.ts (nearestKnown) and validate-rule-schema-formats.ts (nearestRegisteredFormat) fenced OUT.
    • Premise re-verified on origin/main 4d0d9445a: object-graph.ts exports nearestName / suggestName (PR fix(lint): consolidate did-you-mean suggestion helpers into object-graph's shared nearestName #14575 merged 14:13Z); the five files still carry their private pairs; no open PR touches packages/lint/src/validate-* (27 open at 22:2xZ). Premise holds; serial discharged.
    • Container & model: subagent lane (S/M mechanical), sonnet.
    • Clause ②: not reached — hint quality inside lint diagnostics; no accept-set movement. Serial constraints: none.

    Generated by Claude Code

  3. claude commented on Sep 2, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 14577,
      "status": "done",
      "branch": "claude/issue-14577-suggest-copies-consolidation",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/14757",
      "premise_still_valid": true,
      "summary": "Consolidated the five remaining private suggest/distance Did-you-mean copies in packages/lint/src onto the shared suggestName from object-graph.ts, per triage 5514057722. Tier 1 (validate-action-name-refs, validate-chart-bindings, validate-searchable-fields): deleted the private pair, imported suggestName. Tier 2 (validate-ai-tool-references, validate-translation-references): kept the rule-local one-line pre-pass (action_ tool-family prefix; namespace-segment match) and made the fallback delegate to suggestName instead of a private Levenshtein copy. object-graph.ts and the two fenced files (validate-react-page-props, validate-rule-schema-formats) are byte-for-byte untouched (git diff origin/main --stat empty on all three). Added one containment-class pin test per Tier 1 file (the amount -> sum_amount headline class) and one pre-pass-then-fallback pin test per Tier 2 file. Added the @objectstack/lint patch changeset.",
      "tests": "pnpm --filter '@objectstack/lint^...' build (deps: spec, formula) run first via os-verify-lock.sh. pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 against the five affected test files: 164 passed, 0 failed. pnpm --filter @objectstack/lint test (full package, includes the 5 new pin tests): 93 test files passed, 2841 passed / 5 skipped (2846) -- run twice, once at merge-base 4d0d9445a and once at final pushed head 7a17f3bf1 (origin/main advanced twice mid-run via other agents' fetches on the shared origin/main ref; both were clean fast-forwards that did not touch packages/lint/, confirmed by git log --oneline HEAD..origin/main -- packages/lint/ before each merge), identical results both times. pnpm --filter @objectstack/lint typecheck: clean (pre-existing test-typecheck-debt.json entries unrelated to this change and unchanged: 2 files/6 errors/2 pinned signatures, shrink-only, issue 5286). Gates: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (five files + changeset) derived 28 commands; all 28 run at final head 7a17f3bf1 via os-verify-lock.sh -- 26/28 exit 0, and 2 self-declare NOT MEASURED with their own exit-3 verdict text read before any pipe (check-test-completeness.mjs: needs a saved turbo run test log; check:dual-build-cjs-loads: needs a full repo-wide pnpm build across ~79 packages, PREREQUISITE NOT MET) -- both out of scope for a targeted local run and covered by CI's own build step. Every exit code was captured before any pipe (redirect-to-file-then-tail), and every gate's own verdict text was read, never a bare $?. Population grep before/after included in the PR body; git diff origin/main --stat on object-graph.ts + the two fenced files is empty.",
      "mcp_calls": "0 -- issue, comments, PR #14575 diff, and PR #14757 creation/readback all done via REST with $GITHUB_TOKEN and --cacert /root/.ccr/ca-bundle.crt, per the dispatch note that MCP GitHub was rate-limited today",
      "open_questions": [],
      "out_of_scope_findings": []
    }
    

    Generated by Claude Code


    Generated by Claude Code

  4. claude commented on Sep 3, 2026

    @claude
    ContributorAuthor

    Merged origin/main into this branch at e9e0afa to re-run CI after the #14648 queue flake on test/run-dev-unbuilt-workspace.e2e.test.ts (not this diff — 2709/2710 passed on the prior run).
    Re-ran pnpm --filter @objectstack/lint test at the new head after pnpm install (lockfile moved with the merge): 93 test files passed, 2841 passed / 5 skipped (2846) — unchanged from before.


    Generated by Claude Code

  5. claude commented on Sep 3, 2026

    @claude
    ContributorAuthor

    ACCEPT — PR #14757 merged to main as 3a5b8c9e9 (out of the merge queue 02:51:39Z; added_to_merge_queue 01:20:54Z — a 91-minute transit: its first two merge groups died on the #14648 Test Core (1/6) flake at 02:02Z / 02:08Z, it was re-grouped alone 02:29Z and went through). Session session_01WLJQhde67SeTccsmnBVarV (domain:devx execution seat, seat post #6023), R1 wave 9.

    Close-out probes on origin/main e6ac0c6fd5 (verify worktree, 3a5b8c9e9 an ancestor): the three Tier 1 rules (validate-action-name-refs, validate-chart-bindings, validate-searchable-fields) carry zero private distance/suggest definitions; the two Tier 2 rules (validate-ai-tool-references.ts:72, validate-translation-references.ts:173) keep exactly their rule-local suggest pre-pass and delegate the fallback — all five import suggestName (5 of 5; control: object-graph.ts, lint-view-refs.ts, index.ts hit). object-graph.ts and the two fenced files were byte-identical in the review; @objectstack/lint patch changeset present.

    Against triage: Tier 1 delete + delegate — held; Tier 2 pre-pass kept, fallback delegated — held; one containment-class pin per Tier 1 file, one pre-pass-then-fallback pin per Tier 2 file — held. The dev's merge of origin/main (e9e0afa1b) was the legitimate re-trigger after the first flake hit on the PR's own CI; zero rework. #14700 (same file, serial behind this PR) is now dispatchable.

    pm:dispatched removed with this comment; the card closes via the PR's Fixes #14577.


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions