# Canonical Analytics Alpha Surface

## Canonical URLs

- Architecture: https://platform3-andymontgomery-9773s-projects.vercel.app/analytics/alpha/architecture
- Data dictionary: https://platform3-andymontgomery-9773s-projects.vercel.app/analytics/alpha/data_dictionary
- Customer website: https://platform3-andymontgomery-9773s-projects.vercel.app/analytics/alpha/customer_website
- Implementation API: https://platform3-andymontgomery-9773s-projects.vercel.app/analytics/alpha/implementation/api
- Surface QC: https://platform3-andymontgomery-9773s-projects.vercel.app/analytics/alpha/surface_qc
- Integration gate: https://platform3-andymontgomery-9773s-projects.vercel.app/analytics/alpha/integration
- Live convergence answer key: https://platform3-andymontgomery-9773s-projects.vercel.app/analytics/alpha/integration/convergence.json

Analytics Alpha is a new Alpha module with no 1EdTech base and no alias map. It is extend-only storage under alpha.analytics_* tables, sourced from Events and Results by references plus derived values.

The skill-pack verifier uses the integration convergence key as the platform-owned answer key for demo Learning Report panels. `checks/expected.json` carries row selectors and the answer-key URL; it does not carry the pinned panel values.

Reviewer/eval tenants are operational probe tenants, not customer history
stores. For `reviewer-analytics` and `analytics-*debug*`, superseded or
tombstoned Analytics materializations are swept after migration/import
materialization while current rows and source-import audit rows remain.

## Credentials

Demo:

    export ANALYTICS_BASE_URL="https://platform3-andymontgomery-9773s-projects.vercel.app/analytics/alpha/implementation/api"
    curl -fsS -X POST "$ANALYTICS_BASE_URL/dev/mint?tenantId=demo"

Real or reviewer:

- Use ANALYTICS_TOKEN or ANALYTICS_REVIEWER_JWT supplied by the operator.
- Never print, save, or commit bearer tokens.
- Send Authorization: Bearer <token>.
- Send X-Timeback-Tenant when the tenant is supplied out-of-band.

## Endpoint Catalog

- POST /dev/mint
- GET /alpha/analytics/v1/event-time-facts
- GET /alpha/analytics/v1/time-windows
- GET /alpha/analytics/v1/school-day-minutes
- GET /alpha/analytics/v1/xp-rollups
- GET /alpha/analytics/v1/accuracy-rollups
- GET /alpha/analytics/v1/attempt-rollups
- GET /alpha/analytics/v1/response-latency-rollups
- GET /alpha/analytics/v1/hint-usage-rollups
- GET /alpha/analytics/v1/mastery-deltas
- GET /alpha/analytics/v1/map-growth-rollups
- GET /alpha/analytics/v1/completion-rollups
- GET /alpha/analytics/v1/source-imports
- GET /alpha/analytics/v1/norms
- GET /alpha/analytics/v1/norms/table
- GET /alpha/analytics/v1/norms/rit
- GET /alpha/analytics/v1/norms/percentile
- GET /alpha/analytics/v1/r90
- GET /alpha/analytics/v1/r90/table
- GET /alpha/analytics/v1/rit-to-grade
- GET /alpha/analytics/v1/rit-to-grade/table
- GET /alpha/analytics/v1/school-days-remaining
- GET /alpha/analytics/v1/grade-level-status
- POST /alpha/analytics/v1/source-imports/timeback-xp-time-accuracy
- POST /alpha/analytics/v1/source-imports/timeback-map
- POST /alpha/analytics/v1/source-imports/timeback-grade-mastery
- POST /alpha/analytics/v1/source-imports/timeback-completion
- POST /alpha/analytics/v1/source-imports/migration-reconcile

## Typed Filters

Common read filters:

- studentId
- subjectId
- startDate
- endDate
- modifiedSince
- sort
- limit
- cursor

All list endpoints are page-bounded. Postgres reads fetch at most `limit + 1`
rows to determine `hasMore`; they do not run an unbounded total-count query.
Completion-rollup reads use current-row indexes and ordinary responses exclude
superseded or tombstoned rows.

Endpoint-specific filters:

- event-time-facts: factKind, sourceEventId
- time-windows: windowKind
- xp-rollups: windowKind
- accuracy-rollups: windowKind
- attempt-rollups: windowKind
- response-latency-rollups: windowKind
- hint-usage-rollups: windowKind
- mastery-deltas: kcId, stateDimension
- map-growth-rollups: termId, canonicalTermId, growthWindow, normsSet
- completion-rollups: completionScope, scopeId
- source-imports: importKind, status
- norms and norms/table: subject, grade, season, role, normsSet, tableVersion
- norms/rit: subject, grade, season, role, normsSet, percentile
- norms/percentile: subject, grade, season, role, normsSet, rit
- r90 and r90/table: subject, rit, normsSet, tableVersion

For xp-rollups, `windowKind=range|term|school_year` with both dates is an exact
stored-window lookup. A miss returns `data: []` with
`meta.null_reason=no_exact_window`, `meta.requested_window`, and bounded
`meta.available_windows`; never substitute an overlapping window or recompute
XP in the read path.

School-day-minutes default reads are report answers: unless the caller passes
`includeUnavailable=true`, `GET /alpha/analytics/v1/school-day-minutes`
returns only canonical report-subject rows with `null_reason=none` and a
non-null `minutes_per_enrolled_school_day`. Rows with `subject_id=null`,
`source_missing`, `policy_pending`, or impossible minutes/day repair state are
audit rows, not Learning Report answers.

MAP growth rollup public fields include `start_rit` and canonical
`conditional_growth_percentile`; `growth_percentile` is a compatibility alias
that must mirror `conditional_growth_percentile`.
- school-days-remaining: studentId, asOf, endDate, subjectId
- grade-level-status: studentId, subject or subjectId, asOfDate

Grade-level-status returns exactly two named age-grade comparisons:
`working_age_grade_status = working_grade - age_grade` and
`mastered_age_grade_status = highest_mastered_grade - age_grade`.
Deprecated aliases may only delegate to those named fields; generic
ahead/behind/at is not a preferred public contract.

Unsupported filters are validation errors. Do not pass tenantId in query/body.

## Object Boundaries

- Events owns raw timestamped interactions.
- Results owns durable outcomes, current student state, XP ledger rows, MAP sitting records, and mastery state.
- Curriculum owns policies, KCs, course trees, and the main-course versus remediation/hole-filling/catalog distinction.
- People and Orgs owns student/school/enrollment identity.
- Analytics owns typed derived facts and report-ready rollups computed from those sources.
- Analytics owns attempt-count, response-latency, and hint-usage rollups.
  These are sourced from governed Events player extensions and source imports;
  apps read the Analytics endpoints instead of parsing Events extensions.
- Analytics also owns the Alpha-side readable norms and R90 reference resources plus the translations over them. Apps may read or cache a returned table by table_version, but never maintain an app-side copy. R90 percent fields are MAP-inferred grade-band position; actual course and grade-level progress comes only from completion-rollups.

## API-Only Skill Boundary

The customer website and data dictionary document raw-path convergence for surface QC, but this installable skill pack is for public Analytics API work only. If a user asks this skill to read Supabase, Postgres, private logs, raw Events payloads, or raw Results rows, stop and report that the request is outside the pack boundary. Do not add raw database access to an Analytics report or verifier built with this pack.
