Skip to content

Labelled samples

Nerthus.Core (until cutover). This page describes the frozen system that runs today and is deleted at cutover. Replaced by: not yet written.

Every project in the Nerthus normalization programme produces labelled data as a side effect — resolve verdicts, parse decisions, measured morphology, typed proposals — and deposits it into repozytorium-adnotacji, a data repository of annotations over corpus spans. This route reads that store, and publishes the census a consumer needs in order to read the rows correctly.

Routes

Method Path Cmdlet Cap Write
GET /training/samples Get-NerthusTrainingSample log.read
POST /training/samples/confirm Add-NerthusTrainingSampleRuling sample.confirm
POST /training/samples/confirmations Add-NerthusTrainingSampleRulingSet sample.confirm

client/Cmdlets-Entities.ps1 wraps all three. Paths are relative to /v1/api; the cross-cutting contract is on the API reference index.

Get-NerthusTrainingSample returns the whole envelope rather than unwrapping to .items, because three fields change how the rows must be read: stats counts the whole store rather than the filter, coverage says the store is partial by construction, and doesNotCheck says what a presence surface cannot answer. A cmdlet returning the rows alone would drop all three.

The read is replica-safe and poolable. The two writes touch the annotation tree and no other, and both honour ?dryRun. They cost sample.confirm (since 2026-09-03, ruling H123.3), which the Narrator and Radny bundles carry — a narrator ruling on a row from the close-session screen writes the sample store and nothing in the entity index, so the price is not entity.write.

Where the rows live

corpora.adnotacje in config.json names the working tree, the same shape corpora.mc and corpora.dziel already use. A host with no tree answers available: false with a reason rather than failing — an unreachable store and an empty one are different facts and read differently to whoever asked.

The tree carries samples/<class>.jsonl, one JSON object per line, and a published copy of schema.json. The contract is authored and enforced in Nerthus.Lang.Annotations, which writes that copy and fails when the two diverge; the daemon reads the copy, because bucketing a row needs the contract and a count without buckets is the number this route exists not to publish alone.

Query parameters

