docs: WS-3 session 1b-i lint baseline audit report
Categorises remaining apps/app eslint problems after the Tier 1 + Tier 2 autofix and whitespace cleanup. Six buckets: - X. Tier 3 deferred (vite.config.ts, themeConfig.ts, vitest.config.ts) — 134 - A. Trivial-fix (second-pass autofix residue + unused imports) — 42 - B. Type safety (mostly no-explicit-any in vendored Vuexy code) — 34 - C. Style preference / rule-config — 8 - D. Vuetify / Vuexy idiom (ml-/pl- restricted-class) — 5 - E. Bug-shaped (security, isAxiosError, missing-return, fire-and-forget) — 8 Sum check: 134 + 42 + 34 + 8 + 5 + 8 = 231 ✓ Five open questions for Bert + Claude Chat decisions before 1b-ii: - Should src/@core/** + src/@layouts/** be in ignorePatterns? - vue/no-restricted-class regex scope (RTL-aware vs ml/pl only)? - Stance on vue/prefer-true-attribute-shorthand? - Bucket E.5 axios fire-and-forget pattern (void / .catch / disable)? - Decoupling pnpm lint from --fix? Lint baseline: 1451 → 231 this session, with Tier 3 (134) + non-fixable (66) + second-pass-residue (31) deferred to 1b-ii. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
521
dev-docs/WS-3-LINT-BASELINE-AUDIT-2026-04-29.md
Normal file
521
dev-docs/WS-3-LINT-BASELINE-AUDIT-2026-04-29.md
Normal file
@@ -0,0 +1,521 @@
|
||||
# WS-3 Lint baseline audit — apps/app
|
||||
|
||||
**Date:** 2026-04-29
|
||||
**Source:** `pnpm exec eslint . -c .eslintrc.cjs --ext .ts,.js,.cjs,.vue,.tsx,.jsx --no-fix`
|
||||
after WS-3 session 1b-i Tasks 1, 2, 3 (Tier 1 + Tier 2 autofix +
|
||||
trailing-whitespace cleanup).
|
||||
|
||||
## Counts
|
||||
|
||||
| Stage | Total | Errors | Warnings | Fixable |
|
||||
|-------|-------|--------|----------|---------|
|
||||
| Pre-1b-i (main, dd8430f) | 1451 | 919 | 532 | 1370 |
|
||||
| Post-Tier 1 (47bd533) | 422 | 408 | 14 | 354 |
|
||||
| Post-Tier 2 (a7eaf0f) | 246 | 232 | 14 | 180 |
|
||||
| Post-whitespace (4976b4e) | 231 | 217 | 14 | 165 |
|
||||
|
||||
Reduction: **1451 → 231** mechanical, **with Tier 3 deferred**.
|
||||
|
||||
The 165 still-fixable items break down as:
|
||||
- **134 in Tier 3 files** (vite.config.ts, themeConfig.ts,
|
||||
vitest.config.ts) — explicitly excluded from this session per
|
||||
the risk-tier policy.
|
||||
- **31 in Tier 1/2 paths** — second-pass-autofix residue (mainly
|
||||
24 `indent` errors in one file, plus `lines-around-comment`
|
||||
edge cases on Vue SFC `<script>`-tag-adjacent comments).
|
||||
|
||||
## Bucket summary
|
||||
|
||||
| Bucket | Count | Recommended disposition |
|
||||
|--------|------:|-------------------------|
|
||||
| X. Tier 3 deferred | 134 | 1b-ii, hand-reviewed per file |
|
||||
| A. Trivial-fix | 42 | 1b-ii, mechanical (second-pass --fix or simple delete) |
|
||||
| B. Type safety | 34 | 1b-ii, per-instance review |
|
||||
| C. Style / rule-config | 8 | 1b-ii, rule-level decisions |
|
||||
| D. Vuetify / Vuexy idiom | 5 | 1b-ii, per-pattern overrides |
|
||||
| E. Bug-shaped | 8 | Investigate as bugs |
|
||||
| **Sum** | **231** | |
|
||||
|
||||
Sum check: X(134) + A(42) + B(34) + C(8) + D(5) + E(8) = **231** ✓
|
||||
(matches total).
|
||||
|
||||
---
|
||||
|
||||
## Bucket X — Tier 3 deferred (134 items, all fixable)
|
||||
|
||||
All in three configuration files, all autofix-mechanical. Excluded
|
||||
from this session per the risk-tier policy because the autofix could
|
||||
in principle alter Vite plugin order, theme-token resolution, or
|
||||
vitest setup-hook ordering.
|
||||
|
||||
```
|
||||
apps/app/vite.config.ts — 91 items
|
||||
58 @typescript-eslint/quotes
|
||||
15 semi
|
||||
15 @typescript-eslint/semi
|
||||
2 arrow-parens
|
||||
1 curly
|
||||
|
||||
apps/app/themeConfig.ts — 42 items
|
||||
28 @typescript-eslint/quotes
|
||||
7 semi
|
||||
7 @typescript-eslint/semi
|
||||
|
||||
apps/app/vitest.config.ts — 1 item
|
||||
1 lines-around-comment
|
||||
```
|
||||
|
||||
**Recommendation for 1b-ii:** per-file diff review before applying
|
||||
`--fix`. Focus areas:
|
||||
- `vite.config.ts`: confirm no plugin-order changes (the autofix is
|
||||
pure quotes/semi/arrow-parens; in principle no semantic effect).
|
||||
Smoke `pnpm dev` and `pnpm build` after.
|
||||
- `themeConfig.ts`: confirm no theme-token reordering. Visually
|
||||
check the running app afterwards (login + dashboard).
|
||||
- `vitest.config.ts`: trivial — single `lines-around-comment` fix.
|
||||
|
||||
---
|
||||
|
||||
## Bucket A — Trivial-fix (42 items)
|
||||
|
||||
### A.1 Second-pass autofix residue (31 items, all fixable)
|
||||
|
||||
Items where Tier 1 / Tier 2 `--fix` ran but ESLint's default 10-pass
|
||||
cap left a cascade of dependent fixes unresolved. Re-running `--fix`
|
||||
on the same file resolves them (verified in Task 3 on
|
||||
`DefaultLayoutWithVerticalNav.vue`).
|
||||
|
||||
```
|
||||
By rule:
|
||||
24 indent (all in useTimeSlotDropdown.ts)
|
||||
3 vue/max-attributes-per-line (App.vue — outside Tier 1 glob)
|
||||
3 lines-around-comment (PortalLayout/PublicLayout/AppKpiCard — see A.2)
|
||||
1 (no rule) (VNodeRenderer.tsx — eslint-disable comment that's now unused)
|
||||
|
||||
By file:
|
||||
24 apps/app/src/composables/useTimeSlotDropdown.ts
|
||||
3 apps/app/src/App.vue
|
||||
1 apps/app/src/@layouts/components/VNodeRenderer.tsx
|
||||
1 apps/app/src/components/AppKpiCard.vue
|
||||
1 apps/app/src/layouts/PortalLayout.vue
|
||||
1 apps/app/src/layouts/PublicLayout.vue
|
||||
```
|
||||
|
||||
**Recommendation for 1b-ii:**
|
||||
- Re-run `pnpm exec eslint <file> --fix` on each (single command per
|
||||
file is safest) and verify `git diff --check` clean afterwards.
|
||||
- For App.vue: it's at `src/App.vue`, missed by Tier 1's
|
||||
`src/{components,pages,layouts,views}/**/*.vue` glob. Fix by
|
||||
including `src/App.vue` in the Tier 1 paths in the next pass.
|
||||
- For VNodeRenderer.tsx (`src/@layouts/`): vendored Vuexy code; the
|
||||
fix is just removing an `eslint-disable` directive that's no longer
|
||||
needed. Low risk.
|
||||
|
||||
### A.2 Vue-SFC `lines-around-comment` autofix gap (3 of the 31 above)
|
||||
|
||||
`PortalLayout.vue`, `PublicLayout.vue`, and `AppKpiCard.vue` show
|
||||
`error Expected line before comment lines-around-comment` at line 2:1
|
||||
because the comment block sits immediately after `<script setup
|
||||
lang="ts">` on line 1. The rule has a fixer and the files were inside
|
||||
the Tier 1 glob, but the fix did not apply — likely a vue-eslint-parser
|
||||
boundary quirk (the script-tag opening doesn't register as a "block
|
||||
start" the way a `{` does for `allowBlockStart: true`).
|
||||
|
||||
**Recommendation for 1b-ii:** either (a) hand-add a blank line before
|
||||
the comment in each file, or (b) extend `.eslintrc.cjs` with an
|
||||
overrides block that disables `lines-around-comment` for `*.vue` files
|
||||
(or sets `allowBlockStart: true` more aggressively).
|
||||
|
||||
### A.3 Unused-imports / unused-vars (10 items, manual)
|
||||
|
||||
Trivial deletes. Each item below: file:line, the unused identifier,
|
||||
and recommended action.
|
||||
|
||||
```
|
||||
apps/app/src/components/layout/OrganisationSwitcher.vue:30:10
|
||||
'toggleMenu' is defined but never used.
|
||||
→ Delete the destructured `toggleMenu` from the line 30 destructure.
|
||||
(Both unused-imports/no-unused-vars and @typescript-eslint/no-unused-vars
|
||||
flag the same identifier — count of 2 above is the same identifier.)
|
||||
|
||||
apps/app/src/components/sections/CreateShiftDialog.vue:40:9
|
||||
'scenario' is assigned a value but never used.
|
||||
→ Delete the `const scenario = ...` line.
|
||||
(Two-rule overlap — same identifier.)
|
||||
|
||||
apps/app/src/pages/events/[id]/time-slots/index.vue:242:27
|
||||
'event' is defined but never used. (vue/no-unused-vars)
|
||||
→ Replace with `_event` in the slot scope, or remove from destructure.
|
||||
|
||||
apps/app/src/pages/organisation/companies.vue:8:7
|
||||
'authStore' is assigned a value but never used.
|
||||
→ Delete `const authStore = useAuthStore()` on line 8.
|
||||
(Two-rule overlap.)
|
||||
|
||||
apps/app/src/pages/platform/activity-log/index.vue:11:7
|
||||
'searchDebounced' is assigned a value but never used.
|
||||
→ Delete the line. (Two-rule overlap.)
|
||||
|
||||
apps/app/src/components/persons/PersonDetailPanel.vue:77:58
|
||||
Unnecessary { after 'if' condition. (curly)
|
||||
→ Strip the redundant brace block (one-statement if).
|
||||
```
|
||||
|
||||
Note on the rule overlap: `unused-imports/no-unused-vars` and
|
||||
`@typescript-eslint/no-unused-vars` both fire on the same identifier
|
||||
in 4 of the 5 cases. Removing the identifier will resolve both per
|
||||
report — the **42 Bucket A total** correctly counts these as 2 items
|
||||
each (matching ESLint's actual emit). Effective unique deletes: 6.
|
||||
|
||||
---
|
||||
|
||||
## Bucket B — Type safety (34 items)
|
||||
|
||||
### B.1 `@typescript-eslint/no-explicit-any` (23 items)
|
||||
|
||||
Per CLAUDE.md frontend rule and `.eslintrc.cjs` line 62, this is
|
||||
**erroneously committed** to `error` for the project. Yet 23 instances
|
||||
exist, predominantly in `src/@core/` and `src/@layouts/` (the vendored
|
||||
Vuexy code) and a few in our own files.
|
||||
|
||||
**Distribution:**
|
||||
- `src/@core/**`: 13 (DropZone.vue, I18n.vue, Notifications.vue,
|
||||
AppDateTimePicker.vue, createUrl.ts, useCookie.ts ×4,
|
||||
apexCharConfig.ts ×3, types.ts)
|
||||
- `src/@layouts/**`: 7 (index.ts ×1, types.ts ×6)
|
||||
- `src/layouts/blank.vue`: 1 (line 9 — `ref<any>`)
|
||||
- `src/layouts/default.vue`: 1 (line 21 — `ref<any>`)
|
||||
- `src/layouts/components/NavSearchBar.vue`: 1
|
||||
|
||||
The 13 + 7 = 20 inside `@core` and `@layouts` are vendored Vuexy
|
||||
template code. CLAUDE.md says: "Never: TypeScript `any` type" — but
|
||||
this rule was clearly intended for OUR code, not the vendored copy
|
||||
of Vuexy that we don't maintain.
|
||||
|
||||
**Recommendation for 1b-ii — judgment call needed:**
|
||||
- **Option a** (preferred for honesty): add `src/@core/**` and
|
||||
`src/@layouts/**` to `ignorePatterns` in `.eslintrc.cjs`. This is
|
||||
the same pragma we already apply to `src/plugins/iconify/*.js`
|
||||
(vendored). Reduces the 23 down to 3 (blank.vue, default.vue,
|
||||
NavSearchBar.vue) — all `ref<any>` for the `AppLoadingIndicator`
|
||||
template ref. Those 3 should be properly typed.
|
||||
- **Option b**: keep the rule on, narrow the 20 vendored items to
|
||||
proper types one by one. This is real work and risks breaking the
|
||||
vendored components' contract.
|
||||
- **Option c**: per-file `// eslint-disable-next-line` markers on the
|
||||
vendored `any`s. Noisy but localised.
|
||||
|
||||
The `.eslintrc.cjs` already says (lines 60-62):
|
||||
```
|
||||
// Project rule (CLAUDE.md frontend rules): no `any`. Override the
|
||||
// Vuexy reference (which sets this off) — Crewli's stricter posture.
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
```
|
||||
So the choice was deliberate when the rule was set. Option a is the
|
||||
cleanest re-statement: "the ban applies to our code, not vendored."
|
||||
|
||||
### B.2 `@typescript-eslint/no-use-before-define` (7 items)
|
||||
|
||||
All in `src/components/shifts/ShiftDetailPanel.vue` (lines 249-261).
|
||||
The pattern: handler functions defined at the top of `<script setup>`
|
||||
reference `cancellingAssignment` and `isCancelDialogOpen` refs
|
||||
declared further down in the file.
|
||||
|
||||
```
|
||||
apps/app/src/components/shifts/ShiftDetailPanel.vue:249:3
|
||||
'cancellingAssignment' was used before it was defined.
|
||||
[...6 more in lines 250-261]
|
||||
```
|
||||
|
||||
This is idiomatic Composition API: declare reactive state at the top,
|
||||
then handlers, then watchers. The rule fires because handler
|
||||
*functions* are hoisted but the `ref(...)` call results aren't.
|
||||
|
||||
**Recommendation for 1b-ii:** either (a) reorder the file to declare
|
||||
the refs before the functions that reference them (mechanical, same
|
||||
file only), or (b) add an override clause to `.eslintrc.cjs` allowing
|
||||
this pattern in `<script setup>`. Option a is simpler and one-file.
|
||||
|
||||
### B.3 `@typescript-eslint/no-shadow` (1 item)
|
||||
|
||||
```
|
||||
apps/app/src/stores/useImpersonationStore.ts:119:11
|
||||
'stored' is already declared in the upper scope on line 18 column 9.
|
||||
```
|
||||
|
||||
Local `stored` shadowing an outer `stored` — usually fine but
|
||||
defensive practice is to rename. **Recommendation:** rename the
|
||||
inner `stored` to `storedSnapshot` or similar.
|
||||
|
||||
### B.4 `camelcase` (3 items, same identifier)
|
||||
|
||||
```
|
||||
apps/app/src/composables/api/useFormSchemas.ts:97:30
|
||||
apps/app/src/composables/api/useFormSchemas.ts:99:17
|
||||
apps/app/src/composables/api/useFormSchemas.ts:99:36
|
||||
Identifier 'confirmed_name' is not in camel case.
|
||||
```
|
||||
|
||||
Context (lines 95-100):
|
||||
```ts
|
||||
return useMutation({
|
||||
mutationFn: async ({ id, confirmed_name }: { id: string; confirmed_name?: string }) => {
|
||||
await apiClient.delete(`/organisations/${orgId.value}/forms/schemas/${id}`, {
|
||||
params: confirmed_name ? { confirmed_name } : undefined,
|
||||
})
|
||||
},
|
||||
```
|
||||
|
||||
`confirmed_name` is an **API query parameter** that the backend reads
|
||||
in snake_case. CLAUDE.md says: "DB columns: snake_case" and
|
||||
"TypeScript / JS variables: camelCase". The boundary is at the API
|
||||
layer — the query-param key MUST be `confirmed_name` (snake) on the
|
||||
wire. The local TypeScript variable `confirmed_name` matches the
|
||||
wire format for symmetry (rather than `confirmedName` aliased).
|
||||
|
||||
**Recommendation for 1b-ii — judgment call:** rename the local
|
||||
variable to `confirmedName` and keep the wire key snake_case
|
||||
(`{ confirmed_name: confirmedName }`). Three rule violations resolved
|
||||
with no behaviour change.
|
||||
|
||||
---
|
||||
|
||||
## Bucket C — Style / rule-config (8 items)
|
||||
|
||||
```
|
||||
apps/app/src/components/form-failures/DismissFailureDialog.vue:43:3
|
||||
sonarjs/prefer-single-boolean-return
|
||||
"Replace this if-then-else flow by a single return statement."
|
||||
|
||||
apps/app/src/components/form-failures/FormFailureDetail.vue:44:5
|
||||
no-void
|
||||
"Expected 'undefined' and instead saw 'void'."
|
||||
|
||||
apps/app/src/components/sections/AssignShiftDialog.vue:41:3
|
||||
sonarjs/prefer-single-boolean-return
|
||||
|
||||
apps/app/src/components/sections/SectionsShiftsPanel.vue:333:11
|
||||
vue/prefer-true-attribute-shorthand
|
||||
"Boolean prop with 'true' value should be written in shorthand form."
|
||||
|
||||
apps/app/src/components/shifts/AssignPersonDialog.vue:120:5
|
||||
sonarjs/no-collapsible-if
|
||||
"Merge this if statement with the nested one."
|
||||
apps/app/src/components/shifts/AssignPersonDialog.vue:125:5
|
||||
sonarjs/no-collapsible-if
|
||||
|
||||
apps/app/src/pages/events/[id]/settings/registration-fields.vue:335:13
|
||||
vue/prefer-true-attribute-shorthand
|
||||
|
||||
apps/app/src/stores/useImpersonationStore.ts:105:12
|
||||
sonarjs/no-collapsible-if
|
||||
```
|
||||
|
||||
All readable-style preferences. **Recommendation for 1b-ii:** apply
|
||||
the suggested rewrite per item; all are local single-file edits with
|
||||
no behaviour change.
|
||||
|
||||
---
|
||||
|
||||
## Bucket D — Vuetify / Vuexy idiom (5 items)
|
||||
|
||||
```
|
||||
apps/app/src/components/persons/PersonDetailPanel.vue:272:25
|
||||
vue/no-restricted-class — "'ml-1' class is not allowed."
|
||||
|
||||
apps/app/src/components/sections/SectionsShiftsPanel.vue:357:25
|
||||
vue/no-restricted-class — "'ml-1' class is not allowed."
|
||||
|
||||
apps/app/src/components/shifts/AssignPersonDialog.vue:461:29
|
||||
vue/no-restricted-class — "'pl-4' class is not allowed."
|
||||
|
||||
apps/app/src/components/shifts/AssignPersonDialog.vue:475:23
|
||||
vue/no-restricted-class — "'ml-auto' class is not allowed."
|
||||
|
||||
apps/app/src/components/shifts/AssignPersonDialog.vue:500:27
|
||||
vue/no-restricted-class — "'ml-1' class is not allowed."
|
||||
```
|
||||
|
||||
The rule (`.eslintrc.cjs` line 176) bans `m{l,r}-` and `p{l,r}-`
|
||||
Tailwind utility-class names because the project is Vuetify-only
|
||||
("Use Vuetify utility class … never custom CSS" per CLAUDE.md).
|
||||
Vuetify's spacing utilities are `ms-*`, `me-*`, `ps-*`, `pe-*` (start/
|
||||
end LTR-aware), or `mx-*`/`px-*` for both sides.
|
||||
|
||||
**Recommendation for 1b-ii:** rewrite the 5 occurrences:
|
||||
- `ml-1` → `ms-1`
|
||||
- `pl-4` → `ps-4`
|
||||
- `ml-auto` → `ms-auto`
|
||||
|
||||
Vuetify supports both `m{l,r,s,e}-N` directly; the project preference
|
||||
per the rule is the start/end form. Mechanical 5-line change across
|
||||
3 files.
|
||||
|
||||
---
|
||||
|
||||
## Bucket E — Bug-shaped (8 items)
|
||||
|
||||
### E.1 `import/no-named-as-default-member` × 2 — `EventTabsNav.vue`
|
||||
|
||||
```
|
||||
apps/app/src/components/events/EventTabsNav.vue:53:8
|
||||
apps/app/src/components/events/EventTabsNav.vue:76:9
|
||||
"Caution: `axios` also has a named export `isAxiosError`. Check if
|
||||
you meant to write `import { isAxiosError } from 'axios'` instead."
|
||||
```
|
||||
|
||||
The file accesses `axios.isAxiosError(...)` via the default export
|
||||
namespace; ESLint flags that there's a named export of the same name.
|
||||
Both forms work, but the named import is the modern axios pattern and
|
||||
narrower (no full default-axios import overhead).
|
||||
|
||||
**Recommendation for 1b-ii:** change `import axios from 'axios'` to
|
||||
`import { isAxiosError } from 'axios'` (or add the named import
|
||||
alongside the default). Two-line edit. Zero behaviour change.
|
||||
|
||||
### E.2 `vue/component-api-style` × 1 — vitest spec
|
||||
|
||||
```
|
||||
apps/app/src/composables/api/__tests__/useFormFailures.spec.ts:38:5
|
||||
"Options API is not allowed in your project. `render` function is
|
||||
part of the Options API. Use Composition API instead."
|
||||
```
|
||||
|
||||
A test mock uses Options-API `render` function. CLAUDE.md frontend
|
||||
rules: "Always `<script setup lang='ts'>` — never the Options API".
|
||||
**Recommendation for 1b-ii:** rewrite the mock as Composition API,
|
||||
or add a per-spec-file override.
|
||||
|
||||
### E.3 `vue/return-in-computed-property` × 1 — useTimeSlotDropdown
|
||||
|
||||
```
|
||||
apps/app/src/composables/useTimeSlotDropdown.ts:80:32
|
||||
"Expected to return a value in computed function."
|
||||
```
|
||||
|
||||
A `computed` callback is missing a return path on at least one
|
||||
branch — a real bug-class. **Recommendation for 1b-ii:** read line 80
|
||||
of `useTimeSlotDropdown.ts` and ensure every code path returns.
|
||||
|
||||
### E.4 `vue/no-template-target-blank` × 1 — security
|
||||
|
||||
```
|
||||
apps/app/src/pages/organisation/index.vue:342:21
|
||||
"Using target='_blank' without rel='noopener noreferrer' is a
|
||||
security risk."
|
||||
```
|
||||
|
||||
External anchor with `target="_blank"` and no `rel="noopener"` —
|
||||
classic reverse-tabnabbing exposure. **Recommendation:** add
|
||||
`rel="noopener noreferrer"` to the anchor.
|
||||
|
||||
### E.5 `promise/no-promise-in-callback` × 3 — `axios.ts`
|
||||
|
||||
```
|
||||
apps/app/src/lib/axios.ts:42:12
|
||||
apps/app/src/lib/axios.ts:61:7
|
||||
apps/app/src/lib/axios.ts:73:7
|
||||
"Avoid using promises inside of callbacks."
|
||||
```
|
||||
|
||||
In each case the response interceptor reaches into a dynamic `import()`
|
||||
to lazy-load a store, then chains `.then()`. Pattern:
|
||||
|
||||
```ts
|
||||
import('@/stores/useImpersonationStore').then(({ useImpersonationStore }) => {
|
||||
const impersonationStore = useImpersonationStore()
|
||||
impersonationStore.clearState()
|
||||
window.location.href = '/platform'
|
||||
})
|
||||
```
|
||||
|
||||
The lint rule fires because the interceptor itself is a callback and
|
||||
the promise inside isn't returned/awaited. In practice the
|
||||
interceptor doesn't need to wait for the store dynamic-import (the
|
||||
side effect — set window.location — is fire-and-forget). It's not a
|
||||
real bug, but the rule is correctly identifying that errors inside
|
||||
those `.then()` blocks would be silently swallowed.
|
||||
|
||||
**Recommendation for 1b-ii — judgment call:**
|
||||
- **Option a:** wrap each in a `void` to mark the fire-and-forget
|
||||
intent: `void import('@/stores/...').then(...)`. Tells ESLint and
|
||||
future-readers that the promise discard is intentional.
|
||||
- **Option b:** add `.catch(err => console.error(...))` to each so
|
||||
errors aren't swallowed.
|
||||
- **Option c:** disable the rule for `lib/axios.ts` only — but loses
|
||||
defence everywhere else.
|
||||
|
||||
Option b is most defensive; option a is most honest about current
|
||||
behaviour. Both are 3-line single-file edits.
|
||||
|
||||
---
|
||||
|
||||
## Action plan for session 1b-ii
|
||||
|
||||
| Step | Bucket | Effort | Risk | Notes |
|
||||
|------|--------|--------|------|-------|
|
||||
| 1. Tier 3 hand-review + autofix | X | 1-2h | medium | Smoke `pnpm dev` + `pnpm build` after vite.config.ts |
|
||||
| 2. Bucket A second-pass autofix on Tier 1/2 paths | A.1 | trivial | low | Rerun `--fix` on the 6 listed files; verify `git diff --check` |
|
||||
| 3. Bucket A unused-imports/vars manual deletes | A.3 | trivial | low | 6 unique deletes |
|
||||
| 4. Bucket A `lines-around-comment` Vue SFC fix | A.2 | trivial | low | Either hand-add blank lines or override rule for `*.vue` |
|
||||
| 5. Bucket B `no-explicit-any` ignore-patterns decision | B.1 | trivial config | low | Likely add @core/** + @layouts/** to ignorePatterns |
|
||||
| 6. Bucket B `no-explicit-any` real fixes (3 in our code) | B.1 | low | low | blank.vue / default.vue / NavSearchBar.vue refs |
|
||||
| 7. Bucket B `no-use-before-define` reorder | B.2 | low | low | Reorder ShiftDetailPanel.vue script setup |
|
||||
| 8. Bucket B `no-shadow` rename | B.3 | trivial | low | useImpersonationStore.ts |
|
||||
| 9. Bucket B `camelcase` rename | B.4 | low | low | useFormSchemas.ts — 3-line edit |
|
||||
| 10. Bucket C style fixes | C | low | low | 8 mechanical rewrites |
|
||||
| 11. Bucket D Vuetify class renames | D | low | low | ml-/pl-/etc → ms-/ps-/etc, 5 occurrences |
|
||||
| 12. Bucket E.1 isAxiosError import | E.1 | trivial | low | One file, two lines |
|
||||
| 13. Bucket E.2 spec rewrite to Composition API | E.2 | low | low | Test only |
|
||||
| 14. Bucket E.3 fix missing return in computed | E.3 | low | medium | Real semantic bug — verify with smoke |
|
||||
| 15. Bucket E.4 security: add rel="noopener" | E.4 | trivial | low | One attribute add |
|
||||
| 16. Bucket E.5 axios fire-and-forget pattern | E.5 | low | low | 3 spots, design choice between options a/b/c |
|
||||
|
||||
After 1b-ii completes, the lint count should drop from 231 to a
|
||||
small number (single digits, mostly Bucket-B-judgment-call leftovers
|
||||
if any — depending on the @core/@layouts ignorePatterns decision).
|
||||
|
||||
---
|
||||
|
||||
## Open questions for Bert + Claude Chat
|
||||
|
||||
1. **Vendored Vuexy code under `src/@core/` and `src/@layouts/`** —
|
||||
should it be included in `.eslintrc.cjs`'s `ignorePatterns`? 20 of
|
||||
the 23 `no-explicit-any` items would silently disappear, plus a
|
||||
small number of other rules. The trade-off: future drift in those
|
||||
directories goes uncaught. Counter-trade-off: spending time
|
||||
correcting `any`s in code we copy-pasted is low value and high
|
||||
churn.
|
||||
|
||||
2. **`vue/no-restricted-class` regex `/^(p|m)(l|r)-/`** — the rule
|
||||
bans both `mr-` and `ml-` (and `pr-`, `pl-`). Vuetify supports
|
||||
`mr-N` natively (it's not just a Tailwind name); the migration
|
||||
to `me-N` is for LTR/RTL flexibility. Confirm this is the
|
||||
intended ban, or relax it to `pl/ml` only if RTL isn't a
|
||||
project goal.
|
||||
|
||||
3. **Bucket C `vue/prefer-true-attribute-shorthand`** — the rule
|
||||
prefers `<comp prop>` over `<comp :prop="true">` for boolean
|
||||
props. Some teams find the explicit form clearer when it
|
||||
distinguishes from omitted. Confirm preference.
|
||||
|
||||
4. **Bucket E.5 axios callback-promises** — pick option a/b/c per
|
||||
above before 1b-ii starts. Option b (`.catch`) is the most
|
||||
defensive but introduces error-handling code; option a (`void`)
|
||||
is the most honest about today's behaviour.
|
||||
|
||||
5. **`pnpm lint` script in `package.json`** — currently aliased to
|
||||
`eslint . --fix`. After 1b-ii lands a clean baseline, consider
|
||||
splitting into `pnpm lint` (no-fix) and `pnpm lint:fix` (with
|
||||
--fix). The current aliasing is what made the original 105
|
||||
reference number wrong (someone ran `pnpm lint` and reported
|
||||
the post-fix remainder).
|
||||
|
||||
---
|
||||
|
||||
## Artifacts
|
||||
|
||||
- `/tmp/eslint-pre.json` — pre-1b-i baseline (1451 problems)
|
||||
- `/tmp/eslint-current.json` — post-Task-3 state (231 problems)
|
||||
|
||||
Both retained on the dev machine for 1b-ii reference. Not committed.
|
||||
Reference in New Issue
Block a user