Skip to content

feat(ccaf): align to exam guide v1.0 — 30 task statements, per-objective diagnostics (single-answer items) - #52

Open
Dinesh-Girbide wants to merge 4 commits into
mainfrom
feat/ccaf-exam-guide-v1-alignment
Open

feat(ccaf): align to exam guide v1.0 — 30 task statements, per-objective diagnostics (single-answer items)#52
Dinesh-Girbide wants to merge 4 commits into
mainfrom
feat/ccaf-exam-guide-v1-alignment

Conversation

@Dinesh-Girbide

@Dinesh-Girbide Dinesh-Girbide commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Aligns the CCAF plugin to the revised exam guide (v1.0, effective July 2026, exam code CCAR-F), which publishes a full 30-objective index and corrects several scenario details.

Everything shipped stays self-authored: the guide's published facts are encoded, none of its prose is, and its own sample questions were rewritten as original items rather than quoted. The guide PDF is gitignored so it can never ship with the plugin.

⚠️ One deliberate divergence, up front

The guide gives the item format as multiple-choice and multiple-response, each item stating how many responses to select. This plugin serves single-answer items only — four options, exactly one correct. That is a decision, not an oversight, and it has a consequence reviewers should hold onto:

Single-answer items are the easier format, so a score here is if anything optimistic. 720 on a mock is the floor of readiness, not a comfortable margin. Candidates also never rehearse the all-or-nothing scoring of a real multiple-response item.

Multiple-response support was built, then removed at the owner's direction. It is recorded as a divergence in five places rather than papered over: the blueprint's Deliberate divergence section, the README fidelity table (an explicit ❌ row), CLAUDE.md, the prep guide, and the tutor — which tells learners the format exists and that they will not practise it here. The machinery is in this branch's history if the decision is ever revisited.

What the guide changed, and what we did

Guide delta Response
A published 30-objective index (D1.1–D5.6) Formalized in the blueprint with self-authored descriptions; one bank anchor per task statement
Scenario 1's real tool names Briefs re-authored around get_customer / lookup_order / process_refund / escalate_to_human, the 80%+ FCR target, four research subagents
Percent correct per domain on the report score emits pct=, labelled diagnostic-only
Prep guidance, exercises, program policies New data/ccaf-prep-guide.md
Multiple-choice and multiple-response ❌ single-answer only — see above

Weights unchanged (27/18/20/20/15 → 16/11/12/12/9).

Beyond what the real report gives you

  • Per-objective diagnostics. Every item carries a validated task: tag and score aggregates misses by task statement, so a result says D5.2 Escalation and ambiguity resolution — 0/2 rather than only "D5 is weak". The tag is validated at write time (well-formed, exists, belongs to its own domain) because a mistagged item would send a candidate to study the wrong objective — worse than no diagnostic.
  • Enforced answer-position spread. check_key_spread refuses a skewed mock. This matters because the reference bank's own keys lean toward A, and every anchor explanation names option letters — including English articles like "A bare numeric code (A) assumes…" — so remapping them automatically would have corrupted the prose. Enforcing the property beats hoping 30 examples teach it; the bank now also carries an explicit "do not imitate these answer positions" note.

Verification

  • 101 tests passing, 0 failed.
  • A real blueprint-conformant 60-item exam was accepted by init first trycomposition=OK, domains 16/11/12/12/9, key spread 15/15/15/15, zero keys in the questions file — with a recorded round-trip (including a lowercase answer) and a confirmed per-objective breakdown.
  • An earlier smoke test of the multi-response build found two unspecified administration details that would have hit the first candidate: option label must be the bare letter with the text in description, and (then) that multi answers arrive comma-separated. The first fix survives in this branch.

Performance note

Process creation is slow on some machines (~640 ms/fork measured on Windows + AV). An early grep-per-check version of the validation made the suite exhaust Cygwin's fork table mid-run, so it could not finish at all. Each validation check is now a single awk pass and normalize_answer is pure bash. CLAUDE.md records the constraint, and notes that the suite can still flake on Windows — re-run before investigating.

Still open

No full 60-item human run yet. The schema, composition enforcement, and a driven screen are verified; nobody has sat a complete mock end to end.

🤖 Generated with Claude Code

Dinesh-Girbide and others added 3 commits August 10, 2026 16:59
…sk statements, prep guide

The revised CCAF exam guide (v1.0, effective July 2026, exam code CCAR-F)
changes the item format and publishes a full objective index. Aligns the
plugin to both.