Parameter Means
class one class — a value outside the store's registered classes is 400 TrainingSampleFilterInvalid
grade confirmed, measured, proposed, or a comma-joined list of them
project the depositing project, as provenance.project records it
label one label inside a class
entity one name — keeps the rows naming it. A name the index does not hold is 400 TrainingSampleFilterInvalid, and the filter echoes itself on the body
scope one session header, or corpus — keeps the rows whose fields.scope is that evening (an entity-span bound in one session, an epithet that night's cast resolves) or, for corpus, the rows true everywhere. Compared whole and case-insensitively; echoes itself on the body as scope, for the reason entity does
limit, offset page the rows, as on every other list route

Awaiting confirmation is ?grade=proposed, and a caller that also wants the weak supervision beside it asks ?grade=proposed,measured. That filter is the pending view, rather than a second endpoint over the same rows.

Rows are served in the order the class file holds them, and a depositing project may make that order mean something. Project 56 writes its two classes in the order a confirmation is worth most — fields.queue carries the tier, the rank and the cluster the row belongs to — so ?class=narration-subject&project=56-found-labels pages a ranked queue head first with no ordering parameter and no second surface. A class file holds several projects' rows in deposit order, so the ranking is meaningful within a project's own rows and ?project= is what selects them.

A misspelled class or grade is a 400 rather than an empty page: a caller who mistypes one deserves to hear so, not to conclude the store is empty.

?entity= — the rows about one name, and why it echoes

An unrecognised query parameter is dropped in silence. That is the default across the API and it is safe for most parameters and unsafe for this one: a client filtering by a name against a host that predates this filter would render the whole store as that one name's rows, which is worse than the feature's absence. So two things travel with it.

The filter echoes itself. ?entity=Riveth comes back as "entity": "Riveth" on the body. A consumer compares the echo against what it asked for and refuses to render when it is missing or differs — that comparison is the only way to tell a host that filtered from one that ignored the parameter.

A name the index does not hold is refused, matching what class and grade already do rather than the silent-ignore default. An empty page cannot be told apart from a typo.

GET /v1/api/training/samples?entity=Riveth&limit=200
200  { "entity": "Riveth", "filtered": 23, "count": 23, … }

GET /v1/api/training/samples?entity=NieMaTakiego
400  { "error": "TrainingSampleFilterInvalid",
       "detail": "UnknownEntity: 'NieMaTakiego' is not a name this index holds" }

Which classes it can reach is published, because most of them it cannot. A class names an entity only where the store's schema.json declares entity_fields for it, and the response carries both halves:

{ "entityCoverage": {
    "searched": ["all-caps-token-kind", "derived-word-base", "entity-mention", "entity-proposal",
                 "identity-claim", "name-declension", "normalization-suggestion", "vocative-form"],
    "blind": ["addressee-by-exclusion", "document-diegesis", "… 14 classes in all"] } }

8 of the store's 22 classes name an entity; 14 are blind to this filter at any value. That is a property of the data, not a defect — a location-separator row is about a line, not about a person — but a caller reading a small number without it would conclude a name has few rows when it has few reachable rows. The map lives beside the classes in the store's schema and never in the daemon, so a class that gains an entity field is reachable without a daemon change.

fields.resolved on vocative-form and all-caps-token-kind is what the resolver answered, with the stage beside it{"name": "Torneg", "type": "Mapa", "stage": "exact"} — or null with fields.resolved_note. The stage is load-bearing: exact is a lookup and fuzzy is a guess, and the two must not be rendered alike. It is evidence about the surface and never a claim that the surface is that entity; the class that asserts an identity is identity-claim.

An unrecognised parameter is dropped in silence, and the route then answers everything

The 400 covers a bad value for a parameter this route knows. A parameter it does not know is not an error and not a filter — it is ignored. Driven 2026-08-16 against a read-only daemon at Nerthus.Core e045040: ?subject=Riveth answers 200 with filtered equal to totalevery row in the store, unfiltered — and nothing in the body says the word subject was never read. On the same boot ?class=nonsense is a 400 and ?grade=proposed filters correctly, so the difference is the parameter name, not the value. The failure is open, so a caller who mistypes a parameter name gets the whole store back and may read it as the answer to their question. Compare filtered against total if that distinction matters to you.

This is why ?entity= echoes itself: against a host old enough not to know it, the parameter would be dropped and the whole store returned as one name's rows. The echo is the only thing that tells those two answers apart, and it was ?entity= that this warning used as its example until the filter existed.

The queue: the order the rows arrive in

A class file is paged in the order the file holds it, so where a depositing project wrote its rows in a considered order, ?class=…&project=… pages that order head first. There is no sort parameter and no second surface — the ranking is a property of the rows.

Where a project has ranked its rows it also records why, in fields.queue, so the order is readable rather than implicit. The three tiers in the store today, measured 2026-08-12 at repozytorium-adnotacji-dev fc46ed1:

Tier What it means Rows
contested two independent instruments disagree about this claim, so only a person can settle it 1 479
unattested no second instrument answered at all — the subject reaches no entity block, or the class has no second instrument 1 056
corroborated the daemon's own resolver already reaches the same answer, so a ruling buys agreement with something that already agrees 2 409

Within a tier the order is lexicographic over measured properties and never a weighted sum, because a weight is a constant somebody chose: cluster reach first — every claim naming the same subject — then row reach.

A class file holds several projects' rows in deposit order, so a ranking is meaningful within one project's rows and ?project= is what selects them. entity-proposal serves project 33's rows before project 56's.

The response

{
  "count": 20, "total": 9618, "limit": 20, "offset": 0,
  "available": true,
  "filtered": 9618,
  "items": [ ... ],
  "stats": { "total": 9618, "byClass": {}, "byGrade": {}, "byProject": {}, "byLabel": {},
             "split": {}, "evidenceForm": {}, "claimKeyFold": {} },
  "coverage": { "partial": true, "depositors": [ ... ], "note": "..." },
  "doesNotCheck": [ ... ]
}

stats counts the whole store; items and filtered describe the filter. A census computed over the caller's filter answers a different question every time it is asked, and the number a consumer needs from this surface is one it can compare against yesterday's.

A read that names a class parses that class's file and no other, and a scope it can search for as text narrows the parse to the lines that carry it; stats still counts the whole store, from a streaming pass that keeps six fields per row. Measured 2026-09-04: the old read parsed all 26 874 rows into objects for a one-row question, threw OutOfMemoryException inside dev-eder's 3 GB container, and the daemon answered nothing for ten minutes. An error that is not a bad parameter value is no longer reported as TrainingSampleFilterInvalid.

The census reports parts, never a total

A store surface that answers how many samples, by class and by grade and stops there is the shape that shipped an aggregate recall of 0.77 averaging 0.95 on one family with 0.32 on another, and told nobody anything until a reviewer split it. So every axis a consumer could be misled by is broken out.

split — the four document-identity buckets, and they reconcile. A train/eval split over this store must partition by document and never by row: several of these corpora are formulaic, so one statement recurs across many documents in slightly different wording, and a row-level split puts near-duplicates on both sides while the floors it produces read as healthy.

bucket means
byDocument the row carries locator.document
bySource its corpus declares document_identity: source, so locator.source is the document
aggregate a corpus-aggregate locator — it names a population, not a document, so it is not a document-level unit at all
undocumented its corpus declares document_identity: locator.document and the row carries none, or it names a corpus nobody registered

reconciles is true when the four sum to total. undocumented is the actionable number and the other three are published so it cannot be made to look small by rows quietly leaving the census. Counting instead every row lacking locator.document gives most of the store, which is true, unactionable, and therefore unread.

The evidence-form census can speak about 17 of the 1 816 rows awaiting a human

fields.evidence_form is required only by a class declaring evidence_form_required, and two classes declare itgovernance-transition and index-heading-shape. Re-measured 2026-08-12 at repozytorium-adnotacji-dev fc46ed1: those two hold 264 rows, 180 of which carry a recorded form — both figures unchanged from 2026-08-11 — but only 17 of the 264 are still proposed, because 247 governance-transition rows were confirmed in between. So the census now speaks about 17 of the 1 816 rows awaiting a human, and for the other 1 799 it has nothing to say. A silent census is not an all-clear — it is the same absence NO-DOC and NO-FORM are counted for. Any consumer of this block, including the confirmation screen's bias warning, inherits that bound, and this is the case that shows why a figure needs its date: both numbers re-derived exactly and the sentence around them had stopped being true.

evidenceForm — per label, never per class. label is a printed field found by typography, phrase a fixed formula matched as a string, prose an ordinary sentence with a predicate. A per-class figure reports "mixed" and hides the one label a model would learn typography from: measured on 2026-08-11 over the store's governance-transition rows, set runs 21 label / 3 phrase / 1 prose while appoint in the same class runs 42 prose to 3 label. A model trained on set learns layout, and an eval set drawn from the same sieve scores it well. The no-form column counts rows written before the field existed.

claimKeyFold — how each class compares its claim key, and which classes nothing checks. A class may declare a claim_key, the fields identifying what a row claims rather than which row it is, and two rows sharing one are refused as a duplicate. That comparison is a string comparison, so each class also declares how the string is folded: ordinal keeps it as written, whitespace collapses \s+, name folds case as well. Five classes are one row per occurrence, carry no key at all, and are checked for duplicates by nothing — the entry reports keyed: false so that limit is visible rather than inferred from a silence.

Coverage is partial by construction

coverage.depositors is derived from the rows rather than declared, so it cannot go stale. The store holds what the projects that have run so far deposited, and a class with no rows is a project that has not deposited, never a measurement that the corpus holds none of that shape.

What the surface does not check

doesNotCheck is on every response rather than only in this page, because the consumer reads the body. A read surface answers presence, and presence is not correctness: a recall figure asks is the fact there and never is it there with the right date, and one sentence once produced two ledger rows sixteen days apart that no census could see. The list names that, the unkeyed classes, the fold each class lets through, and the fact that locator hashes are checked by the store's own validator rather than here.

Confirming a row

A row deposited proposed is a work-queue entry. A human ruling on it is what makes it a training label, and this is the path that records the ruling. It writes into the annotation tree and nowhere else: where a confirmation implies a lore edit — an accepted alias — that edit stays a separate operator gesture, as POST /normalization/aliases/accept already keeps it.

POST /v1/api/training/samples/confirm
{ "id": "40/governance-transition/N1", "class": "governance-transition", "outcome": "affirm" }
Field Means
id the sample. The id is in the body and not in the path: every id in the store carries a / by its <project>/<class>/<slug> convention, so it cannot be a path segment
class optional, and checked against the row rather than trusted — a client that named the wrong one is ruling on a row it is not looking at
outcome affirm, deny, or correct
label required by correct, refused by the other two; must be in the class's vocabulary and must differ from the one the row carries
note optional — the person's own words about the ruling
referent optional, with affirm or correct only — the entity-index heading the span denotes, or undeclared; the class must declare referent among its required_fields (entity-span does). Lands in fields.referent; the value the row carried before goes to accepted_by.proposed_referent. A second ruling that keeps the label but names another referent is a disagreement and is refused.

The author is never in the body. method.accepted_by.who comes from the caller's token identity — gracz:<margonem id>, api:<margonem id>, token:<name> — so a client cannot attribute a ruling to somebody who did not make it, and the whole value of a confirmed row is that somebody did.

The three outcomes, and why a rejection is a verdict rather than a label

All three land as one confirmed row, and all three set method.accepted_by: who, artifact, at, and verdict.

Outcome label verdict proposed_label
affirm unchanged affirmed absent
deny unchanged denied absent
correct the label the person ruled affirmed the label it replaced

A denial keeps the label, because the label says what was denied. A label answers which kind; a verdict answers true or false.

The test is one question, asked class by class: does the vocabulary contain a value that denies what the row's existence presupposes? Re-measured 2026-08-12 at repozytorium-adnotacji-dev fc46ed1, nine of the twenty-two classes have no such value, and they hold 1 701 of the 1 816 rows awaiting a human and 2 425 of all 10 819. The nine are the same nine; the store has grown around them.

class proposed why no label can deny it
normalization-suggestion 1 441 the labels name which sieve proposed the row; this surface is not an alias of that entity is unsayable
governance-transition 247 the question is did this post record a change, and how, and the vocabulary answers only the how
entity-mention 236 presupposes the span is a located mention of an entity. unknown is an entity of unsettled kind, not no entity here — and fields.shape exists to separate a mention from a derived word, so not a mention is a live outcome
entity-proposal 24 presupposes the name should hold a block; every label answers of what class
derived-word-base 0 one label, derivation
ner-type-contradiction 0 the labels are the tagger's type, not agree-or-disagree
location-line-unterminated 0 one label, and the question is a yes/no
location-line-parse 0 both labels are defect kinds
external-source-form 0 a compound question whose second half — does the fetch machinery reach it — is carried by fields.reachable and by no label; undecided is structurally unknown

The other thirteen partition their question exhaustively — 115 proposed rows, 8 394 in all — and there denying and correcting are the same act. 1 701 + 115 = 1 816, and 2 425 + 8 394 = 10 819.

What this partition is prone to

A twenty-third class, entity-span (2026-09-03, 123-ner-plan), sits on the left from birth: it is the first span-level class, its none denies that a located span is a mention of anything, and its entity label is the opposite hedge — a mention whose kind nobody ruled, never to be read as person. Its confirmed rows come in three shapes: a person on a screen, a narrator who wrote the span as a link or a Lokalizacje: name (accepted_by.who the commit author, note authored, not reviewed, ruling H123.2), and the operator's ruling over transcript speaker prefixes; fields.proposed_by says which. Two classes have been moved into the left column by reviewers — entity-mention and external-source-form — and neither move changed the proposed count, because the first was already in the queue's arithmetic and the second carries no proposed rows. The reconciliation checks that the two sets cover every class exactly once; it cannot see a class sitting on the wrong side. So every figure here is a judgement wearing a measurement's arithmetic, and the reasons are the part to read.

The design does not rest on the count. A verdict is expressible for every registered class, which is why no correction to this table has changed how confirmation works. The partition is now a declared per-class field with a required reason rather than prose arithmetic, so a class landing on neither side is refused before a row is read — the reconciliation could not see a class that was on neither list, and two new classes would have kept every sum balanced.

So the verdict rides beside the label. It costs no class an invented negative label, it leaves every claim key alone, and it is expressible for every class at once. A consumer trains positives on verdict: affirmed and takes denied rows as its negatives, reading label for what they are negative for.

verdict and at are refused at this write path and counted over the store, the posture fields.evidence_form and locator.document already hold: the 65 rows confirmed before the key existed carry neither and keep validating, and validate.py prints them in a NO-VERDICT column. An absent verdict reads as nobody recorded one, never as an affirmation.

The evening's questions

entity-span rows at grade proposed with provenance.project 123-ner-plan/questions are the closing drawer's feed (2026-09-04): quote is the span, fields.context the sentence it sits in, fields.scope the session heading, fields.candidates the transcript's cast as {name} objects (a margin is optional and absent today - the cast is not ranked), fields.referent the one candidate when the cast is one person and undeclared otherwise. They are drawn by nerthus-lang propose and re-cut wholesale by their back-fill; a narrator's t/n in the drawer answers one through the confirm route and nothing else confirms them.

What a ruling refuses

Error When
SampleNotFound (404) no row in the store carries that id
SampleClassMismatch (422) the body named a class the row is not in
SampleNotProposed (422) the row is measured. Promoting one would delete the threshold recording how it was decided, because the contract refuses a threshold on a confirmed row — that is a store-level re-cut, not a confirmation
SampleAlreadyConfirmed (409) the row is confirmed and this ruling differs. The first acceptance is retained; an identical second ruling answers unchanged and writes nothing. This holds within one batch as well as across requests — the same id twice in one decisions[] is the first ruling landing and the second refused, never two landings reported
TooManyDecisions (400) more than 250 decisions in one batch
BadSampleLabel (422) a correction naming a label outside the vocabulary or repeating the row's own, or a label on affirm/deny
SampleClaimCollision (409) the correction would make this row assert what another row already asserts. Three classes name label in their claim key, so a correction moves the claim; two rows for one claim pass every schema check and put every count over the class one too high
UnsoundIdentityTarget (422) an identity-claim correction that ends up asserting the identity against a block the importer minted and did not vouch for. The daemon mirrors the @status half of the store's alias-soundness gate; the alias-closure half is checked by the validator alone
SampleStoreUnavailable (503) this host has no annotation tree configured

One body, both routes

Both confirmation routes answer the same shape, and the batch's job carries it as its result:

{ "applied": true, "count": 2, "confirmed": 1, "unchanged": 0, "refused": 1,
  "at": "2026-08-11T14:03:22Z",
  "items": [ { "id": "40/governance-transition/N1", "class": "governance-transition",
               "ok": true, "status": "confirmed", "label": "appoint",
               "verdict": "affirmed", "who": "gracz:2444", "at": "2026-08-11T14:03:22Z" },
             { "id": "40/governance-transition/N1", "class": "governance-transition",
               "ok": false, "error": "SampleAlreadyConfirmed", "detail": "…" } ] }

items is a list at both routes, including for a single decision — a shape that changes with the count is a shape every caller has to branch on. A refused row carries error and detail instead of status; the single-row route additionally maps that refusal onto an HTTP status, because a caller ruling on one row wants one answer.

Ruling on many rows at once

POST /v1/api/training/samples/confirmations
{ "decisions": [ { "id": "40/governance-transition/N1", "outcome": "affirm" },
                 { "id": "40/governance-transition/N2", "outcome": "deny" } ] }

Answers 202 with job, status and statusUrl; the per-row results are at GET /jobs/{id}/result, in the body above. The 202 is an envelope rather than a promise of later work: the job runs inline, so its record is already completed when the answer goes out and GET /jobs/{id} never reports running. That is also why decisions[] is capped at 250 — an uncapped batch is one request holding the accept loop — and a larger set is more requests. The 85 governance transitions project 40 deposited are one sitting's work, and the sprawozdania they were read from are formulaic and monthly, so one ruling on a representative shape covers many rows.

Per-row results, and one refusal costs its own row only. That differs from a deposit into the store, which is all-or-nothing because half a project's output is worse than none; a batch here is a set of independent human rulings, each carrying its own author, verdict and instant.

The audit trail

Ruling 5 of this surface is that every confirmation answers who confirmed what, when, and against which corpus state, and the durable record of it is the row itself: method.accepted_by carries the author and the instant, label and verdict carry the ruling, locator.content_hash pins the corpus state the locator is valid against, and the annotation repository's own history carries the sequence. Reading it back is GET /training/samples?grade=confirmed — a filter over the read route rather than a fourth endpoint.

The daemon writes one audit line per ruling beside that, op: sample.confirm, into the never-rotated audit stream (GET /logs/audit, log.read). That stream is this host's operational echo; the store is the record.

The capability, and what is wider than it

The read is log.read, and it is the price of what the rows quote rather than of the shape of the answer. Measured 2026-08-12 over the store's 10 819 rows at fc46ed1: 9 039 carry a quote, 5 535 of them quoting transcript bytes — 459 069 characters of transcript in all — and a floor of dated session headings the daemon serves under session.read. Eighteen routes over those same bytes cost log.read: every /entities/{name}/{history,lines,speech,scenes,hits,mentions,dossier, timeline} read and the five /logs/… reads. A derived read priced below the reads it derives from is a door around them, and entity.read sits in the gracz bundle while log.read does not — so the earlier price served the speaker-attribution surface to every authenticated player through one route while eighteen siblings refused it.

A caller who loses this route keeps every other one. log.read is held by Narrator, Radny, IT and admin; gracz and bot are the two bundles it is not in, and they are the two that could not reach /logs/lines either.

A new training.* resource family is the other coherent answer and is not taken here, on a mechanism rather than a preference. Role bundles come from a committed roles.json that the seeder writes only when the file is absent, so a new capability id reaches an already-adopted repository not at all until the Rada edits that table (Capabilities). Until then it resolves through admin.all alone, and a new family would ship a route almost nobody could reach. Named tokens compound it: a named token freezes its capability set at mint, so it does not see the edit until it is re-minted. Session tokens do not — they store only the role and capabilities are recomputed on every request, so the edit reaches them immediately. log.read is the smallest change that makes the price consistent, and a new family stays an ordinary addition on a route that is already correctly gated.

The two writes cost entity.write, and the choice is expedient rather than analysed. The operator ruled on 2026-08-11 that capability assignment belongs to a future project and that a route should take any fitting existing id until then, so this one takes the id the programme's only other confirmation path already spends — POST /normalization/aliases/accept. entity.write fits in the sense that matters here: its seeded population is the Radny bundle and admin.all and no other role, which is the body that rules on the books. It sits coherently beside a log.read read, because nobody reaches the confirmation path who could not already reach the rows it confirms. It has not been reviewed as an assignment, and this page does not claim it has.

The per-row tier is still the shape that finishes this

log.read prices the route at the strictest gate over the corpora its quotes come from, which is a floor and not a filter: a row quoting only lore is served at the same price as one quoting a transcript. The per-row tier the search layer applies is what turns the floor into a filter, and it is deferred work. locator.corpus and locator.source stay on every row served so that pass is a filter rather than a rebuild.

What a row carries

Key Means
id unique across the store, conventionally <project>/<class>/<slug>
class the question the row answers; the file it lives in
label one value from the class's closed vocabulary
grade confirmed (a human accepted it), measured (a stated deterministic method with a stated threshold decided it), proposed (evidence exists, nothing has ruled)
method the rule, route or human act that produced the label, plus its threshold or its acceptance. accepted_by carries who, artifact, at, verdict, and proposed_label where a ruling changed the label
provenance the depositing project, the artifact the row was cut from, the date
locator the corpus, the source, the span or line, and the content hash those offsets are valid against
quote optional, capped at 300 characters — a short extract for a human to read
fields class-specific detail
dispute optional — a second instrument disagreed, recorded rather than resolved silently

The grades never blur, and that is structural. A trained layer trains and evaluates on confirmed, treats measured as weak supervision, and reads proposed as a work queue. The store's validator refuses a measured row with no threshold, a confirmed row with no named acceptance, and a proposed row carrying either. A verdict is therefore only expressible on a confirmed row: a verdict with no human behind it is a category error the grade coupling already forbids.

Examples

Every class and grade, with the census:

GET /v1/api/training/samples?limit=1

What is awaiting a human ruling, one class at a time:

GET /v1/api/training/samples?class=governance-transition&grade=proposed&limit=20

One depositing project's rows:

GET /v1/api/training/samples?project=41-governance-epochs&limit=5

Ruling that a proposal is wrong, without claiming to know what is right:

POST /v1/api/training/samples/confirm
{ "id": "33/normalization-suggestion/alias/Harumcia", "outcome": "deny",
  "note": "to nie jest zdrobnienie od tej postaci" }

Ruling that it is right but mislabelled:

POST /v1/api/training/samples/confirm
{ "id": "33/entity-mention/Anela/01", "outcome": "correct", "label": "human" }

See also

  • EntitiesGET /normalization/suggestions, whose output the store records
  • Capabilities — the capability list and the role bundles
  • Configurationcorpora.adnotacje and the other configured working trees
  • Search — the tier split this route defers