# XP Policy Surface Map

Use this page when an app builder asks, "where do I read my course XP policy?"
There is no single course XP policy object. XP spans expected effort, reporting
rollups, and awarded-XP ledger behavior, and each concept has one owning surface.

## Short Answer

Read item expected effort from Content and course/tree expected-effort denominators
from Curriculum. Read report and motivational XP rollups from Analytics. Read
student-facing course/grade progress, XP remaining, and awarded-XP history from Results.
Award formulas are platform-owned: apps do not configure, copy, or run the
award, discount, penalty, passive-activity, or reversal math.

## Ownership Table

| XP concept | Owning surface | Read path | Write or configure? | App-builder rule |
| --- | --- | --- | --- | --- |
| Content item `expected_xp` | Content Alpha | `GET /tenants/{tenantId}/alpha/content/items/{contentId}` and list/detail responses | Content authoring/import may set the student-independent value; apps read it | Display the returned value. Do not average student attempts or infer expected effort from duration locally. |
| Source import expected-XP defaults and duration conversion | Content Alpha policy | Content descriptor/skill; tenant policy projection at `GET /tenants/{tenantId}/alpha/content/policies?policy_key=alpha.policy.content.source_adapter` when provisioned | Read-only import/calibration behavior; the runtime's versioned policy is authoritative even when a real tenant has no projected policy row yet | Use returned normalized Content fields. Do not copy `defaults_by_content_kind`, `minutes_to_xp`, or adapter rules into an app. |
| Expected-XP discovery band | Content Alpha policy and catalog facets | `GET /tenants/{tenantId}/alpha/content/items?...expected_xp_band=10_to_20`, `GET /catalog-facets`, and tenant `/policies?policy_key=alpha.policy.content.expected_xp_band` when present | Read-only governed facet assignment; Content writes/materializes it | Use the returned band/filter. `expected_xp_band=...` is numeric-only; `facet=expected_xp_band:...` requires the stored assignment and numeric match. Do not recompute or treat one as proof of the other. |
| Course, grade, and track expected-XP totals | Curriculum Alpha | `GET /alpha/curriculum/v1/components/{componentId}/tree` and component reads | Curriculum authoring/import may carry leaf/fallback `expected_xp`; aggregate totals are derived | Read Curriculum for course structure and expected-XP denominators. Do not double-count container and descendant values. |
| GOALS daily XP target and XP goal | Analytics Alpha | `GET /alpha/analytics/v1/xp-rollups` returns `daily_xp_goal`, `xp_goal`, `xp_remaining`, `xp_goal_percent`, `xp_completion_percent`, and `policy_ref` | Not app-configurable; Analytics owns the named policy such as `alpha.policy.analytics.xp_goal` | Render returned fields exactly. Do not fill missing goal fields with local formulas. |
| Learning Report XP total and XP rollups | Analytics Alpha | `GET /alpha/analytics/v1/xp-rollups` | Not app-configurable; rollups are report reads over platform evidence | Read `positive_xp`, `negative_xp`, `net_xp`, counts, refs, and policy refs. Do not sum ledgers or processed facts in the app. |
| Completion rollup evidence | Analytics Alpha | `GET /alpha/analytics/v1/completion-rollups` | Not app-configurable; evidence/report materialization only | Use for typed reporting evidence. Do not promote it into a second student-progress authority. |
| Student-facing course progress and grade-level progress | Results Alpha | `GET /alpha/results/v1/students/{studentId}/course-progress` and `GET /alpha/results/v1/students/{studentId}/grade-level-progress` | Not app-configurable; Results resolves Analytics evidence against Curriculum denominators | Read XP remaining before percent complete. Do not derive progress from MAP/RIT/R90, join Analytics locally, or walk Curriculum trees in the app. |
| School-days remaining and XP-to-time effort projection | Analytics Alpha | `GET /alpha/analytics/v1/school-days-remaining?...&xpRemaining=...` | Not app-configurable | Use returned `xp_unit` and `effort`; do not count calendar days or convert XP to hours locally. |
| Awarded-XP ledger history | Results Alpha | `GET /alpha/results/v1/students/{studentId}/xp-ledger` | Results writes immutable awards/reversals from settled attempts and platform evidence | Display ledger rows: `awarded_xp`, `award_reason`, `calculation_policy_ref`, `expected_xp_ref`, and `reversal_of_xp_award_id`. |
| Award, discount, penalty, passive-activity, and reversal formula | Results Alpha, platform-owned | Ledger rows expose `calculation_policy_ref`; Results may expose read-only `GET /alpha/results/v1/policy-config?policyName=alpha.policy.xp_award_formula` for policy evidence | Not app-configurable. There is no Content-style `/policies` route for Results/Analytics award math; `/policies` on those surfaces is expected to be absent | Do not compute or tune XP awards in a client. Do not award XP for articles/videos from engagement alone; Results verifies learning and writes the ledger. |
| First-attempt accuracy evidence and rollups | Events evidence; Analytics Alpha typed rollups; Results Alpha award consumer | Analytics accuracy rollups and Results ledger/source evidence refs | Events/renderers emit evidence only; they do not own thresholds | Players emit response, first-attempt, attempt ordinal, component/question provenance, and effort evidence. Results alone applies the current 80% lesson/video/article, 90% assessment, and 100% ×1.25 rules declared by its policy. |
| Raw TimeBack import expected-XP fallback | Results Alpha policy config | Results policy evidence under `alpha.policy.results.raw_timeback_import_expected_xp_default` when raw imports lack expected/source XP | Platform-owned import policy; not app-configurable | Preserve the returned `expected_xp_ref` source. Do not hardcode fallback expected XP in import clients. |

## Route Boundary Notes

Content `/policies` is a tenant-visible projection of versioned Content-owned
expected-XP configuration; it can be empty on real tenants that have not yet been
provisioned, so consumers must not treat it as the request-time adapter authority.
Results and Analytics do not use that route shape for
award math; a Results or Analytics `/policies` 404 is the ownership boundary,
not an invitation to configure awards in an app. Results `policy-config`, where
documented, is read-only policy evidence for platform-owned behavior.

## Practical Flow

1. To show "how much this course/content is expected to take," read Content
   `expected_xp` and Curriculum tree totals.
2. To show motivational/report XP against a daily or reporting goal, read
   Analytics `xp-rollups`, `daily-xp`, or `school-days-remaining`.
3. To show student-facing course/grade progress or remaining work, read Results
   `course-progress` or `grade-level-progress`; do not substitute Analytics evidence.
4. To show "what XP the student actually earned and why," read Results
   `xp-ledger`.
5. If the app needs an award formula value that is not returned by Results,
   file a Platform3 surface gap. Do not implement a local award formula.

## Source Anchors

- `loop/context/xp-system-brainlift.md`: expected vs awarded XP product intent.
- `loop/context/alpha-xp-decision.md`: XP rollup and award-formula ownership.
- `CLAUDE.md` non-negotiables 4–5 and `loop/DECISIONS.md`: actual-progress and expected-effort ownership.
- `loop/DECISIONS.md`: cross-module reference ownership.
- Content Alpha skill pack: expected-XP fields, policy reads, catalog facets.
- Analytics Alpha skill pack: XP rollups, completion rollups, and effort reads.
- Results Alpha skill pack: XP ledger and no local XP award math.
