Skip to content

Phase 3 (2/3): behavioural_duel + mental_maths_blitz mechanics#13

Merged
TahaKhanM merged 11 commits into
phase-3-arena-hub-cv-battlefrom
phase-3-behavioural-mental-maths
May 15, 2026
Merged

Phase 3 (2/3): behavioural_duel + mental_maths_blitz mechanics#13
TahaKhanM merged 11 commits into
phase-3-arena-hub-cv-battlefrom
phase-3-behavioural-mental-maths

Conversation

@TahaKhanM

Copy link
Copy Markdown
Owner

Summary

Phase 3 sub-plan #2 of the Ascendra v2 master plan. Two more mechanics shipped end-to-end through the new pipeline (built in sub-plan #1):

  • behavioural_duel — Professional arena. 4 behavioural questions (text answers), Gemini-primary multi-judge rubric scoring (2 rubric LLM + 1 pairwise vs. STRONG anchor). Production profile uses Claude Opus 4.7 + Sonnet 4.6.
  • mental_maths_blitz — Technical arena. 30 deterministic numeric questions in 90 s (mix of multiplication, percentages, FX cross-rates, divisions). Scored by DeterministicNumericJudge with accuracy + time bonus.

Both mechanics now appear live on the /arena hub and dispatch to dedicated room composites from /arena/[mechanic]/play.

Plan: docs/superpowers/plans/2026-05-15-phase3-behavioural-mental-maths.md
Spec: docs/superpowers/specs/2026-05-15-phase3-behavioural-mental-maths-design.md
Stacked on: #12. Will be re-targeted to `main` once #12 merges.

Security fix included

mental_maths_blitz carries the answer key (correct_answers + per-question answer) in its rubric_definition. The public template-read endpoints (GET /api/v1/challenge-templates/{id} and list) now sanitize these fields out before returning to clients. The scoring pipeline still reads the unfiltered rubric via the ORM directly, so DeterministicNumericJudge keeps working. New endpoints get_challenge_template_or_error_for_public_api + list_challenge_templates_for_public_api enforce the sanitization at the API boundary.

ADRs governing this work

0006 (LLM cost / judge mix), 0008 (contexts), 0011 (Workflow topology), 0012 (Judge Protocol), 0013 (Gemini dev), 0014 (direct provider APIs). No new ADRs.

Test plan

Backend

  • `uv run pytest -q` — 269 passed, 1 deselected (was 249 baseline + 20 new across Tasks A1–A5 + A3 sanitization).
  • `uv run ruff check app tests scripts` — clean.
  • `uv run python scripts/check_context_boundaries.py` — clean.

Frontend-v2

  • `npm run test` — 314 tests passing, 41 files (was 291 baseline + 23 new tests across composites + dispatcher + COMING_SOON pruning).
  • `npm run lint` — clean.
  • `npm run build` — clean (Next 16.2.6 + Turbopack); all 11 routes compiled.

Commit highlights

  • `feat(scoring): seed behavioural_duel mechanic + Gemini-primary judge mix + 3 anchors (Task A1)`
  • `feat(scoring): seed mental_maths_blitz mechanic + 30 deterministic questions (Task A2)`
  • `feat(templates): sanitize answer-key fields out of public template reads (Task A3)` — security fix
  • `feat(scoring): include behavioural_duel + mental_maths_blitz in seed_reference_data (Task A4)`
  • `feat(skill): link Phase 3 templates into existing skill nodes (Task A5)`
  • `feat(frontend-v2): BehaviouralDuelRoom composite (Task B1)`
  • `feat(frontend-v2): MentalMathsBlitzRoom composite (Task B2)`
  • `feat(frontend-v2): /arena/[mechanic]/play dispatches behavioural_duel + mental_maths_blitz (Task B3)`
  • `feat(frontend-v2): mark behavioural_duel + mental_maths_blitz live on /arena (Task B4)`
  • `docs: CLAUDE.md hierarchy + master plan updates for Phase 3 sub-plan Phase 0: Foundations — contexts, frontend-v2, CI, observability #2 (Task C2)`

Open follow-ups (deferred to sub-plan #3 or later phases)

Same set as PR #12's follow-ups, plus:

🤖 Generated with Claude Code

TahaKhanM and others added 11 commits May 15, 2026 15:09
…maths_blitz)

Spec at docs/superpowers/specs/2026-05-15-phase3-behavioural-mental-maths-design.md;
TDD plan at docs/superpowers/plans/2026-05-15-phase3-behavioural-mental-maths.md.

Scope: behavioural_duel (Pro arena, 4-Q rubric LLM) + mental_maths_blitz
(Tech arena, deterministic 30-Q streak). Both reuse the Phase 3 sub-plan #1
pipeline. Also includes a security fix: sanitize answer-key fields out of
public template reads.

ADRs governing: 0006, 0008, 0011, 0012, 0013, 0014. No new ADRs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a 4-question STAR-style wizard room for the behavioural_duel
mechanic, mirroring CvBattleRoom's submit + confirmation pattern.

