---
name: timeback-people-and-orgs-alpha
description: Use when building apps, reports, QA probes, migration checks, or deliverables against the TimeBack platform3 People & Orgs Alpha surface. The skill teaches agents to read roster people, places, dated place/class memberships, school periods, first-class guide rosters, school calendar day/range counts, NWEA district boundaries, sensitive profiles, app credential status, governed tags, and policy config by calling the live surface only. Do not infer roles from names, parse school periods, compute tenure buckets or calendar rules, reconstruct NWEA account grain, expose secrets, dedupe soft deletes, read OneRoster/raw databases, or implement Results/Events/Content/Curriculum logic.
---

# TimeBack People & Orgs Alpha

Use this skill when the user asks for a working app, report, migration reconciliation, QA probe, or customer deliverable that uses the TimeBack platform3 People & Orgs Alpha surface.

Do not use it for generic SIS/OneRoster work, direct database access, Results, Events, Content, Curriculum, mastery, MAP scoring, report cards, gradebook rollups, or credential-secret retrieval.

## Install

Install the whole `pack/` directory as one skill. Do not split the reference files away from `SKILL.md`.

Hosted one-command installs:

Claude Code:

```bash
curl -fsSLL https://platform3-andymontgomery-9773s-projects.vercel.app/people_and_orgs/alpha/skill_pack/install.sh | bash -s -- claude
```

Codex:

```bash
curl -fsSLL https://platform3-andymontgomery-9773s-projects.vercel.app/people_and_orgs/alpha/skill_pack/install.sh | bash -s -- codex
```

Perplexity Computer or another file-backed agent:

```bash
curl -fsSLL https://platform3-andymontgomery-9773s-projects.vercel.app/people_and_orgs/alpha/skill_pack/install.sh | bash -s -- perplexity ./skills/timeback-people-and-orgs-alpha
```

Local workspace install, when you already have this `OUT_DIR` checked out:

Claude Code:

```bash
mkdir -p ~/.claude/skills/timeback-people-and-orgs-alpha
cp -R pack/* ~/.claude/skills/timeback-people-and-orgs-alpha/
```

Codex:

```bash
mkdir -p ~/.codex/skills/timeback-people-and-orgs-alpha
cp -R pack/* ~/.codex/skills/timeback-people-and-orgs-alpha/
```

Perplexity Computer or another file-backed agent:

```bash
mkdir -p ./skills/timeback-people-and-orgs-alpha
cp -R pack/* ./skills/timeback-people-and-orgs-alpha/
```

The hosted installer fetches `SKILL.md`, all agent adapters, all references, the verifier, and the latest evidence. Set a custom install directory with the optional second argument, for example `bash -s -- codex /tmp/timeback-people-and-orgs-alpha`.

## People & Orgs In 60 Seconds

People & Orgs is the roster in school language. It answers who someone is and where they belong, as of a date.

Core objects:

- `person`: one human in the roster. A person's roles are memberships, not hidden inside the person row.
- `place`: a district, school, level, or other roster place. A district is the NWEA account boundary when it carries `nwea_district_id`.
- `place_membership`: dated link between a person and a place, with `role_kind`, `begin_date`, `end_date`, and `is_time_locatable`.
- `class_membership`: dated link between a person, a school, and a learning group.
- `school_period`: typed school-year or term. Use the typed fields; never parse the title.
- `school_calendar`: Ed-Fi CalendarDate-backed school-day facts exposed through People & Orgs reads.
- `person_sensitive_profile`: lawful sensitive profile fields behind the documented sensitive scope.
- `app_credential`: credential existence and rotation status only. The API never returns `secret_ref`.
- `tag_definition` and `object_tag`: governed reporting tags. Relationships are fields, not tags.
- `policy_config`: named `alpha.policy.*` config such as tenure buckets and school-year boundaries.

Alpha is views over OneRoster for renamed/cut/restricted roster objects. Only pure Alpha extensions add tables. The skill pack never reads those tables directly; it calls the public API.

## First Actions

