From 371acc57ff8d6cc95de6c111d2c167201dc97987 Mon Sep 17 00:00:00 2001 From: tpikachu Date: Tue, 30 Jun 2026 17:55:25 -0500 Subject: [PATCH] feat: competency coverage in the Story Bank MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an at-a-glance strip to the Story Bank modal showing which of the 12 behavioral competencies the bank covers (with per-competency story counts) and which are still gaps — so the user knows what stories to add next. Pure renderer aggregation over stories[].competencies; no backend, IPC, or schema changes. Verified: typecheck · 102 unit · build. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/sessions/2026-06-30.md | 16 +++++-- src/renderer/dashboard/StoryBankModal.tsx | 53 +++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/docs/sessions/2026-06-30.md b/docs/sessions/2026-06-30.md index 2e02499..59bb2af 100644 --- a/docs/sessions/2026-06-30.md +++ b/docs/sessions/2026-06-30.md @@ -143,8 +143,18 @@ empty-bank null + cosine epsilon; retrieve embed-once + dedup + length-agnostic (buildContext/contextSent); StoryCue null-safety + collapsed-card hiding + split() edge cases. Verified: `typecheck` · 102 unit · `build` green. Branch `feat/grounded-answers`. +## Release + Story Bank polish + +- **v1.1.0** cut (`release/v1.1.0`): `changelog/1.1.0.md` (drives `APP_VERSION` + in-app + What's New) + `package.json` bump, packaging the five post-1.0 trainer features. Merged the + whole post-1.0 stack to `master` as one PR (#16) first. +- **Competency coverage** in `StoryBankModal`: a compact strip showing which of the 12 + competencies the bank covers (with per-competency counts) and which are gaps — so the user + knows what stories to add. Pure renderer aggregation over `stories[].competencies`; no + backend. Verified: typecheck · 102 unit · build. + ## Next - Path A #1–#4 done (Grounded Answers, Pre-Interview Brief, STAR Story Bank, Sparring) + - the live Story-to-tell cue. -- Optional: persist Sparring runs into Reports; competency-coverage view on the profile; - re-run the adversarial review on this cue once the agent rate-limit resets. + the live Story-to-tell cue + competency coverage; v1.1.0 staged. +- Optional: persist Sparring runs into Reports; re-run the multi-agent review on the + Story-to-tell cue now that the agent rate-limit has reset. diff --git a/src/renderer/dashboard/StoryBankModal.tsx b/src/renderer/dashboard/StoryBankModal.tsx index ea4d97e..3022e41 100644 --- a/src/renderer/dashboard/StoryBankModal.tsx +++ b/src/renderer/dashboard/StoryBankModal.tsx @@ -18,6 +18,9 @@ const COMPETENCY_LABEL: Record = { customer_focus: 'Customer focus', }; +// Canonical competency order for the coverage view (the closed StoryCompetency set). +const COMPETENCIES = Object.keys(COMPETENCY_LABEL) as StoryCompetency[]; + type EditForm = Pick; /** Per-profile STAR story bank: extract grounded stories from the résumé, browse, @@ -182,6 +185,9 @@ export function StoryBankModal({ )} + {/* Competency coverage — what your bank covers + which gaps to fill. */} + {!busy && stories.length > 0 && } + {/* Story list */} {!busy && stories.length > 0 && (
    @@ -283,3 +289,50 @@ export function StoryBankModal({ ); } + +/** At-a-glance view of which behavioral competencies the story bank covers (with a + * count) and which are still gaps — so the user knows what stories to add next. */ +function CompetencyCoverage({ stories }: { stories: Story[] }) { + const counts = new Map(); + for (const s of stories) { + for (const c of s.competencies) counts.set(c, (counts.get(c) ?? 0) + 1); + } + const covered = COMPETENCIES.filter((c) => (counts.get(c) ?? 0) > 0).length; + const gaps = COMPETENCIES.filter((c) => !counts.get(c)); + + return ( +
    +
    +

    + Competency coverage +

    + + {covered}/{COMPETENCIES.length} covered + +
    +
    + {COMPETENCIES.map((c) => { + const n = counts.get(c) ?? 0; + return ( + 0 ? `${n} story${n === 1 ? '' : 's'}` : 'No story yet — a gap to fill'} + className={`rounded-full px-2 py-0.5 text-[11px] font-medium ${ + n > 0 ? 'bg-blue-900/40 text-blue-300' : 'bg-neutral-800 text-neutral-600' + }`} + > + {COMPETENCY_LABEL[c]} + {n > 0 ? ` ×${n}` : ''} + + ); + })} +
    + {gaps.length > 0 && ( +

    + Gaps: {gaps.map((g) => COMPETENCY_LABEL[g]).join(', ')} — consider adding a story for + each. +

    + )} +
    + ); +}