Per spec §4.1: one question at a time, per-question 180s countdown
that force-advances on expiry (or auto-submits if it's the last
question), Skip + Next buttons, Submit-with-confirm-dialog on Q4.

Also extracts the shared Countdown pill into `_countdown.tsx` so
the new room and CvBattleRoom can both consume it. The existing
`data-testid="countdown"` contract is preserved so CvBattleRoom
tests stay green.

Plan: docs/superpowers/plans/2026-05-15-phase3-behavioural-mental-maths.md (Task B1)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the keyboard-driven 30-question streak room for the
mental_maths_blitz mechanic. Per spec §4.2:

- Big tabular-nums prompt + autofocused numeric input.
- Enter advances regardless of whether the answer is correct — no
  per-question feedback (deterministic judge runs server-side).
- Single 90s overall countdown (NOT per-question).
- "X answered" counter renders the running fill count.
- Auto-submits on either time-out OR when the user finishes the
  last prompt. No confirmation dialog per blitz convention.

Empty + Enter leaves the slot null so the deterministic judge can
mark unanswered questions explicitly wrong.

Reuses the shared <Countdown> helper introduced in Task B1.

Plan: docs/superpowers/plans/2026-05-15-phase3-behavioural-mental-maths.md (Task B2)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mix + 3 anchors (Task A1)

Phase 3 sub-plan #2 Task A1. Mirrors the cv_battle seed pattern from
seed_phase1_cv_battle_mechanic.py but targets the Professional arena:

- New ``behavioural_duel`` mechanic with a STAR-style 5-category
  default rubric (star_structure, specificity, ownership, learning,
  fit) and four embedded behavioural questions used by the frontend
  room composite.
- Dev judge mix is Gemini-primary (2x RubricLLMJudge + 1x
  PairwiseLLMJudge vs. STRONG anchor) per ADR 0013; production mix
  is Claude-primary per ADR 0006. Every judge attributes to the
  ``professional`` JudgeType.
- One default ``behavioural-duel-phase3-default`` template linked
  to the mechanic with ``judge_mix_definition`` left NULL so the
  factory inherits from the mechanic.
- Three STRONG ``CalibrationAnchor`` rows with role-flavoured 4-answer
  blobs (SWE intern, consulting placement, junior PM) joined by
  ``\n\n---\n\n`` separators so a single rubric pass reads the full
  submission. Indexed by ``model_baseline_scores['anchor_index']`` so
  the upsert path is stable across reruns (cv_battle only kept one
  anchor per tier; this seed keeps three STRONG anchors).
- Added the enum values the new mechanics require:
  ``ChallengeType.MENTAL_MATHS``, ``ScoringMode.JUDGE_LLM``,
  ``ScoringMode.DETERMINISTIC``, ``InputType.LONG_TEXT``,
  ``InputType.NUMERIC_STREAK``, and ``OutputType.STRUCTURED``.

Plan: docs/superpowers/plans/2026-05-15-phase3-behavioural-mental-maths.md (Task A1)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…estions (Task A2)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… + mental_maths_blitz (Task B3)

Extends the per-mechanic play page from a single-mechanic
dispatcher (cv-battle only) to a three-way dispatch covering
behavioural-duel and mental-maths-blitz alongside the existing
cv-battle path.

- `cv-battle` -> `CvBattlePlay` (unchanged)
- `behavioural-duel` -> `BehaviouralDuelPlay`:
  - Fetches the full ChallengeTemplateRead to access
    `rubric_definition.questions`
  - Wires `BehaviouralDuelRoom` with the question list +
    180s per-question timer (spec 4.1)
  - Submits `{submission_payload: {answers: [{question, answer}, ...]}}`
- `mental-maths-blitz` -> `MentalMathsBlitzPlay`:
  - Same full-template fetch to access the sanitized
    `rubric_definition.questions[].prompt` (the `answer`
    field is stripped server-side by Task A3)
  - Wires `MentalMathsBlitzRoom` with the prompt list +
    template-driven `totalTimeSeconds` (defaults to 90s
    if neither attempt nor mechanic exposes one)
  - Submits `{submission_payload: {answers: (number | null)[]}}`
- Anything else (e.g. `leetcode-medium`) falls through to the
  shared `ComingSoonPlaceholder`.

Shared helpers (`useDomainProfileQuery`, `deriveEffectiveMode`,
`useCreateAttemptMutation`, `useFullTemplateQuery`,
`extractBehaviouralQuestions`, `extractMentalMathsPrompts`,
`NoDomainProfile`) keep the three play components symmetric while
preserving the cv_battle flow exactly. The dispatcher remains a
single file so the wiring story stays auditable.

Tests:
- 14 tests, all green.
- Existing 10 cv_battle / Coming-soon / error tests preserved.
- New: BehaviouralDuelRoom renders with the resolved question list,
  and full 4-question submit posts the correct payload shape.
- New: MentalMathsBlitzRoom renders with the resolved prompt list,
  and short-prompt submit posts `{answers: (number | null)[]}`.