1. Open the canonical docs before making claims:
   - Customer website: https://platform3-andymontgomery-9773s-projects.vercel.app/people_and_orgs/alpha/customer_website
   - Data dictionary: https://platform3-andymontgomery-9773s-projects.vercel.app/people_and_orgs/alpha/data_dictionary
   - Architecture decisions: https://platform3-andymontgomery-9773s-projects.vercel.app/people_and_orgs/alpha/architecture
2. Use `PEOPLE_AND_ORGS_BASE_URL` if credentials supply it. Otherwise use:
   `https://platform3-andymontgomery-9773s-projects.vercel.app/people_and_orgs/alpha/implementation/api`
3. Choose the credential path:
   - Demo: call `POST $PEOPLE_AND_ORGS_BASE_URL/dev/mint?tenantId=demo`, then use the returned bearer token.
   - Real or reviewer tenant: use the operator-supplied bearer token such as `PEOPLE_AND_ORGS_REVIEWER_JWT`. Never print or commit tokens.
4. Read `GET /` and keep its docs links and endpoint list next to you.
5. For roster, calendar, guide inverse, NWEA boundary, app credential, period membership, sensitive-profile, or migration checks, read `references/roster-recipes.md`.
6. To prove the pack is asking the platform correctly, run `node checks/verify-skill-pack.mjs`.
7. Before final output, read `references/leak-check.md` and confirm the binary leak check passes.

## Happy Path: North Valley Roster

```bash
export PEOPLE_AND_ORGS_BASE_URL="${PEOPLE_AND_ORGS_BASE_URL:-https://platform3-andymontgomery-9773s-projects.vercel.app/people_and_orgs/alpha/implementation/api}"
export PEOPLE_AND_ORGS_TOKEN="${PEOPLE_AND_ORGS_TOKEN:-$(curl -fsS -X POST "$PEOPLE_AND_ORGS_BASE_URL/dev/mint?tenantId=demo" | jq -r '.token')}"
export AS_OF_DATE="${AS_OF_DATE:-2026-09-15}"
export SCHOOL_ID="${SCHOOL_ID:-place_north_valley_school}"

curl -fsS "$PEOPLE_AND_ORGS_BASE_URL/places/$SCHOOL_ID/people?asOfDate=$AS_OF_DATE&roleKind=student" \
  -H "Authorization: Bearer $PEOPLE_AND_ORGS_TOKEN" \
  | jq '{count, students:[.data[] | {person_id, first_name, last_name, age_grade, membership_id:.membership.membership_id}]}'

curl -fsS "$PEOPLE_AND_ORGS_BASE_URL/school_periods/period_fall_2026/active_memberships?asOfDate=$AS_OF_DATE" \
  -H "Authorization: Bearer $PEOPLE_AND_ORGS_TOKEN" \
  | jq --arg school "$SCHOOL_ID" '{place_memberships:[.place_memberships[] | select(.place_id == $school) | .membership_id], class_memberships:[.class_memberships[] | select(.school_id == $school) | .membership_id]}'

curl -fsS "$PEOPLE_AND_ORGS_BASE_URL/guides/person_katherine_johnson/students?asOfDate=$AS_OF_DATE" \
  -H "Authorization: Bearer $PEOPLE_AND_ORGS_TOKEN" \
  | jq '.data[] | select(.student_person_id == "person_ada_lovelace") | {guide_person_id, student_person_id, learning_group_id, school_id, begin_date, end_date}'
```

Use the school-specific `/places/{schoolId}/people` response as the roster source. The period membership endpoint is period-wide; when a roster card needs class or guide evidence, attach returned membership rows to the `person_id` values already returned by the roster call. Use the returned `person_id`, `membership_id`, `learning_group_id`, `role_kind`, `brand`, `modality`, `nwea_district_id`, and `is_time_locatable` fields directly. Do not infer them from names or titles, and do not add a local dedupe layer to hide unexpected surface rows.

## Happy Path: North Valley School Calendar

Ask the surface for school-day facts. These answers come from `alpha.school_calendar`, a view over Ed-Fi `CalendarDate`; clients must not count weekdays, infer holidays, or keep local reason tables.