Multiple-response items. The guide specifies multiple-choice *and*
multiple-response items, each stating how many responses to select, so a mock
is now 45 `select: 1` + 11 `select: 2` + 4 `select: 3` (25% multiple-response,
enforced exactly; choose-three capped at 5). Items carry a `select:` line that
survives into the key-free questions file, because the administering skill
needs the count without ever seeing the key. `init` rejects any item whose
`select:` disagrees with its `answer_key`, repeats a letter, or lists letters
out of A-D order — and holds a pre-filled `user_answer` to the same shape,
since scoring is exact string comparison and an unsorted "DB" would otherwise
score a correct "BD" as wrong. `record` accepts a letter set in any case or
order and normalizes it, so scoring is exact set equality: all-or-nothing, no
partial credit. Multiple-response items render with `multiSelect: true`; a
wrong response count is re-asked once. The key-spread guard now covers the
single-answer items only, with a band proportional to their count.

Known divergence, disclosed in the README rather than hidden: every item has
four options (A-D) because AskUserQuestion renders at most four. That makes a
choose-three item easier than a five-option one would be, which is why
choose-three stays a minority.

The 30 task statements are now real. The blueprint formalizes D1.1-D5.6 with
self-authored descriptions naming the exact identifiers items use (Task /
allowedTools, AgentDefinition, fork_session, PostToolUse, context: fork,
allowed-tools, argument-hint, Explore subagent, @import, ~/.claude.json,
--json-schema, Pydantic, state manifests, the interview pattern, the
Edit->Read+Write fallback). This closes a dangling reference: ccaf-tutor and
ccaf-check-author already cited these codes against a blueprint that never
defined them.

Scenario facts corrected. Case briefs re-authored (still our own prose) around
the guide's actual identifiers get_customer / lookup_order / process_refund /
escalate_to_human, the 80%+ first-contact-resolution target, and four research
subagents including a separate report generator. The previously invented names
are gone.

Bank grown 12 -> 24. Twelve new self-authored anchors covering D1.2, D1.4,
D1.5, D2.1, D2.4, D3.3, D3.4, D4.5, D4.6, D5.1, D5.2, D5.3 — five of them
multiple-response so that format has anchors too. All 24 tagged with `task:`
and `select:`. Six task statements still lack an anchor (D1.7, D2.5, D3.5,
D4.2, D4.4, D5.4); flagged in the bank, not hidden.

Reporting. `score` emits `pct=` per domain, matching the real score report's
percent-correct-by-domain, presented as diagnostic-only since pass/fail is the
total scaled score.

New data/ccaf-prep-guide.md. Study routes, four hands-on exercises mapped to
task statements, multiple-response answering strategy, and certification
logistics (fee, Pearson VUE delivery, 14/30/90-day retake ladder, 12-month
validity, free renewal assessment). The tutor can assign exercises at domain
boundaries.

Everything shipped stays self-authored: the guide's published facts are
encoded, none of its prose is, and its own sample questions were rewritten as
original items rather than quoted. The guide PDF is gitignored so it can never
ship with the plugin.