- Frontend-v2 vitest count: 313 (was ~291 baseline; +B1 +B2 +B3
  additions land here).

Plan: docs/superpowers/plans/2026-05-15-phase3-behavioural-mental-maths.md
Spec: docs/superpowers/specs/2026-05-15-phase3-behavioural-mental-maths-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ads (Task A3)

The mental_maths_blitz mechanic embeds the deterministic answer key in
``rubric_definition.correct_answers`` and in each
``rubric_definition.questions[i].answer``. Both fields must NEVER reach
the client — otherwise the deterministic judge is trivially defeated.

Adds ``_sanitize_rubric_for_public_read`` to the templates service, plus
``list_challenge_templates_for_public_api`` and
``get_challenge_template_for_public_api`` wrappers that detach the
template from the session and overwrite ``rubric_definition`` with a
sanitized deep copy. The router now uses these wrappers. The pipeline /
scoring helpers continue to call the unsanitised ``get_challenge_template_or_error``
so ``DeterministicNumericJudge`` still has the full answer key.

Schema additions: ``RubricQuestion(prompt: str)`` and
``RubricDefinition.questions: list[RubricQuestion]`` so the room
composites can read the question prompts; the matching ``answer`` field
on each entry is stripped at the read boundary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… /arena (Task B4)

Drops `behavioural_duel` and `mental_maths_blitz` from
`COMING_SOON_MECHANICS` now that their `BehaviouralDuelRoom` /
`MentalMathsBlitzRoom` composites + page dispatcher are live
(Tasks B1, B2, B3). Once the backend Task A1/A2/A4 seeds land in
`seed_reference_data` the arena hub will render them via the live
template path automatically.

`leetcode_medium` stays on the placeholder list until sub-plan #3
ships its `ChallengeTemplate` + Vercel Sandbox-driven judge.

Tests updated:
- `COMING_SOON_MECHANICS` keys reduced to just `["leetcode_medium"]`.
- New test verifies that when the backend returns
  `behavioural_duel` / `mental_maths_blitz` templates, they show up
  as `live` mechanics and don't reappear as coming-soon stubs.
- Frontend-v2 vitest count: 314 (one new arena.test assertion).

Plan: docs/superpowers/plans/2026-05-15-phase3-behavioural-mental-maths.md
Spec: docs/superpowers/specs/2026-05-15-phase3-behavioural-mental-maths-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…reference_data (Task A4)

Adds two reusable entry points — ``seed_behavioural_duel(db, profile)`` and
``seed_mental_maths_blitz(db)`` — to the Phase 3 seed scripts so the
``seed_reference_data`` bootstrap can drive them with a caller-supplied
session. The bootstrap loads the seed modules via ``importlib.util``
(scripts/ is not a Python package). Both are idempotent — re-running is a
no-op for row counts.

Adjusts ``test_seed_behavioural_duel.py`` so the anchor-related tests
expect the conftest fixture to have already seeded the 3 STRONG anchors
(``upsert_anchors`` now returns 0 on rerun in test). Widens
``RubricDefinition.questions`` to accept either ``RubricQuestion`` dicts
or plain strings, since ``behavioural_duel`` stores 4 STAR prompts as
strings while ``mental_maths_blitz`` stores ``{prompt, answer}`` dicts
(the ``answer`` field is stripped at the public read boundary).

Adds ``test_seed_reference_data_phase3.py`` to lock in that both
mechanics + their default templates exist after the fixture runs.

Also drops an unused ``DomainPathway`` import + wraps the over-long
usage docstring in the behavioural seed (pre-existing ruff hits).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds ``_link_phase3_templates_to_skill_nodes`` to the bootstrap so the
new ``behavioural-duel-phase3-default`` template surfaces in the
Professional repair path (via ``star-storytelling``, ``stakeholder-judgement``,
``conflict-resolution``, ``leadership-framing``) and
``mental-maths-blitz-phase3-default`` feeds the Technical repair drills
(via ``numerical-accuracy``, ``problem-decomposition``).

No new skill nodes — existing leaves cover the relevant signals. Deeper
skill-tree expansion lands in Phase 4. Idempotent: re-running upserts the
link rows in place.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… (Task C2)

- Root CLAUDE.md: bump frontend-v2 surface to include behavioural-duel + mental-maths-blitz mechanics.
- frontend-v2/CLAUDE.md: rename section to "Phase 3 sub-plans #1 + #2 additions"; add the two new room composites; explain mechanic-specific submission_payload shapes; note that leetcode_medium is the only remaining COMING_SOON entry.
- backend/CLAUDE.md: document the public-template-read sanitization split (get_*_for_public_api vs ORM-direct reads for the scoring pipeline).
- Master plan §9 Phase 3: mark sub-plan #2 complete; note PR #13.

Plan: docs/superpowers/plans/2026-05-15-phase3-behavioural-mental-maths.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TahaKhanM
TahaKhanM merged commit 7c36fa4 into phase-3-arena-hub-cv-battle May 15, 2026
4 of 6 checks passed
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