```bash
export PEOPLE_AND_ORGS_BASE_URL="${PEOPLE_AND_ORGS_BASE_URL:-https://platform3-andymontgomery-9773s-projects.vercel.app/people_and_orgs/alpha/implementation/api}"
export PEOPLE_AND_ORGS_TOKEN="${PEOPLE_AND_ORGS_TOKEN:-$(curl -fsS -X POST "$PEOPLE_AND_ORGS_BASE_URL/dev/mint?tenantId=demo" | jq -r '.token')}"
export SCHOOL_ID="${SCHOOL_ID:-place_north_valley_school}"

curl -fsS "$PEOPLE_AND_ORGS_BASE_URL/places/$SCHOOL_ID/school_calendar/2025-08-13" \
  -H "Authorization: Bearer $PEOPLE_AND_ORGS_TOKEN" \
  | jq '{calendar_date, is_school_day, reason}'

curl -fsS "$PEOPLE_AND_ORGS_BASE_URL/places/$SCHOOL_ID/school_calendar/2025-11-24" \
  -H "Authorization: Bearer $PEOPLE_AND_ORGS_TOKEN" \
  | jq '{calendar_date, is_school_day, reason}'

curl -fsS "$PEOPLE_AND_ORGS_BASE_URL/places/$SCHOOL_ID/school_calendar?startDate=2025-08-13&endDate=2026-07-24" \
  -H "Authorization: Bearer $PEOPLE_AND_ORGS_TOKEN" \
  | jq '{calendar_days:.count, school_days_between:.school_days_in_range, reason_values:([.data[].reason] | unique | sort)}'

curl -fsS "$PEOPLE_AND_ORGS_BASE_URL/places/$SCHOOL_ID/school_days_remaining?asOfDate=2026-06-12" \
  -H "Authorization: Bearer $PEOPLE_AND_ORGS_TOKEN" \
  | jq '{school_days_remaining}'
```

The count endpoint `GET /places/{placeId}/school_days_between?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD` returns the same server-side school-day count without enumerating every row. Use the range endpoint when you need the reason values present in the returned calendar rows.

## API Perimeter

The public People & Orgs Alpha surface exposes:

```text
POST /dev/mint?tenantId=demo
GET  /

GET  /people
POST /people
GET  /people/{personId}
PATCH /people/{personId}
DELETE /people/{personId}
GET  /people/{personId}/memberships

GET  /places
POST /places
GET  /places/{placeId}
PATCH /places/{placeId}
DELETE /places/{placeId}
GET  /places/{placeId}/people
GET  /places/{placeId}/schools
GET  /guides/{guidePersonId}/students?asOfDate=YYYY-MM-DD
GET  /places/{placeId}/school_calendar/{date}
GET  /places/{placeId}/school_calendar?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD
GET  /places/{placeId}/school_days_between?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD
GET  /places/{placeId}/school_days_remaining?asOfDate=YYYY-MM-DD
GET  /places/{placeId}/next_school_year_start?asOfDate=YYYY-MM-DD

GET  /place_memberships
POST /place_memberships
GET  /place_memberships/{membershipId}
PATCH /place_memberships/{membershipId}
DELETE /place_memberships/{membershipId}

GET  /class_memberships
POST /class_memberships
GET  /class_memberships/{membershipId}
PATCH /class_memberships/{membershipId}
DELETE /class_memberships/{membershipId}

GET  /school_periods
POST /school_periods
GET  /school_periods/{periodId}
PATCH /school_periods/{periodId}
DELETE /school_periods/{periodId}
GET  /school_periods/{periodId}/active_memberships

GET  /person_sensitive_profiles
POST /person_sensitive_profiles
GET  /person_sensitive_profiles/{personId}
PATCH /person_sensitive_profiles/{personId}
DELETE /person_sensitive_profiles/{personId}

GET  /app_credentials
POST /app_credentials
GET  /app_credentials/{studentSourcedId}/{appId}
PATCH /app_credentials/{studentSourcedId}/{appId}
DELETE /app_credentials/{studentSourcedId}/{appId}

GET  /tag_definitions
POST /tag_definitions
GET  /tag_definitions/{anchor}/{tagKey}
PATCH /tag_definitions/{anchor}/{tagKey}
DELETE /tag_definitions/{anchor}/{tagKey}

GET  /object_tags
POST /object_tags
GET  /object_tags/{objectKind}/{objectId}/{tagKey}
PATCH /object_tags/{objectKind}/{objectId}/{tagKey}
DELETE /object_tags/{objectKind}/{objectId}/{tagKey}

GET  /policy_config
```

Use `Authorization: Bearer <token>` on every non-mint call. Use `Idempotency-Key` on writes. Use the ETag from a detail read in `If-Match` for PATCH and DELETE. After deployed Vercel conditional writes, prefer a detail refetch; if chaining immediately, use `X-People-Orgs-ETag` as the next validator.

## Non-Negotiables

Treat these as binary rules:

- Do not infer role, school, class, guide, grade, brand, modality, or NWEA account from a name, email, title, URL, or free string.
- Do not parse school period titles. Use `period_kind`, `start_date`, `end_date`, `parent_period_id`, `school_year`, and `asOfDate`.
- Do not compute tenure buckets, school-year boundaries, weekdays, holidays, school-day counts, remaining-days counts, or calendar reason values in app code. Read `GET /policy_config` and the `school_calendar` routes.
- Do not make a current-only roster shortcut. Every roster answer must state and pass `asOfDate`.
- Do not include rows where the surface returns `is_time_locatable=false` for point-in-time membership questions.
- Do not reconstruct NWEA account grouping. Read the district parent, `nwea_district_id`, `brand`, and `modality` from Places.
- Do not store or display credential secrets. `app_credential` exposes existence and last rotation only.
- Do not expose sensitive profile data unless the user's job and token scope require it.
- Do not read `oneroster.*`, `alpha.*`, Supabase, Postgres, implementation source, private logs, or raw database tables for a user-facing workflow.
- Do not implement soft-delete dedupe, hygiene filters, real-student filters, join grain, or effective-date rules locally. The surface and dictionary own them.
- Do not mix this module with Events, Results, Content, Curriculum, gradebook, mastery, MAP scoring, or content effectiveness.
- Do not create local persistence for People & Orgs domain data. Browser UI preferences are acceptable; roster-shaped data belongs in the surface.

If the user job needs one of those missing primitives, stop and report a surface gap. Do not work around the gap with private code.

## Common Build Pattern

1. Mint or accept credentials.
2. Read the descriptor and canonical docs.
3. Pick the smallest endpoint that already returns the requested roster fact.
4. Pass typed filters such as `asOfDate`, `roleKind`, `filter`, `limit`, `cursor`, or `modifiedSince`.
5. Render only fields returned by the surface.
6. For writes, use `Idempotency-Key`; for corrections and logical deletes, read detail first and send that ETag as `If-Match`.
7. Verify demo examples with the integration convergence answer, not embedded client logic.
8. Run the leak check before final output.

## Reference Files

- Read `references/canonical-surface.md` for endpoint, credential, filter, header, scope, status, boundary, and field details.
- Read `references/roster-recipes.md` for teacher roster, school calendar, guide inverse, student context, NWEA boundary, period membership, app credential, sensitive-profile, tag, policy, and migration-check recipes.
- Read `references/worked-example.md` for an end-to-end North Valley roster workflow.
- Read `references/response-samples.md` for live demo JSON response shapes.
- Read `references/runnable-check.md` when you need the shortest proof that the pack reproduces platform-owned answers.
- Read `references/leak-check.md` before shipping an app, report, QA probe, migration check, or deliverable.
- Run `node checks/verify-skill-pack.mjs` to prove the pack still passes static no-leak checks and live surface checks.

## Expected Output Quality

Every output should make platform3 more useful than a generic agent:

- It should run against the live People & Orgs Alpha API, not a mock.
- It should include exact API calls or response evidence.
- It should name which canonical docs it used.
- It should preserve Problem JSON when calls fail.
- It should disclose any missing surface primitive instead of hiding it.