Fork-cost discipline: process creation is slow on some machines (~640ms/fork
measured on Windows + AV), so normalize_answer is pure bash and each
validation check is a single awk pass. Net effect is that `score` got faster
than before this change despite the added checks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The test suite could not finish on Windows. It died partway through with
Cygwin's `fork: Resource temporarily unavailable` (`dofork: child died
unexpectedly ... 0xC000026B`) — not an assertion failure, but the shell running
out of fork capacity. Process creation costs ~640ms here (20 sequential greps
= 12.8s), and check_composition_questions spent ~15 subprocesses per call
while running on every init and every score.

Folds check_response_mix into check_composition_questions and rewrites the
whole thing as a single awk pass covering domain quotas, the item-format mix,
the scenario count, per-scenario case blocks, unknown scenarios, and
case-section layout. Structural problems are recorded rather than reported on
sight, so failures still surface in the documented order (quotas, mix,
scenarios, then layout) and every existing error message is preserved verbatim.

Measured on a valid 60-item exam: init 2.8s, audit 2.6s, score 1.8s. The full
suite now completes — 109 passed, 0 failed — where it previously aborted around
37. Verified every rejection branch individually (bad quota, all-single-select,
three scenarios, missing case block, question under the wrong case block,
up-front case blocks, duplicate case block, unlisted scenario) still produces
its own specific message.

Records the constraint in CLAUDE.md so the grep-per-check shape does not come
back: it is what made the suite unrunnable, not a micro-optimisation.

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

Three follow-ups, plus two administration bugs the smoke test caught.

Per-task-statement diagnostics. Every item now carries a validated `task:` tag
(D1.1-D5.6) and `score` aggregates misses by task statement, so a result names
the objectives to revisit ("D5.2 Escalation and ambiguity resolution 0/2")
rather than only the weak domain. The real score report stops at domains; this
is the most actionable thing a mock can add, because it turns a weak domain
into a short reading list. `score` reads both files to join question to task
rather than widening the answers-file line format. The tag is validated at
write time — it must be well-formed, exist, and belong to the item's own domain
(D1 publishes 7 task statements, D2 5, D3-D5 6 each) — because a mistagged item
would send a candidate to study the wrong objective, which is worse than no
diagnostic at all.

Bank 24 -> 30, one anchor per task statement. Adds the six that were missing:
D1.7 (stale session state vs resume), D2.5 (Grep/Glob/Edit selection and the
Read+Write fallback), D3.5 (the interview pattern), D4.2 (few-shot for
structural variety), D4.4 (which validation failures a retry can actually fix),
D5.4 (context degradation and scratchpads). Two are multiple-response, so the
mix is now 23 single / 6 choose-two / 1 choose-three. Every task statement has
exactly one anchor, so a generator always has an in-objective example to match.

Two administration bugs, found by driving a real screen. Both were unspecified
in the skills and would have hit the first candidate:
  * AskUserQuestion option `label` must be the bare letter and the option text
    must go in `description`. Labels are expected to be a few words; a
    full-sentence label renders badly and truncates.
  * A multiSelect answer arrives comma-separated ("A, B") and must be joined
    ("AB") before recording. The helper rejects the raw string rather than
    guessing, so this fails loudly instead of mis-recording — but it stops the
    screen, so the skill has to strip the separators itself.

Also gitignores .vscode/ and records that the suite can flake on Windows when a
fork fails inside a fixture (re-run before investigating).

Verified: 119 passed, 0 failed. Separately, assembled a real blueprint-
conformant 60-item exam that init accepted first try (composition=OK, domains
16/11/12/12/9, mix 45/11/4, key spread 12/11/11/11, zero keys in the questions
file), drove one four-item screen through AskUserQuestion with a genuine
multiSelect item, recorded the round-trip, confirmed the per-objective
breakdown, and cleared the state. That exercise used placeholder stems for the
56 items it did not display, so it verifies the schema and the administration
path — not the quality of 60 generated items, which only the per-item verifier
subagents can judge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Dinesh-Girbide
Dinesh-Girbide requested a review from a team August 10, 2026 16:42
…port

Dinesh's call: keep single-option questions, and otherwise follow the guide.

This is a known DIVERGENCE from exam guide v1.0, not alignment with it. The
guide gives the item format as "multiple-choice and multiple-response items;
each item states how many responses to select". Serving single-answer only is a
deliberate decision for this plugin, and it is recorded as such in five places
rather than papered over: the blueprint's "Deliberate divergence" section, the
README fidelity table (now an explicit ❌ row), CLAUDE.md conventions, the prep
guide, and the tutor — which tells learners the format exists, that it is scored
all-or-nothing, and that they will not get to rehearse it here.

The consequence is stated wherever the divergence appears: single-answer items
are the easier format, so a score here is if anything OPTIMISTIC. 720 on a mock
is the floor of readiness, not a comfortable margin.

Removed rather than left dormant, per the Clean Code pack's dead-code rule —
version control is the archive: the `select:` payload field, multi-letter keys
and answer-set normalization, the 45/11/4 mix enforcement,
check_select_alignment, the select histogram in `audit`, multiSelect rendering,
the response-count re-ask, and the comma-join step. `normalize_answer` shrank to
a case-insensitive single letter, which still earns its place — a candidate can
type `c` into the free-text field instead of picking the option.

The seven multiple-response bank anchors were REWRITTEN as single-answer
questions on the same task statements, not deleted, so all 30 task statements
keep exactly one anchor.

One side effect worth naming. Rewriting those seven left the bank's key letters
skewed toward A, and every explanation names option letters — including English
articles ("A bare numeric code (A) assumes...") — so an automated remap would
have corrupted the prose. Rather than massage the examples, the property is now
enforced: check_key_spread tightened from a sixth-to-a-half band to a
sixth-to-a-third, and the bank carries an explicit "do not imitate these answer
positions" note. Enforcing beats hoping 30 examples teach it.

Everything else from guide v1.0 stays: the 30 task statements, the corrected
scenario identifiers, percent-per-domain plus per-objective reporting, the prep
guide, and the certification logistics.

Verified: 101 passed, 0 failed. A real 60-item assembly was accepted first try —
composition=OK, domains 16/11/12/12/9, key spread 15/15/15/15, zero keys in the
questions file — and a lowercase answer recorded and scored correctly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Dinesh-Girbide Dinesh-Girbide changed the title feat(ccaf): align to exam guide v1.0 — multiple-response items, 30 task statements, per-objective diagnostics feat(ccaf): align to exam guide v1.0 — 30 task statements, per-objective diagnostics (single-answer items) Aug 12, 2026
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