PM Cram's study content is open-source JSON. Anyone can add a question, a case study, a guide or a flashcard by dropping a small JSON file into the repo and opening a pull request. This guide shows you exactly how each content type is shaped, with real, copyable examples.
The content lives as plain JSON under content/ in the Forge-Deck/pmcram repository. There's no CMS and no build step to learn — you edit text files.
content/questions/, content/cases/, and so on). Adding new content? Create a new file (e.g. content/questions/contrib-2026-07-yourhandle.json) rather than editing the big curated banks. Fixing a mistake? Edit the record where it lives.Files are JSON arrays. Every content file is a [ … ] array of records, even if it holds just one. You add content by appending a record to the array (or creating a new file that is a one-element array).
id is optional everywhere. The app derives a stable content-hash id from each record, so you never have to invent or track ids. Only set an id to pin a correctable identity (must be kebab-case) — and never reuse one that already exists.
Every record may carry contributor — your GitHub handle, for attribution. It's optional and never affects grading.
You don't have to run anything locally, but if you want to check your work before pushing, npm install then npm run validate runs the same checks CI does. npm run manifest regenerates the index — but CI regenerates it on merge, so you can skip it.
This applies to every question type that has options — practice questions, dynamic pools, and case steps. It's the single most important thing that separates a fair PMP question from a giveaway.
1. Distractors must be plausible "right-but-not-best" actions — things a competent PM might genuinely consider, that are wrong on target, sequence, owner, scope or timing, not because they're obviously silly. Common failure modes to lean on:
2. The correct answer must NOT be the longest or most-detailed option. If the key is always the wordiest, length telegraphs it. Keep all options comparable in length and specificity.
3. Never rely on order. Options are assembled and shuffled at runtime — never write "A/B/C/D", "the first option", or "all of the above". The app renders the correct answer plus a random sample of distractors and shuffles them.
No absolutes ("always", "never", "immediately"), no unethical tells, and no self-justifying phrasing in a distractor that gives the game away. If a served set of four options doesn't read as four legitimate PMP moves, it's too easy.
content/questions/ schema: "question/v1"
The workhorse. A single- or multi-select question that the app assembles at runtime: it renders present options made of the correct answer(s) plus a random sample of distractors, then shuffles. That's why you store a pool, not a fixed list.
domain required — one of People, Process, Business Environment.type required — single (choose one) or multi (choose N).select required — how many the user picks (integer, 1 for single).present required — total options shown. Must be > select and ≤ correct.length + distractors.length. Typically 4.prompts required — an array of one or more equivalent wordings; the app picks one at random.correct required — array of { text, why? }. For single this is a pool of interchangeable correct phrasings (the app shows one); for multi, its length must equal select.distractors required — array of { text, trap? }. Provide more than needed; the app samples present - select of them.difficulty topic subtopic subDomain eco_task explanation references signals trace rules tags group contributor
A bare question (no why/trap/explanation) still works and is scored right/wrong — but the per-option rationale is where most of the study value lives, so add it when you can.
[
{
"schema": "question/v1",
"domain": "Process",
"subDomain": "Schedule Management",
"difficulty": "medium",
"topic": "Critical Path",
"type": "single",
"select": 1,
"present": 4,
"prompts": [
"Two weeks into execution, an activity on the critical path slips by three days. What should the project manager do first?"
],
"correct": [
{ "text": "Analyse the schedule impact and identify options such as fast-tracking or crashing before committing.",
"why": "You assess the impact and evaluate compression options before choosing a response." }
],
"distractors": [
{ "text": "Crash the delayed activity by adding resources to recover the three days.",
"trap": "Premature solution — crashing adds cost and should follow impact analysis, not precede it." },
{ "text": "Update the baseline finish date to reflect the three-day slip.",
"trap": "Changing the baseline without change control hides the variance instead of managing it." },
{ "text": "Report the slippage to the sponsor and ask how to proceed.",
"trap": "Procedural deflection — escalating before you have analysed options and a recommendation." }
],
"explanation": "First quantify the impact on the critical path, then evaluate compression options; act, escalate or re-baseline only after that.",
"references": ["PMBOK Guide, Schedule Management"],
"contributor": "your-github-handle"
}
]
trap names the exact failure mode (premature solution, no change control, procedural deflection).type:"multi", select:2, present:6, put exactly 2 items in correct, and give ≥4 distractors.These are the same question/v1 schema — there is no separate "dynamic" type. The difference is entirely in how much pool you author.
prompt, one correct phrasing, just enough distractors. Every exposure is identical. Great for crisp factual recall.prompts, several interchangeable correct phrasings, and extra distractors. The engine draws one prompt + one correct + a sample of distractors and shuffles, so each exposure feels fresh.Use group to tie variants of one concept together. Questions that share a group are treated as phrasings of the same idea, so a mock or focused batch never serves two of them at once.
Because only one correct answer and a sample of distractors appear together, the correct answer must never be the longest of the four shown. Rule of thumb: at most two distractors may be shorter than the longest correct phrasing — keep them all comparable.
[
{
"schema": "question/v1",
"id": "dyn-single-point-of-accountability",
"domain": "People",
"subDomain": "Lead Project Team",
"type": "single",
"select": 1,
"present": 4,
"group": "raci-accountable-count",
"prompts": [
"A team is confused about a deliverable because two people are both marked Accountable on the RACI chart. What is the core problem?",
"On a RACI chart, one task lists two names in the Accountable column. Why is this a defect?"
],
"correct": [
{ "text": "Each task should have exactly one Accountable person, so a single owner can be held answerable.",
"why": "RACI allows many Responsible, but only one Accountable per task — otherwise ownership is ambiguous." },
{ "text": "Accountability must sit with a single person per task; two 'A's leaves no clear owner.",
"why": "The defining RACI rule is one Accountable per row." }
],
"distractors": [
{ "text": "Both names should be moved to the Responsible column so the work is shared.",
"trap": "Fixes the symptom by reshuffling letters but ignores the one-Accountable rule." },
{ "text": "The Consulted and Informed roles are missing, which is what causes the confusion.",
"trap": "Wrong target — the defect is the duplicate Accountable, not the C/I columns." },
{ "text": "A RACI chart is the wrong tool here; a responsibility histogram would be clearer.",
"trap": "Deflects to another tool instead of correcting the accountability error." },
{ "text": "The task simply needs a deadline added so the two owners know when to deliver.",
"trap": "Adds an unrelated attribute; timing was never the issue." }
],
"explanation": "RACI permits multiple Responsible parties but exactly one Accountable per task; two 'A's is the defect."
}
]
Same schema as example 1 — it just carries more prompts, more correct, more distractors, and a group key.
content/cases/ schema: "case/v1"
One shared scenario plus a steps array (2–8 steps). Each step is a mini-question with its own options. The app pins the scenario, reveals one step at a time, and only unlocks the next step once the current one is answered — so later steps can't spoil earlier ones. The golden rule for options applies to every step.
scenario required — the shared situation, shown above every step.steps required — 2–8 mini-questions. Each step needs prompt, correct[], distractors[]; optional per-step label, type, select, present, domain, topic, explanation, references.[
{
"schema": "case/v1",
"title": "Vendor slippage on a hybrid rollout",
"domain": "Process",
"difficulty": "medium",
"scenario": "You are leading a hybrid CRM rollout. A key integration vendor has quietly fallen two sprints behind, and your sponsor has just heard about it from a stakeholder rather than from you.",
"steps": [
{
"label": "First response",
"prompt": "What should you do first now that the sponsor knows about the delay?",
"correct": [
{ "text": "Gather the facts on the vendor's actual status and schedule impact before responding.",
"why": "You investigate and quantify the impact before communicating or acting." }
],
"distractors": [
{ "text": "Apologise to the sponsor and promise to recover the two sprints.",
"trap": "Commits to a recovery you have not yet analysed." },
{ "text": "Issue a formal notice to the vendor threatening penalty clauses.",
"trap": "Escalates contractually before establishing the facts." },
{ "text": "Add the delay to the risk register and continue as planned.",
"trap": "Logs the symptom but takes no action on a risk that has already occurred." }
],
"domain": "Process"
},
{
"label": "Governance",
"prompt": "Analysis confirms the delay threatens a contractual go-live date. What is the best next move?",
"correct": [
{ "text": "Raise it through integrated change control with impact options for the sponsor to decide.",
"why": "A schedule-baseline threat with contractual stakes belongs in change control." }
],
"distractors": [
{ "text": "Quietly fast-track internal tasks to absorb the vendor's slippage.",
"trap": "Hides a governance-level issue and absorbs risk without authority." },
{ "text": "Replace the vendor immediately to protect the go-live date.",
"trap": "Premature and disproportionate before options are weighed." }
],
"domain": "Business Environment"
}
]
}
]
content/sequences/ schema: "sequence/v1"
A drag-to-order question. You store the steps in the correct order; the app shuffles them and the user drags them back. 3–8 steps. Each step is { text, note? } — the optional note is a one-line rationale shown on review for that step's position.
prompt required — the instruction.steps required — 3–8 steps in correct order.[
{
"schema": "sequence/v1",
"prompt": "Order the steps of the Perform Integrated Change Control flow after a change request is raised.",
"domain": "Business Environment",
"topic": "Change Control",
"steps": [
{ "text": "Record the change request in the change log", "note": "Every request is logged before it is assessed." },
{ "text": "Assess the impact on scope, schedule, cost and risk", "note": "Evaluate before deciding." },
{ "text": "Submit the analysed request to the change control board", "note": "The CCB, not the PM alone, decides." },
{ "text": "Communicate the decision and update the affected plans", "note": "Approved changes update baselines; rejected ones are still recorded." }
],
"explanation": "Log, assess, decide via the CCB, then communicate and update — a request is never actioned before impact analysis."
}
]
content/figuregen/ schema: "figuregen/v1"
The advanced one. A figuregen record is a template, not a fixed question: it declares random variables and the app's figuregen engine rolls a fresh randomised variant each time it's served. Many numeric fields are expression strings evaluated against the rolled scope (e.g. "30 + i*60"). The engine draws the figure and grades taps/drags/choices against those expressions.
Every record needs schema ("figuregen/v1"), id (kebab-case), kind, domain, viewBox ([w, h]) and prompt. The kind is one of hotspot · choice · dragdrop · gantt · pareto · assign.
Figuregen is expression-driven and genuinely complex — draw ops, chart styles, guards, derived values. Rather than authoring one from scratch, open an existing file in content/figuregen/, copy the closest kind, and adapt its numbers and text. The worked templates there are the real reference.
The simplest kind is choice — a multiple-choice question about a small figure. Its answer block is choices[], each { text, correct, why? }, where correct is an expression that is true for the right option:
[
{
"schema": "figuregen/v1",
"id": "network-float-choice",
"kind": "choice",
"domain": "Process",
"topic": "Critical Path",
"viewBox": [280, 160],
"prompt": "Activity B has an early start of 4 and a late start of 9. How much total float does it have?",
"vars": { "es": { "sampleInt": [2, 6] }, "slack": { "sampleInt": [3, 7] } },
"derived": { "ls": "es + slack" },
"draw": [
{ "op": "rect", "x": 40, "y": 50, "w": 60, "h": 40 },
{ "op": "text", "x": 70, "y": 75, "text": "B" }
],
"choices": [
{ "text": "slack", "correct": true, "why": "Total float = late start minus early start." },
{ "text": "es", "correct": false, "why": "That is the early start, not the float." },
{ "text": "ls", "correct": false, "why": "That is the late start, not the float." },
{ "text": "0", "correct": false, "why": "Zero float would put the activity on the critical path." }
]
}
]
A hotspot template swaps choices for regions[] ({ id, x, y, w, h, correct, label?, why? }) so the user taps the correct area; dragdrop adds tokens[] dragged onto those regions. Again — copy a real one from content/figuregen/.
content/guides/ schema: "guide/v1"
Read-and-absorb study material — mindset, agile, strategy, cheat sheets. A guide is identified by its title (a natural key that dedupes case-insensitively), a category, and an array of sections.
title required, category required, sections required.heading + body required; optional tip, figure, blocks, signals, trace, rules, kicker.intro (the card teaser in the Guides list) and a summary.body is plain text with two rules only: a blank line starts a new paragraph, and a line starting with - becomes a bullet. There is no markdown parser, so don't use #, **bold**, or links in body.
For richer layout, a section can carry blocks — ordered structured blocks of kind tip · steps · cards · table · terms · formulas. Figures are inline svg (single-quoted attributes, currentColor for theming) or a raster image path.
[
{
"schema": "guide/v1",
"title": "Reading Earned Value at a Glance",
"category": "Formulae",
"summary": "Turn EV, PV and AC into a one-look health check.",
"intro": "CV and SV tell you where you stand; CPI and SPI tell you how fast the gap is growing. Learn to read all four in seconds.",
"sections": [
{
"heading": "The four numbers that matter",
"body": "Earned value analysis compares what you planned, what you did, and what it cost.\n\n- PV — the budgeted cost of work planned by now\n- EV — the budgeted cost of work actually completed\n- AC — what that completed work really cost",
"tip": "Variances (CV, SV) are money and schedule differences; indices (CPI, SPI) are ratios. Negative variance and an index below 1.0 both mean 'behind'.",
"blocks": [
{
"kind": "formulas",
"title": "Core EVM formulas",
"lines": [
"CV = EV - AC",
"SV = EV - PV",
"CPI = EV / AC",
"SPI = EV / PV"
]
}
]
}
]
}
]
content/guides/ schema: "guide/v1" category: "Exam"
Exam tips aren't a separate type — they're just guides with "category": "Exam". The app surfaces those in its dedicated Exam Prep tab and keeps them out of the main Guides list. Keep them tactical and heuristic: quick decision rules, trap-spotting, time management.
[
{
"schema": "guide/v1",
"title": "When in doubt, investigate first",
"category": "Exam",
"summary": "The single most reliable PMP tie-breaker.",
"intro": "Stuck between two good options? PMI almost always rewards understanding the situation before acting on it.",
"sections": [
{
"heading": "The investigate-first heuristic",
"body": "When two answers both look reasonable, prefer the one that gathers facts or assesses impact over the one that acts, escalates, or blames.\n\n- 'Meet with the team to understand the cause' usually beats 'reassign the work'\n- 'Assess the impact' usually beats 'submit a change request'\n- Escalation is rarely the first step",
"tip": "Watch for absolutes in the options — 'always', 'never', 'immediately' are usually traps."
}
]
}
]
Fill in a short form and get a valid question/v1 record you can paste straight into a file.