25 deferred findings (1 P1 + 11 P2 + 14 P3) → 4 task batches: 1. P1 Pa11y scope migration to live Vue app 2. P2 dead code + dev hygiene (knip findings + DemoSeeder + schema header) 3. P2 coverage debt (ReminderDialog + AdminLayout + api/admin via TDD) 4. P3 tooling + structural mini-fixes Plan: docs/superpowers/plans/2026-05-14-audit3-deferred-fixes.md Source audit: docs/superpowers/audits/2026-05-14-portal-full-audit-report.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
31 KiB
Audit #3 Deferred Fixes Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use
superpowers:subagent-driven-development(recommended) orsuperpowers:executing-plansto implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.
Goal: Close all 25 deferred findings from Portal Full Audit #3 (report) — 1 P1 + 11 P2 + 14 P3 — without regressions.
Architecture: Tasks 1→2→3→4 sequentially (each isolates the surface it modifies). Inside Task 3 — 3 parallel subagent dispatches (one per coverage target — fully isolated). Atomic commit per logical change. Final regression suite (Pest --parallel + Vitest + Vite build + gitleaks + lychee) gates push.
Tech Stack: Laravel 13.7 + Vue 3.5 + Vuetify 3.12 + Vitest 4.1 + @vue/test-utils + Pest 4.7 + pa11y-ci 4.1 + Playwright MCP + Lefthook + GitHub Actions
Branch: work directly on main (current 🟢 GREEN baseline f9d2452); each commit small + atomic; push at the very end after Phase 14-style regression check.
Out of scope: Sentry SDK install (blocked by Б-1), advisory TODO cleanup at Б-1 closure, F-BUN-02/03 Vuetify-structural (carryforward, no fix path until upstream change), F-COV-04..07 P3 (acknowledged as observational — router/index.ts 24+ lazy factories inflate denominator, no real fix; Vuetify plugin needs visual tests).
Task 1 — P1 Pa11y scope migration to live Vue app
Why: Pa11y config указывает на liderra_v8_handoff/concepts/v8_*.html (handoff prototypes), не на live Vue. Audit #2 baseline «0 errors» был неточен — реальный портал НЕ покрыт. Сегодня axe-core via Playwright показал 0 production a11y issues, но Pa11y нужно перевести на актуальную мишень для CI safety net.
Files:
- Create:
app/.github/workflows/a11y.yml - Create:
pa11y-handoff.config.json(historical handoff baseline, opt-in viaa11y:handoff) - Modify:
pa11y.config.json(live Vue app URLs) - Modify:
package.json(adda11y:handoffscript; ensurea11yscript unchanged invocation but new target) - Reference:
app/routes/web.php(SPA routes catalogue: 26Route::view+ fallback)
Step 1.1 — Read current handoff baseline & freeze as historical config
- Read
pa11y.config.jsonto capture exact handoff URL list (3 entries: v8_login.html, v8_dashboard.html, v8_deals.html). - Copy that content (with same
defaultsblock) to a new filepa11y-handoff.config.json.
cp pa11y.config.json pa11y-handoff.config.json
Expected: pa11y-handoff.config.json exists, contains 3 handoff URLs verbatim.
Step 1.2 — Rewrite pa11y.config.json to target live Vue app (guest pages — MVP scope)
- Replace
pa11y.config.jsoncontents with the live-app config below. Authenticated pages deferred to Step 1.7 (require Pa11yactionslogin flow — adds complexity; guest pages first).
{
"defaults": {
"standard": "WCAG2AA",
"timeout": 30000,
"wait": 1500,
"hideElements": ".js-skip-a11y, [data-a11y-skip], .dev-index-badge, .dev-index-num, .v-overlay-container",
"ignore": [
"warning",
"notice"
],
"chromeLaunchConfig": {
"args": [
"--no-sandbox",
"--disable-dev-shm-usage"
]
},
"viewport": {
"width": 1440,
"height": 900
}
},
"urls": [
{
"url": "http://localhost:8000/login",
"screenCapture": "./bin/a11y-screenshots/live-01-login.png"
},
{
"url": "http://localhost:8000/register",
"screenCapture": "./bin/a11y-screenshots/live-02-register.png"
},
{
"url": "http://localhost:8000/forgot",
"screenCapture": "./bin/a11y-screenshots/live-03-forgot.png"
},
{
"url": "http://localhost:8000/2fa",
"screenCapture": "./bin/a11y-screenshots/live-04-twofactor.png"
},
{
"url": "http://localhost:8000/recovery",
"screenCapture": "./bin/a11y-screenshots/live-05-recovery.png"
},
{
"url": "http://localhost:8000/403",
"screenCapture": "./bin/a11y-screenshots/live-06-403.png"
},
{
"url": "http://localhost:8000/500",
"screenCapture": "./bin/a11y-screenshots/live-07-500.png"
}
]
}
Rationale on hideElements: scoping out (a) .dev-index-badge + .dev-index-num — TEMPORARY DevIndexBadge feature (project_dev_indices.md — заказчик «уберём в конечном релизе»); (b) .v-overlay-container — Vuetify portal container is empty when overlays closed but axe-core/Pa11y flags it as missing region landmark — known Vuetify structural pattern.
Step 1.3 — Add a11y:handoff script + ensure a11y invokes new config
- Modify
package.json— add new script + keep existinga11yinvocation unchanged (it already points topa11y.config.jsonwhich we just repointed).
Apply edit (in package.json scripts block, insert after current a11y line):
"a11y": "pa11y-ci --config pa11y.config.json",
"a11y:handoff": "pa11y-ci --config pa11y-handoff.config.json",
Step 1.4 — Start dev-server, run live baseline locally
- In one terminal, start dev-server:
cd app
php artisan serve --port=8000
- In second terminal, ensure tenant DB is seeded (DemoSeeder will be auto-run after Task 2.4, for now run manually if Step 1 executes first):
cd app
php artisan migrate:fresh --seed
php artisan db:seed --class=DemoSeeder
- In third terminal, run Pa11y:
npm run a11y
Expected: 7 URLs scanned. Capture exit code + per-URL violation count.
Step 1.5 — Resolve violations: either fix code OR document explicit ignore
- If
color-contrastviolations on auth pages → fix inapp/resources/js/layouts/AuthLayout.vueor specific view CSS (likely none expected; brand palette OKLCH-tested). - If structural violations (landmarks missing on guest pages) → fix in respective
views/auth/*View.vue(add<main>/<header>semantics if missing). - Any remaining unfixable (Vuetify structural) — add to
pa11y.config.jsonper-URLignorearray, with comment in JSON ref-document or inline if format allows. Note: JSON doesn't allow comments — track ignored selectors indocs/audit-baseline-pa11y.md(Step 1.6).
Step 1.6 — Document baseline + ignore decisions
- Create
docs/audit-baseline-pa11y.mdcapturing:- Date of first live baseline
- Per-URL violation summary at baseline
- Per-violation: status (fixed / ignored-permanent / ignored-temp DevIndexBadge / ignored-structural Vuetify) + reference
# Pa11y Live Baseline — YYYY-MM-DD
> First live-Vue baseline after Pa11y scope migration (Audit #3 P1 closure).
## URLs scanned
| URL | Violations baseline | Status |
|---|---|---|
| /login | 0 | ✅ clean |
| /register | 0 | ✅ clean |
| ... | | |
## Ignored selectors (rationale)
| Selector | Why ignored |
|---|---|
| `.dev-index-badge`, `.dev-index-num` | TEMPORARY DevIndexBadge feature; removed at final release per `project_dev_indices.md` |
| `.v-overlay-container` | Vuetify portal container — empty when overlays closed; structural pattern, not a real a11y issue |
Step 1.7 — Add CI workflow (.github/workflows/a11y.yml)
- Create
app/.github/workflows/a11y.ymlto run Pa11y in CI with dev-server lifecycle:
name: Accessibility (Pa11y live)
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install JS deps
run: npm ci
- name: Install PHP deps
working-directory: app
run: composer install --no-progress --no-interaction --prefer-dist
- name: Setup database
working-directory: app
run: |
cp .env.example .env
php artisan key:generate
php artisan migrate:fresh --seed
php artisan db:seed --class=DemoSeeder
- name: Start dev-server
working-directory: app
run: php artisan serve --port=8000 &
- name: Wait for dev-server
run: npx wait-on http://localhost:8000
- name: Run Pa11y
run: npm run a11y
Note: wait-on may not be installed; add to devDependencies if needed. Alternatively, busy-loop with curl in CI script.
Step 1.8 — Verify CI workflow locally (act if available, otherwise visual review only)
git statusshows: pa11y.config.json modified, pa11y-handoff.config.json new, package.json modified, docs/audit-baseline-pa11y.md new, app/.github/workflows/a11y.yml new.- Run final Pa11y locally:
npm run a11y→ exit 0. - Run historical handoff:
npm run a11y:handoff→ still scans 3 handoff URLs (unchanged baseline — 10 contrast violations).
Step 1.9 — Commit
git add pa11y.config.json pa11y-handoff.config.json package.json \
docs/audit-baseline-pa11y.md app/.github/workflows/a11y.yml
git commit -m "$(cat <<'EOF'
feat(a11y): migrate Pa11y scope from handoff prototypes to live Vue app
Closes F-A11Y-PA11Y-SCOPE-01 (Audit #3 sole P1).
- pa11y.config.json now targets http://localhost:8000/<route> for 7 guest pages
(login, register, forgot, 2fa, recovery, 403, 500)
- pa11y-handoff.config.json preserves historical 3-URL handoff baseline
(opt-in via `npm run a11y:handoff`)
- DevIndexBadge + Vuetify .v-overlay-container hidden globally via
hideElements (TEMPORARY feature + structural Vuetify pattern)
- New `.github/workflows/a11y.yml` runs Pa11y in CI with dev-server lifecycle
(php artisan serve + wait-on http://localhost:8000)
- New docs/audit-baseline-pa11y.md captures first live baseline + ignore rationale
Related: Audit #3 report, findings.md F-A11Y-PA11Y-SCOPE-01.
EOF
)"
Task 2 — P2 dead code + dev hygiene batch
Why: 5 documented P2 deferred items — все низкорисковые, удаление/добавление без изменения публичного API.
Files:
- Delete:
app/resources/js/views/admin/AdminPlaceholderView.vue - Modify:
app/package.json(removeconcurrently) - Modify:
app/resources/js/api/admin.ts(remove 4 unused exported types) - Modify:
app/resources/js/api/notifications.ts(remove 1 unused type) - Modify:
app/resources/js/api/reports.ts(remove 5 unused types) - Modify:
app/resources/js/composables/mockBilling.ts(remove 1 unused type) - Modify:
app/resources/js/composables/useStatusPill.ts(remove 1 unused type) - Modify:
app/database/seeders/DatabaseSeeder.php(env-conditionalDemoSeeder::class) - Modify:
db/schema.sql(header line 4: «62 базовые таблицы» → «63 базовые таблицы + 12 партиций»)
Step 2.1 — Confirm AdminPlaceholderView is truly unreferenced + delete
- Grep one more time before delete (paranoia):
cd app
git grep -n "AdminPlaceholderView"
Expected: matches only inside AdminPlaceholderView.vue itself (no router/import references).
- Delete the file:
git rm app/resources/js/views/admin/AdminPlaceholderView.vue
Step 2.2 — Remove concurrently from app/package.json
- Verify in
app/package.jsonline ~27 (devDependencies block) thatconcurrently@^9.0.1is present. - Verify no script invokes
concurrently:
grep -n concurrently app/package.json
Expected: only the devDependencies entry, no scripts.
- Uninstall:
cd app
npm uninstall concurrently
Expected: package.json + package-lock.json updated; concurrently gone from node_modules.
Step 2.3 — Remove 12 unused exported types
For each file, locate the named export (search by export type <Name> or export interface <Name>) and delete that block. If the type is part of a barrel or used in a re-export, also remove the re-export.
app/resources/js/api/admin.ts: removeAdminTenantsStats,ApiTenantMetrics,ApiAdminBillingSummary,ApiAdminIncidentsSummary.app/resources/js/api/notifications.ts: removeNotificationEvent.app/resources/js/api/reports.ts: removeApiReportType,ApiReportFormat,ApiReportParameters,ReportCounts,ReportQuota.app/resources/js/composables/mockBilling.ts: removeTxType.app/resources/js/composables/useStatusPill.ts: removeStatusPillSlug.
After each file's edit, run:
cd app
npx vue-tsc --noEmit
Expected: 0 type errors. If errors appear (a removed type is actually used internally), restore the type and document in finding.
Step 2.4 — Make DemoSeeder auto-runnable but only in non-production
- Modify
app/database/seeders/DatabaseSeeder.php:
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
use WithoutModelEvents;
/**
* Seed the application's database.
*
* PricingTierSeeder runs in all environments (production needs the 7-tier
* config bootstrapped). DemoSeeder runs only in local/testing — populates
* a demo tenant + admin user + 3 projects + ~14 demo deals for dev/UI smoke.
*/
public function run(): void
{
$this->call(PricingTierSeeder::class);
if (app()->environment('local', 'testing')) {
$this->call(DemoSeeder::class);
}
}
}
- Verify locally:
cd app
php artisan migrate:fresh --seed
php artisan tinker --execute='echo \App\Models\User::count();'
Expected: User count ≥ 1 (DemoSeeder created admin@demo.local).
Step 2.5 — Fix schema.sql header drift (62 → 63 base tables)
- Read
db/schema.sqlline 4 — current text:-- Метрики: 62 базовые таблицы + 12 партиций / 117 индексов / 39 RLS-политик / 5 функций / 13 триггеров - Replace with:
-- Метрики: 63 базовые таблицы (61 regular + 2 partitioned parents: deals + supplier_lead_costs) + 12 партиций / 117 индексов / 39 RLS-политик / 5 функций / 13 триггеров
Apply via Edit tool, exact old_string → new_string.
Step 2.6 — Also reflect drift in CLAUDE.md §2 + §0 + §8 baseline numbers
- Update
CLAUDE.mdreferences to «62 базовые таблицы» (3 occurrences — §0 «Источник истины» row «Схема БД», §2 «Стек проекта» row «БД», §8 «Self-review триггеры» rowdb/schema.sql).
NB on CLAUDE.md edit channel: per CLAUDE.md §5 п.10, CLAUDE.md modifications go ONLY through claude-md-management plugin. This step is invoked separately — see Step 2.8.
Step 2.7 — Verify nothing broken
- Run from
app/:
cd app
npx vue-tsc --noEmit
npx eslint resources/js/**/*.{ts,vue}
npx vite build
./vendor/bin/pest --parallel
Expected: all green. Vitest will be run in Final verification (Task end).
Step 2.8 — Commit (code part)
git add app/resources/js/views/admin/AdminPlaceholderView.vue \
app/package.json app/package-lock.json \
app/resources/js/api/admin.ts \
app/resources/js/api/notifications.ts \
app/resources/js/api/reports.ts \
app/resources/js/composables/mockBilling.ts \
app/resources/js/composables/useStatusPill.ts \
app/database/seeders/DatabaseSeeder.php \
db/schema.sql
git commit -m "$(cat <<'EOF'
chore(cleanup): dead code removal + DemoSeeder auto-run + schema header drift
Closes Audit #3 P2 batch (knip dead exports/components, DemoSeeder hygiene,
schema header drift).
- Remove AdminPlaceholderView.vue (unreferenced placeholder view)
- Uninstall concurrently devDep (no scripts invoked it)
- Remove 12 unused exported types in api/{admin,notifications,reports}.ts
and composables/{mockBilling,useStatusPill}.ts
- DatabaseSeeder::run() now calls DemoSeeder in local+testing envs
(dev `migrate:fresh --seed` produces demo tenant + admin@demo.local)
- schema.sql header: «62 базовые таблицы» → «63 (61 regular + 2 partitioned)»
Verification: vue-tsc 0 / ESLint 0 / Vite build OK / Pest --parallel green.
EOF
)"
Step 2.9 — CLAUDE.md update via plugin
Invoke /claude-md-management:claude-md-improver skill to update CLAUDE.md references «62 базовые таблицы» → «63 базовые таблицы (61 regular + 2 partitioned parents)» in §0 / §2 / §8. Skill produces separate commit per its conventions.
Task 3 — P2 coverage debt (3 files, parallel subagents)
Why: Carryforward F-COV-01/02/03 from Audit #2 — three files with low coverage represent regression risk if changed without tests.
Parallelism: Each file is fully isolated (different test file, different source file). Dispatch 3 subagents in parallel via superpowers:dispatching-parallel-agents.
Files (per subagent):
| Subagent | Source file | Test file | Target coverage |
|---|---|---|---|
| A | app/resources/js/components/reminders/ReminderDialog.vue |
app/tests/Frontend/components/reminders/ReminderDialog.spec.ts (new) |
Stmts ≥ 60% |
| B | app/resources/js/layouts/AdminLayout.vue |
app/tests/Frontend/layouts/AdminLayout.spec.ts (new) |
Stmts ≥ 60% |
| C | app/resources/js/api/admin.ts |
app/tests/Frontend/api/admin.spec.ts (extend existing or create) |
Stmts ≥ 80% |
Step 3.1 — Pre-flight per subagent: read source + existing tests
Before launching subagents, the orchestrator reads the 3 source files + checks for existing test scaffolds. This enables targeted briefing per subagent.
- Read
app/resources/js/components/reminders/ReminderDialog.vue - Read
app/resources/js/layouts/AdminLayout.vue - Read
app/resources/js/api/admin.ts - Check existing tests:
app/tests/Frontend/components/reminders/,app/tests/Frontend/layouts/,app/tests/Frontend/api/
Step 3.2 — Dispatch 3 parallel subagents (each writes TDD-style)
Each subagent runs superpowers:test-driven-development skill. Prompts include:
- Exact source file path + content excerpt
- Existing test patterns to mirror (from co-located neighbour tests)
- Coverage target
- Vitest + @vue/test-utils API (Vuetify mounting helper from
app/tests/Frontend/setup.ts)
Subagent A briefing template:
Source: app/resources/js/components/reminders/ReminderDialog.vue (full content)
Test: NEW app/tests/Frontend/components/reminders/ReminderDialog.spec.ts
Target: Stmts ≥ 60% (currently 0%)
Approach: TDD. Mount with vuetify stubs, verify dialog open/close, form submit,
validation states, emit('save')/emit('close') events. Mock store/api with vi.mock.
Pattern: mirror app/tests/Frontend/components/deals/NewDealDialog.spec.ts shape.
Subagent B briefing template:
Source: app/resources/js/layouts/AdminLayout.vue (full content)
Test: NEW app/tests/Frontend/layouts/AdminLayout.spec.ts
Target: Stmts ≥ 60% / Branch ≥ 40% / Funcs ≥ 40% (currently 9.09% / 0% / 0%)
Approach: TDD. Mount with vue-router stub + Pinia, verify sidebar nav renders,
active route highlight, logo + brand link, breadcrumb container.
Pattern: mirror app/tests/Frontend/layouts/AppLayout.spec.ts.
Subagent C briefing template:
Source: app/resources/js/api/admin.ts (full content)
Test: app/tests/Frontend/api/admin.spec.ts (new or extend)
Target: Stmts ≥ 80% / Funcs ≥ 80% (currently 11.53% / 27.27%)
Approach: TDD. Mock axios via vi.mock, test each exported function:
request URL+payload, response transformer, error path (401/422/500).
Pattern: mirror app/tests/Frontend/api/deals.spec.ts shape.
Each subagent is required to:
- Write failing test first
- Run to confirm fail
- Implement (or no implementation needed — testing existing code)
- Verify pass
- Report coverage delta (
npx vitest run --coverage <path>before/after) - Return ONLY the new test file content + raw coverage numbers (raw output, not summary, per economy 0% rule)
Step 3.3 — Orchestrator review of 3 subagent outputs
- Verify each test file was created with proper structure
- Run combined coverage check:
cd app
npx vitest run --coverage
Expected: all 3 target files at or above threshold; total Stmts ≥ 79% (was 78.64%).
Step 3.4 — Commit (one commit per subagent output OR one combined commit; choose combined for atomicity of «coverage debt closure»)
git add app/tests/Frontend/components/reminders/ReminderDialog.spec.ts \
app/tests/Frontend/layouts/AdminLayout.spec.ts \
app/tests/Frontend/api/admin.spec.ts
git commit -m "$(cat <<'EOF'
test(coverage): close F-COV-01/02/03 — ReminderDialog/AdminLayout/api-admin
Closes Audit #2+#3 P2 carryforward triplet.
- ReminderDialog.vue: 0% → ≥60% Stmts (dialog/form/emit lifecycle covered)
- AdminLayout.vue: 9.09% → ≥60% Stmts (sidebar nav + active route + logo)
- api/admin.ts: 11.53% → ≥80% Stmts (8 endpoints + error paths)
Total Vitest Stmts coverage ↑ from 78.64% to <new total> (+<delta> pp).
Approach: TDD via @vue/test-utils + Vuetify stubs + vi.mock axios — no
production code changed, only test infrastructure added.
EOF
)"
Task 4 — P3 tooling + structural mini-fixes
Files:
- Create:
app/.knip-config-cleanup.ts(or modify existingapp/knip.config.ts) - Modify:
package.json(add npmoverridesforlodashtransitive) - Modify:
app/resources/js/views/admin/AdminTenantsView.vue(table header) - Modify:
app/resources/js/views/admin/AdminBillingView.vue(if same pattern) - Modify:
app/tests/Frontend/setup.tsor test-specific config (axe-core ruleregionwhitelist for.v-overlay-container) - Decision document:
docs/superpowers/decisions/2026-05-14-pgformatter-perl-decision.md(capture chosen path)
Step 4.1 — pgFormatter perl carryforward — decision document only
Decision: keep [FIX-DEFER] (Q.HARD.002 from Audit #2). Installing perl on Windows dev machine is one-line choco install strawberryperl, but adds toolchain weight. Action: document explicit decision + alternative paths in docs/superpowers/decisions/2026-05-14-pgformatter-perl-decision.md:
- Create decision doc:
# Decision — pgFormatter on Windows dev (Q.HARD.002)
> **Date:** 2026-05-14
> **Status:** carryforward [FIX-DEFER]
> **Audits:** raised in #2, confirmed in #3
## Context
`npm run format:sql:check` falls because `perl` isn't on the PATH of the
Windows dev VM. pgFormatter lives in `bin/pgFormatter/pg_format` (Perl
script).
## Options
1. **`choco install strawberryperl`** — adds ~150 MB toolchain; works.
2. **Replace with `sqlfluff`** — Python-based; needs Python toolchain
(already partially present); different format opinions than pgFormatter,
would force a re-format of `db/schema.sql` (~6284 lines).
3. **Use `pg_format` via Docker** — adds Docker dep; we don't currently use
Docker on this VM (project_phase1_strategy.md — native Windows stack).
4. **Drop automated SQL formatting** — accept manual hygiene + squawk
(linter) covers correctness.
## Decision
Carry forward to Б-1 closure cycle. Production SQL hygiene is covered by
squawk (lint). pgFormatter is purely cosmetic in our flow.
Step 4.2 — npm overrides for lodash transitive (pa11y-ci dev-only vuln)
- Modify root
package.json— addoverridesblock:
{
...,
"overrides": {
"pa11y-ci": {
"lodash": "^4.17.21"
}
}
}
- Run
npm installto apply override. - Verify:
npm audit --omit=optional— lodash entry should be gone or downgraded severity to none.
Step 4.3 — Add table header label for actions column
- Read
app/resources/js/views/admin/AdminTenantsView.vue— locate the VDataTable headers definition. - Update the actions column header (8-th column) from empty title to visually hidden label:
If headers are defined as object array { title: '', key: 'actions', ... }, change to:
{ title: 'Действия', key: 'actions', sortable: false, headerProps: { class: 'sr-only-header' } }
Add CSS class:
.sr-only-header .v-data-table-header__content {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
- Same change for
AdminBillingView.vueactions column (if pattern present). - Verify with axe-core (Playwright run):
cd app
npm run test:a11y -- /admin/tenants
Expected: 0 empty-table-header violations.
Step 4.4 — axe-core test config: whitelist Vuetify .v-overlay-container region rule
- Find axe-core invocation in
app/tests/Frontend/(smoke test that invokes axe). If it's a Playwright/Pest dual-runtime test, locate the run config. - Add exclusion to axe-core options:
axe.run({
exclude: [['.v-overlay-container']],
...
})
OR set region rule disabled for that selector via disableOtherRules: false + per-rule selector.
- Verify:
npm run test:a11yexits 0.
Step 4.5 — Clean up knip.config.ts stale hints (P3 item)
- Read
app/knip.config.ts— locateignore: ['tests/**']andignoreDependencies: ['@vue/test-utils', 'jsdom', 'vitest']. - Remove those entries.
- Add
entry: [..., 'histoire.config.ts']to silence histoire.setup.ts + @histoire/plugin-vue FPs. - Re-run:
cd app && npx knip— expected: clean output (12 dead exports already gone in Task 2, FPs silenced).
Step 4.6 — Commit
git add docs/superpowers/decisions/2026-05-14-pgformatter-perl-decision.md \
package.json package-lock.json \
app/resources/js/views/admin/AdminTenantsView.vue \
app/resources/js/views/admin/AdminBillingView.vue \
app/tests/Frontend/setup.ts \
app/knip.config.ts
git commit -m "$(cat <<'EOF'
chore(p3): close P3 tooling and structural mini-fixes
Closes:
- pgFormatter perl carryforward — decision doc only (Q.HARD.002 stays deferred)
- npm overrides for lodash transitive (pa11y-ci dev-only vuln)
- AdminTenantsView/AdminBillingView actions column header sr-only label
(closes axe empty-table-header on /admin/tenants)
- axe-core test config whitelists .v-overlay-container region rule
(Vuetify structural pattern, not real a11y issue)
- knip.config.ts: remove 4 stale hints + add histoire.config.ts to entry
EOF
)"
Final verification — full regression sweep
Mirror Phase 14 of Audit #3 — same suite, same thresholds.
Step F.1 — Pest --parallel
cd app
./vendor/bin/pest --parallel
Expected: 742/739/3sk/0 failed (or improved). 0 regressions.
Step F.2 — Vitest
cd app
npx vitest run
Expected: 88+ files / 683+ passed (3+ new test files from Task 3) / 3 skipped / 0 failed.
Step F.3 — Vite build
cd app
npx vite build
Expected: exit 0, build time < 3.5s (Audit #3 baseline 2.21s).
Step F.4 — gitleaks full history
./bin/gitleaks.exe detect --source . --redact -v --log-level info
Expected: 0 leaks across now ~440+ commits.
Step F.5 — lychee link check
npm run links
Expected: 319+ OK / 0 broken.
Step F.6 — vue-tsc + ESLint + Prettier + markdownlint + cspell sanity
cd app
npx vue-tsc --noEmit
npx eslint resources/js/**/*.{ts,vue}
npx prettier --check resources/js/**/*.{ts,vue,css}
cd ..
npx markdownlint-cli2 "docs/**/*.md" "*.md"
npx cspell --no-progress "docs/**/*.md" "*.md"
All expected: exit 0.
Step F.7 — Cleanup package-lock.json CRLF flap (cosmetic — only if dirty)
git checkout -- package-lock.json
(Safe — git diff empty; pure line-ending normalisation.)
Push to origin/main
Step P.1 — Final git status sanity
git status
git log --oneline origin/main..HEAD
Expected: clean working tree; ~4–7 new commits ahead of origin/main.
Step P.2 — Push
git push origin main
Pre-push hooks (lefthook): gitleaks-full-history + lychee + (other configured). Must all pass.
Step P.3 — Memory updates
Invoke /claude-md-management:revise-claude-md to capture this session's learnings into memory:
project_state.md— bump HEAD to new commitproject_full_audit_2026-05-14.md— append «Audit #3 deferred fixes closure session» sectionfeedback_environment.md— capture any new quirks discovered during Task 1 (Pa11y CI lifecycle, dev-server bootstrap) or Task 4 (npm overrides syntax)
Step P.4 — Открытые_вопросы registry bump
Reflect closures in docs/Открытые_вопросы_v8_3.md if any of the deferred items had a Q-prefixed code. (Audit #3 documented 0 BLOCKED Q-items — but technical-debt closures still benefit from registry note.)
Self-review checklist
Spec coverage:
- ✅ P1 Pa11y scope → Task 1
- ✅ P2 AdminPlaceholderView → Task 2.1
- ✅ P2 concurrently → Task 2.2
- ✅ P2 12 dead exports → Task 2.3
- ✅ P2 DemoSeeder hygiene → Task 2.4
- ✅ P2 schema header drift → Task 2.5 (+ CLAUDE.md 2.6/2.9)
- ✅ P2 F-COV-01/02/03 → Task 3
- ✅ P2 GITHUB_TOKEN in cache — NOT addressed (already gitignored, mitigations in place per Audit #3 — documentation-only, no code action; skip)
- ✅ P2 Sentry SDK — OUT OF SCOPE (blocked by Б-1)
- ✅ P2 Handoff prototypes contrast — addressed by relocating to
pa11y-handoff.config.json(opt-in) - ✅ P3 knip config → Task 4.5
- ✅ P3 npm lodash → Task 4.2
- ✅ P3 empty-table-header → Task 4.3
- ✅ P3 region landmarks → Task 4.4
- ✅ P3 pgFormatter perl → Task 4.1 (decision doc only)
- ✅ P3 F-COV-04..07 — OUT OF SCOPE (observational, no actionable fix per audit reasoning)
- ✅ P3 generic-api-key FPs in vendor → addressed indirectly by gitleaks allowlist in
.gitleaks.toml(optional Task 4 micro-step; skip if already present) - ✅ P3 npm lodash dev-only → Task 4.2
- ✅ P3 F-BUN-01/02/03 — OUT OF SCOPE (Vuetify structural)
- ✅ P3 advisory TODO at Б-1 closure — OUT OF SCOPE
Placeholder scan:
- 0 «TBD» / 0 «implement later» / 0 «similar to Task N». All code blocks contain full content.
Type consistency:
- All Vue test-utils signatures consistent. All Pest command invocations consistent. All git command shapes consistent.
Execution choice
Plan saved to docs/superpowers/plans/2026-05-14-audit3-deferred-fixes.md.
Two execution options per superpowers:writing-plans:
- Subagent-Driven (recommended) — fresh subagent per task, orchestrator review between tasks, fast iteration. Task 3 already designed for parallel subagent dispatch.
- Inline Execution — all tasks in this session via
superpowers:executing-planswith checkpoint reviews.
Recommendation for this work: Subagent-Driven for Task 3 (3 parallel) + Inline for Tasks 1/2/4 (sequential, shared file state across steps). Mixed mode.