Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions docs/sessions/2026-06-30.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
53 changes: 53 additions & 0 deletions src/renderer/dashboard/StoryBankModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ const COMPETENCY_LABEL: Record<StoryCompetency, string> = {
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<Story, 'title' | 'situation' | 'task' | 'action' | 'result'>;

/** Per-profile STAR story bank: extract grounded stories from the résumé, browse,
Expand Down Expand Up @@ -182,6 +185,9 @@ export function StoryBankModal({
</div>
)}

{/* Competency coverage — what your bank covers + which gaps to fill. */}
{!busy && stories.length > 0 && <CompetencyCoverage stories={stories} />}

{/* Story list */}
{!busy && stories.length > 0 && (
<ul className="space-y-2.5">
Expand Down Expand Up @@ -283,3 +289,50 @@ export function StoryBankModal({
</Modal>
);
}

/** 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<StoryCompetency, number>();
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 (
<div className="rounded-xl border border-white/10 bg-neutral-950/40 p-3">
<div className="mb-2 flex items-baseline justify-between gap-2">
<h4 className="text-xs font-semibold uppercase tracking-wide text-neutral-400">
Competency coverage
</h4>
<span className="text-xs text-neutral-500">
{covered}/{COMPETENCIES.length} covered
</span>
</div>
<div className="flex flex-wrap gap-1.5">
{COMPETENCIES.map((c) => {
const n = counts.get(c) ?? 0;
return (
<span
key={c}
title={n > 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}` : ''}
</span>
);
})}
</div>
{gaps.length > 0 && (
<p className="mt-2 text-xs text-neutral-500">
Gaps: {gaps.map((g) => COMPETENCY_LABEL[g]).join(', ')} — consider adding a story for
each.
</p>
)}
</div>
);
}
Loading