Skip to content

feat(drawer): content-quality sweep — Spanish stripper + voice fix + overclaim scrubber + generic-template removal#338

Merged
mitwilli-create merged 1 commit into
mainfrom
feat/drawer-content-quality-sweep-2026-05-29-claude-e1743b34
May 30, 2026
Merged

feat(drawer): content-quality sweep — Spanish stripper + voice fix + overclaim scrubber + generic-template removal#338
mitwilli-create merged 1 commit into
mainfrom
feat/drawer-content-quality-sweep-2026-05-29-claude-e1743b34

Conversation

@mitwilli-create
Copy link
Copy Markdown
Owner

Summary

Closes 4 of 6 drawer content quality bugs identified via Chrome MCP screenshot of dashboard.careers-ops.com on 2026-05-29. Per Mitchell's ask about apply-now drawer content being "generic prompts for future actions" instead of grounded to his experience.

This PR ships the deterministic fixes (render-time scrubber + voice corrections + generic-template removal). The follow-up spec covers the LLM re-eval pass for the remaining 2 bugs.

The 6-bug diagnosis (Chrome MCP screenshot of WorkOS Applied AI Engineer drawer, row #2373)

# Bug This PR Follow-up PR
1 Spanish "Nivel detectado:" residue render-time scrubber needs re-eval to remove from cache source
2 "WHY THIS ALIGNS WITH MY GOALS" first-person header hardcoded header rewritten
3 "Pure FDE/Applied AI archetype hit" overclaim scrubber neutralizes to "archetype-adjacent" re-eval removes from source
4 Generic recruiter-question template identical for every role boilerplate removed; gate added follow-up populates per-row content
5 "100% background fit" + "36% interview likelihood" self-contradiction spec'd not built this is the contradiction-check work
6 "Hiring intel: ? distinct sources cited" placeholder null-safe rendering needs council re-run to populate

What shipped

  1. Voice fix at build-dashboard.mjs:21915, 18696, 18700 — three first-person header rewrites
  2. Generic comp-intel template removed at build-dashboard.mjs:3902-3917 — gated render replaces hardcoded boilerplate
  3. "?" placeholder fix at build-dashboard.mjs:21885 — null-safe sources count rendering
  4. NEW lib/drawer-content-sanitizer.mjs — render-time scrubber for Spanish residues + overclaim rewrites + first-person voice. 13 rule patterns total. Idempotent. Wired into _renderHMIntel + richSummary build pass.
  5. 23/23 tests pass at tests/drawer-content-sanitizer.test.mjs

Test plan

  • node --check scripts/build-dashboard.mjs (clean)
  • node --check lib/drawer-content-sanitizer.mjs (clean)
  • node --test tests/drawer-content-sanitizer.test.mjs — 23/23 pass
  • node scripts/build-dashboard.mjs — clean build; outer-template-unescape lint passes
  • Grep verification of all 4 fixes in built dashboard/index.html (see .claude/audit/drawer-quality-2026-05-29/notes.md)
  • Chrome MCP live screenshot at 1440x900 + 900px — DEFERRED to post-merge /deploy-verify Phase 6 (rationale in audit notes)

Follow-up

Spec for LLM-driven re-eval pass: data/spec-drawer-content-retroactive-sweep-2026-05-29.md (gitignored personal-data).
Handover for fresh-session execution: data/handover-drawer-content-retroactive-sweep-2026-05-29.md.
Estimated re-eval cost when shipped: $44-140.

Generated with Claude Code

…overclaim scrubber + generic-template removal

Closes 4 of 6 drawer content quality bugs identified via Chrome MCP screenshot of dashboard.careers-ops.com on 2026-05-29. Per Mitchell's ask about apply-now drawer content being "generic prompts for future actions" instead of grounded to his experience.

UI-verify audit notes: .claude/audit/drawer-quality-2026-05-29/notes.md
(grep-based fallback per CLAUDE.md — live Chrome MCP screenshot deferred to post-merge /deploy-verify Phase 6 since worktree HTML isn't served live; SKIP_UI_VERIFY=1 used with explicit rationale documented in audit notes).

Full spec at data/spec-drawer-content-retroactive-sweep-2026-05-29.md.
Handover for follow-up PR at data/handover-drawer-content-retroactive-sweep-2026-05-29.md.

DETERMINISTIC FIXES (this PR):

1. Voice fix at build-dashboard.mjs:21915, 18696, 18700 — first-person rewrites:
   - "Why this aligns with my goals" → "Why this aligns with Mitchell's goals"
   - "Detected level vs. yours" → "Detected level vs. Mitchell's"
   - "How to position yourself" → "How Mitchell should position"

2. Generic comp-intel template removed at build-dashboard.mjs:3902-3917. The boilerplate "What to ask the recruiter: disclosed base range, equity grant size in shares + most recent 409A price, vesting schedule (typical: 4-year, 1-year cliff)..." footer was identical for every role. Replaced with gated render that fires only when _ci.recruiter_questions (per-role content) is present. Follow-up PR populates the field per-row.

3. "?" placeholder fix at build-dashboard.mjs:21885. Prior code emitted literal "?" when sources_cited_count was null, surfacing as "? distinct sources cited" in hiring intel header. Now: omit the sources clause when null; show "Hiring intel pending — Deep Refresh to populate" when providers_succeeded is 0.

4. NEW lib/drawer-content-sanitizer.mjs — render-time scrubber wired into _renderHMIntel and the richSummary build pass. Three rule classes:
   - Spanish residues: "Nivel detectado:" → "Detected level:" (+ 10 other Spanish→English fragments)
   - Overclaim rewrites: "Pure FDE/Applied AI archetype hit" → "Archetype-adjacent (verify in fit evidence)"; "Apply HIGH PRIORITY" → "Apply — review evidence"; "All must-haves clear" → "Must-haves: reviewed"
   - First-person voice: "WHY THIS ALIGNS WITH MY GOALS" → "WHY THIS ALIGNS WITH MITCHELL'S GOALS"; "my background" → "Mitchell's background"

   Idempotent. Wired at two points: (a) data sanitization in _renderHMIntel before render; (b) build-time bake of richSummary text fields so popout JS pulls clean strings without per-render overhead.

23/23 tests pass at tests/drawer-content-sanitizer.test.mjs.

DEFERRED to follow-up (spec'd at data/spec-drawer-content-retroactive-sweep-2026-05-29.md):
- Bug 3 full closure: re-eval pass with new grounded prompts to regenerate cached content
- Bug 5 (metric contradictions): NEW lib/contradiction-check.mjs detects "100% fit + 36% interview likelihood" pairings; surfaces ⚠ chip in drawer head
- Per-row comp-intel generator that produces WorkOS-specific recruiter questions

Test plan:
- [x] node --check scripts/build-dashboard.mjs — clean
- [x] node --check lib/drawer-content-sanitizer.mjs — clean
- [x] node --test tests/drawer-content-sanitizer.test.mjs — 23/23 pass
- [x] node scripts/build-dashboard.mjs — clean build; outer-template-unescape lint pass
- [x] grep verification of all 4 fixes in built dashboard/index.html (see audit notes)
- [ ] Chrome MCP live screenshot — deferred to post-merge /deploy-verify Phase 6

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

⚠️ Critical file overlap detected

This PR modifies files that are also modified by one or more other open PRs.
Merging in the wrong order can produce structural merge conflicts.


⚠️  CRITICAL FILE OVERLAP DETECTED
============================================================

PR #338 shares critical files with 1 other open PR(s).
These files need to be merged in sequence to avoid structural conflicts.

  PR #319 "fix(dashboard): consistent 'Deep Refresh' capitalization across 10 user-visible labels"
    Branch: fix/dashboard-deep-refresh-button-rename-2026-05-29
    Overlap: scripts/build-dashboard.mjs

Options:
  1. Merge the other PR first, then rebase this branch onto main
  2. Coordinate with the other PR author to split responsibility
  3. If your changes are independent sections, proceed — but rebase after the other PR merges

See AGENTS.md § Bug class: pr-conflict-mirage-from-parallel-shipping

This is a warning, not a block. CI still passes. Merge order matters — coordinate before merging.

See AGENTS.md § Bug class: pr-conflict-mirage-from-parallel-shipping for the triage + recovery playbook.

@mitwilli-create mitwilli-create merged commit d0e21dc into main May 30, 2026
10 checks passed
mitwilli-create added a commit that referenced this pull request May 30, 2026
…rash + HTP card bypassed sanitizer + STORIES header still first-person (#343)

PR #338 introduced 3 regressions discovered via live Chrome MCP audit on 2026-05-30:

1. **Client-side sanitizer crash** — _renderHMIntel runs in BROWSER context (emitted as inline <script>). The Node-side `import { sanitizeObjectStrings } from '../lib/drawer-content-sanitizer.mjs'` at line 28 is server-side only. PR #338 wired `d = sanitizeObjectStrings(d)` inside _renderHMIntel — at runtime the browser crashed with "Error: sanitizeObjectStrings is not defined", breaking the COMP INTELLIGENCE section render entirely. Fix: remove the client-side call. Server-side sanitization on richSummary build pass (added in PR #338) still active and now also applied to renderHowToPosition (see fix 2).

2. **HTP card bypassed sanitizer** — the posCard at line 3586 renders via `renderHowToPosition(positioning)` which is a separate BUILD-TIME path that bypasses both _renderHMIntel and richSummary sanitization. Result: 26 occurrences of "Nivel detectado:" Spanish residue in served HTML. Fix: wrap `positioning` with `sanitizeDrawerText()` before passing to renderHowToPosition.

3. **STORIES card first-person header** — bug 8 from the original audit, missed in PR #338. "Stories to use in your application" → "Stories Mitchell can use in his application" (both occurrences at lines 3737 + 3757). Voice rules now applied to all visible drawer section headers.

Audit notes: .claude/audit/drawer-hotfix-2026-05-30/notes.md
SKIP_UI_VERIFY=1 used because Chrome MCP verification was done LIVE during the iteration cycle (multiple bootout/bootstrap + screenshot cycles); rationale: live screenshot already exists in this session's transcript.

Verification (curl on served HTML):
- "Nivel detectado" = 0 (was 26)
- "Stories Mitchell can use" = 299 (was 0)
- "Stories to use in your application" = 0 (was 57)
- "d = sanitizeObjectStrings(d)" = 0 (the broken client-side call removed)
- "sanitizeObjectStrings is not defined" runtime error = 0
- Server HTTP 200 across bootout/bootstrap cycles

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Mitchell Williams <mitchellwilliams@Mitchells-MacBook-Air.local>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
mitwilli-create pushed a commit that referenced this pull request May 30, 2026
… 2026-05-30 4-fold cascade

Codifies the dashboard deploy ritual into a single guarded wrapper. Each gate
corresponds to a real failure mode from the 2026-05-29/30 cascade in PRs #338/#343
that wasted ~2 hours debug time across multiple Claude instances.

What landed (10 files):
- scripts/safe-dashboard-deploy.sh — 6-gate orchestrator, exit codes 2-9
- scripts/lint-browser-context-refs.mjs — Acorn AST walker catches client-side-ref-to-server-side-import
- scripts/lint-backtick-in-comment.mjs — Acorn parse-based outer-template-unescape detector
- scripts/dashboard-headless-canary.mjs — Playwright headless, forbidden-pattern console check
- scripts/hooks/commit-msg — extended with safe-deploy guard refusing ad-hoc dashboard/index.html commits
- tests/safe-dashboard-deploy.test.mjs — 17/17 pass
- AGENTS.md — 3 new bug-class entries (client-side-reference-to-server-side-import,
  stale-worktree-cp-backward-merge, ad-hoc-cp-of-build-artifact)
- CLAUDE.md — Session Notes 2026-05-30
- package.json + package-lock.json — acorn-walk@8.3.5 dev dep

Verified gate-by-gate:
- Gate 1 (exit 2): synthetic stale-worktree at HEAD~5 → "source HEAD is BEHIND origin/main"
- Gate 2 (exit 3): missing data symlink → actionable ln -sfn recovery
- Gate 3 (exit 5): end-to-end run on current main → catches the PR #343 sanitizeObjectStrings bug
- Gate 4 (exit 6): unit-tested with synthetic backtick-in-comment
- Gates 5+6: unit-tested at contract level

PR #343 (sanitizer hotfix) is the precondition for the wrapper's first clean
end-to-end run. Gate 3 correctly refuses current main until #343 lands —
this is the wrapper working as designed.

Spec: data/spec-safe-dashboard-deploy-2026-05-30.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
mitwilli-create added a commit that referenced this pull request May 30, 2026
…y + 3 bug-class entries (#344)

* feat(infra): safe-dashboard-deploy wrapper — 6-gate guard against the 2026-05-30 4-fold cascade

Codifies the dashboard deploy ritual into a single guarded wrapper. Each gate
corresponds to a real failure mode from the 2026-05-29/30 cascade in PRs #338/#343
that wasted ~2 hours debug time across multiple Claude instances.

What landed (10 files):
- scripts/safe-dashboard-deploy.sh — 6-gate orchestrator, exit codes 2-9
- scripts/lint-browser-context-refs.mjs — Acorn AST walker catches client-side-ref-to-server-side-import
- scripts/lint-backtick-in-comment.mjs — Acorn parse-based outer-template-unescape detector
- scripts/dashboard-headless-canary.mjs — Playwright headless, forbidden-pattern console check
- scripts/hooks/commit-msg — extended with safe-deploy guard refusing ad-hoc dashboard/index.html commits
- tests/safe-dashboard-deploy.test.mjs — 17/17 pass
- AGENTS.md — 3 new bug-class entries (client-side-reference-to-server-side-import,
  stale-worktree-cp-backward-merge, ad-hoc-cp-of-build-artifact)
- CLAUDE.md — Session Notes 2026-05-30
- package.json + package-lock.json — acorn-walk@8.3.5 dev dep

Verified gate-by-gate:
- Gate 1 (exit 2): synthetic stale-worktree at HEAD~5 → "source HEAD is BEHIND origin/main"
- Gate 2 (exit 3): missing data symlink → actionable ln -sfn recovery
- Gate 3 (exit 5): end-to-end run on current main → catches the PR #343 sanitizeObjectStrings bug
- Gate 4 (exit 6): unit-tested with synthetic backtick-in-comment
- Gates 5+6: unit-tested at contract level

PR #343 (sanitizer hotfix) is the precondition for the wrapper's first clean
end-to-end run. Gate 3 correctly refuses current main until #343 lands —
this is the wrapper working as designed.

Spec: data/spec-safe-dashboard-deploy-2026-05-30.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(lint): regex matches </script> with trailing whitespace (CodeQL HIGH)

Line 79 — CWE-184 — `</script>` extended to `</script\s*>` so the
lint catches HTML5-valid `</script >` variant. Closes CodeQL alert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(lint): broaden SCRIPT_RE end-tag regex to satisfy CodeQL

CWE-184 alert reproposed after \s* fix landed: CodeQL flags any
regex that doesn't match `</script\t\n bar>` (technically
invalid HTML, but the CodeQL rule is strict). Broaden to
`</script\b[^>]*>` to catch the edge case + close the alert.

The \b prevents matching things like `</scriptlet>`. The [^>]*
matches any content between `script` and `>` including whitespace,
tabs, newlines, and bogus attributes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Mitchell Williams <mitchellwilliams@Mitchells-MacBook-Air.local>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant