PU & elections¶
PU (Punkty Umiejętności) is the daemon's authoritative skill-point ledger. SUMA grows
only through the monthly award batch — by the month's granted PU, base-added and
cap-limited, with the overflow routed into NADMIAR. This page covers running the batch,
reading the dated timeline and assignment history, previewing a new character's baseline,
and the election eligibility roll. Reads need pu.read; the batch needs pu.award. The
examples use the canonical session dated 2026-07-01,
which credits Eraster (owned by the Gracz Stefan) and Lord Tussal (owned by
Roman) 0.2 PU each.
Every example runs against a live daemon — see how the reference is tested.
Routes¶
| Method | Path | Cmdlet | Cap | Write |
|---|---|---|---|---|
| POST | /workflows/award-pu |
Invoke-NerthusPUAssignment (Test-NerthusPUAssignment forces ?dryRun=true) |
pu.award |
✓ |
| GET | /pu/timeline |
Get-NerthusPUTimeline |
pu.read |
— |
| GET | /pu/history |
Get-NerthusPUAssignmentHistory |
pu.read |
— |
| GET | /pu/new-character-count |
Get-NerthusNewPlayerCharacterPUCount |
pu.read |
— |
| GET | /elections/eligibility |
Get-NerthusElectionEligibility |
pu.read |
— |
Cap is the required capability (— = public, no token); Write (✓) marks routes that pass the write gate; a — in Cmdlet is reached directly, no wrapper. Paths are relative to /v1/api; the cross-cutting contract — middleware, envelopes, status codes — is on the API reference index.
GET /elections/eligibility requires pu.read (eligibility is PU-derived data); a pu.read.own caller self-checks, forced to their own Gracz. Formula and window: PU model.
Run the monthly PU batch (dry run)¶
POST /v1/api/workflows/award-pu with { month: "YYYY-MM" } runs (or, with
?dryRun=true, previews) the month's batch. The batch is fail-early: any unresolved @PU
character name aborts the whole month with no partial writes. A dry run computes the
Awards and returns JobId but touches no disk — proven by the follow-up read showing
Eraster's PuSuma unchanged at 60.
Response 200 (trimmed):
{ "Applied": false, "Month": "2026-07",
"CountedSessions": ["2026-07-01, Eraster rozmawia z Tussalem, Anward"],
"SkippedAlreadyCounted": 0,
"Awards": [{ "Character": "Eraster", "Granted": 1.2, "SumaBefore": 60, "SumaAfter": 61.2 }],
"JobId": "job_ab12cd34ef56" }
Run the batch as a background job¶
The batch runs as an in-daemon job. ?async=true returns 202 with the job envelope
{ jobId, statusUrl, status } instead of the inline result; poll statusUrl for the
outcome. Here it is paired with ?dryRun=true so the preview persists nothing.
Response 202:
{ "jobId": "job_ab12cd34ef56", "statusUrl": "/v1/api/jobs/job_ab12cd34ef56", "status": "completed" }
Rejected: month is required¶
A body without month is 400, nothing computed.
Response 400:
The PU timeline for a character¶
GET /v1/api/pu/timeline?character={name} returns the { count, items } envelope — the
dated assignment batches that credited the Postać, each carrying its granted sum and the
exact session headers. Without ?character=, one entry per batch.
Response 200:
The assignment history¶
GET /v1/api/pu/history returns every award batch as { count, assignments }, each row a
{ date, sessions } count of the sessions it settled.
Response 200:
New-character PU baseline¶
GET /v1/api/pu/new-character-count?player={name} previews the STARTOWE a new Postać of
that Gracz would receive — derived from the sum of ZDOBYTE across their existing
characters, with a Council-tunable divisor, bonus, and floor. Stefan owns Eraster
(PuZdobyte 22.5).
Response 200:
Election eligibility¶
GET /v1/api/elections/eligibility computes the WYBORY → UPRAWNIONY voting roll over the
rolling window as { count, items }. Each row scores a Gracz by summed session PU plus one
per distinct active month, against the threshold. Both Stefan and Roman appear from the
2026-07-01 session.
Response 200 (trimmed):
{ "count": 2, "items": [
{ "Gracz": "Stefan", "PuScore": 0.2, "ActiveMonths": 1, "Total": 1.2, "Status": "NIEUPRAWNIONY" },
{ "Gracz": "Roman", "PuScore": 0.2, "ActiveMonths": 1, "Total": 1.2, "Status": "NIEUPRAWNIONY" }
] }
Rejected: a token without pu.read¶
The PU routes require pu.read. A token scoped to entity.read alone is 403, naming the
capability it lacks.
Response 403: