How the task library, share-task dialog, and mixed-assessment card display a
DESIGN_REVIEW whiteboard task — which by design has the same competencies
as a coding task, no GitHub repo, and a different task_type.
A system-design task deliberately assesses the same competencies as a coding task —
that is the product's point, not a display bug. So competency pills can never distinguish the two.
The UI needs a second, visually different dimension: a format badge driven by
tasks.task_type, which is already fetched and sitting unused on TaskData.taskType
(transform-task.ts:124). No new data plumbing is required — this is purely a rendering change.
DESIGN_REVIEW → “System Design”, PR_REVIEW → “PR Review”. BUILD (coding) stays unbadged — a badge that appears only when something differs is the one recruiters actually notice.Today the collapsed card shows title + skill pills only (task-library-card.tsx:41-81).
The single change: render the format badge next to the title when taskType contains a non-BUILD value.
The sidebar rows today show title only (task-skills-sidebar.tsx:173) — at the exact
moment the recruiter decides what to send, nothing marks the format. Add a small whiteboard glyph on
the row, and give the right-hand detail panel (task-detail-panel.tsx) three additions:
the badge in the header, a one-line format strip, and a proper
“View design board” button that opens a read-only preview of the starter board
(Mock 3). This is the design-task equivalent of the coding task's “View starter code” gist button —
the recruiter sees exactly what the candidate gets at minute zero, so they can be sure about the
problem statement and what's on the board. Everything else stays the same read-only task info a coding
task shows — problem statement, time, competencies, outcomes.
task-detail-panel.tsx:140) for this format. It opens the starter board read-only — exactly what the candidate sees at minute zero — never the reference answer or a candidate's submission.A read-only preview dialog rendering the task's starter board. The sample below is real
data from the url-shortener-service-design task (tasks.task_blob in Supabase dev;
same JSON as generated_tasks/system_design/url-shortener-service-design/task.json):
the given Functional Requirements arrive locked, three dashed answer zones are empty, and the
High-Level Design area holds the given starter context the candidate extends.
Implementation is cheap: task_blob.starter_scene is already fetched by
TASK_SELECT (orgTasksOperations.ts:13 selects the whole task_blob) —
the dialog lazy-loads @excalidraw/excalidraw in viewModeEnabled and hands it the
scene. No new API call, no candidate-side route reuse.
Duration cards (task-duration-cards.tsx:87-116) currently show title + skill pills + duration
select. The badge carries through so the format stays visible at the final confirmation step.
MixedTestCard hardcodes “coding task(s)” (mixed-test-card.tsx:12-16, 39), which
misdescribes a design task. Since taskType is on hand for the badge anyway, make the phrase
type-aware by counting selected tasks per format.
The format list is open-ended — prompts, UI/UX design, more coding variants are already on the horizon.
The rule that keeps this from decaying into per-type switch statements:
task types are open-ended, but the things the UI branches on stay a small closed set of
capabilities. No recruiter surface ever branches on a type name. Every surface renders a
display contract that every type — present or future — must fill. The candidate app already
proves the pattern: SandboxLayout renders whatever interaction_surfaces.ports[]
the backend returns, and new types cost zero frontend code there. This section applies the same
philosophy to the recruiter side.
| Field | Recruiter question it answers | System design | Prompts (future) | UI/UX design (future) |
|---|---|---|---|---|
| Format label + icon | What kind of assessment is this? | System Design · whiteboard | Prompt Engineering · chat | UI/UX Review · frame |
| “Candidate does…” line | What will my candidate experience? | Designs the architecture on an interactive whiteboard | Writes and iterates prompts against a live model | Critiques a seeded design file and proposes fixes |
| Deliverable | What comes back to me? | Architecture board + filled answer zones | Final prompt set + model outputs | Annotated frames + written rationale |
| Short problem statement + time | Is the content right for my role? | short_overview + time_for_task_mins — identical treatment for every type |
||
| Competencies + proficiency | Does it measure the right skills? | criterias — identical for every type; never the differentiator |
||
| Preview pointer | Can I see what the candidate gets? | excalidraw-scene → starter_scene |
document → prompt brief |
figma-link → seeded file |
| Capability | Values today | What consumes it |
|---|---|---|
preview_kind |
code-gist · excalidraw-scene · document · pr-diff · none |
The preview dialog picks a renderer. A new type reuses one; only a genuinely new modality needs a new renderer — an honest, unavoidable cost. |
submission_kind |
git-push · zip-upload · board-scene · text |
Mixed-card phase phrasing, submission expectations, report entry point. |
| Workspace surface (candidate side, shipped) | interaction_surfaces.ports[] |
SandboxLayout tabs — already fully data-driven; the precedent this model copies. |
A task_formats registry — one row per task type: task_type, label, icon,
“candidate does” copy, deliverable copy, preview_kind, submission_kind.
Per-task specifics (the preview source, e.g. task_blob.starter_scene) stay on the
task row. Launching a new type then means: one registry row + the generator filling the contract —
zero recruiter-UI deploys, and copy stays consistent across every task of the type.
A client-side config map is an acceptable v1 of the same idea (one file, one entry per type), upgraded
to the table when type launches decouple from frontend releases. The registry is enforced at the
pipeline: the DAO's validate-and-insert rejects a task whose type has no registry entry — growth stays
safe because an undisplayable task cannot be stored.
Graceful-degradation rule. An unknown task_type must still render:
title, competencies, time, problem statement, a neutral badge showing the humanized type name, and no
preview button. The recruiter UI never crashes on — and never hides — a task whose type it hasn't heard
of. This is what actually decouples UI releases from new-type launches.
Badge-rule expiry. “Badge only non-BUILD types” (Principle, above) is right at 2–3 formats and wrong at 5+. Trigger to revisit: once three or more formats carry real volume, badge everything — including “Coding” — and add a format facet next to the competency rails. The library's grouping stays competency-first; format becomes a secondary filter, never the primary hierarchy, because recruiters hire for skills, not formats.
| Value | Verdict | Trade-off |
|---|---|---|
| Format badge + strip | Show | One per-type copy map (2–3 entries). Prevents “where's the starter code — is this broken?” |
| Problem statement, time, competencies, outcomes | Show (unchanged) | Identical treatment reinforces “same skills, different format”. |
| “View design board” button → read-only starter-board preview | Show | The format's equivalent of “View starter code” — the starter board is exactly what the candidate sees at minute zero, so previewing it leaks nothing the candidate doesn't get anyway. Cost: the Excalidraw bundle — lazy-load it inside the dialog only. |
Deliverable chips (board_spec answer-zone titles) | Skip | Redundant once the preview exists — the empty zones are visible on the board itself. |
| Reference answer, candidate submissions, hints, rubric detail | Never | The preview renders starter_scene only. The answer key lives in the top-level answer column and must never reach this dialog; submission review belongs to the task report. |
| Format filter/tab in library | Later | Competency rails stay primary; revisit when non-code formats have volume. |
Problem-statement fallback dumps the whole brief.
transformTaskRow reads short_overview → falls back to full question
(transform-task.ts:90-97), but the system-design generator writes task_overview
into the blob instead. Until fixed, design tasks show the entire multi-paragraph brief as the “problem statement”.
Preferred fix: write short_overview from the generator (system-design repo,
payload.py) — one line, no UI change. Alternative: teach the transform to also read task_overview.
task_type is not copied into the session blob.
build_task_session_task_blob (Utkrushta/flask_service/routes/v2/task_sessions.py:79-135)
omits it, while FastAPI task_sessions.py:551 reads it from that per-entry blob. Candidate-side
provisioning works today only because sessions.py:922 re-fetches the full task row. Copy it through
for reliability before more type-branching lands.
Badge style must not look like a skill pill. Keep it squared + outlined + icon-led (see mocks). If it ships as another filled green pill, recruiters will read “System Design” as a competency — recreating the exact confusion this spec exists to remove.
Companion docs: the candidate-side whiteboard delivery is specified in
system-design/page.html (Whiteboard System-Design Assessment — Design Doc); task storage shape in
system-design/flows/non_tech/system_design/payload.py; recruiter report treatment TBD in a follow-up to
docs/task-report-v2-frontend.html.